1 /* 2 * Copyright (C) 2021 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.server.scheduling; 18 19 import android.content.BroadcastReceiver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.IntentFilter; 23 import android.provider.DeviceConfig; 24 import android.scheduling.RebootReadinessManager; 25 26 import com.android.modules.utils.BasicShellCommandHandler; 27 28 import java.io.PrintWriter; 29 import java.time.LocalDateTime; 30 import java.time.format.DateTimeFormatter; 31 import java.util.concurrent.TimeUnit; 32 33 /** 34 * Interprets and executes "adb shell cmd reboot_readiness [args]". 35 */ 36 class RebootReadinessShellCommand extends BasicShellCommandHandler { 37 38 final RebootReadinessManagerService mService; 39 final Context mContext; 40 41 // How long to perform reboot readiness checks for. 42 private long mTimeoutSecs = TimeUnit.MINUTES.toSeconds(5); 43 44 // When true, blocking app uids or subsystem identifiers may be listed. 45 private boolean mListBlocking; 46 47 // DeviceConfig properties 48 private static final String PROPERTY_ACTIVE_POLLING_INTERVAL_MS = "active_polling_interval_ms"; 49 private static final String PROPERTY_INTERACTIVITY_THRESHOLD_MS = "interactivity_threshold_ms"; 50 private static final String PROPERTY_DISABLE_INTERACTIVITY_CHECK = 51 "disable_interactivity_check"; 52 private static final String PROPERTY_DISABLE_APP_ACTIVITY_CHECK = "disable_app_activity_check"; 53 private static final String PROPERTY_DISABLE_SUBSYSTEMS_CHECK = "disable_subsystems_check"; 54 RebootReadinessShellCommand(RebootReadinessManagerService service, Context context)55 RebootReadinessShellCommand(RebootReadinessManagerService service, Context context) { 56 mService = service; 57 mContext = context; 58 } 59 60 @Override onCommand(String cmd)61 public int onCommand(String cmd) { 62 if (cmd == null) { 63 return handleDefaultCommands(cmd); 64 } 65 66 switch (cmd) { 67 case "check-interactivity-state": 68 runCheckInteractivityState(); 69 break; 70 case "check-subsystems-state": 71 runCheckSubsystemsState(); 72 break; 73 case "check-app-activity-state": 74 runCheckAppActivityState(); 75 break; 76 case "start-readiness-checks": 77 runStartReadinessChecks(); 78 break; 79 default: 80 return handleDefaultCommands(cmd); 81 } 82 return 1; 83 } 84 handleOptions()85 private void handleOptions() { 86 String arg; 87 while ((arg = getNextArg()) != null) { 88 switch (arg) { 89 case "--timeout-secs": 90 mTimeoutSecs = Long.parseLong(getNextArgRequired()); 91 break; 92 case "--list-blocking": 93 mListBlocking = true; 94 break; 95 case "--polling-interval-ms": 96 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_REBOOT_READINESS, 97 PROPERTY_ACTIVE_POLLING_INTERVAL_MS, getNextArgRequired(), false); 98 break; 99 case "--interactivity-threshold-ms": 100 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_REBOOT_READINESS, 101 PROPERTY_INTERACTIVITY_THRESHOLD_MS, getNextArgRequired(), false); 102 break; 103 case "--disable-app-activity-check": 104 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_REBOOT_READINESS, 105 PROPERTY_DISABLE_APP_ACTIVITY_CHECK, "true", false); 106 break; 107 case "--disable-subsystems-check": 108 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_REBOOT_READINESS, 109 PROPERTY_DISABLE_SUBSYSTEMS_CHECK, "true", false); 110 break; 111 case "disable-interactivity-check": 112 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_REBOOT_READINESS, 113 PROPERTY_DISABLE_INTERACTIVITY_CHECK, "true", false); 114 break; 115 default: 116 break; 117 } 118 } 119 // Allow DeviceConfig values to propagate. 120 try { 121 Thread.sleep(1000); 122 } catch (Exception ignored) { 123 } 124 } 125 126 /** 127 * Registers for reboot readiness state change broadcasts for a certain amount of time. If 128 * the state changes, it will be printed along with a timestamp. 129 */ runStartReadinessChecks()130 private void runStartReadinessChecks() { 131 handleOptions(); 132 IntentFilter filter = new IntentFilter(RebootReadinessManager.ACTION_REBOOT_READY); 133 BroadcastReceiver receiver = new BroadcastReceiver() { 134 @Override 135 public void onReceive(Context context, Intent intent) { 136 LocalDateTime dt = LocalDateTime.now(); 137 getOutPrintWriter().println("State changed to " + intent.getBooleanExtra( 138 RebootReadinessManager.EXTRA_IS_READY_TO_REBOOT, false) 139 + " at time: " + dt.format(DateTimeFormatter.ISO_LOCAL_TIME)); 140 getOutPrintWriter().flush(); 141 } 142 }; 143 try { 144 mContext.registerReceiver(receiver, filter); 145 getOutPrintWriter().println("Initial state: " + mService.isReadyToReboot()); 146 getOutPrintWriter().flush(); 147 mService.markRebootPending(mContext.getPackageName()); 148 while (mTimeoutSecs-- > 0) { 149 Thread.sleep(1000); 150 } 151 } catch (Exception ignored) { 152 } finally { 153 mService.cancelPendingReboot(mContext.getPackageName()); 154 } 155 } 156 157 /** 158 * Checks the device interactivity state. Prints false if the reboot is blocked by device 159 * interactivity, true otherwise. 160 */ runCheckInteractivityState()161 private void runCheckInteractivityState() { 162 handleOptions(); 163 getOutPrintWriter().println("Interactivity state: " + mService.checkDeviceInteractivity()); 164 } 165 166 /** 167 * Checks the subsystem reboot readiness. Prints false if the reboot is blocked by any 168 * subsystems, true otherwise. If --list-blocking is passed, the culprit subsystems 169 * will be printed. 170 */ runCheckSubsystemsState()171 private void runCheckSubsystemsState() { 172 handleOptions(); 173 getOutPrintWriter().println("Subsystem state: " + mService.checkSystemComponentsState()); 174 if (mListBlocking) { 175 mService.writeBlockingSubsystems(getOutPrintWriter()); 176 } 177 } 178 179 /** 180 * Checks the app activity reboot readiness. Prints false if the reboot is blocked by any 181 * app uids, true otherwise. If --list-blocking is passed, the culprit packages will be printed. 182 */ runCheckAppActivityState()183 private void runCheckAppActivityState() { 184 handleOptions(); 185 getOutPrintWriter().println("App activity state: " + mService.checkBackgroundAppActivity()); 186 if (mListBlocking) { 187 mService.writeBlockingUids(getOutPrintWriter()); 188 } 189 } 190 191 @Override onHelp()192 public void onHelp() { 193 final PrintWriter pw = getOutPrintWriter(); 194 pw.println("Reboot readiness (reboot_readiness) commands: "); 195 pw.println(" help: "); 196 pw.println(" Prints this help text."); 197 pw.println(" check-interactivity-state:"); 198 pw.println(" Checks interactivity state."); 199 pw.println(" check-app-activity-state [--list-blocking]:"); 200 pw.println(" Checks background app activity state. If --list-blocking is passed, a"); 201 pw.println(" list of blocking uids will be printed if any exist."); 202 pw.println(" check-subsystems-state [--list-blocking]:"); 203 pw.println(" Checks subsystems state. If --list-blocking is passed, a list of"); 204 pw.println(" blocking subsystems will be printed if any exist."); 205 pw.println(" start-readiness-checks [--timeout-secs <TIMEOUT-SECS>]:"); 206 pw.println(" Performs reboot readiness checks for either 5 minutes, or the"); 207 pw.println(" number of seconds declared by TIMEOUT-SECS. Prints the new reboot"); 208 pw.println(" readiness state along with a timestamp whenever the state changes."); 209 pw.println(); 210 pw.println("Additional flags that may be passed:"); 211 pw.println(" --polling-interval-ms <POLLING-INTERVAL-MS>:"); 212 pw.println(" How frequently the reboot readiness state is polled, in milliseconds."); 213 pw.println(" --interactivity-threshold-ms <INTERACTIVITY-THRESHOLD-MS>:"); 214 pw.println(" How long the device must not have been interacted with before"); 215 pw.println(" being deemed ready to reboot."); 216 pw.println(" --disable-interactivity-checks:"); 217 pw.println(" Disable interactivity checks."); 218 pw.println(" --disable-subsystems-check:"); 219 pw.println(" Disable subsystems checks:"); 220 pw.println(" --disable-app-activity-check:"); 221 pw.println(" Disable app activity checks."); 222 } 223 } 224