• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_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     /** Suspend is successful. */
48     int SUSPEND_RESULT_SUCCESS = 1;
49     /** Suspend attempt fails, and could be successful with retry. */
50     int SUSPEND_RESULT_RETRY = 2;
51     /** Suspend fails due to kernel abort. */
52     int SUSPEND_RESULT_ABORT = 3;
53 
54     static final String TAG = SystemStateInterface.class.getSimpleName();
55 
shutdown()56     void shutdown();
57     /**
58      * Put the device into Suspend to RAM mode
59      *
60      * @return suspend result
61      */
enterDeepSleep()62     int enterDeepSleep();
63 
64     /**
65      * Puts the device into Suspend-to-disk (hibernation)
66      *
67      * @return suspend result
68      */
enterHibernation()69     int enterHibernation();
70 
71     /**
72      * Schedules an action to run after delay after boot completed for car service user.
73      *
74      * If the boot is already completed, the action will be run after delay.
75      *
76      * @param action The action to run after boot comoplete.
77      * @param delay The delay for the action. Can be 0.
78      */
scheduleActionForBootCompleted(Runnable action, Duration delay)79     default void scheduleActionForBootCompleted(Runnable action, Duration delay) {
80         scheduleActionForBootCompleted(action, delay, /* delayRange= */ Duration.ZERO);
81     }
82 
83     /**
84      * Schedules an action to run after delay after boot completed for car service user.
85      *
86      * If the boot is already completed, the action will be run after delay. If delayRange is not
87      * 0, then we will add randomness to the delay. The delay will be randomly picked between
88      * [max(0, delay-delayRange), delay+delayRange). This is useful to prevent all the boot complete
89      * actions to be executed at the same time.
90      *
91      * @param action The action to run after boot comoplete.
92      * @param delay The delay for the action. Can be 0.
93      * @param delayRange The range for the delay. If not 0, then delay will be randomized within
94      *   the range.
95      */
scheduleActionForBootCompleted(Runnable action, Duration delay, Duration delayRange)96     void scheduleActionForBootCompleted(Runnable action, Duration delay, Duration delayRange);
97 
isWakeupCausedByError()98     default boolean isWakeupCausedByError() {
99         return false;
100     }
101 
isWakeupCausedByTimer()102     default boolean isWakeupCausedByTimer() {
103         //TODO bug: 32061842, check wake up reason and do necessary operation information should
104         // come from kernel. it can be either power on or wake up for maintenance
105         // power on will involve GPIO trigger from power controller
106         // its own wakeup will involve timer expiration.
107         return false;
108     }
109 
110     /**
111      * Gets whether the device supports deep sleep
112      */
isSystemSupportingDeepSleep()113     default boolean isSystemSupportingDeepSleep() {
114         return true;
115     }
116 
117     /**
118      * Gets whether the device supports hibernation
119      */
isSystemSupportingHibernation()120     default boolean isSystemSupportingHibernation() {
121         return true;
122     }
123 
124     /**
125      * @deprecated see {@link ProcfsInspector}
126      */
127     @Deprecated
getRunningProcesses()128     default List<ProcessInfo> getRunningProcesses() {
129         return ProcfsInspector.readProcessTable();
130     }
131 
132     /**
133      * Default implementation that is used internally.
134      */
135     @VisibleForTesting
136     class DefaultImpl implements SystemStateInterface {
137         private static final boolean DEBUG = Slogf.isLoggable(TAG, Log.DEBUG);
138         private static final String RESUME_REASON_ABORT = "Abort";
139 
140         private final Object mLock = new Object();
141         @GuardedBy("mLock")
142         private ScheduledExecutorService mExecutorService;
143         @GuardedBy("mLock")
144         private List<Pair<Runnable, Duration>> mActionsList = new ArrayList<>();
145         @GuardedBy("mLock")
146         private boolean mBootCompleted;
147 
148         private final Context mContext;
149 
150         private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
151             @Override
152             public void onReceive(Context context, Intent intent) {
153                 if (!Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
154                     return;
155                 }
156                 Slogf.i(TAG, "Received ACTION_BOOT_COMPLETED intent");
157                 List<Pair<Runnable, Duration>> actionsListCopy;
158                 synchronized (mLock) {
159                     // After mBootCompleted is set, no more actions will be added to mActionsList.
160                     mBootCompleted = true;
161                     actionsListCopy = new ArrayList<>(mActionsList);
162                     mActionsList.clear();
163                 }
164                 for (int i = 0; i < actionsListCopy.size(); i++) {
165                     runActionWithDelay(actionsListCopy.get(i).first, actionsListCopy.get(i).second);
166                 }
167             }
168         };
169 
170         @SuppressWarnings("FutureReturnValueIgnored")
runActionWithDelay(Runnable action, Duration delay)171         private void runActionWithDelay(Runnable action, Duration delay) {
172             long delayInMs = delay.toMillis();
173             if (DEBUG) {
174                 Slogf.d(TAG, "Schedule bootup action after: " + delayInMs + "ms");
175             }
176             getExecutorService().schedule(action, delayInMs, TimeUnit.MILLISECONDS);
177         }
178 
179         @SuppressWarnings("FutureReturnValueIgnored")
getExecutorService()180         private ScheduledExecutorService getExecutorService() {
181             synchronized (mLock) {
182                 if (mExecutorService == null) {
183                     mExecutorService = Executors.newScheduledThreadPool(/* corePoolSize= */ 1);
184                 }
185                 return mExecutorService;
186             }
187         }
188 
189         @GuardedBy("mLock")
isBootCompletedLocked()190         private boolean isBootCompletedLocked() {
191             // There is no corresponding state for ACTION_BOOT_COMPLETED, so we use user unlock
192             // instead. Technically isUserUnlocked might be true slightly before
193             // ACTION_BOOT_COMPLETED intent is sent, however, the time difference is tiny and
194             // as long as system user is unlocked, we should be okay.
195             return mBootCompleted
196                     || mContext.getSystemService(UserManager.class).isUserUnlocked();
197         }
198 
199         @VisibleForTesting
DefaultImpl(Context context)200         public DefaultImpl(Context context) {
201             mContext = context;
202         }
203 
204         @Override
shutdown()205         public void shutdown() {
206             PowerManagerHelper.shutdown(mContext, /* confirm= */ false , /* reason= */ null,
207                     /* wait= */ true);
208         }
209 
210         @Override
enterDeepSleep()211         public int enterDeepSleep() {
212             // TODO(b/32061842) Set wake up time via VHAL
213 
214             int suspendResult = SUSPEND_RESULT_RETRY;
215             try {
216                 int retVal = SystemPowerControlHelper.forceDeepSleep();
217                 if (retVal == SUSPEND_SUCCESS) {
218                     suspendResult = SUSPEND_RESULT_SUCCESS;
219                 }
220             } catch (Exception e) {
221                 Slogf.e(TAG, "Unable to enter deep sleep", e);
222             }
223             return suspendResult;
224         }
225 
226         @Override
enterHibernation()227         public int enterHibernation() {
228             int suspendResult = SUSPEND_RESULT_RETRY;
229             try {
230                 int retVal = SystemPowerControlHelper.forceHibernate();
231                 if (retVal == SUSPEND_SUCCESS) {
232                     suspendResult = isWakeupCausedByError() ? SUSPEND_RESULT_ABORT
233                             : SUSPEND_RESULT_SUCCESS;
234                 }
235             } catch (Exception e) {
236                 Slogf.e(TAG, "Unable to enter hibernation", e);
237             }
238             return suspendResult;
239         }
240 
241         @VisibleForTesting
getRandomizedDelay(Duration delay, Duration delayRange)242         static Duration getRandomizedDelay(Duration delay, Duration delayRange) {
243             if (delayRange == Duration.ZERO) {
244                 return delay;
245             }
246             Duration bootCompleteDelayMin = delay.minus(delayRange);
247             if (bootCompleteDelayMin.isNegative()) {
248                 bootCompleteDelayMin = Duration.ZERO;
249             }
250             Duration bootCompleteDelayMax = delay.plus(delayRange);
251             long minMillis = bootCompleteDelayMin.toMillis();
252             long maxMillis = bootCompleteDelayMax.toMillis();
253             long delayMillis = minMillis + (long) (Math.random() * (maxMillis - minMillis));
254             return Duration.ofMillis(delayMillis);
255         }
256 
257         /**
258          * Schedule an action to be run with delay when this user (system) has finished booting.
259          *
260          * If the user has already finished booting, then the action will be executed with the
261          * speicified delay.
262          */
263         @Override
scheduleActionForBootCompleted(Runnable action, Duration delay, Duration delayRange)264         public void scheduleActionForBootCompleted(Runnable action, Duration delay,
265                 Duration delayRange) {
266             Duration bootCompleteDelay = getRandomizedDelay(delay, delayRange);
267             Slogf.i(TAG, "scheduleActionForBootCompleted, delay: " + bootCompleteDelay.toMillis()
268                     + "ms");
269             boolean receiverRegistered = false;
270             synchronized (mLock) {
271                 if (isBootCompletedLocked()) {
272                     Slogf.i(TAG, "The boot is already completed");
273                     runActionWithDelay(action, bootCompleteDelay);
274                     return;
275                 }
276                 Slogf.i(TAG, "The boot is not completed yet, waiting for boot to complete");
277                 if (!mActionsList.isEmpty()) {
278                     receiverRegistered = true;
279                 }
280                 mActionsList.add(Pair.create(action, bootCompleteDelay));
281             }
282             if (!receiverRegistered) {
283                 IntentFilter intentFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
284                 Slogf.i(TAG, "register ACTION_BOOT_COMPLETED receiver");
285                 mContext.registerReceiver(mBroadcastReceiver, intentFilter,
286                         Context.RECEIVER_NOT_EXPORTED);
287             }
288         }
289 
290         @Override
isWakeupCausedByError()291         public boolean isWakeupCausedByError() {
292             String[] tokens = SystemPowerControlHelper.readLastResumeReason().split(":");
293             if (tokens.length == 0) {
294                 return false;
295             }
296             boolean wakeupByError = tokens[0].compareToIgnoreCase(RESUME_REASON_ABORT) == 0;
297             if (wakeupByError && tokens.length == 2) {
298                 Slogf.i(TAG, "Wake up due to kernel abort: %s", tokens[1]);
299             }
300             return wakeupByError;
301         }
302 
303         @Override
isSystemSupportingDeepSleep()304         public boolean isSystemSupportingDeepSleep() {
305             return SystemPowerControlHelper.isSystemSupportingDeepSleep();
306         }
307 
308         @Override
isSystemSupportingHibernation()309         public boolean isSystemSupportingHibernation() {
310             return SystemPowerControlHelper.isSystemSupportingHibernation();
311         }
312     }
313 }
314