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