在关机dialog添加reboot功能

ndroid手机有很多的第三方固件,其中有不少固件中有reboot的功能,这个功能是如何实现的呢?我这里会做一个简单的示例。先看一个效果图吧!

 

 

     如图在dialog中的最后一项是reboot功能。下面看看代码是怎么修改的:

文件路径: frameworks/policies/base/phone/com/android/internal/policy/impl/GlobalActions.java   


private AlertDialog createDialog() {
        ....
        mItems = Lists.newArrayList(
                // silent mode
                mSilentModeToggle,
                // next: airplane mode
                mAirplaneModeOn,
                // last: power off
                new SinglePressAction(
                        com.android.internal.R.drawable.ic_lock_power_off,
                        R.string.global_action_power_off) {

                    public void onPress() {
                        // shutdown by making sure radio and power are handled accordingly.
                        ShutdownThread.shutdown(mContext, true );
                    }

                    public boolean showDuringKeyguard() {
                        return true ;
                    }

                    public boolean showBeforeProvisioning() {
                        return true ;
                    }
                },
              // this is add code
              // last: reboot
              new SinglePressAction(
                    com.android.internal.R.drawable.ic_lock_power_off,
                    R.string.global_action_reboot) {

                    public void onPress() {
                        // shutdown by making sure radio and power are handled accordingly.
                        ShutdownThread.reboot(mContext, "" , true );
                    }

                    public boolean showDuringKeyguard() {
                        return true ;
                    }

                    public boolean showBeforeProvisioning() {
                        return true ;
                    }
                });

        mAdapter = new MyAdapter();

        ....
        return dialog;
    }


      代码中ShutdownThread.reboot()方法有三个参数其中第二个参数是一个String,如果这个String是NULL的话,就是正常重启,如果是recovery,系统重启进入recovery mode。R.string.global_action_reboot是我在系统资源文件中添加的字符串。呵呵是不是很简单阿!

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 }
最新发布
06-27
<think>我们正在分析一个Android系统关机/重启流程的代码,主要关注ShutdownThread类。这个类负责处理系统的关机或重启序列。代码中包含了关机/重启的流程,包括发送广播、关闭ActivityManager、关闭PackageManager、关闭无线电、等待子系统关闭等步骤。同时,代码中还有一些厂商定制的逻辑,比如关机检测(shutdowndetect)和振动效果。我们需要理解整个关机流程,并注意其中的关键步骤和定制点。关键方法:shutdown():用于关机reboot():用于重启rebootSafeMode():用于重启进入安全模式beginShutdownSequence():开始关机序列run():执行关机任务的主线程在run()方法中,按顺序执行以下步骤:1.记录开始时间(用于性能度量)2.通知SurfaceFlinger设备即将关闭3.启动一个线程来dump关机检查点(checkpoints)4.设置系统属性,表示关机/重启原因5.如果是安全模式重启,设置相应属性6.记录预关机信息(PreRebootLogger)7.发送关机广播(ACTION_SHUTDOWN),并等待广播处理完成(有超时机制)8.关闭ActivityManager9.关闭PackageManager10.关闭无线电(radios)11.如果是为了更新而重启,则处理uncrypt(解密更新包)12.等待dump检查点的线程完成13.保存度量数据(metrics)14.最后调用rebootOrShutdown()执行底层的重启或关机rebootOrShutdown()方法中:-首先等待关键外设子系统(vendorsubsystems)关闭(如果有定义)-然后调用PowerManagerService的底层方法进行重启或关机-如果是关机,还会播放关机振动此外,代码中还有一些厂商添加功能,例如:-doShutdownDetect():在关机过程中的不同阶段写入/proc/shutdown_detect,用于检测关机流程是否卡住。-在关闭ActivityManager和PackageManager时,如果超时,也会写入shutdown_detect。我们注意到,代码中有很多条件编译的注释(如#ifdefOPLUS_FEATURE_SHUTDOWN_DETECT),这表示这些代码段是厂商(如OPPO)添加的定制功能。现在,我们重点分析一下关机振动部分:在关机时,会调用playShutdownVibration()方法,该方法会:-获取振动器(Vibrator)服务-从配置文件中读取关机振动效果(路径由config_defaultShutdownVibrationFile指定)-如果解析失败或振动效果无效,则使用默认的振动(500ms的一次振动)-播放振动,并等待振动完成(通过睡眠相应时间)问题:如何定制关机振动?答案:可以通过在设备overlay中覆盖`config_defaultShutdownVibrationFile`字符串资源,指定一个振动效果文件(XML格式)。该文件应该是一个合法的VibrationEffect序列化文件。另外,我们注意到在关机过程中有进度条更新(如果mRebootHasProgressBar为true),这通常用于显示在更新时的进度(如OTA)。现在,我们提出一些相关问题:</think>### Android系统关机/重启流程分析 这段代码是Android系统中负责关机/重启的核心逻辑(`ShutdownThread`类),主要处理系统关机/重启的完整流程。以下是关键部分的解释: #### 1. 关机/重启入口方法 ```java // 关机入口 public static void shutdown(final Context context, String reason, boolean confirm) { mReboot = false; mRebootSafeMode = false; mReason = reason; shutdownInner(context, confirm); } // 重启入口 public static void reboot(final Context context, String reason, boolean confirm) { mReboot = true; mRebootSafeMode = false; mRebootHasProgressBar = false; mReason = reason; shutdownInner(context, confirm); } // 安全模式重启 public static void rebootSafeMode(final Context context, boolean confirm) { // ...权限检查... mReboot = true; mRebootSafeMode = true; shutdownInner(context, confirm); } ``` #### 2. 关机流程核心逻辑 (`run()` 方法) ```java public void run() { // 1. 记录开始时间 metricShutdownStart(); // 2. 通知SurfaceFlinger准备关闭 SurfaceControl.notifyShutdown(); // 3. 设置关机属性 SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason); // 4. 发送关机广播 sendShutdownBroadcast(); // 5. 关闭系统服务 shutdownActivityManager(); shutdownPackageManager(); // 6. 关闭无线电 shutdownRadios(MAX_RADIO_WAIT_TIME); // 7. 处理OTA更新(如果需要) if (mRebootHasProgressBar) uncrypt(); // 8. 最终关机/重启 rebootOrShutdown(mContext, mReboot, mReason); } ``` #### 3. 底层关机实现 ```java public static void rebootOrShutdown(final Context context, boolean reboot, String reason) { // 1. 等待外设子系统关闭 waitForVendorSubsystems(); // 2. 进入睡眠状态 sInstance.mPowerManager.goToSleep(SystemClock.uptimeMillis()); // 3. 播放关机震动 if (!reboot) sInstance.playShutdownVibration(context); // 4. 调用底层关机 PowerManagerService.lowLevelShutdown(reason); } ``` #### 4. 关机震动实现 ```java void playShutdownVibration(Context context) { Vibrator vibrator = new SystemVibrator(context); VibrationEffect effect = getValidShutdownVibration(context, vibrator); // 播放震动并等待完成 vibrator.vibrate(effect, VibrationAttributes.USAGE_TOUCH); sleep(effect.getDuration()); } // 震动效果来源 private VibrationEffect getValidShutdownVibration(Context context, Vibrator vibrator) { // 从配置文件获取震动效果 String filePath = context.getResources().getString( com.android.internal.R.string.config_defaultShutdownVibrationFile); // 解析失败时使用默认震动 return parseVibrationEffectFromFile(filePath, vibrator) ?: createDefaultVibrationEffect(); } ``` #### 5. 厂商定制功能 (OPPO示例) ```java // 关机检测点写入 private static void doShutdownDetect(String count) { try { FileWriter writer = new FileWriter("/proc/shutdown_detect"); writer.write(count); // 写入检测点编号 } catch (IOException e) { Log.w(TAG, "Failed to write shutdown_detect", e); } } // 在关键流程插入检测点 void run() { doShutdownDetect("40"); // 开始关机 // ...系统服务关闭... doShutdownDetect("45"); // 关键服务关闭完成 // ...最终关机... doShutdownDetect("4"); // 进入底层关机 } ``` ### 关键设计要点: 1. **异步处理**:使用Handler和同步锁协调异步操作 2. **进度反馈**:通过ProgressDialog显示关机进度(特别是OTA更新时) 3. **健壮性保障**: - 超时机制(广播、无线电关闭等) - 关机指标记录(保存到/data/system/shutdown-metrics.txt) 4. **厂商扩展点**: - 通过`SystemProperties`读取设备特定配置 - `/proc/shutdown_detect`提供关机流程追踪 - 震动效果文件路径可配置 ### 完整关机流程: 1. 显示确认对话框 → 2. 获取唤醒锁 → 3. 发送系统广播 → 4. 关闭核心服务 → 5. 关闭无线电 → 6. 等待外设关闭 → 7. 播放关机震动 → 8. 调用底层关机
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值