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 case "--disable-interactivity-check": 113 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_REBOOT_READINESS, 114 PROPERTY_DISABLE_INTERACTIVITY_CHECK, "true", false); 115 break; 116 default: 117 break; 118 } 119 } 120 // Allow DeviceConfig values to propagate. 121 try { 122 Thread.sleep(1000); 123 } catch (Exception ignored) { 124 } 125 } 126 127 /** 128 * Registers for reboot readiness state change broadcasts for a certain amount of time. If 129 * the state changes, it will be printed along with a timestamp. 130 */ runStartReadinessChecks()131 private void runStartReadinessChecks() { 132 handleOptions(); 133 IntentFilter filter = new IntentFilter(RebootReadinessManager.ACTION_REBOOT_READY); 134 BroadcastReceiver receiver = new BroadcastReceiver() { 135 @Override 136 public void onReceive(Context context, Intent intent) { 137 LocalDateTime dt = LocalDateTime.now(); 138 getOutPrintWriter().println("State changed to " + intent.getBooleanExtra( 139 RebootReadinessManager.EXTRA_IS_READY_TO_REBOOT, false) 140 + " at time: " + dt.format(DateTimeFormatter.ISO_LOCAL_TIME)); 141 getOutPrintWriter().flush(); 142 } 143 }; 144 try { 145 mContext.registerReceiver(receiver, filter); 146 getOutPrintWriter().println("Initial state: " + mService.isReadyToReboot()); 147 getOutPrintWriter().flush(); 148 mService.markRebootPending(mContext.getPackageName()); 149 while (mTimeoutSecs-- > 0) { 150 Thread.sleep(1000); 151 } 152 } catch (Exception ignored) { 153 } finally { 154 mService.cancelPendingReboot(mContext.getPackageName()); 155 } 156 } 157 158 /** 159 * Checks the device interactivity state. Prints false if the reboot is blocked by device 160 * interactivity, true otherwise. 161 */ runCheckInteractivityState()162 private void runCheckInteractivityState() { 163 handleOptions(); 164 getOutPrintWriter().println("Interactivity state: " + mService.checkDeviceInteractivity()); 165 } 166 167 /** 168 * Checks the subsystem reboot readiness. Prints false if the reboot is blocked by any 169 * subsystems, true otherwise. If --list-blocking is passed, the culprit subsystems 170 * will be printed. 171 */ runCheckSubsystemsState()172 private void runCheckSubsystemsState() { 173 handleOptions(); 174 getOutPrintWriter().println("Subsystem state: " + mService.checkSystemComponentsState()); 175 if (mListBlocking) { 176 mService.writeBlockingSubsystems(getOutPrintWriter()); 177 } 178 } 179 180 /** 181 * Checks the app activity reboot readiness. Prints false if the reboot is blocked by any 182 * app uids, true otherwise. If --list-blocking is passed, the culprit packages will be printed. 183 */ runCheckAppActivityState()184 private void runCheckAppActivityState() { 185 handleOptions(); 186 getOutPrintWriter().println("App activity state: " + mService.checkBackgroundAppActivity()); 187 if (mListBlocking) { 188 mService.writeBlockingUids(getOutPrintWriter()); 189 } 190 } 191 192 @Override onHelp()193 public void onHelp() { 194 final PrintWriter pw = getOutPrintWriter(); 195 pw.println("Reboot readiness (reboot_readiness) commands: "); 196 pw.println(" help: "); 197 pw.println(" Prints this help text."); 198 pw.println(" check-interactivity-state:"); 199 pw.println(" Checks interactivity state."); 200 pw.println(" check-app-activity-state [--list-blocking]:"); 201 pw.println(" Checks background app activity state. If --list-blocking is passed, a"); 202 pw.println(" list of blocking uids will be printed if any exist."); 203 pw.println(" check-subsystems-state [--list-blocking]:"); 204 pw.println(" Checks subsystems state. If --list-blocking is passed, a list of"); 205 pw.println(" blocking subsystems will be printed if any exist."); 206 pw.println(" start-readiness-checks [--timeout-secs <TIMEOUT-SECS>]:"); 207 pw.println(" Performs reboot readiness checks for either 5 minutes, or the"); 208 pw.println(" number of seconds declared by TIMEOUT-SECS. Prints the new reboot"); 209 pw.println(" readiness state along with a timestamp whenever the state changes."); 210 pw.println(); 211 pw.println("Additional flags that may be passed:"); 212 pw.println(" --polling-interval-ms <POLLING-INTERVAL-MS>:"); 213 pw.println(" How frequently the reboot readiness state is polled, in milliseconds."); 214 pw.println(" --interactivity-threshold-ms <INTERACTIVITY-THRESHOLD-MS>:"); 215 pw.println(" How long the device must not have been interacted with before"); 216 pw.println(" being deemed ready to reboot."); 217 pw.println(" --disable-interactivity-check / disable-interactivity-check:"); 218 pw.println(" Disable interactivity checks."); 219 pw.println(" --disable-subsystems-check:"); 220 pw.println(" Disable subsystems checks:"); 221 pw.println(" --disable-app-activity-check:"); 222 pw.println(" Disable app activity checks."); 223 } 224 } 225