Android(安卓)4.4关机流程
基于Android 4.4得源码分析得。
最近有客户反馈Android得关机流程出现关机logo显示很久得问题,所有今天看下Android得关机流程(项目是基于4.4版本得)
长按power降会出现关机选择框源码在PhoneWindowManager.java中得interceptPowerKeyDown进行处理。
private void interceptPowerKeyDown(boolean handled) { mPowerKeyHandled = handled; if (!handled) { mHandler.postDelayed(mPowerLongPress, ViewConfiguration.getGlobalActionKeyTimeout()); } }
private final Runnable mPowerLongPress = new Runnable() { @Override public void run() { mBootFastRuning = true; // The context isn't read if (mLongPressOnPowerBehavior < 0) { mLongPressOnPowerBehavior = mContext.getResources().getInteger( com.android.internal.R.integer.config_longPressOnPowerBehavior); } int resolvedBehavior = mLongPressOnPowerBehavior; if (FactoryTest.isLongPressOnPowerOffEnabled()) { resolvedBehavior = LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM; } Slog.e(TAG, "resolvedBehavior: " + resolvedBehavior); switch (resolvedBehavior) { case LONG_PRESS_POWER_NOTHING: break; //弹框确认是否关机 case LONG_PRESS_POWER_GLOBAL_ACTIONS: mPowerKeyHandled = true; if (!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) { performAuditoryFeedbackForAccessibilityIfNeed(); } sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);showGlobalActionsDialog(); break; //直接关机 case LONG_PRESS_POWER_SHUT_OFF: case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM: mPowerKeyHandled = true; performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false); sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS); mWindowManagerFuncs.shutdown(resolvedBehavior == LONG_PRESS_POWER_SHUT_OFF); break; }if(DEBUG_BOOTFAST)Slog.d(TAG,"shutdown finish out");mBootFastRuning = false; } };
处理过程是在mPowerLongPress线程了,可以选择立刻关机或者弹框选择。接着看下showGlobalActionsDialog();得流程。
void showGlobalActionsDialog() { if (mGlobalActions == null) { //创建GlobalActions对象 mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs); } final boolean keyguardShowing = keyguardIsShowingTq(); //显示弹框 mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned()); if (keyguardShowing) { // since it took two seconds of long press to bring this up, // poke the wake lock so they have some time to see the dialog. mPowerManager.userActivity(SystemClock.uptimeMillis(), false); } }
调用showDialog来显示。
/** * Show the global actions dialog (creating if necessary) * @param keyguardShowing True if keyguard is showing */ public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) { mKeyguardShowing = keyguardShowing; mDeviceProvisioned = isDeviceProvisioned; if (mDialog != null) { //如果显示了,先关闭 mDialog.dismiss(); mDialog = null; // Show delayed, so that the dismiss of the previous dialog completes mHandler.sendEmptyMessage(MESSAGE_SHOW); } else { handleShow(); } }
如何dialog显示,先关闭,然后再显示,否则调用handleShow();
private void handleShow() { awakenIfNecessary(); mDialog = createDialog(); //创建dialog对象 prepareDialog(); //准备dialog WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes(); attrs.setTitle("GlobalActions"); mDialog.getWindow().setAttributes(attrs); mDialog.show(); //显示 mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND);mWindowManagerFuncs.shutdown(true); }
主要是创建dialog然后显示,接下里得关机动作在dialog中进行选择,看下createDialog得源码。
/** * Create the global actions dialog. * @return A new dialog. */ private GlobalActionsDialog createDialog() { // Simple toggle style if there's no vibrator, otherwise use a tri-state if (!mHasVibrator) { mSilentModeAction = new SilentModeToggleAction(); } else { mSilentModeAction = new SilentModeTriStateAction(mContext, mAudioManager, mHandler); } mAirplaneModeOn = new ToggleAction( R.drawable.ic_lock_airplane_mode, R.drawable.ic_lock_airplane_mode_off, R.string.global_actions_toggle_airplane_mode, R.string.global_actions_airplane_mode_on_status, R.string.global_actions_airplane_mode_off_status) { void onToggle(boolean on) { if (mHasTelephony && Boolean.parseBoolean( SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) { mIsWaitingForEcmExit = true; // Launch ECM exit dialog Intent ecmDialogIntent = new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null); ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(ecmDialogIntent); } else { changeAirplaneModeSystemSetting(on); } } @Override protected void changeStateFromPress(boolean buttonOn) { if (!mHasTelephony) return; // In ECM mode airplane state cannot be changed if (!(Boolean.parseBoolean( SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)))) { mState = buttonOn ? State.TurningOn : State.TurningOff; mAirplaneState = mState; } } public boolean showDuringKeyguard() { return true; } public boolean showBeforeProvisioning() { return true; } }; onAirplaneModeChanged(); mItems = new ArrayList(); // first: power off mItems.add( 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. mWindowManagerFuncs.shutdown(true); } public boolean onLongPress() { mWindowManagerFuncs.rebootSafeMode(true); return true; } public boolean showDuringKeyguard() { return true; } public boolean showBeforeProvisioning() { return true; } }); // reboot, added by yemao, 2013-8-22 12:57:21 mItems.add( new SinglePressAction( com.android.internal.R.drawable.ic_lock_reboot, R.string.global_action_reboot) { public void onPress() { // reboot mWindowManagerFuncs.reboot("Global Action Reboot!!", true); } public boolean onLongPress() { return true; } public boolean showDuringKeyguard() { return true; } public boolean showBeforeProvisioning() { return true; } }); // next: airplane mode // remove Airplane Toggle in non-telephony applications, yemao, 2013-5-27 20:39:12 // use the same property with which of baseband version used in the Settings.apk if (SystemProperties.getBoolean("ro.sw.embeded.telephony", false)) { mItems.add(mAirplaneModeOn); } // next: bug report, if enabled if (Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) { mItems.add( new SinglePressAction(com.android.internal.R.drawable.stat_sys_adb, R.string.global_action_bug_report) { public void onPress() { AlertDialog.Builder builder = new AlertDialog.Builder(mContext); builder.setTitle(com.android.internal.R.string.bugreport_title); builder.setMessage(com.android.internal.R.string.bugreport_message); builder.setNegativeButton(com.android.internal.R.string.cancel, null); builder.setPositiveButton(com.android.internal.R.string.report, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // Add a little delay before executing, to give the // dialog a chance to go away before it takes a // screenshot. mHandler.postDelayed(new Runnable() { @Override public void run() { try { ActivityManagerNative.getDefault() .requestBugReport(); } catch (RemoteException e) { } } }, 500); } }); AlertDialog dialog = builder.create(); dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); dialog.show(); } public boolean onLongPress() { return false; } public boolean showDuringKeyguard() { return true; } public boolean showBeforeProvisioning() { return false; } }); }
可以看到关机得函数是在mWindowManagerFuncs.shutdown(true);shutdown函数得实现是在WindowManagerService中得。
// Called by window manager policy. Not exposed externally. @Override public void shutdown(boolean confirm) { ShutdownThread.shutdown(mContext, PowerManager.SHUTDOWN_USER_REQUESTED, confirm); }
主要实现得还是子啊ShutdownThread得线程里。
/** * 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. * @param confirm true if user confirmation is needed before shutting down. */ public static void shutdown(final Context context, boolean confirm) { mReboot = false; mRebootSafeMode = false; shutdownInner(context, confirm); }
接着看下shutdownInner是如何实现得。
static void shutdownInner(final Context context, boolean confirm) { // 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; } } 获取长按资源id 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(); } // add for reboot Global Action, yemao, 2013-8-22 22:08:49 (mRebootSafeMode == true){ //再次确认是否关机,所以需要创建弹框 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(); } else { if(Settings.Global.getInt(context.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 1)==1&&SystemProperties.getBoolean("ro.sys.bootfast", false)){ boolean [] enableBootFast = {false}; enableBootFast[0] = Settings.System.getIntForUser(context.getContentResolver(), Settings.System.BOOT_FAST_ENABLE, 0,UserHandle.USER_CURRENT)==0?false:true; sConfirmDialog = new AlertDialog.Builder(context) .setTitle(mRebootSafeMode ? com.android.internal.R.string.reboot_safemode_title : com.android.internal.R.string.power_off) .setMultiChoiceItems(com.android.internal.R.array.quick_boot_mode,enableBootFast,new DialogInterface.OnMultiChoiceClickListener(){ public void onClick(DialogInterface dialog, int which, boolean isChecked) { Log.d(TAG,"which = "+ which + "isChecked = " + isChecked); if(which==0){ if(isChecked){ Settings.System.putIntForUser(context.getContentResolver(), Settings.System.BOOT_FAST_ENABLE, 1,UserHandle.USER_CURRENT); }else{ Settings.System.putIntForUser(context.getContentResolver(), Settings.System.BOOT_FAST_ENABLE, 0,UserHandle.USER_CURRENT); } } } }) .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) {if(mPolicy!=null){mPolicy.acquireBAView();} beginShutdownSequence(context); } }) .setNegativeButton(com.android.internal.R.string.no, null) .create(); }else{ beginShutdownSequence(context); } } } else { //如果不需要确认就直接关机 beginShutdownSequence(context); } }
接着看下beginShutdownSequence得流程。
private static void beginShutdownSequence(Context context) { synchronized (sIsStartedGuard) { if (sIsStarted) { Log.d(TAG, "Shutdown sequence already running, returning."); return; } sIsStarted = true; }SystemProperties.set("sys.start_shutdown", "1");SystemProperties.set("sys.fasboot_shutdown", "1");if(SystemProperties.getBoolean("ro.sys.bootfast", false)&&(1==Settings.System.getIntForUser(context.getContentResolver(), Settings.System.BOOT_FAST_ENABLE, 0,UserHandle.USER_CURRENT))){ mBootFastEnable = true; }else{ mBootFastEnable = false; }if(mReboot){mBootFastEnable = false;Log.d(TAG,"reboot!");}if(mRebootSafeMode){mBootFastEnable = false;Log.d(TAG,"Go Into Safe Mode real reboot");}if(Zygote.systemInSafeMode){mBootFastEnable = false;Log.d(TAG,"In Safe Mode real reboot");}if(mRebootReason!=null){mBootFastEnable = false;Log.d(TAG,"have reason " + mRebootReason + "real reboot");}if(SystemProperties.getInt("sys.battery_zero",0)==1){mBootFastEnable = false;Log.d(TAG,"Battery to low we really shutdown!");}if(SystemProperties.getInt("sys.temperature_high",0)==1){mBootFastEnable = false;Log.d(TAG,"temperature high we relly shutdown!");} //显示关机进度得画面。 final ProgressDialog pd = new ProgressDialog(context); if(mReboot){pd.setTitle(context.getText(com.android.internal.R.string.global_action_reboot));pd.setMessage(context.getText(com.android.internal.R.string.reboot_title));}else{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); //关机得处理核心流程sInstance = new ShutdownThread(); 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; } } // start the thread that initiates shutdown sInstance.mHandler = new Handler() { @Override public void handleMessage(Message msg){ switch(msg.what) { case CLOSE_PROCESS_DIALOG: Log.v(TAG,"close process dialog now"); pd.dismiss(); break; } } }; sInstance.start(); }
显示关机画面,然后掉ShutDownThread得润函数。
/** * Makes sure we handle the shutdown gracefully. * Shuts off power regardless of radio and bluetooth state if the alloted time has passed. */ public void run() { 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(); } }; if(!mBootFastEnable){ int shutdownanimation = SystemProperties.getInt("persist.sys.shutdownanimation", 0); int value = Settings.System.getIntForUser(mContext.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT); int rotation = Surface.ROTATION_0; if (shutdownanimation == 1) { IWindowManager mWindowManager = IWindowManager.Stub.asInterface(ServiceManager.getService("window")); if (mWindowManager != null) { try{ rotation = mWindowManager.getRotation(); mWindowManager.freezeRotation(Surface.ROTATION_0); mWindowManager.updateRotation(true, true); } catch (RemoteException e) { e.printStackTrace(); } } if (rotation != Surface.ROTATION_0) { SystemClock.sleep(600); } else { SystemClock.sleep(200); } SystemProperties.set("sys.shutdown_animation", "shutdown"); SystemProperties.set("service.bootanim.exit", "0"); SystemProperties.set("ctl.start", "bootanim"); } /* * 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") + (mRebootReason != null ? mRebootReason : ""); 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"); }killRemoveActivity(mContext);killRemoveService(mContext); 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); 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; } try { mActionDoneSync.wait(delay); } catch (InterruptedException e) { } } } Log.i(TAG, "Shutting down activity manager..."); final IActivityManager am = ActivityManagerNative.asInterface(ServiceManager.checkService("activity")); if (am != null) { try { am.shutdown(MAX_BROADCAST_TIME); } catch (RemoteException e) { } } //Allwinner, if no radio,not need shutdown if(SystemProperties.get(PROPERTY_EMBEDED_TELEPHONY).equals("true")){ shutdownRadios(MAX_RADIO_WAIT_TIME); } // Shutdown MountService to ensure media is in a safe state IMountShutdownObserver observer = new IMountShutdownObserver.Stub() { public void onShutDownComplete(int statusCode) throws RemoteException { Log.w(TAG, "Result code " + statusCode + " from MountService.shutdown"); actionDone(); } }; Log.i(TAG, "Shutting down MountService"); // Set initial variables and time out time. mActionDone = false; final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME; synchronized (mActionDoneSync) { try { final IMountService mount = IMountService.Stub.asInterface( ServiceManager.checkService("mount")); if (mount != null) { mount.shutdown(observer); } else { Log.w(TAG, "MountService unavailable for shutdown"); } } catch (Exception e) { Log.e(TAG, "Exception during MountService shutdown", e); } while (!mActionDone) { long delay = endShutTime - SystemClock.elapsedRealtime(); if (delay <= 0) { Log.w(TAG, "Shutdown wait timed out"); break; } try { mActionDoneSync.wait(delay); } catch (InterruptedException e) { } } } if (shutdownanimation == 1) { if (value == 0) { Settings.System.putInt(mContext.getContentResolver(), Settings.System.USER_ROTATION, rotation); } Settings.System.putIntForUser(mContext.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, value, UserHandle.USER_CURRENT); } rebootOrShutdown(mReboot, mRebootReason); } else{//Allwiner, fast boot shut begin if (SHUTDOWN_VIBRATE_MS > 0) { // vibrate before shutting down Vibrator vibrator = new SystemVibrator(); if(vibrator.hasVibrator()){ try { vibrator.vibrate(SHUTDOWN_VIBRATE_MS); } catch (Exception e) { // Failure to vibrate shouldn't interrupt shutdown. Just log it. Log.w(TAG, "Failed to vibrate during shutdown.", e); } } }if(mPolicy!=null){ //关屏mPolicy.hideScreen(true);mPolicy.enableKeyguard(true);Log.d(TAG,"enable keyguard true");}IWindowManager mWindowManager;mWindowManager = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));if(mWindowManager != null){try{boolean alreadyfreeze = Settings.System.getIntForUser(mContext.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) == 0;Log.d(TAG,"alreadyfreeze = " + alreadyfreeze);Log.d(TAG,"rotation = " + mWindowManager.getRotation());Settings.System.putInt(mContext.getContentResolver(), Settings.System.ROTATION_MODE_SET,alreadyfreeze?(0 - mWindowManager.getRotation()):1); mWindowManager.freezeRotation(Surface.ROTATION_0); mWindowManager.updateRotation(true, true); mWindowManager.setEventDispatching(false); }catch (RemoteException e) { }}//shutdownRadios(MAX_RADIO_WAIT_TIME); //设置飞行模式setAirplaneModeState(true); //set airplane mode SystemClock.sleep(BOOTFAST_WAIT_TIME); //杀掉activity killRemoveActivity(mContext); //杀掉service killRemoveService(mContext);//MobileDirectController.getInstance().setNetworkEnable(false);sIsStarted = false;try{sInstance.mCpuWakeLock.release();sInstance.mScreenWakeLock.release();}catch(SecurityException e){SystemProperties.set("sys.start_shutdown", "0");} SystemClock.sleep(BOOTFAST_WAIT_TIME);sInstance.mHandler.sendEmptyMessage(CLOSE_PROCESS_DIALOG);Log.v(TAG,"CLOSE_PROCESS_DIALOG");SystemProperties.set("sys.start_shutdown", "0");sInstance.mPowerManager.goToBootFastSleep(SystemClock.uptimeMillis());} }
主要流程是关闭一下模块:
ActivityManagerService
PackageManagerService
phone
Bluetooth Radio
MountService
有的模块可能需要监听手机关机事件,所以在关机时发送关机广播,通知相关模块处理。
而在关机过程中为了不损坏手机性能,记录当前一些状态,需要将一些模块服务进程先关闭,然后才进行关机。在本问题将着重讲解该过程。
最后,调用power进行关机,实际就是在SystemProperties中设置相关数据,让底层进行读取,执行Builtins.c的do_powerctl函数,最终通过bionic的reboot.cpp 调用kernel中kernel_power_off进行关机。
接着看下关于关机sd卡得处理逻辑。
(图片链接https://img-blog.csdn.net/20170119201115531?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZnVfa2V2aW4wNjA2/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
在关机的时候如果sdcard处于MOUNTED状态下,就需要发送消息H_UNMOUNT_PM_UPDATE将sdcard进行unmount操作。
如果sdcard为emulated模拟内卡,就不需要进行unmount操作,所以模拟内卡比物理内卡的手机关机速度要快很多。
在进行unmount操作时,需要先调用PackageManagerService进行更新一下包状态,删除安装在sd卡上的软件。完成之后回调到MountService中。
之后需要向底层发送命令查询占用sd卡的进程(如数据库,slog所在进程),然后调用AMS将占用sd卡的进程杀死。由于占用sd卡的进程比较顽强,可能杀死后还会起来,所以杀完后再进行查询占用sd卡的进程。如果所查进程数仍然不为NULL,就会再次杀,最多尝试杀四次,如果还杀不死就调用底层vold进行强制杀死。故,如果占用sd卡的进程较多,这部分就会较耗时。
上层的准备工作处理完毕后,就需要向底层发送命令来进行卸载sd卡。
底层在卸载sd卡的过程中,要将sd卡的状态改变发广播到上层,通知MountSevice。
sd卡状态由Mounted变为Unmounting时,为了给上层framework一定的反应时间,底层在发送广播后睡了1s。
之后开始umount操作,如果在上层没有将占用sd卡的进程杀死,就会在底层强制杀进程。系统原生给了10次机会,每次umount失败一次系统都会睡1s之后在尝试umount,如果在最后一次仍然没有成功就强制杀进程。所以重新umount的次数越多耗时就会越长。
所以在关机得时间长,很有可能是跟sd卡得卸载异常有关系,可能是当时得sd卡得状态有异常。
更多相关文章
- Android翻译:应用程序的生命周期 kill进程
- 通过网络使用ADB ( Connect to android with ADB over TCP )
- Android开发艺术探索读书笔记(第二章)
- Android(安卓)横竖屏切换时候重新进行数据请求问题
- Android(安卓)进程管理
- android SystemClock设置系统时间需要system权限
- android文件系统挂载分析(1)---正常开机挂载,分区信息解读
- Android(安卓)IPC 机制,进程间通信
- Android(安卓)7.1.2(Android(安卓)N) Activity启动流程分析