Confirm which is NULL

博客围绕Linux内核中`rds_ib_recv_cache_put`函数展开分析。通过`crash`工具获取调用栈信息,发现寄存器`rsi`指向`NULL`,进而确定`struct rds_ib_refill_cache`为`NULL`。结合代码和调试信息,判断这是一个使用后释放(use-after-free)的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Confirm which is NULL
1.
crash> bt
PID: 6165   TASK: ffff883ed37b4600  CPU: 5   COMMAND: "rds-stress"
 #0 [ffff883ed2fdf7f0] machine_kexec at ffffffff81060320
 #1 [ffff883ed2fdf860] crash_kexec at ffffffff81115ed8
 #2 [ffff883ed2fdf930] oops_end at ffffffff8101a8d8
 #3 [ffff883ed2fdf960] no_context at ffffffff8106fbd1
 #4 [ffff883ed2fdf9b0] __bad_area_nosemaphore at ffffffff8106fdcd
 #5 [ffff883ed2fdfa00] bad_area_nosemaphore at ffffffff8106fee3
 #6 [ffff883ed2fdfa10] __do_page_fault at ffffffff81070468
 #7 [ffff883ed2fdfa80] do_page_fault at ffffffff81070787
 #8 [ffff883ed2fdfac0] page_fault at ffffffff8170329f
    [exception RIP: rds_ib_recv_cache_put+48]
    RIP: ffffffffa04a6a30  RSP: ffff883ed2fdfb78  RFLAGS: 00010006
    RAX: 0000000000000005  RBX: ffff883ed4c8ec00  RCX: dead000000000200
    RDX: 0000000000004000  RSI: 00000000000009c0  RDI: ffff883ee1f00f10
    RBP: ffff883ed2fdfb98   R8: ffff883f6248d450   R9: 00000000000007b3
    R10: ffff883ed2fdfc78  R11: ffff883f5d80b140  R12: ffff883ee1f00f10
    R13: 00000000000009c0  R14: 0000000000000206  R15: ffff883ed4c8ec20
    ORIG_RAX: ffffffffffffffff  CS: 0010  SS: 0018
 #9 [ffff883ed2fdfba0] rds_ib_inc_free at ffffffffa04a6f5c [rds_rdma]
#10 [ffff883ed2fdfbf0] rds_inc_put at ffffffffa05e330c [rds]
#11 [ffff883ed2fdfc10] rds_recvmsg at ffffffffa05e35f7 [rds]
#12 [ffff883ed2fdfd00] sock_recvmsg at ffffffff81603680
#13 [ffff883ed2fdfd30] ___sys_recvmsg at ffffffff81600ebe
#14 [ffff883ed2fdfeb0] __sys_recvmsg at ffffffff81602e79
#15 [ffff883ed2fdff40] sys_recvmsg at ffffffff81602ed9
#16 [ffff883ed2fdff50] system_call_fastpath at ffffffff816fdb1a
    RIP: 0000003786ae9ac0  RSP: 00007ffe5cf1d9d8  RFLAGS: 00000246
    RAX: ffffffffffffffda  RBX: 00007ffe5cf1fe60  RCX: 0000003786ae9ac0
    RDX: 0000000000000040  RSI: 00007ffe5cf1fd80  RDI: 0000000000000003
    RBP: 00007ffe5cf1fe50   R8: 00007f583fa43000   R9: 0000000000000000
    R10: 00000000000c4e8a  R11: 0000000000000246  R12: 00007ffe5cf1fe60
    R13: 00007ffe5cf21500  R14: 00007f583fa46800  R15: 00007ffe5cf21500
    ORIG_RAX: 000000000000002f  CS: 0033  SS: 002b

 

2.
crash> dis -lr rds_ib_recv_cache_put+48
/home/yanjzhu/rpmbuild/BUILD/kernel-4.1.12/linux-4.1.12-1.el6uek/net/rds/ib_recv.c: 715
0xffffffffa04a6a00 <rds_ib_recv_cache_put>:     push   %rbp
0xffffffffa04a6a01 <rds_ib_recv_cache_put+1>:   mov    %rsp,%rbp
0xffffffffa04a6a04 <rds_ib_recv_cache_put+4>:   push   %r14
0xffffffffa04a6a06 <rds_ib_recv_cache_put+6>:   push   %r13
0xffffffffa04a6a08 <rds_ib_recv_cache_put+8>:   push   %r12
0xffffffffa04a6a0a <rds_ib_recv_cache_put+10>:  push   %rbx
0xffffffffa04a6a0b <rds_ib_recv_cache_put+11>:  nopl   0x0(%rax,%rax,1)
0xffffffffa04a6a10 <rds_ib_recv_cache_put+16>:  mov    %rdi,%r12
0xffffffffa04a6a13 <rds_ib_recv_cache_put+19>:  mov    %rsi,%r13
/home/yanjzhu/rpmbuild/BUILD/kernel-4.1.12/linux-4.1.12-1.el6uek/./arch/x86/include/asm/paravirt.h: 810
0xffffffffa04a6a16 <rds_ib_recv_cache_put+22>:  pushfq
0xffffffffa04a6a17 <rds_ib_recv_cache_put+23>:  pop    %rax
0xffffffffa04a6a18 <rds_ib_recv_cache_put+24>:  nopl   0x0(%rax,%rax,1)
0xffffffffa04a6a1d <rds_ib_recv_cache_put+29>:  mov    %rax,%r14
/home/yanjzhu/rpmbuild/BUILD/kernel-4.1.12/linux-4.1.12-1.el6uek/./arch/x86/include/asm/paravirt.h: 820
0xffffffffa04a6a20 <rds_ib_recv_cache_put+32>:  cli
0xffffffffa04a6a21 <rds_ib_recv_cache_put+33>:  nopw   0x0(%rax,%rax,1)
/home/yanjzhu/rpmbuild/BUILD/kernel-4.1.12/linux-4.1.12-1.el6uek/net/rds/ib_recv.c: 721
0xffffffffa04a6a27 <rds_ib_recv_cache_put+39>:  mov    %gs:0x5fb66702(%rip),%eax        # 0xd130
0xffffffffa04a6a2e <rds_ib_recv_cache_put+46>:  cltq
0xffffffffa04a6a30 <rds_ib_recv_cache_put+48>:  mov    (%rsi),%rbx

 

From the above, the register rsi address is to point to NULL.

 

3.
crash> whatis rds_ib_recv_cache_put
void rds_ib_recv_cache_put(struct list_head *, struct rds_ib_refill_cache *);

 

rdi is struct list_head *
rsi is struct rds_ib_refill_cache *
And from the above rsi is not changed from the beginning.

 

So struct rds_ib_refill_cache is NULL.

 

4.
...
        cache = ic->rds_ibdev->i_cache_frags + ic->i_frag_cache_inx;
        rds_ib_recv_cache_put(&frag->f_cache_entry, cache);
...
...
        rdsdebug("freeing ibinc %p inc %p\n", ibinc, inc);
        rds_ib_recv_cache_put(&ibinc->ii_cache_entry,
                              &ic->rds_ibdev->i_cache_incs);
...
There are 2 parameters:i_cache_incs and i_cache_frags.

 

5.
    [exception RIP: rds_ib_recv_cache_put+48]
    RIP: ffffffffa04a6a30  RSP: ffff883ed2fdfb78  RFLAGS: 00010006
    RAX: 0000000000000005  RBX: ffff883ed4c8ec00  RCX: dead000000000200
    RDX: 0000000000004000  RSI: 00000000000009c0  RDI: ffff883ee1f00f10
    RBP: ffff883ed2fdfb98   R8: ffff883f6248d450   R9: 00000000000007b3
    R10: ffff883ed2fdfc78  R11: ffff883f5d80b140  R12: ffff883ee1f00f10
    R13: 00000000000009c0  R14: 0000000000000206  R15: ffff883ed4c8ec20
    ORIG_RAX: ffffffffffffffff  CS: 0010  SS: 0018
RSI is 00000000000009c0

 

crash> eval 00000000000009c0
hexadecimal: 9c0
    decimal: 2496   <---this
      octal: 4700
     binary: 0000000000000000000000000000000000000000000000000000100111000000

 

6.
crash> struct rds_ib_device -o
struct rds_ib_device {
     [0] struct list_head list;
    [16] struct list_head ipaddr_list;
    [32] struct list_head conn_list;
    [48] struct ib_device *dev;
    [56] struct ib_pd *pd;
    [64] bool use_fastreg;
    [68] int fastreg_cq_vector;
    [72] struct ib_cq *fastreg_cq;
    [80] struct ib_wc fastreg_wc[32];
  [2128] struct ib_qp *fastreg_qp;
  [2136] struct tasklet_struct fastreg_tasklet;
  [2176] atomic_t fastreg_wrs;
  [2184] struct rw_semaphore fastreg_lock;
  [2224] struct work_struct fastreg_reset_w;
  [2256] struct ib_mr *mr;
  [2264] struct rds_ib_mr_pool *mr_1m_pool;
  [2272] struct rds_ib_mr_pool *mr_8k_pool;
  [2280] unsigned int fmr_max_remaps;
  [2284] unsigned int max_8k_fmrs;
  [2288] unsigned int max_1m_fmrs;
  [2292] int max_sge;
  [2296] unsigned int max_wrs;
  [2300] unsigned int max_initiator_depth;
  [2304] unsigned int max_responder_resources;
  [2308] spinlock_t spinlock;
  [2312] atomic_t refcount;
  [2320] struct work_struct free_work;
  [2352] struct rds_ib_srq *srq;
  [2360] struct rds_ib_port *ports;
  [2368] struct ib_event_handler event_handler;
  [2400] int *vector_load;
  [2408] struct mutex vector_load_lock;
  [2448] atomic_t free_dev;
  [2456] struct mutex free_dev_lock;
  [2496] struct rds_ib_refill_cache i_cache_incs;     <-----2496 is here.
  [2528] struct rds_ib_refill_cache i_cache_frags[3];
}
SIZE: 2624

 

i_cache_incs is NULL.

And RSI is 00000000000009c0.
From the source code,
...
        rdsdebug("freeing ibinc %p inc %p\n", ibinc, inc);
        rds_ib_recv_cache_put(&ibinc->ii_cache_entry,
                              &ic->rds_ibdev->i_cache_incs);
...
ic->rds_ibdev->i_cache_incs is 00000000000009c0, so probably ic->rds_ibdev is NULL.
To now, this is a use-after-free problem.

public static void shutdown(final Context context, String reason, boolean confirm) { mReboot = false; mRebootSafeMode = false; mReason = reason; shutdownInner(context, confirm); } private static void shutdownInner(final Context context, boolean confirm) { // ShutdownThread is called from many places, so best to verify here that the context passed // in is themed. context.assertRuntimeOverlayThemable(); // ensure that only one thread is trying to power down. // any additional calls are just returned synchronized (sIsStartedGuard) { if (sIsStarted) { Log.d(TAG, "Request to shutdown already running, returning."); return; } } // Add checkpoint for this shutdown attempt. The user might still cancel the dialog, but // this point preserves the system trace of the trigger point of the ShutdownThread. ShutdownCheckPoints.recordCheckPoint(/* reason= */ null); final int longPressBehavior = context.getResources().getInteger( com.android.internal.R.integer.config_longPressOnPowerBehavior); final int resourceId = mRebootSafeMode ? com.android.internal.R.string.reboot_safemode_confirm : (longPressBehavior == 2 ? com.android.internal.R.string.shutdown_confirm_question : com.android.internal.R.string.shutdown_confirm); Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior); if (confirm) { final CloseDialogReceiver closer = new CloseDialogReceiver(context); if (sConfirmDialog != null) { sConfirmDialog.dismiss(); } sConfirmDialog = new AlertDialog.Builder(context) .setTitle(mRebootSafeMode ? com.android.internal.R.string.reboot_safemode_title : com.android.internal.R.string.power_off) .setMessage(resourceId) .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { beginShutdownSequence(context); } }) .setNegativeButton(com.android.internal.R.string.no, null) .create(); closer.dialog = sConfirmDialog; sConfirmDialog.setOnDismissListener(closer); sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); sConfirmDialog.show(); } else { beginShutdownSequence(context); } } private static class CloseDialogReceiver extends BroadcastReceiver implements DialogInterface.OnDismissListener { private Context mContext; public Dialog dialog; CloseDialogReceiver(Context context) { mContext = context; IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); context.registerReceiver(this, filter); } @Override public void onReceive(Context context, Intent intent) { dialog.cancel(); } public void onDismiss(DialogInterface unused) { mContext.unregisterReceiver(this); } } /** * Request a clean shutdown, waiting for subsystems to clean up their * state etc. Must be called from a Looper thread in which its UI * is shown. * * @param context Context used to display the shutdown progress dialog. This must be a context * suitable for displaying UI (aka Themable). * @param reason code to pass to the kernel (e.g. "recovery"), or null. * @param confirm true if user confirmation is needed before shutting down. */ public static void reboot(final Context context, String reason, boolean confirm) { mReboot = true; mRebootSafeMode = false; mRebootHasProgressBar = false; mReason = reason; shutdownInner(context, confirm); } /** * Request a reboot into safe mode. Must be called from a Looper thread in which its UI * is shown. * * @param context Context used to display the shutdown progress dialog. This must be a context * suitable for displaying UI (aka Themable). * @param confirm true if user confirmation is needed before shutting down. */ public static void rebootSafeMode(final Context context, boolean confirm) { UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); if (um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) { return; } mReboot = true; mRebootSafeMode = true; mRebootHasProgressBar = false; mReason = null; shutdownInner(context, confirm); } private static ProgressDialog showShutdownDialog(Context context) { // Throw up a system dialog to indicate the device is rebooting / shutting down. ProgressDialog pd = new ProgressDialog(context); // Path 1: Reboot to recovery for update // Condition: mReason startswith REBOOT_RECOVERY_UPDATE // // Path 1a: uncrypt needed // Condition: if /cache/recovery/uncrypt_file exists but // /cache/recovery/block.map doesn't. // UI: determinate progress bar (mRebootHasProgressBar == True) // // * Path 1a is expected to be removed once the GmsCore shipped on // device always calls uncrypt prior to reboot. // // Path 1b: uncrypt already done // UI: spinning circle only (no progress bar) // // Path 2: Reboot to recovery for factory reset // Condition: mReason == REBOOT_RECOVERY // UI: spinning circle only (no progress bar) // // Path 3: Regular reboot / shutdown // Condition: Otherwise // UI: spinning circle only (no progress bar) // mReason could be "recovery-update" or "recovery-update,quiescent". if (mReason != null && mReason.startsWith(PowerManager.REBOOT_RECOVERY_UPDATE)) { // We need the progress bar if uncrypt will be invoked during the // reboot, which might be time-consuming. mRebootHasProgressBar = RecoverySystem.UNCRYPT_PACKAGE_FILE.exists() && !(RecoverySystem.BLOCK_MAP_FILE.exists()); pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title)); if (mRebootHasProgressBar) { pd.setMax(100); pd.setProgress(0); pd.setIndeterminate(false); pd.setProgressNumberFormat(null); pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); pd.setMessage(context.getText( com.android.internal.R.string.reboot_to_update_prepare)); } else { if (showSysuiReboot()) { return null; } pd.setIndeterminate(true); pd.setMessage(context.getText( com.android.internal.R.string.reboot_to_update_reboot)); } } else if (mReason != null && mReason.equals(PowerManager.REBOOT_RECOVERY)) { if (showSysuiReboot()) { return null; } else if (RescueParty.isAttemptingFactoryReset()) { // We're not actually doing a factory reset yet; we're rebooting // to ask the user if they'd like to reset, so give them a less // scary dialog message. pd.setTitle(context.getText(com.android.internal.R.string.power_off)); pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress)); pd.setIndeterminate(true); } else { // Factory reset path. Set the dialog message accordingly. pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title)); pd.setMessage(context.getText( com.android.internal.R.string.reboot_to_reset_message)); pd.setIndeterminate(true); } } else { if (showSysuiReboot()) { return null; } pd.setTitle(context.getText(com.android.internal.R.string.power_off)); pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress)); pd.setIndeterminate(true); } pd.setCancelable(false); pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); ///M: added for Shutdown Enhancement @{ if(sInstance.mIsShowShutdownDialog(context)) { pd.show(); } /// @} return pd; } private static boolean showSysuiReboot() { ///M: added for Shutdown Enhancement @{ if(!sInstance.mIsShowShutdownSysui()) { return false; } /// @} Log.d(TAG, "Attempting to use SysUI shutdown UI"); try { StatusBarManagerInternal service = LocalServices.getService( StatusBarManagerInternal.class); if (service.showShutdownUi(mReboot, mReason)) { // Sysui will handle shutdown UI. Log.d(TAG, "SysUI handling shutdown UI"); return true; } } catch (Exception e) { // If anything went wrong, ignore it and use fallback ui } Log.d(TAG, "SysUI is unavailable"); return false; } private static void beginShutdownSequence(Context context) { synchronized (sIsStartedGuard) { if (sIsStarted) { Log.d(TAG, "Shutdown sequence already running, returning."); return; } sIsStarted = true; } sInstance.mProgressDialog = showShutdownDialog(context); sInstance.mContext = context; sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); // make sure we never fall asleep again sInstance.mCpuWakeLock = null; try { sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock( PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu"); sInstance.mCpuWakeLock.setReferenceCounted(false); sInstance.mCpuWakeLock.acquire(); } catch (SecurityException e) { Log.w(TAG, "No permission to acquire wake lock", e); sInstance.mCpuWakeLock = null; } // also make sure the screen stays on for better user experience sInstance.mScreenWakeLock = null; if (sInstance.mPowerManager.isScreenOn()) { try { sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock( PowerManager.FULL_WAKE_LOCK, TAG + "-screen"); sInstance.mScreenWakeLock.setReferenceCounted(false); sInstance.mScreenWakeLock.acquire(); } catch (SecurityException e) { Log.w(TAG, "No permission to acquire wake lock", e); sInstance.mScreenWakeLock = null; } } if (SecurityLog.isLoggingEnabled()) { SecurityLog.writeEvent(SecurityLog.TAG_OS_SHUTDOWN); } // start the thread that initiates shutdown sInstance.mHandler = new Handler() { }; ///M: added for Shutdown Enhancement @{ if(sInstance.mStartShutdownSeq(context)) { sInstance.start(); } /// @} } void actionDone() { synchronized (mActionDoneSync) { mActionDone = true; mActionDoneSync.notifyAll(); } } /** * Makes sure we handle the shutdown gracefully. * Shuts off power regardless of radio state if the allotted time has passed. */ public void run() { TimingsTraceLog shutdownTimingLog = newTimingsLog(); shutdownTimingLog.traceBegin("SystemServerShutdown"); metricShutdownStart(); metricStarted(METRIC_SYSTEM_SERVER); // Start dumping check points for this shutdown in a separate thread. Thread dumpCheckPointsThread = ShutdownCheckPoints.newDumpThread( new File(CHECK_POINTS_FILE_BASENAME)); dumpCheckPointsThread.start(); BroadcastReceiver br = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // We don't allow apps to cancel this, so ignore the result. actionDone(); } }; /* * Write a system property in case the system_server reboots before we * get to the actual hardware restart. If that happens, we'll retry at * the beginning of the SystemServer startup. */ { String reason = (mReboot ? "1" : "0") + (mReason != null ? mReason : ""); SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason); } /* * If we are rebooting into safe mode, write a system property * indicating so. */ if (mRebootSafeMode) { SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1"); } shutdownTimingLog.traceBegin("DumpPreRebootInfo"); try { Slog.i(TAG, "Logging pre-reboot information..."); PreRebootLogger.log(mContext); } catch (Exception e) { Slog.e(TAG, "Failed to log pre-reboot information", e); } shutdownTimingLog.traceEnd(); // DumpPreRebootInfo metricStarted(METRIC_SEND_BROADCAST); shutdownTimingLog.traceBegin("SendShutdownBroadcast"); Log.i(TAG, "Sending shutdown broadcast..."); // First send the high-level shut down broadcast. mActionDone = false; Intent intent = new Intent(Intent.ACTION_SHUTDOWN); intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY); mContext.sendOrderedBroadcastAsUser(intent, UserHandle.ALL, null, br, mHandler, 0, null, null); final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME; synchronized (mActionDoneSync) { while (!mActionDone) { long delay = endTime - SystemClock.elapsedRealtime(); if (delay <= 0) { Log.w(TAG, "Shutdown broadcast timed out"); break; } else if (mRebootHasProgressBar) { int status = (int)((MAX_BROADCAST_TIME - delay) * 1.0 * BROADCAST_STOP_PERCENT / MAX_BROADCAST_TIME); sInstance.setRebootProgress(status, null); } try { mActionDoneSync.wait(Math.min(delay, ACTION_DONE_POLL_WAIT_MS)); } catch (InterruptedException e) { } } } if (mRebootHasProgressBar) { sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null); } shutdownTimingLog.traceEnd(); // SendShutdownBroadcast metricEnded(METRIC_SEND_BROADCAST); Log.i(TAG, "Shutting down activity manager..."); shutdownTimingLog.traceBegin("ShutdownActivityManager"); metricStarted(METRIC_AM); final IActivityManager am = IActivityManager.Stub.asInterface(ServiceManager.checkService("activity")); if (am != null) { try { am.shutdown(MAX_BROADCAST_TIME); } catch (RemoteException e) { } } if (mRebootHasProgressBar) { sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null); } shutdownTimingLog.traceEnd();// ShutdownActivityManager metricEnded(METRIC_AM); Log.i(TAG, "Shutting down package manager..."); shutdownTimingLog.traceBegin("ShutdownPackageManager"); metricStarted(METRIC_PM); final PackageManagerService pm = (PackageManagerService) ServiceManager.getService("package"); if (pm != null) { pm.shutdown(); } if (mRebootHasProgressBar) { sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null); } shutdownTimingLog.traceEnd(); // ShutdownPackageManager metricEnded(METRIC_PM); // Shutdown radios. shutdownTimingLog.traceBegin("ShutdownRadios"); metricStarted(METRIC_RADIOS); shutdownRadios(MAX_RADIO_WAIT_TIME); if (mRebootHasProgressBar) { sInstance.setRebootProgress(RADIO_STOP_PERCENT, null); } shutdownTimingLog.traceEnd(); // ShutdownRadios metricEnded(METRIC_RADIOS); if (mRebootHasProgressBar) { sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null); // If it's to reboot to install an update and uncrypt hasn't been // done yet, trigger it now. uncrypt(); } // Wait for the check points dump thread to finish, or kill it if not finished in time. shutdownTimingLog.traceBegin("ShutdownCheckPointsDumpWait"); try { dumpCheckPointsThread.join(MAX_CHECK_POINTS_DUMP_WAIT_TIME); } catch (InterruptedException ex) { } shutdownTimingLog.traceEnd(); // ShutdownCheckPointsDumpWait ///M: added for Shutdown Enhancement@{ mShutdownSeqFinish(mContext); /// @} shutdownTimingLog.traceEnd(); // SystemServerShutdown metricEnded(METRIC_SYSTEM_SERVER); saveMetrics(mReboot, mReason); // Remaining work will be done by init, including vold shutdown rebootOrShutdown(mContext, mReboot, mReason); } private static TimingsTraceLog newTimingsLog() { return new TimingsTraceLog("ShutdownTiming", Trace.TRACE_TAG_SYSTEM_SERVER); } private static void metricStarted(String metricKey) { synchronized (TRON_METRICS) { TRON_METRICS.put(metricKey, -1 * SystemClock.elapsedRealtime()); } } private static void metricEnded(String metricKey) { synchronized (TRON_METRICS) { TRON_METRICS .put(metricKey, SystemClock.elapsedRealtime() + TRON_METRICS.get(metricKey)); } } private static void metricShutdownStart() { synchronized (TRON_METRICS) { TRON_METRICS.put(METRIC_SHUTDOWN_TIME_START, System.currentTimeMillis()); } } private void setRebootProgress(final int progress, final CharSequence message) { mHandler.post(new Runnable() { @Override public void run() { if (mProgressDialog != null) { mProgressDialog.setProgress(progress); if (message != null) { mProgressDialog.setMessage(message); } } } }); } private void shutdownRadios(final int timeout) { // If a radio is wedged, disabling it may hang so we do this work in another thread, // just in case. final long endTime = SystemClock.elapsedRealtime() + timeout; final boolean[] done = new boolean[1]; Thread t = new Thread() { public void run() { TimingsTraceLog shutdownTimingsTraceLog = newTimingsLog(); boolean radioOff; TelephonyManager telephonyManager = mContext.getSystemService( TelephonyManager.class); radioOff = telephonyManager == null || !telephonyManager.isAnyRadioPoweredOn(); if (!radioOff) { Log.w(TAG, "Turning off cellular radios..."); metricStarted(METRIC_RADIO); telephonyManager.shutdownAllRadios(); } Log.i(TAG, "Waiting for Radio..."); long delay = endTime - SystemClock.elapsedRealtime(); while (delay > 0) { if (mRebootHasProgressBar) { int status = (int)((timeout - delay) * 1.0 * (RADIO_STOP_PERCENT - PACKAGE_MANAGER_STOP_PERCENT) / timeout); status += PACKAGE_MANAGER_STOP_PERCENT; sInstance.setRebootProgress(status, null); } if (!radioOff) { radioOff = !telephonyManager.isAnyRadioPoweredOn(); if (radioOff) { Log.i(TAG, "Radio turned off."); metricEnded(METRIC_RADIO); shutdownTimingsTraceLog .logDuration("ShutdownRadio", TRON_METRICS.get(METRIC_RADIO)); } } if (radioOff) { Log.i(TAG, "Radio shutdown complete."); done[0] = true; break; } SystemClock.sleep(RADIOS_STATE_POLL_SLEEP_MS); delay = endTime - SystemClock.elapsedRealtime(); } } }; t.start(); try { t.join(timeout); } catch (InterruptedException ex) { } if (!done[0]) { Log.w(TAG, "Timed out waiting for Radio shutdown."); } } /** * Do not call this directly. Use {@link #reboot(Context, String, boolean)} * or {@link #shutdown(Context, String, boolean)} instead. * * @param context Context used to vibrate or null without vibration * @param reboot true to reboot or false to shutdown * @param reason reason for reboot/shutdown */ public static void rebootOrShutdown(final Context context, boolean reboot, String reason) { if (reboot) { Log.i(TAG, "Rebooting, reason: " + reason); PowerManagerService.lowLevelReboot(reason); Log.e(TAG, "Reboot failed, will attempt shutdown instead"); reason = null; } else if (SHUTDOWN_VIBRATE_MS > 0 && context != null) { // vibrate before shutting down Vibrator vibrator = new SystemVibrator(context); try { vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES); } catch (Exception e) { // Failure to vibrate shouldn't interrupt shutdown. Just log it. Log.w(TAG, "Failed to vibrate during shutdown.", e); } // vibrator is asynchronous so we need to wait to avoid shutting down too soon. try { Thread.sleep(SHUTDOWN_VIBRATE_MS); } catch (InterruptedException unused) { } } ///M: added for shutdown Enhancement@{ sInstance.mLowLevelShutdownSeq(context); /// @} // Shutdown power Log.i(TAG, "Performing low-level shutdown..."); PowerManagerService.lowLevelShutdown(reason); } private static void saveMetrics(boolean reboot, String reason) { StringBuilder metricValue = new StringBuilder(); metricValue.append("reboot:"); metricValue.append(reboot ? "y" : "n"); metricValue.append(",").append("reason:").append(reason); final int metricsSize = TRON_METRICS.size(); for (int i = 0; i < metricsSize; i++) { final String name = TRON_METRICS.keyAt(i); final long value = TRON_METRICS.valueAt(i); if (value < 0) { Log.e(TAG, "metricEnded wasn't called for " + name); continue; } metricValue.append(',').append(name).append(':').append(value); } File tmp = new File(METRICS_FILE_BASENAME + ".tmp"); boolean saved = false; try (FileOutputStream fos = new FileOutputStream(tmp)) { fos.write(metricValue.toString().getBytes(StandardCharsets.UTF_8)); saved = true; } catch (IOException e) { Log.e(TAG,"Cannot save shutdown metrics", e); } if (saved) { tmp.renameTo(new File(METRICS_FILE_BASENAME + ".txt")); } } private void uncrypt() { Log.i(TAG, "Calling uncrypt and monitoring the progress..."); final RecoverySystem.ProgressListener progressListener = new RecoverySystem.ProgressListener() { @Override public void onProgress(int status) { if (status >= 0 && status < 100) { // Scale down to [MOUNT_SERVICE_STOP_PERCENT, 100). status = (int)(status * (100.0 - MOUNT_SERVICE_STOP_PERCENT) / 100); status += MOUNT_SERVICE_STOP_PERCENT; CharSequence msg = mContext.getText( com.android.internal.R.string.reboot_to_update_package); sInstance.setRebootProgress(status, msg); } else if (status == 100) { CharSequence msg = mContext.getText( com.android.internal.R.string.reboot_to_update_reboot); sInstance.setRebootProgress(status, msg); } else { // Ignored } } }; final boolean[] done = new boolean[1]; done[0] = false; Thread t = new Thread() { @Override public void run() { RecoverySystem rs = (RecoverySystem) mContext.getSystemService( Context.RECOVERY_SERVICE); String filename = null; try { filename = FileUtils.readTextFile(RecoverySystem.UNCRYPT_PACKAGE_FILE, 0, null); rs.processPackage(mContext, new File(filename), progressListener); } catch (IOException e) { Log.e(TAG, "Error uncrypting file", e); } done[0] = true; } }; t.start(); try { t.join(MAX_UNCRYPT_WAIT_TIME); } catch (InterruptedException unused) { } if (!done[0]) { Log.w(TAG, "Timed out waiting for uncrypt."); final int uncryptTimeoutError = 100; String timeoutMessage = String.format("uncrypt_time: %d\n" + "uncrypt_error: %d\n", MAX_UNCRYPT_WAIT_TIME / 1000, uncryptTimeoutError); try { FileUtils.stringToFile(RecoverySystem.UNCRYPT_STATUS_FILE, timeoutMessage); } catch (IOException e) { Log.e(TAG, "Failed to write timeout message to uncrypt status", e); } } }这里面有设置关机时长的代码吗
06-04
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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mounter625

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值