1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.car.systeminterface;
18 
19 import static com.android.car.systeminterface.SystemPowerControlHelper.SUSPEND_RESULT_SUCCESS;
20 
21 import android.car.builtin.power.PowerManagerHelper;
22 import android.car.builtin.util.Slogf;
23 import android.content.BroadcastReceiver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.os.UserManager;
28 import android.util.Log;
29 import android.util.Pair;
30 
31 import com.android.car.procfsinspector.ProcessInfo;
32 import com.android.car.procfsinspector.ProcfsInspector;
33 import com.android.internal.annotations.GuardedBy;
34 import com.android.internal.annotations.VisibleForTesting;
35 
36 import java.time.Duration;
37 import java.util.ArrayList;
38 import java.util.List;
39 import java.util.concurrent.Executors;
40 import java.util.concurrent.ScheduledExecutorService;
41 import java.util.concurrent.TimeUnit;
42 
43 /**
44  * Interface that abstracts system status (booted, sleeping, ...) operations
45  */
46 public interface SystemStateInterface {
47     static final String TAG = SystemStateInterface.class.getSimpleName();
shutdown()48     void shutdown();
49     /**
50      * Put the device into Suspend to RAM mode
51      * @return boolean true if suspend succeeded
52      */
enterDeepSleep()53     boolean enterDeepSleep();
54 
55     /**
56      * Puts the device into Suspend-to-disk (hibernation)
57      *
58      * @return boolean {@code true} if hibernation succeeded
59      */
enterHibernation()60     boolean enterHibernation();
61 
62     /**
63      * Schedules an action to run after delay after boot completed for car service user.
64      *
65      * If the boot is already completed, the action will be run after delay.
66      *
67      * @param action The action to run after boot comoplete.
68      * @param delay The delay for the action. Can be 0.
69      */
scheduleActionForBootCompleted(Runnable action, Duration delay)70     default void scheduleActionForBootCompleted(Runnable action, Duration delay) {
71         scheduleActionForBootCompleted(action, delay, /* delayRange= */ Duration.ZERO);
72     }
73 
74     /**
75      * Schedules an action to run after delay after boot completed for car service user.
76      *
77      * If the boot is already completed, the action will be run after delay. If delayRange is not
78      * 0, then we will add randomness to the delay. The delay will be randomly picked between
79      * [max(0, delay-delayRange), delay+delayRange). This is useful to prevent all the boot complete
80      * actions to be executed at the same time.
81      *
82      * @param action The action to run after boot comoplete.
83      * @param delay The delay for the action. Can be 0.
84      * @param delayRange The range for the delay. If not 0, then delay will be randomized within
85      *   the range.
86      */
scheduleActionForBootCompleted(Runnable action, Duration delay, Duration delayRange)87     void scheduleActionForBootCompleted(Runnable action, Duration delay, Duration delayRange);
88 
isWakeupCausedByTimer()89     default boolean isWakeupCausedByTimer() {
90         //TODO bug: 32061842, check wake up reason and do necessary operation information should
91         // come from kernel. it can be either power on or wake up for maintenance
92         // power on will involve GPIO trigger from power controller
93         // its own wakeup will involve timer expiration.
94         return false;
95     }
96 
97     /**
98      * Gets whether the device supports deep sleep
99      */
isSystemSupportingDeepSleep()100     default boolean isSystemSupportingDeepSleep() {
101         return true;
102     }
103 
104     /**
105      * Gets whether the device supports hibernation
106      */
isSystemSupportingHibernation()107     default boolean isSystemSupportingHibernation() {
108         return true;
109     }
110 
111     /**
112      * @deprecated see {@link ProcfsInspector}
113      */
114     @Deprecated
getRunningProcesses()115     default List<ProcessInfo> getRunningProcesses() {
116         return ProcfsInspector.readProcessTable();
117     }
118 
119     /**
120      * Default implementation that is used internally.
121      */
122     @VisibleForTesting
123     class DefaultImpl implements SystemStateInterface {
124         private static final boolean DEBUG = Slogf.isLoggable(TAG, Log.DEBUG);
125 
126         private final Object mLock = new Object();
127         @GuardedBy("mLock")
128         private ScheduledExecutorService mExecutorService;
129         @GuardedBy("mLock")
130         private List<Pair<Runnable, Duration>> mActionsList = new ArrayList<>();
131         @GuardedBy("mLock")
132         private boolean mBootCompleted;
133 
134         private final Context mContext;
135 
136         private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
137             @Override
138             public void onReceive(Context context, Intent intent) {
139                 if (!Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
140                     return;
141                 }
142                 Slogf.i(TAG, "Received ACTION_BOOT_COMPLETED intent");
143                 List<Pair<Runnable, Duration>> actionsListCopy;
144                 synchronized (mLock) {
145                     // After mBootCompleted is set, no more actions will be added to mActionsList.
146                     mBootCompleted = true;
147                     actionsListCopy = new ArrayList<>(mActionsList);
148                     mActionsList.clear();
149                 }
150                 for (int i = 0; i < actionsListCopy.size(); i++) {
151                     runActionWithDelay(actionsListCopy.get(i).first, actionsListCopy.get(i).second);
152                 }
153             }
154         };
155 
156         @SuppressWarnings("FutureReturnValueIgnored")
runActionWithDelay(Runnable action, Duration delay)157         private void runActionWithDelay(Runnable action, Duration delay) {
158             long delayInMs = delay.toMillis();
159             if (DEBUG) {
160                 Slogf.d(TAG, "Schedule bootup action after: " + delayInMs + "ms");
161             }
162             getExecutorService().schedule(action, delayInMs, TimeUnit.MILLISECONDS);
163         }
164 
165         @SuppressWarnings("FutureReturnValueIgnored")
getExecutorService()166         private ScheduledExecutorService getExecutorService() {
167             synchronized (mLock) {
168                 if (mExecutorService == null) {
169                     mExecutorService = Executors.newScheduledThreadPool(/* corePoolSize= */ 1);
170                 }
171                 return mExecutorService;
172             }
173         }
174 
175         @GuardedBy("mLock")
isBootCompletedLocked()176         private boolean isBootCompletedLocked() {
177             // There is no corresponding state for ACTION_BOOT_COMPLETED, so we use user unlock
178             // instead. Technically isUserUnlocked might be true slightly before
179             // ACTION_BOOT_COMPLETED intent is sent, however, the time difference is tiny and
180             // as long as system user is unlocked, we should be okay.
181             return mBootCompleted
182                     || mContext.getSystemService(UserManager.class).isUserUnlocked();
183         }
184 
185         @VisibleForTesting
DefaultImpl(Context context)186         public DefaultImpl(Context context) {
187             mContext = context;
188         }
189 
190         @Override
shutdown()191         public void shutdown() {
192             PowerManagerHelper.shutdown(mContext, /* confirm= */ false , /* reason= */ null,
193                     /* wait= */ true);
194         }
195 
196         @Override
enterDeepSleep()197         public boolean enterDeepSleep() {
198             // TODO(b/32061842) Set wake up time via VHAL
199 
200             boolean deviceEnteredSleep = false;
201             try {
202                 int retVal = SystemPowerControlHelper.forceDeepSleep();
203                 deviceEnteredSleep = retVal == SUSPEND_RESULT_SUCCESS;
204             } catch (Exception e) {
205                 Slogf.e(TAG, "Unable to enter deep sleep", e);
206             }
207             return deviceEnteredSleep;
208         }
209 
210         @Override
enterHibernation()211         public boolean enterHibernation() {
212             boolean deviceHibernated = false;
213             try {
214                 int retVal = SystemPowerControlHelper.forceHibernate();
215                 deviceHibernated = retVal == SUSPEND_RESULT_SUCCESS;
216             } catch (Exception e) {
217                 Slogf.e(TAG, "Unable to enter hibernation", e);
218             }
219             return deviceHibernated;
220         }
221 
222         @VisibleForTesting
getRandomizedDelay(Duration delay, Duration delayRange)223         static Duration getRandomizedDelay(Duration delay, Duration delayRange) {
224             if (delayRange == Duration.ZERO) {
225                 return delay;
226             }
227             Duration bootCompleteDelayMin = delay.minus(delayRange);
228             if (bootCompleteDelayMin.isNegative()) {
229                 bootCompleteDelayMin = Duration.ZERO;
230             }
231             Duration bootCompleteDelayMax = delay.plus(delayRange);
232             long minMillis = bootCompleteDelayMin.toMillis();
233             long maxMillis = bootCompleteDelayMax.toMillis();
234             long delayMillis = minMillis + (long) (Math.random() * (maxMillis - minMillis));
235             return Duration.ofMillis(delayMillis);
236         }
237 
238         /**
239          * Schedule an action to be run with delay when this user (system) has finished booting.
240          *
241          * If the user has already finished booting, then the action will be executed with the
242          * speicified delay.
243          */
244         @Override
scheduleActionForBootCompleted(Runnable action, Duration delay, Duration delayRange)245         public void scheduleActionForBootCompleted(Runnable action, Duration delay,
246                 Duration delayRange) {
247             Duration bootCompleteDelay = getRandomizedDelay(delay, delayRange);
248             Slogf.i(TAG, "scheduleActionForBootCompleted, delay: " + bootCompleteDelay.toMillis()
249                     + "ms");
250             boolean receiverRegistered = false;
251             synchronized (mLock) {
252                 if (isBootCompletedLocked()) {
253                     Slogf.i(TAG, "The boot is already completed");
254                     runActionWithDelay(action, bootCompleteDelay);
255                     return;
256                 }
257                 Slogf.i(TAG, "The boot is not completed yet, waiting for boot to complete");
258                 if (!mActionsList.isEmpty()) {
259                     receiverRegistered = true;
260                 }
261                 mActionsList.add(Pair.create(action, bootCompleteDelay));
262             }
263             if (!receiverRegistered) {
264                 IntentFilter intentFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
265                 Slogf.i(TAG, "register ACTION_BOOT_COMPLETED receiver");
266                 mContext.registerReceiver(mBroadcastReceiver, intentFilter,
267                         Context.RECEIVER_NOT_EXPORTED);
268             }
269         }
270 
271         @Override
isSystemSupportingDeepSleep()272         public boolean isSystemSupportingDeepSleep() {
273             return SystemPowerControlHelper.isSystemSupportingDeepSleep();
274         }
275 
276         @Override
isSystemSupportingHibernation()277         public boolean isSystemSupportingHibernation() {
278             return SystemPowerControlHelper.isSystemSupportingHibernation();
279         }
280     }
281 }
282