package com.android.server.power;
19
20 import android.app.ActivityManagerInternal;
21 import android.app.AlertDialog;
22 import android.app.BroadcastOptions;
23 import android.app.Dialog;
24 import android.app.IActivityManager;
25 import android.app.ProgressDialog;
26 import android.app.admin.SecurityLog;
27 import android.content.BroadcastReceiver;
28 import android.content.Context;
29 import android.content.DialogInterface;
30 import android.content.IIntentReceiver;
31 import android.content.Intent;
32 import android.content.IntentFilter;
33 import android.content.pm.PackageManagerInternal;
34 import android.content.pm.ActivityInfo;
35 import android.os.Bundle;
36 import android.os.FileUtils;
37 import android.os.Handler;
38 import android.os.PowerManager;
39 import android.os.RecoverySystem;
40 import android.os.RemoteException;
41 import android.os.ServiceManager;
42 import android.os.SystemClock;
43 import android.os.SystemProperties;
44 import android.os.SystemVibrator;
45 import android.os.Trace;
46 import android.os.UserHandle;
47 import android.os.UserManager;
48 import android.os.VibrationAttributes;
49 import android.os.VibrationEffect;
50 import android.os.Vibrator;
51 import android.os.vibrator.persistence.VibrationXmlParser;
52 import android.telephony.TelephonyManager;
53 import android.text.TextUtils;
54 import android.util.ArrayMap;
55 import android.util.Log;
56 import android.util.Slog;
57 import android.util.TimingsTraceLog;
58 import android.view.SurfaceControl;
59 import android.view.WindowManager;
60 import android.view.IWindowManager;
61 import android.view.Surface;
62 import com.android.server.pm.OplusDLCUtils;
63
64 //#ifdef OPLUS_FEATURE_SHUTDOWN_DETECT
65 //ODM_WT.AD.Framework ZhangFei@BSP.KERNEL.STABILITY, 2020/04/17, Add for shutdown detect.
66 import java.io.FileWriter;
67 import libcore.io.IoUtils;
68 //#endif /* OPLUS_FEATURE_SHUTDOWN_DETECT */
69
70 import com.android.internal.annotations.VisibleForTesting;
71 import com.android.server.LocalServices;
72 import com.android.server.RescueParty;
73 import com.android.server.statusbar.StatusBarManagerInternal;
74
75 import java.io.File;
76 import java.io.FileOutputStream;
77 import java.io.FileReader;
78 import java.io.IOException;
79 import java.nio.charset.StandardCharsets;
80
81 public final class ShutdownThread extends Thread {
82 // constants
83 private static final boolean DEBUG = true;
84 private static final String TAG = "ShutdownThread";
85 private static final int ACTION_DONE_POLL_WAIT_MS = 500;
86 private static final int RADIOS_STATE_POLL_SLEEP_MS = 100;
87 // maximum time we wait for the shutdown broadcast before going on.
88 private static final int MAX_BROADCAST_TIME = 10 * 1000;
89 private static final int MAX_CHECK_POINTS_DUMP_WAIT_TIME = 10 * 1000;
90 private static final int MAX_RADIO_WAIT_TIME = 12 * 1000;
91 private static final int MAX_UNCRYPT_WAIT_TIME = 15 * 60 * 1000;
92 // constants for progress bar. the values are roughly estimated based on timeout.
93 private static final int BROADCAST_STOP_PERCENT = 2;
94 private static final int ACTIVITY_MANAGER_STOP_PERCENT = 4;
95 private static final int PACKAGE_MANAGER_STOP_PERCENT = 6;
96 private static final int RADIO_STOP_PERCENT = 18;
97 private static final int MOUNT_SERVICE_STOP_PERCENT = 20;
98 // Time we should wait for vendor subsystem shutdown
99 // Sleep times(ms) between checks of the vendor subsystem state
100 private static final int VENDOR_SUBSYS_STATE_CHECK_INTERVAL_MS = 100;
101 // Max time we wait for vendor subsystems to shut down before resuming
102 // with full system shutdown
103 private static final int VENDOR_SUBSYS_MAX_WAIT_MS = 10000;
104
105 // length of vibration before shutting down
106 @VisibleForTesting static final int DEFAULT_SHUTDOWN_VIBRATE_MS = 500;
107
108 // state tracking
109 private static final Object sIsStartedGuard = new Object();
110 private static boolean sIsStarted = false;
111
112 private static boolean mReboot;
113 private static boolean mRebootSafeMode;
114 private static boolean mRebootHasProgressBar;
115 private static String mReason;
116
117 // Provides shutdown assurance in case the system_server is killed
118 public static final String SHUTDOWN_ACTION_PROPERTY = "sys.shutdown.requested";
119
120 // Indicates whether we are rebooting into safe mode
121 public static final String REBOOT_SAFEMODE_PROPERTY = "persist.sys.safemode";
122 public static final String RO_SAFEMODE_PROPERTY = "ro.sys.safemode";
123
124 // static instance of this thread
125 private static final ShutdownThread sInstance = new ShutdownThread();
126
127 // Metrics that will be reported to tron after reboot
128 private static final ArrayMap<String, Long> TRON_METRICS = new ArrayMap<>();
129
130 // File to use for saving shutdown metrics
131 private static final String METRICS_FILE_BASENAME = "/data/system/shutdown-metrics";
132 // File to use for saving shutdown check points
133 private static final String CHECK_POINTS_FILE_BASENAME =
134 "/data/system/shutdown-checkpoints/checkpoints";
135
136 private static final int MIN_SHUTDOWN_ANIMATION_PLAY_TIME = 5*1000;
137 private static long beginAnimationTime = 0;
138 private static long endAnimationTime = 0;
139
140 // Metrics names to be persisted in shutdown-metrics file
141 private static String METRIC_SYSTEM_SERVER = "shutdown_system_server";
142 private static String METRIC_SEND_BROADCAST = "shutdown_send_shutdown_broadcast";
143 private static String METRIC_AM = "shutdown_activity_manager";
144 private static String METRIC_PM = "shutdown_package_manager";
145 private static String METRIC_RADIOS = "shutdown_radios";
146 private static String METRIC_RADIO = "shutdown_radio";
147 private static String METRIC_SHUTDOWN_TIME_START = "begin_shutdown";
148
149 private final Injector mInjector;
150
151 private final Object mActionDoneSync = new Object();
152 private boolean mActionDone;
153 private Context mContext;
154 private PowerManager mPowerManager;
155 private PowerManager.WakeLock mCpuWakeLock;
156 private PowerManager.WakeLock mScreenWakeLock;
157 private Handler mHandler;
158
159 private static AlertDialog sConfirmDialog;
160 private ProgressDialog mProgressDialog;
161
162 private static final String PROP_IS_MONKEYKING_RUNNING = "oplus.autotest.monkeyRunning";
163 private static final String PROP_IS_MONKEY_RUNNING = "oppo.autotest.monkeyRunning";
164 private static final String PROP_IS_AGINGVERSION = "ro.build.aging_version";
165
166 private ShutdownThread() {
167 this(new Injector());
168 }
169
170 @VisibleForTesting
171 ShutdownThread(Injector injector) {
172 mInjector = injector;
173 }
174
175 /**
176 * Request a clean shutdown, waiting for subsystems to clean up their
177 * state etc. Must be called from a Looper thread in which its UI
178 * is shown.
179 *
180 * @param context Context used to display the shutdown progress dialog. This must be a context
181 * suitable for displaying UI (aka Themable).
182 * @param reason code to pass to android_reboot() (e.g. "userrequested"), or null.
183 * @param confirm true if user confirmation is needed before shutting down.
184 */
185 public static void shutdown(final Context context, String reason, boolean confirm) {
186 mReboot = false;
187 mRebootSafeMode = false;
188 mReason = reason;
189 shutdownInner(context, confirm);
190 }
191
192 private static void shutdownInner(final Context context, boolean confirm) {
193 // ShutdownThread is called from many places, so best to verify here that the context passed
194 // in is themed.
195 context.assertRuntimeOverlayThemable();
196
197 // ensure that only one thread is trying to power down.
198 // any additional calls are just returned
199 synchronized (sIsStartedGuard) {
200 if (sIsStarted) {
201 if (DEBUG) {
202 Log.d(TAG, "Request to shutdown already running, returning.");
203 }
204 return;
205 }
206 }
207
208 // Add checkpoint for this shutdown attempt. The user might still cancel the dialog, but
209 // this point preserves the system trace of the trigger point of the ShutdownThread.
210 ShutdownCheckPoints.recordCheckPoint(/* reason= */ null);
211
212 //#ifdef ODM_WT_EDIT Gaohan.Wang@ODM_WT.AD, 2023/12/6 Add for Monkey call emergency, rebootPhone.......
213 if (isAgingTestNotAllowShutdown(mReason)) {
214 return;
215 }
216 //#endif ODM_WT_EDIT
217
218 final int longPressBehavior = context.getResources().getInteger(
219 com.android.internal.R.integer.config_longPressOnPowerBehavior);
220 final int resourceId = mRebootSafeMode
221 ? com.android.internal.R.string.reboot_safemode_confirm
222 : (longPressBehavior == 2
223 ? com.android.internal.R.string.shutdown_confirm_question
224 : com.android.internal.R.string.shutdown_confirm);
225
226 if (DEBUG) {
227 Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
228 }
229
230 if (confirm) {
231 final CloseDialogReceiver closer = new CloseDialogReceiver(context);
232 if (sConfirmDialog != null) {
233 sConfirmDialog.dismiss();
234 }
235 sConfirmDialog = new AlertDialog.Builder(context)
236 .setTitle(mRebootSafeMode
237 ? com.android.internal.R.string.reboot_safemode_title
238 : com.android.internal.R.string.power_off)
239 .setMessage(resourceId)
240 .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
241 public void onClick(DialogInterface dialog, int which) {
242 beginShutdownSequence(context);
243 }
244 })
245 .setNegativeButton(com.android.internal.R.string.no, null)
246 .create();
247 closer.dialog = sConfirmDialog;
248 sConfirmDialog.setOnDismissListener(closer);
249 sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
250 sConfirmDialog.show();
251 } else {
252 beginShutdownSequence(context);
253 }
254 }
255
256 public static boolean isAgingTestNotAllowShutdown(String reason) {
257 boolean isAgingTest = "yes".equals(SystemProperties.get(PROP_IS_AGINGVERSION));
258 boolean isMonkeyRunning = SystemProperties.getBoolean(PROP_IS_MONKEY_RUNNING, false);
259 boolean isMonkeyKingRunning = SystemProperties.getBoolean(PROP_IS_MONKEYKING_RUNNING, false);
260 Slog.d(TAG, " reason=" + reason
261 + ", isAgingTest=" + isAgingTest
262 + ", isMonkeyRunning=" + isMonkeyRunning
263 + ", isMonkeyKingRunning=" + isMonkeyKingRunning);
264 if (isAgingTest && (isMonkeyRunning || isMonkeyKingRunning)
265 && !PowerManager.SHUTDOWN_LOW_BATTERY.equals(reason)) {
266 Slog.d(TAG, "AgingTestNotAllowShutdown");
267 return true;
268 }
269 if (isAgingTest && "silent".equals(reason)) {
270 Slog.d(TAG, "AgingTestNotAllowShutdown silence reboot");
271 return true;
272 }
273 return false;
274 }
275
276 private static class CloseDialogReceiver extends BroadcastReceiver
277 implements DialogInterface.OnDismissListener {
278 private Context mContext;
279 public Dialog dialog;
280
281 CloseDialogReceiver(Context context) {
282 mContext = context;
283 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
284 context.registerReceiver(this, filter, Context.RECEIVER_EXPORTED);
285 }
286
287 @Override
288 public void onReceive(Context context, Intent intent) {
289 dialog.cancel();
290 }
291
292 public void onDismiss(DialogInterface unused) {
293 mContext.unregisterReceiver(this);
294 }
295 }
296
297 /**
298 * Request a clean shutdown, waiting for subsystems to clean up their
299 * state etc. Must be called from a Looper thread in which its UI
300 * is shown.
301 *
302 * @param context Context used to display the shutdown progress dialog. This must be a context
303 * suitable for displaying UI (aka Themable).
304 * @param reason code to pass to the kernel (e.g. "recovery"), or null.
305 * @param confirm true if user confirmation is needed before shutting down.
306 */
307 public static void reboot(final Context context, String reason, boolean confirm) {
308 mReboot = true;
309 mRebootSafeMode = false;
310 mRebootHasProgressBar = false;
311 mReason = reason;
312 shutdownInner(context, confirm);
313 }
314
315 /**
316 * Request a reboot into safe mode. Must be called from a Looper thread in which its UI
317 * is shown.
318 *
319 * @param context Context used to display the shutdown progress dialog. This must be a context
320 * suitable for displaying UI (aka Themable).
321 * @param confirm true if user confirmation is needed before shutting down.
322 */
323 public static void rebootSafeMode(final Context context, boolean confirm) {
324 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
325 if (um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
326 return;
327 }
328
329 // #ifdef dongzhenbo@ANDROID.FRAMEWORK, modified for India DLC
330 if (!isDeviceProvisioned(context) && OplusDLCUtils.isDLCVersion()) {
331 return;
332 }
333 // endif
334
335 mReboot = true;
336 mRebootSafeMode = true;
337 mRebootHasProgressBar = false;
338 mReason = null;
339 shutdownInner(context, confirm);
340 }
341
342 // #ifdef dongzhenbo@ANDROID.FRAMEWORK, modified for India DLC
343 private static boolean isDeviceProvisioned(Context context) {
344 return android.provider.Settings.Global.getInt(context.getContentResolver(),
345 android.provider.Settings.Global.DEVICE_PROVISIONED, 0) != 0;
346 }
347 // endif
348
349 private static ProgressDialog showShutdownDialog(Context context) {
350 // Throw up a system dialog to indicate the device is rebooting / shutting down.
351 ProgressDialog pd = new ProgressDialog(context);
352
353 // Path 1: Reboot to recovery for update
354 // Condition: mReason startswith REBOOT_RECOVERY_UPDATE
355 //
356 // Path 1a: uncrypt needed
357 // Condition: if /cache/recovery/uncrypt_file exists but
358 // /cache/recovery/block.map doesn't.
359 // UI: determinate progress bar (mRebootHasProgressBar == True)
360 //
361 // * Path 1a is expected to be removed once the GmsCore shipped on
362 // device always calls uncrypt prior to reboot.
363 //
364 // Path 1b: uncrypt already done
365 // UI: spinning circle only (no progress bar)
366 //
367 // Path 2: Reboot to recovery for factory reset
368 // Condition: mReason == REBOOT_RECOVERY
369 // UI: spinning circle only (no progress bar)
370 //
371 // Path 3: Regular reboot / shutdown
372 // Condition: Otherwise
373 // UI: spinning circle only (no progress bar)
374
375 // mReason could be "recovery-update" or "recovery-update,quiescent".
376 if (mReason != null && mReason.startsWith(PowerManager.REBOOT_RECOVERY_UPDATE)) {
377 // We need the progress bar if uncrypt will be invoked during the
378 // reboot, which might be time-consuming.
379 mRebootHasProgressBar = RecoverySystem.UNCRYPT_PACKAGE_FILE.exists()
380 && !(RecoverySystem.BLOCK_MAP_FILE.exists());
381 pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title));
382 if (mRebootHasProgressBar) {
383 pd.setMax(100);
384 pd.setProgress(0);
385 pd.setIndeterminate(false);
386 boolean showPercent = context.getResources().getBoolean(
387 com.android.internal.R.bool.config_showPercentageTextDuringRebootToUpdate);
388 if (!showPercent) {
389 pd.setProgressPercentFormat(null);
390 }
391 pd.setProgressNumberFormat(null);
392 pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
393 pd.setMessage(context.getText(
394 com.android.internal.R.string.reboot_to_update_prepare));
395 } else {
396 if (showSysuiReboot()) {
397 return null;
398 }
399 pd.setIndeterminate(true);
400 pd.setMessage(context.getText(
401 com.android.internal.R.string.reboot_to_update_reboot));
402 }
403 } else if (mReason != null && mReason.equals(PowerManager.REBOOT_RECOVERY)) {
404 if (RescueParty.isRecoveryTriggeredReboot()) {
405 // We're not actually doing a factory reset yet; we're rebooting
406 // to ask the user if they'd like to reset, so give them a less
407 // scary dialog message.
408 pd.setTitle(context.getText(com.android.internal.R.string.power_off));
409 pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
410 pd.setIndeterminate(true);
411 } else if (showSysuiReboot()) {
412 return null;
413 } else {
414 // Factory reset path. Set the dialog message accordingly.
415 pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title));
416 pd.setMessage(context.getText(
417 com.android.internal.R.string.reboot_to_reset_message));
418 pd.setIndeterminate(true);
419 }
420 } else {
421 if (showSysuiReboot()) {
422 return null;
423 }
424 pd.setTitle(context.getText(com.android.internal.R.string.power_off));
425 pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
426 pd.setIndeterminate(true);
427 }
428 pd.setCancelable(false);
429 pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
430 if(isPreVersion()){
431 pd.show();
432 }
433 return pd;
434 }
435
436 private static void showShutdownAnimation() {
437 Log.w(TAG, "showShutdownAnimation");
438 SystemProperties.set("service.bootanim.exit", "0");
439 SystemProperties.set("ctl.start", "banim_shut");
440 }
441
442 private static boolean isPreVersion(){
443 return "true".equals(SystemProperties.get("ro.build.oplus.pre_version"));
444 }
445
446 private static void delayForPlayAnimation() {
447 Log.i(TAG,"set service.shutanim.running to 1");
448 if(beginAnimationTime <= 0){
449 return;
450 }
451 endAnimationTime = beginAnimationTime-SystemClock.elapsedRealtime();
452 if(endAnimationTime > 0){
453 try {
454 Log.w(TAG, "sleep delayForPlayAnimation");
455 Thread.currentThread().sleep(1500);
456 } catch (InterruptedException e) {
457 Log.e(TAG, "Shutdown stop bootanimation Thread.currentThread().sleep exception!");
458 }
459 }
460 }
461
462 private static boolean showSysuiReboot() {
463 if (DEBUG) {
464 Log.d(TAG, "Attempting to use SysUI shutdown UI");
465 }
466 try {
467 StatusBarManagerInternal service = LocalServices.getService(
468 StatusBarManagerInternal.class);
469 if (service.showShutdownUi(mReboot, mReason)) {
470 // Sysui will handle shutdown UI.
471 if (DEBUG) {
472 Log.d(TAG, "SysUI handling shutdown UI");
473 }
474 return true;
475 }
476 } catch (Exception e) {
477 // If anything went wrong, ignore it and use fallback ui
478 }
479 if (DEBUG) {
480 Log.d(TAG, "SysUI is unavailable");
481 }
482 return false;
483 }
484
485 private static void beginShutdownSequence(Context context) {
486 synchronized (sIsStartedGuard) {
487 if (sIsStarted) {
488 if (DEBUG) {
489 Log.d(TAG, "Shutdown sequence already running, returning.");
490 }
491 return;
492 }
493 sIsStarted = true;
494 }
495 if(isPreVersion()){
496 sInstance.mProgressDialog = showShutdownDialog(context);
497 } else {
498 beginAnimationTime = SystemClock.elapsedRealtime()+MIN_SHUTDOWN_ANIMATION_PLAY_TIME;
499 showShutdownAnimation();
500 }
501
502 sInstance.mContext = context;
503 sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
504
505 // make sure we never fall asleep again
506 sInstance.mCpuWakeLock = null;
507 try {
508 sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
509 PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");
510 sInstance.mCpuWakeLock.setReferenceCounted(false);
511 sInstance.mCpuWakeLock.acquire();
512 } catch (SecurityException e) {
513 Log.w(TAG, "No permission to acquire wake lock", e);
514 sInstance.mCpuWakeLock = null;
515 }
516
517 // also make sure the screen stays on for better user experience
518 sInstance.mScreenWakeLock = null;
519 if (sInstance.mPowerManager.isScreenOn()) {
520 try {
521 sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
522 PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
523 sInstance.mScreenWakeLock.setReferenceCounted(false);
524 sInstance.mScreenWakeLock.acquire();
525 } catch (SecurityException e) {
526 Log.w(TAG, "No permission to acquire wake lock", e);
527 sInstance.mScreenWakeLock = null;
528 }
529 }
530
531 if (SecurityLog.isLoggingEnabled()) {
532 SecurityLog.writeEvent(SecurityLog.TAG_OS_SHUTDOWN);
533 }
534 delayForPlayAnimation();
535
536 // start the thread that initiates shutdown
537 sInstance.mHandler = new Handler() {
538 };
539 sInstance.start();
540 }
541
542 void actionDone() {
543 synchronized (mActionDoneSync) {
544 mActionDone = true;
545 mActionDoneSync.notifyAll();
546 }
547 }
548
549 //#ifdef OPLUS_FEATURE_SHUTDOWN_DETECT
550 //ODM_WT.AD.Framework ZhangFei@BSP.KERNEL.STABILITY, 2020/04/17, Add for shutdown detect.
551 /**
552 * @return void doShutdownDetect
553 */
554 private static void doShutdownDetect(String count) {
555 File procFile = null;
556 FileWriter shutdown_detect = null;
557 try {
558 Log.e(TAG, "doShutdownDetect " + count);
559 procFile = new File("/proc/shutdown_detect");
560 shutdown_detect = new FileWriter(procFile);
561 shutdown_detect.write(count);
562 } catch (java.io.IOException e) {
563 Log.w(TAG, "Failed to write to /proc/shutdown_detect", e);
564 } finally {
565 IoUtils.closeQuietly(shutdown_detect);
566 }
567 }
568 //#endif /* OPLUS_FEATURE_SHUTDOWN_DETECT */
569
570 /**
571 * Makes sure we handle the shutdown gracefully.
572 * Shuts off power regardless of radio state if the allotted time has passed.
573 */
574 public void run() {
575 TimingsTraceLog shutdownTimingLog = newTimingsLog();
576 shutdownTimingLog.traceBegin("SystemServerShutdown");
577 metricShutdownStart();
578 metricStarted(METRIC_SYSTEM_SERVER);
579
580 //#ifdef OPLUS_EXTENSION_HOOK
581 //ODM_WT.AD.Framework ZhangFei@BSP.KERNEL.STABILITY, 2020/04/17, Add for shutdown detect.
582 doShutdownDetect("40");
583 //#endif /* OPLUS_EXTENSION_HOOK */
584
585 // Notify SurfaceFlinger that the device is shutting down.
586 // Transaction traces should be captured at this stage.
587 SurfaceControl.notifyShutdown();
588
589 // Start dumping check points for this shutdown in a separate thread.
590 Thread dumpCheckPointsThread = ShutdownCheckPoints.newDumpThread(
591 new File(CHECK_POINTS_FILE_BASENAME));
592 dumpCheckPointsThread.start();
593
594 /*
595 * Write a system property in case the system_server reboots before we
596 * get to the actual hardware restart. If that happens, we'll retry at
597 * the beginning of the SystemServer startup.
598 */
599 {
600 String reason = (mReboot ? "1" : "0") + (mReason != null ? mReason : "");
601 SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
602 }
603
604 /*
605 * If we are rebooting into safe mode, write a system property
606 * indicating so.
607 */
608 if (mRebootSafeMode) {
609 SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
610 }
611
612 shutdownTimingLog.traceBegin("DumpPreRebootInfo");
613 try {
614 Slog.i(TAG, "Logging pre-reboot information...");
615 PreRebootLogger.log(mContext);
616 } catch (Exception e) {
617 Slog.e(TAG, "Failed to log pre-reboot information", e);
618 }
619 shutdownTimingLog.traceEnd(); // DumpPreRebootInfo
620
621 metricStarted(METRIC_SEND_BROADCAST);
622 shutdownTimingLog.traceBegin("SendShutdownBroadcast");
623 Log.i(TAG, "Sending shutdown broadcast...");
624
625 // First send the high-level shut down broadcast.
626 mActionDone = false;
627 Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
628 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY);
629 final Bundle opts = BroadcastOptions.makeBasic()
630 .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
631 .toBundle();
632 final ActivityManagerInternal activityManagerInternal = LocalServices.getService(
633 ActivityManagerInternal.class);
634 activityManagerInternal.broadcastIntentWithCallback(intent,
635 new IIntentReceiver.Stub() {
636 @Override
637 public void performReceive(Intent intent, int resultCode, String data,
638 Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
639 mHandler.post(ShutdownThread.this::actionDone);
640 }
641 }, null, UserHandle.USER_ALL, null, null, opts);
642 final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
643 synchronized (mActionDoneSync) {
644 while (!mActionDone) {
645 long delay = endTime - SystemClock.elapsedRealtime();
646 if (delay <= 0) {
647 Log.w(TAG, "Shutdown broadcast timed out");
648 //#ifdef OPLUS_EXTENSION_HOOK
649 //ODM_WT.AD.Framework ZhangFei@BSP.KERNEL.STABILITY, 2020/04/17, Add for shutdown detect.
650 doShutdownDetect("47");
651 //#endif /* OPLUS_EXTENSION_HOOK */
652 break;
653 } else if (mRebootHasProgressBar) {
654 int status = (int)((MAX_BROADCAST_TIME - delay) * 1.0 *
655 BROADCAST_STOP_PERCENT / MAX_BROADCAST_TIME);
656 sInstance.setRebootProgress(status, null);
657 }
658 try {
659 mActionDoneSync.wait(Math.min(delay, ACTION_DONE_POLL_WAIT_MS));
660 } catch (InterruptedException e) {
661 }
662 }
663 }
664 if (mRebootHasProgressBar) {
665 sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null);
666 }
667 shutdownTimingLog.traceEnd(); // SendShutdownBroadcast
668 metricEnded(METRIC_SEND_BROADCAST);
669
670 Log.i(TAG, "Shutting down activity manager...");
671 shutdownTimingLog.traceBegin("ShutdownActivityManager");
672 metricStarted(METRIC_AM);
673 //#ifdef OPLUS_EXTENSION_HOOK
674 //ODM_WT.AD.Framework ZhangFei@BSP.KERNEL.STABILITY, 2020/04/17, Add for shutdown detect.
675 long startTime = SystemClock.elapsedRealtime();
676 //#endif /* OPLUS_EXTENSION_HOOK */
677
678 final IActivityManager am =
679 IActivityManager.Stub.asInterface(ServiceManager.checkService("activity"));
680 if (am != null) {
681 try {
682 am.shutdown(MAX_BROADCAST_TIME);
683 } catch (RemoteException e) {
684 }
685 }
686 if (mRebootHasProgressBar) {
687 sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null);
688 }
689 //#ifdef OPLUS_EXTENSION_HOOK
690 //ODM_WT.AD.Framework ZhangFei@BSP.KERNEL.STABILITY, 2020/04/17, Add for shutdown detect.
691 if((SystemClock.elapsedRealtime() - startTime) > MAX_BROADCAST_TIME) {
692 doShutdownDetect("46");
693 }
694 //#endif /* OPLUS_EXTENSION_HOOK */
695
696 shutdownTimingLog.traceEnd();// ShutdownActivityManager
697 metricEnded(METRIC_AM);
698
699 Log.i(TAG, "Shutting down package manager...");
700 shutdownTimingLog.traceBegin("ShutdownPackageManager");
701 metricStarted(METRIC_PM);
702 //#ifdef OPLUS_EXTENSION_HOOK
703 //ODM_WT.AD.Framework ZhangFei@BSP.KERNEL.STABILITY, 2020/04/17, Add for shutdown detect.
704 startTime = SystemClock.elapsedRealtime();
705 //#endif /* OPLUS_EXTENSION_HOOK */
706
707 final PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
708 if (pm != null) {
709 pm.shutdown();
710 }
711 if (mRebootHasProgressBar) {
712 sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);
713 }
714 //#ifdef OPLUS_EXTENSION_HOOK
715 //ODM_WT.AD.Framework ZhangFei@BSP.KERNEL.STABILITY, 2020/04/17, Add for shutdown detect.
716 if((SystemClock.elapsedRealtime() - startTime) > MAX_BROADCAST_TIME) {
717 doShutdownDetect("45");
718 }
719 //#endif /* OPLUS_EXTENSION_HOOK */
720
721 shutdownTimingLog.traceEnd(); // ShutdownPackageManager
722 metricEnded(METRIC_PM);
723
724 // Shutdown radios.
725 shutdownTimingLog.traceBegin("ShutdownRadios");
726 metricStarted(METRIC_RADIOS);
727 shutdownRadios(MAX_RADIO_WAIT_TIME);
728 if (mRebootHasProgressBar) {
729 sInstance.setRebootProgress(RADIO_STOP_PERCENT, null);
730 }
731 shutdownTimingLog.traceEnd(); // ShutdownRadios
732 metricEnded(METRIC_RADIOS);
733
734 if (mRebootHasProgressBar) {
735 sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null);
736
737 // If it's to reboot to install an update and uncrypt hasn't been
738 // done yet, trigger it now.
739 uncrypt();
740 }
741
742 // Wait for the check points dump thread to finish, or kill it if not finished in time.
743 shutdownTimingLog.traceBegin("ShutdownCheckPointsDumpWait");
744 try {
745 dumpCheckPointsThread.join(MAX_CHECK_POINTS_DUMP_WAIT_TIME);
746 } catch (InterruptedException ex) {
747 }
748 Log.i(TAG, "run() shutdownAnimationService");
749
750 shutdownTimingLog.traceEnd(); // ShutdownCheckPointsDumpWait
751
752 shutdownTimingLog.traceEnd(); // SystemServerShutdown
753 metricEnded(METRIC_SYSTEM_SERVER);
754 saveMetrics(mReboot, mReason);
755 //#ifdef OPLUS_EXTENSION_HOOK
756 //ODM_WT.AD.Framework ZhangFei@BSP.KERNEL.STABILITY, 2020/04/17, Add for shutdown detect.
757 //Shutdown from Systemserver, set timeout 120s, 4*30.
758 doShutdownDetect("4");
759 //#endif /* OPLUS_EXTENSION_HOOK */
760 // Remaining work will be done by init, including vold shutdown
761 rebootOrShutdown(mContext, mReboot, mReason);
762 }
763
764 private static TimingsTraceLog newTimingsLog() {
765 return new TimingsTraceLog("ShutdownTiming", Trace.TRACE_TAG_SYSTEM_SERVER);
766 }
767
768 private static void metricStarted(String metricKey) {
769 synchronized (TRON_METRICS) {
770 TRON_METRICS.put(metricKey, -1 * SystemClock.elapsedRealtime());
771 }
772 }
773
774 private static void metricEnded(String metricKey) {
775 synchronized (TRON_METRICS) {
776 TRON_METRICS
777 .put(metricKey, SystemClock.elapsedRealtime() + TRON_METRICS.get(metricKey));
778 }
779 }
780
781 private static void metricShutdownStart() {
782 synchronized (TRON_METRICS) {
783 TRON_METRICS.put(METRIC_SHUTDOWN_TIME_START, System.currentTimeMillis());
784 }
785 }
786
787 private void setRebootProgress(final int progress, final CharSequence message) {
788 mHandler.post(new Runnable() {
789 @Override
790 public void run() {
791 if (mProgressDialog != null) {
792 mProgressDialog.setProgress(progress);
793 if (message != null) {
794 mProgressDialog.setMessage(message);
795 }
796 }
797 }
798 });
799 }
800
801 private void shutdownRadios(final int timeout) {
802 // If a radio is wedged, disabling it may hang so we do this work in another thread,
803 // just in case.
804 final long endTime = SystemClock.elapsedRealtime() + timeout;
805 final boolean[] done = new boolean[1];
806 Thread t = new Thread() {
807 public void run() {
808 TimingsTraceLog shutdownTimingsTraceLog = newTimingsLog();
809 boolean radioOff;
810
811 TelephonyManager telephonyManager = mContext.getSystemService(
812 TelephonyManager.class);
813
814 radioOff = telephonyManager == null
815 || !telephonyManager.isAnyRadioPoweredOn();
816 if (!radioOff) {
817 Log.w(TAG, "Turning off cellular radios...");
818 metricStarted(METRIC_RADIO);
819 telephonyManager.shutdownAllRadios();
820 }
821
822 Log.i(TAG, "Waiting for Radio...");
823
824 long delay = endTime - SystemClock.elapsedRealtime();
825 while (delay > 0) {
826 if (mRebootHasProgressBar) {
827 int status = (int)((timeout - delay) * 1.0 *
828 (RADIO_STOP_PERCENT - PACKAGE_MANAGER_STOP_PERCENT) / timeout);
829 status += PACKAGE_MANAGER_STOP_PERCENT;
830 sInstance.setRebootProgress(status, null);
831 }
832
833 if (!radioOff) {
834 radioOff = !telephonyManager.isAnyRadioPoweredOn();
835 if (radioOff) {
836 Log.i(TAG, "Radio turned off.");
837 metricEnded(METRIC_RADIO);
838 shutdownTimingsTraceLog
839 .logDuration("ShutdownRadio", TRON_METRICS.get(METRIC_RADIO));
840 }
841 }
842
843 if (radioOff) {
844 Log.i(TAG, "Radio shutdown complete.");
845 done[0] = true;
846 break;
847 }
848 SystemClock.sleep(RADIOS_STATE_POLL_SLEEP_MS);
849 delay = endTime - SystemClock.elapsedRealtime();
850 }
851 }
852 };
853
854 t.start();
855 try {
856 t.join(timeout);
857 } catch (InterruptedException ex) {
858 }
859 if (!done[0]) {
860 Log.w(TAG, "Timed out waiting for Radio shutdown.");
861 }
862 }
863
864 /**
865 * Do not call this directly. Use {@link #reboot(Context, String, boolean)}
866 * or {@link #shutdown(Context, String, boolean)} instead.
867 *
868 * @param context Context used to vibrate or null without vibration
869 * @param reboot true to reboot or false to shutdown
870 * @param reason reason for reboot/shutdown
871 */
872 public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {
873 String subsysProp;
874 subsysProp = SystemProperties.get("vendor.peripheral.shutdown_critical_list",
875 "ERROR");
876 //If we don't have the shutdown critical subsystem list we can't
877 //really do anything. Proceed with full system shutdown.
878 if (!subsysProp.equals("ERROR")) {
879 Log.i(TAG, "Shutdown critical subsyslist is :"+subsysProp+": ");
880 Log.i(TAG, "Waiting for a maximum of " +
881 (VENDOR_SUBSYS_MAX_WAIT_MS) + "ms");
882 String[] subsysList = subsysProp.split(" ");
883 int wait_count = 0;
884 boolean okToShutdown = true;
885 String subsysState;
886 int subsysListLength = subsysList.length;
887 do {
888 okToShutdown = true;
889 for (int i = 0; i < subsysListLength; i++) {
890 subsysState =
891 SystemProperties.get(
892 "vendor.peripheral." +
893 subsysList[i] +
894 ".state",
895 "ERROR");
896 if(subsysState.equals("ONLINE")) {
897 //We only want to delay shutdown while
898 //one of the shutdown critical
899 //subsystems still shows as 'ONLINE'.
900 okToShutdown = false;
901 }
902 }
903 if (okToShutdown == false) {
904 SystemClock.sleep(VENDOR_SUBSYS_STATE_CHECK_INTERVAL_MS);
905 wait_count++;
906 }
907 } while (okToShutdown != true &&
908 wait_count < (VENDOR_SUBSYS_MAX_WAIT_MS/VENDOR_SUBSYS_STATE_CHECK_INTERVAL_MS));
909 if (okToShutdown != true) {
910 for (int i = 0; i < subsysList.length; i++) {
911 subsysState =
912 SystemProperties.get(
913 "vendor.peripheral." +
914 subsysList[i] +
915 ".state",
916 "ERROR");
917 if(subsysState.equals("ONLINE")) {
918 Log.w(TAG, "Subsystem " + subsysList[i]+
919 "did not shut down within timeout");
920 }
921 }
922 //#ifdef OPLUS_EXTENSION_HOOK
923 //koulongfei@ODM_WT.AD.Framework, 2023/08/22, Add for shutdown detect.
924 doShutdownDetect("43");
925 //#endif /* OPLUS_EXTENSION_HOOK */
926 } else {
927 Log.i(TAG, "Vendor subsystem(s) shutdown successful");
928 }
929 }
930 if (sInstance.mPowerManager != null){
931 Log.i(TAG, "mPowerManager.goToSleep");
932 sInstance.mPowerManager.goToSleep(SystemClock.uptimeMillis());
933 }
934 if (reboot) {
935 Log.i(TAG, "Rebooting, reason: " + reason);
936 PowerManagerService.lowLevelReboot(reason);
937 Log.e(TAG, "Reboot failed, will attempt shutdown instead");
938 reason = null;
939 } else if (context != null) {
940 // vibrate before shutting down
941 try {
942 sInstance.playShutdownVibration(context);
943 } catch (Exception e) {
944 // Failure to vibrate shouldn't interrupt shutdown. Just log it.
945 Log.w(TAG, "Failed to vibrate during shutdown.", e);
946 }
947
948 }
949 // Shutdown power
950 Log.i(TAG, "Performing low-level shutdown...");
951 PowerManagerService.lowLevelShutdown(reason);
952 }
953
954 /**
955 * Plays a vibration for shutdown. Along with playing a shutdown vibration, this method also
956 * sleeps the current Thread for some time, to allow the vibration to finish before the device
957 * shuts down.
958 */
959 @VisibleForTesting // For testing vibrations without shutting down device
960 void playShutdownVibration(Context context) {
961 Vibrator vibrator = mInjector.getVibrator(context);
962 if (!vibrator.hasVibrator()) {
963 return;
964 }
965
966 VibrationEffect vibrationEffect = getValidShutdownVibration(context, vibrator);
967 vibrator.vibrate(
968 vibrationEffect,
969 VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH));
970
971 // vibrator is asynchronous so we have to wait to avoid shutting down too soon.
972 long vibrationDuration = vibrationEffect.getDuration();
973 // A negative vibration duration may indicate a vibration effect whose duration is not
974 // known by the system (e.g. pre-baked effects). In that case, use the default shutdown
975 // vibration duration.
976 mInjector.sleep(vibrationDuration < 0 ? DEFAULT_SHUTDOWN_VIBRATE_MS : vibrationDuration);
977 }
978
979 private static void saveMetrics(boolean reboot, String reason) {
980 StringBuilder metricValue = new StringBuilder();
981 metricValue.append("reboot:");
982 metricValue.append(reboot ? "y" : "n");
983 metricValue.append(",").append("reason:").append(reason);
984 final int metricsSize = TRON_METRICS.size();
985 for (int i = 0; i < metricsSize; i++) {
986 final String name = TRON_METRICS.keyAt(i);
987 final long value = TRON_METRICS.valueAt(i);
988 if (value < 0) {
989 Log.e(TAG, "metricEnded wasn't called for " + name);
990 continue;
991 }
992 metricValue.append(',').append(name).append(':').append(value);
993 }
994 File tmp = new File(METRICS_FILE_BASENAME + ".tmp");
995 boolean saved = false;
996 try (FileOutputStream fos = new FileOutputStream(tmp)) {
997 fos.write(metricValue.toString().getBytes(StandardCharsets.UTF_8));
998 saved = true;
999 } catch (IOException e) {
1000 Log.e(TAG,"Cannot save shutdown metrics", e);
1001 }
1002 if (saved) {
1003 tmp.renameTo(new File(METRICS_FILE_BASENAME + ".txt"));
1004 }
1005 }
1006
1007 private void uncrypt() {
1008 Log.i(TAG, "Calling uncrypt and monitoring the progress...");
1009
1010 final RecoverySystem.ProgressListener progressListener =
1011 new RecoverySystem.ProgressListener() {
1012 @Override
1013 public void onProgress(int status) {
1014 if (status >= 0 && status < 100) {
1015 // Scale down to [MOUNT_SERVICE_STOP_PERCENT, 100).
1016 status = (int)(status * (100.0 - MOUNT_SERVICE_STOP_PERCENT) / 100);
1017 status += MOUNT_SERVICE_STOP_PERCENT;
1018 CharSequence msg = mContext.getText(
1019 com.android.internal.R.string.reboot_to_update_package);
1020 sInstance.setRebootProgress(status, msg);
1021 } else if (status == 100) {
1022 CharSequence msg = mContext.getText(
1023 com.android.internal.R.string.reboot_to_update_reboot);
1024 sInstance.setRebootProgress(status, msg);
1025 } else {
1026 // Ignored
1027 }
1028 }
1029 };
1030
1031 final boolean[] done = new boolean[1];
1032 done[0] = false;
1033 Thread t = new Thread() {
1034 @Override
1035 public void run() {
1036 RecoverySystem rs = (RecoverySystem) mContext.getSystemService(
1037 Context.RECOVERY_SERVICE);
1038 String filename = null;
1039 try {
1040 filename = FileUtils.readTextFile(RecoverySystem.UNCRYPT_PACKAGE_FILE, 0, null);
1041 rs.processPackage(mContext, new File(filename), progressListener);
1042 } catch (IOException e) {
1043 Log.e(TAG, "Error uncrypting file", e);
1044 }
1045 done[0] = true;
1046 }
1047 };
1048 t.start();
1049
1050 try {
1051 t.join(MAX_UNCRYPT_WAIT_TIME);
1052 } catch (InterruptedException unused) {
1053 }
1054 if (!done[0]) {
1055 Log.w(TAG, "Timed out waiting for uncrypt.");
1056 final int uncryptTimeoutError = 100;
1057 String timeoutMessage = String.format("uncrypt_time: %d\n" + "uncrypt_error: %d\n",
1058 MAX_UNCRYPT_WAIT_TIME / 1000, uncryptTimeoutError);
1059 try {
1060 FileUtils.stringToFile(RecoverySystem.UNCRYPT_STATUS_FILE, timeoutMessage);
1061 } catch (IOException e) {
1062 Log.e(TAG, "Failed to write timeout message to uncrypt status", e);
1063 }
1064 }
1065 }
1066
1067 /**
1068 * Provides a {@link VibrationEffect} to be used for shutdown.
1069 *
1070 * <p>The vibration to be played is derived from the shutdown vibration file (which the device
1071 * should specify at `com.android.internal.R.string.config_defaultShutdownVibrationFile`). A
1072 * fallback vibration maybe used in one of these conditions:
1073 * <ul>
1074 * <li>A vibration file has not been specified, or if the specified file does not exist
1075 * <li>If the content of the file does not represent a valid serialization of a
1076 * {@link VibrationEffect}
1077 * <li>If the {@link VibrationEffect} specified in the file is not suitable for
1078 * a shutdown vibration (such as indefinite vibrations)
1079 * </ul>
1080 */
1081 private VibrationEffect getValidShutdownVibration(Context context, Vibrator vibrator) {
1082 VibrationEffect parsedEffect = parseVibrationEffectFromFile(
1083 mInjector.getDefaultShutdownVibrationEffectFilePath(context),
1084 vibrator);
1085
1086 if (parsedEffect == null) {
1087 return createDefaultVibrationEffect();
1088 }
1089
1090 long parsedEffectDuration = parsedEffect.getDuration();
1091 if (parsedEffectDuration == Long.MAX_VALUE) {
1092 // This means that the effect does not have a defined end.
1093 // Since we don't want to vibrate forever while trying to shutdown, we ignore this
1094 // parsed effect and use the default one instead.
1095 Log.w(TAG, "The parsed shutdown vibration is indefinite.");
1096 return createDefaultVibrationEffect();
1097 }
1098
1099 return parsedEffect;
1100 }
1101
1102 private static VibrationEffect parseVibrationEffectFromFile(
1103 String filePath, Vibrator vibrator) {
1104 if (!TextUtils.isEmpty(filePath)) {
1105 try {
1106 return VibrationXmlParser.parseDocument(new FileReader(filePath)).resolve(vibrator);
1107 } catch (Exception e) {
1108 Log.e(TAG, "Error parsing default shutdown vibration effect.", e);
1109 }
1110 }
1111 return null;
1112 }
1113
1114 private static VibrationEffect createDefaultVibrationEffect() {
1115 return VibrationEffect.createOneShot(
1116 DEFAULT_SHUTDOWN_VIBRATE_MS, VibrationEffect.DEFAULT_AMPLITUDE);
1117 }
1118
1119 /** Utility class to inject instances, for easy testing. */
1120 @VisibleForTesting
1121 static class Injector {
1122 public Vibrator getVibrator(Context context) {
1123 return new SystemVibrator(context);
1124 }
1125
1126 public void sleep(long durationMs) {
1127 try {
1128 Thread.sleep(durationMs);
1129 } catch (InterruptedException unused) {
1130 // this is not critical and does not require logging.
1131 }
1132 }
1133
1134 public String getDefaultShutdownVibrationEffectFilePath(Context context) {
1135 return context.getResources().getString(
1136 com.android.internal.R.string.config_defaultShutdownVibrationFile);
1137 }
1138 }
1139 }
最新发布