基于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卡得状态有异常。