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