问题描述
之前有遇到一个bug,在一个第三方apk中播放视频时,按下电源键确认关机后,需要等待很长时间屏幕才会熄灭,而且在熄灭之前播放视频的画面会不断闪烁。下面跟踪一下问题发生的根源。
Android的关机流程是在一个ShutdownThread的线程中执行的,线程执行体run方法如下。本文主要关注两个点:ActivityManagerService的关闭和MountService的关闭。
/frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java
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();
}
};
/*
* 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");
}
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) {
}
}
Log.i(TAG, "Shutting down package manager...");
final PackageManagerService pm = (PackageManagerService)
ServiceManager.getService("package");
if (pm != null) {
pm.shutdown();
}
// Shutdown radios.
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) {
}
}
}
rebootOrShutdown(mReboot, mRebootReason);
}
ActivityManagerService的关闭要求
ActivityManagerService#shutdown的timeout参数为10s,表示如果超时时间为10s,超过这个时间就会直接返回。
/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public boolean shutdown(int timeout) {
if (checkCallingPermission(android.Manifest.permission.SHUTDOWN)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires permission "
+ android.Manifest.permission.SHUTDOWN);
}
boolean timedout = false;
synchronized(this) {
mShuttingDown = true;
updateEventDispatchingLocked();
timedout = mStackSupervisor.shutdownLocked(timeout);
}
mAppOpsService.shutdown();
if (mUsageStatsService != null) {
mUsageStatsService.prepareShutdown();
}
mBatteryStatsService.shutdown();
synchronized (this) {
mProcessStats.shutdownLocked();
notifyTaskPersisterLocked(null, true);
}
return timedout;
}
goingToSleepLocked用来进行关闭操作,内部会首次调用到ActivityStackSupervisor#checkReadyForSleepLocked来检测是否达到了ActivityManagerService的关闭要求,若未达到,进行Activity生命周期的转换。若达到了,会对每一个ActivityStack调用goToSleep函数,唤醒阻塞在mService的等待代码,那么shutdownLocked会提前返回,不用等到到超时时间到来。
ActivityManagerService的关闭要求是:1.每个ActivityStack没有mResumedActivity和mPausingActivity,即处于交互状态的activity和正在暂停的activity;2.ActivityStackSupervisor中mStoppingActivities和mGoingToSleepActivities为空,即没有正在停止的activity和正在睡眠的activity。这个目标条件在ActivityStackSupervisor#checkReadyForSleepLocked有体现。在关闭ActivityManagerService进行Activity状态变换的过程中,会多次调用ActivityStackSupervisor#checkReadyForSleepLocked来检测是否符合关闭的要求了,从而唤醒shutdownLocked结束掉关闭ActivityManagerService的流程。
ActivityStack#checkReadyForSleepLocked就是判断是否符合上一段阐述的ActivityManagerService的第一点关闭要求,若mResumedActivity不为空(这是大多数情况),则调用startPausingLocked将其pause掉,而startPausingLocked是一个异步过程。所以cantShutdown的值一般为true,于是shutdownLocked就会阻塞在mService.wait,等待达到关闭要求或者超时。
/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
boolean shutdownLocked(int timeout) {
goingToSleepLocked();//
boolean timedout = false;
final long endTime = System.currentTimeMillis() + timeout;
while (true) {
boolean cantShutdown = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
cantShutdown |= stacks.get(stackNdx).checkReadyForSleepLocked();
}
}
if (cantShutdown) {
long timeRemaining = endTime - System.currentTimeMillis();
if (timeRemaining > 0) {
try {
mService.wait(timeRemaining);
} catch (InterruptedException e) {
}
} else {
Slog.w(TAG, "Activity manager shutdown timed out");
timedout = true;
break;
}
} else {
break;
}
}
// Force checkReadyForSleep to complete.
mSleepTimeout = true;
checkReadyForSleepLocked();
return timedout;
}
/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
void goingToSleepLocked() {
scheduleSleepTimeout();
if (!mGoingToSleep.isHeld()) {
mGoingToSleep.acquire();
if (mLaunchingActivity.isHeld()) {
if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) {
throw new IllegalStateException("Calling must be system uid");
}
mLaunchingActivity.release();
mService.mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
}
}
checkReadyForSleepLocked();
}
/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
void checkReadyForSleepLocked() {
if (!mService.isSleepingOrShuttingDown()) {
// Do not care.
return;
}
if (!mSleepTimeout) {
boolean dontSleep = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
dontSleep |= stacks.get(stackNdx).checkReadyForSleepLocked();
}
}
if (mStoppingActivities.size() > 0) {
// Still need to tell some activities to stop; can't sleep yet.
if (DEBUG_PAUSE) Slog.v(TAG, "Sleep still need to stop "
+ mStoppingActivities.size() + " activities");
scheduleIdleLocked();
dontSleep = true;
}
if (mGoingToSleepActivities.size() > 0) {
// Still need to tell some activities to sleep; can't sleep yet.
if (DEBUG_PAUSE) Slog.v(TAG, "Sleep still need to sleep "
+ mGoingToSleepActivities.size() + " activities");
dontSleep = true;
}
if (dontSleep) {
return;
}
}
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
stacks.get(stackNdx).goToSleep();
}
}
removeSleepTimeouts();
if (mGoingToSleep.isHeld()) {
mGoingToSleep.release();
}
if (mService.mShuttingDown) {
mService.notifyAll();
}
}
/frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
boolean checkReadyForSleepLocked() {
if (mResumedActivity != null) {
// Still have something resumed; can't sleep until it is paused.
if (DEBUG_PAUSE) Slog.v(TAG, "Sleep needs to pause " + mResumedActivity);
if (DEBUG_USER_LEAVING) Slog.v(TAG, "Sleep => pause with userLeaving=false");
startPausingLocked(false, true, false, false);
return true;
}
if (mPausingActivity != null) {
// Still waiting for something to pause; can't sleep yet.
if (DEBUG_PAUSE) Slog.v(TAG, "Sleep still waiting to pause " + mPausingActivity);
return true;
}
return false;
}
startPausingLocked过程
startPausingLocked的四个参数分别是false,true,false,false。startPausingLocked的操作包括:1.清空mResumedActivity,设置mPausingActivity,修改ActivityState为PAUSING。2.异步调用schedulePauseActivity。
/frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, boolean resuming,
boolean dontWait) {
...
ActivityRecord prev = mResumedActivity;
if (prev == null) {
if (!resuming) {
Slog.wtf(TAG, "Trying to pause when nothing is resumed");
mStackSupervisor.resumeTopActivitiesLocked();
}
return false;
}
...
if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSING: " + prev);
else if (DEBUG_PAUSE) Slog.v(TAG, "Start pausing: " + prev);
mResumedActivity = null;
mPausingActivity = prev;//更新mPausingActivity
mLastPausedActivity = prev;//更新mLastPausedActivity
mLastNoHistoryActivity = (prev.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
|| (prev.info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0 ? prev : null;
prev.state = ActivityState.PAUSING;//更新状态为PAUSING
prev.task.touchActiveTime();
clearLaunchTime(prev);
final ActivityRecord next = mStackSupervisor.topRunningActivityLocked();
if (mService.mHasRecents && (next == null || next.noDisplay || next.task != prev.task || uiSleeping)) {
prev.updateThumbnailLocked(screenshotActivities(prev), null);
}
stopFullyDrawnTraceIfNeeded();
mService.updateCpuStats();
if (prev.app != null && prev.app.thread != null) {
if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending pause: " + prev);
try {
EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY,
prev.userId, System.identityHashCode(prev),
prev.shortComponentName);
mService.updateUsageStats(prev, false);
prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
userLeaving, prev.configChangeFlags, dontWait);//异步调用
} catch (Exception e) {
// Ignore exception, if process died other code will cleanup.
Slog.w(TAG, "Exception thrown during pause", e);
mPausingActivity = null;
mLastPausedActivity = null;
mLastNoHistoryActivity = null;
}
} else {
mPausingActivity = null;
mLastPausedActivity = null;
mLastNoHistoryActivity = null;
}
...
/frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
enum ActivityState {
INITIALIZING,
RESUMED,
PAUSING,
PAUSED,
STOPPING,
STOPPED,
FINISHING,
DESTROYING,
DESTROYED
}
schedulePauseActivity最终会调用到ActivityThread#handlePauseActivity。token参数表示ActivityManagerService端的ActivityRecord传过来的binder对象,finished表示该Activity是否是调用finish()来结束生命周期的,userLeaving表示是否是用户手动切换Activity,dontReport表示是否向ActivityManagerService报告Activity已暂停(false为会报告)。
/frameworks/base/core/java/android/app/ActivityThread.java
private void handlePauseActivity(IBinder token, boolean finished,
boolean userLeaving, int configChanges, boolean dontReport) {
ActivityClientRecord r = mActivities.get(token);
if (r != null) {
//Slog.v(TAG, "userLeaving=" + userLeaving + " handling pause of " + r);
if (userLeaving) {
//onUserInteraction和onUserLeaveHint在这里被调用,但本文中userLeaving为false,不走这里
performUserLeavingActivity(r);
}
r.activity.mConfigChangeFlags |= configChanges;
//onPause在这里被调用
performPauseActivity(token, finished, r.isPreHoneycomb());
// Make sure any pending writes are now committed.
if (r.isPreHoneycomb()) {
QueuedWork.waitToFinish();
}
// Tell the activity manager we have paused.
if (!dontReport) {
try {
ActivityManagerNative.getDefault().activityPaused(token);
} catch (RemoteException ex) {
}
}
mSomeActivitiesChanged = true;
}
}
/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
@Override
public final void activityPaused(IBinder token) {
final long origId = Binder.clearCallingIdentity();
synchronized(this) {
//找到Activity所在的ActivityStack
ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack != null) {
stack.activityPausedLocked(token, false);
}
}
Binder.restoreCallingIdentity(origId);
}
&esmp; startPausingLocked的dontWait参数( handlePauseActivity的dontReport参数)决定了completePauseLocked的调用时机,若为true,则在startPausingLocked异步调用后马上调用completePauseLocked;若为false,则等待Activity调用onPause后再调用completePauseLocked。这个参数通常和android:resumeWhilePausing属性相关联,表示不需要等待上一个Activity完全pause就可以resume当前的Activity。
/frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
final void activityPausedLocked(IBinder token, boolean timeout) {
if (DEBUG_PAUSE) Slog.v(
TAG, "Activity paused: token=" + token + ", timeout=" + timeout);
final ActivityRecord r = isInStackLocked(token);
if (r != null) {
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
if (mPausingActivity == r) {
if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSED: " + r
+ (timeout ? " (due to timeout)" : " (pause complete)"));
completePauseLocked(true);
} else {
EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE,
r.userId, System.identityHashCode(r), r.shortComponentName,
mPausingActivity != null
? mPausingActivity.shortComponentName : "(none)");
}
}
}
/frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
private void completePauseLocked(boolean resumeNext) {
ActivityRecord prev = mPausingActivity;
if (DEBUG_PAUSE) Slog.v(TAG, "Complete pause: " + prev);
if (prev != null) {
//更新ActivityState从PAUSING到PAUSED
prev.state = ActivityState.PAUSED;
if (prev.finishing) {
//需要finish的Activity走此分支(本文不走这里)
if (DEBUG_PAUSE) Slog.v(TAG, "Executing finish of activity: " + prev);
prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false);
} else if (prev.app != null) {
if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending stop: " + prev);
if (prev.waitingVisible) {
//waitingVisible在pause上一个activity完成后到新的Activity显现出来期间会被设为true(本文不走这里)
prev.waitingVisible = false;
mStackSupervisor.mWaitingVisibleActivities.remove(prev);
if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(
TAG, "Complete pause, no longer waiting: " + prev);
}
if (prev.configDestroy) {
//配置发生改变,需要destroy掉这个Activity(本文不走这里)
// The previous is being paused because the configuration
// is changing, which means it is actually stopping...
// To juggle the fact that we are also starting a new
// instance right now, we need to first completely stop
// the current instance before starting the new one.
if (DEBUG_PAUSE) Slog.v(TAG, "Destroying after pause: " + prev);
destroyActivityLocked(prev, true, "pause-config");
} else if (!hasVisibleBehindActivity()) {
//hasVisibleBehindActivity表示是否在被一个半透明的activity覆盖的时候继续显示,本文为false,若还能被用户看见,只需要pause不需要stop
// If we were visible then resumeTopActivities will release resources before
// stopping.
//加入到ActivityStackSupervisor的mStoppingActivities里面
mStackSupervisor.mStoppingActivities.add(prev);
//以下两种情况需要立刻进入stop流程:1.mStoppingActivities的数量大于3;2.TaskRecord数量小于或等于1且Activity处于该所在TaskRecord最上层位置
if (mStackSupervisor.mStoppingActivities.size() > 3 ||
prev.frontOfTask && mTaskHistory.size() <= 1) {
// If we already have a few activities waiting to stop,
// then give up on things going idle and start clearing
// them out. Or if r is the last of activity of the last task the stack
// will be empty and must be cleared immediately.
if (DEBUG_PAUSE) Slog.v(TAG, "To many pending stops, forcing idle");
mStackSupervisor.scheduleIdleLocked();
} else {
//本文走此分支,由于更新了mStoppingActivities,需要检查是否符合关闭ActivityManagerService的条件
mStackSupervisor.checkReadyForSleepLocked();
}
}
} else {
if (DEBUG_PAUSE) Slog.v(TAG, "App died during pause, not stopping: " + prev);
prev = null;
}
//清空mPausingActivity
mPausingActivity = null;
}
//resumeNext为true
if (resumeNext) {
final ActivityStack topStack = mStackSupervisor.getFocusedStack();
if (!mService.isSleepingOrShuttingDown()) {
mStackSupervisor.resumeTopActivitiesLocked(topStack, prev, null);
} else {
//本文走此分支,例行检查
mStackSupervisor.checkReadyForSleepLocked();
ActivityRecord top = topStack.topRunningActivityLocked(null);
//当最上面的TaskRecord的最上面的ActivityRecord为空或者不是当前处理的Activity时,需要启动一个Activity,本文不符合此条件。
if (top == null || (prev != null && top != prev)) {
// If there are no more activities available to run,
// do resume anyway to start something. Also if the top
// activity on the stack is not the just paused activity,
// we need to go ahead and resume it to ensure we complete
// an in-flight app switch.
mStackSupervisor.resumeTopActivitiesLocked(topStack, null, null);
}
}
}
...
ActivityStackSupervisor#checkReadyForSleepLocked当发现mStoppingActivities不为空时,会调用scheduleIdleLocked进入stop流程。
/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
if (mStoppingActivities.size() > 0) {
// Still need to tell some activities to stop; can't sleep yet.
if (DEBUG_PAUSE) Slog.v(TAG, "Sleep still need to stop "
+ mStoppingActivities.size() + " activities");
scheduleIdleLocked();
dontSleep = true;
}
activityIdleInternalLocked会处理mStoppingActivities和mFinishingActivities中的Activity的状态转换。
/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
Configuration config) {
if (localLOGV) Slog.v(TAG, "Activity idle: " + token);
ArrayList<ActivityRecord> stops = null;
ArrayList<ActivityRecord> finishes = null;
ArrayList<UserStartedState> startingUsers = null;
int NS = 0;
int NF = 0;
boolean booting = false;
boolean activityRemoved = false;
ActivityRecord r = ActivityRecord.forToken(token);
...
// Atomically retrieve all of the other things to do.
stops = processStoppingActivitiesLocked(true);
NS = stops != null ? stops.size() : 0;
if ((NF=mFinishingActivities.size()) > 0) {
finishes = new ArrayList<ActivityRecord>(mFinishingActivities);
mFinishingActivities.clear();
}
if (mStartingUsers.size() > 0) {
startingUsers = new ArrayList<UserStartedState>(mStartingUsers);
mStartingUsers.clear();
}
// Stop any activities that are scheduled to do so but have been
// waiting for the next one to start.
for (int i = 0; i < NS; i++) {
r = stops.get(i);
final ActivityStack stack = r.task.stack;
if (r.finishing) {
stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false);
} else {
stack.stopActivityLocked(r);
}
}
// Finish any activities that are scheduled to do so but have been
// waiting for the next one to start.
for (int i = 0; i < NF; i++) {
r = finishes.get(i);
activityRemoved |= r.task.stack.destroyActivityLocked(r, true, "finish-idle");
}
if (!booting) {
// Complete user switch
if (startingUsers != null) {
for (int i = 0; i < startingUsers.size(); i++) {
mService.finishUserSwitch(startingUsers.get(i));
}
}
// Complete starting up of background users
if (mStartingBackgroundUsers.size() > 0) {
startingUsers = new ArrayList<UserStartedState>(mStartingBackgroundUsers);
mStartingBackgroundUsers.clear();
for (int i = 0; i < startingUsers.size(); i++) {
mService.finishUserBoot(startingUsers.get(i));
}
}
}
mService.trimApplications();
//dump();
//mWindowManager.dump();
if (activityRemoved) {
resumeTopActivitiesLocked();
}
return r;
}
processStoppingActivitiesLocked的remove参数为true,遍历mStoppingActivities中所有要被处理的ActivityRecord,如果这个ActivityRecord没有在等待另一个ActivityRecord显示出来或者正在ActivtyManagerService正在睡眠或关闭,则将该ActivityRecord加入到stops中,最后返回。
/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
final ArrayList<ActivityRecord> processStoppingActivitiesLocked(boolean remove) {
int N = mStoppingActivities.size();
if (N <= 0) return null;
ArrayList<ActivityRecord> stops = null;
final boolean nowVisible = allResumedActivitiesVisible();
for (int i=0; i<N; i++) {
ActivityRecord s = mStoppingActivities.get(i);
...
if ((!s.waitingVisible || mService.isSleepingOrShuttingDown()) && remove) {
if (localLOGV) Slog.v(TAG, "Ready to stop: " + s);
if (stops == null) {
stops = new ArrayList<ActivityRecord>();
}
stops.add(s);
mStoppingActivities.remove(i);
N--;
i--;
}
}
return stops;
}
再回到activityIdleInternalLocked中,接下来会对processStoppingActivitiesLocked返回的ActivityRecord列表进行遍历,如果这个ActivityRecord需要被finish,则调用finishCurrentActivityLocked;否则,调用stopActivityLocked。本文走的是stopActivityLocked流程。
&esmp; stopActivityLocked关健步骤为:1.异步调用scheduleStopActivity进入stop流程。2.调用setSleeping进入睡眠状态。
/frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
final void stopActivityLocked(ActivityRecord r) {
...
if (r.app != null && r.app.thread != null) {
adjustFocusedActivityLocked(r, "stopActivity");
r.resumeKeyDispatchingLocked();
try {
r.stopped = false;
if (DEBUG_STATES) Slog.v(TAG, "Moving to STOPPING: " + r
+ " (stop requested)");
//调整ActivityState为STOPPING
r.state = ActivityState.STOPPING;
if (DEBUG_VISBILITY) Slog.v(
TAG, "Stopping visible=" + r.visible + " for " + r);
if (!r.visible) {//r.visible为true
mWindowManager.setAppVisibility(r.appToken, false);
}
r.app.thread.scheduleStopActivity(r.appToken, r.visible, r.configChangeFlags);
//ActivityManagerService正在关闭,则设置ActivityRecord为睡眠状态
if (mService.isSleepingOrShuttingDown()) {
r.setSleeping(true);
}
Message msg = mHandler.obtainMessage(STOP_TIMEOUT_MSG, r);
mHandler.sendMessageDelayed(msg, STOP_TIMEOUT);
} catch (Exception e) {
// Maybe just ignore exceptions here... if the process
// has crashed, our death notification will clean things
// up.
Slog.w(TAG, "Exception thrown during pause", e);
// Just in case, assume it to be stopped.
r.stopped = true;
if (DEBUG_STATES) Slog.v(TAG, "Stop failed; moving to STOPPED: " + r);
r.state = ActivityState.STOPPED;
if (r.configDestroy) {
destroyActivityLocked(r, true, "stop-except");
}
}
}
}
handleStopActivity的show参数为true,表示将要stop掉一个目前还可见的Activity。然后就是调用performStopActivityInner和post一个StopInfo的Runnable。
/frameworks/base/core/java/android/app/ActivityThread.java
private void handleStopActivity(IBinder token, boolean show, int configChanges) {
ActivityClientRecord r = mActivities.get(token);
r.activity.mConfigChangeFlags |= configChanges;
StopInfo info = new StopInfo();
performStopActivityInner(r, info, show, true);
if (localLOGV) Slog.v(
TAG, "Finishing stop of " + r + ": show=" + show
+ " win=" + r.window);
//更新Activity可见性
updateVisibility(r, show);
// Make sure any pending writes are now committed.
if (!r.isPreHoneycomb()) {
QueuedWork.waitToFinish();
}
// Schedule the call to tell the activity manager we have
// stopped. We don't do this immediately, because we want to
// have a chance for any other pending work (in particular memory
// trim requests) to complete before you tell the activity
// manager to proceed and allow us to go fully into the background.
info.activity = r;
info.state = r.state;
info.persistentState = r.persistentState;
mH.post(info);
mSomeActivitiesChanged = true;
}
先看performStopActivityInner。尤其要注意的是r,r.state是Activity保存数据的Bundle对象,在performStopActivityInner中,当r.state为null且不是用户手动finish这个Activity时,通过Activity#OnSaveInstanceState将View树的状态(滑块滚动位置、文本框输入等)数据保存在r.state表示的Bundle中,以便下次重启该Activity调用onRestoreInstanceState时将这些状态恢复。这对解决本文问题的一个关键点。
/frameworks/base/core/java/android/app/ActivityThread.java
private void performStopActivityInner(ActivityClientRecord r,
StopInfo info, boolean keepShown, boolean saveState) {
...
// Next have the activity save its current state and managed dialogs...
if (!r.activity.mFinished && saveState) {
if (r.state == null) {
callCallActivityOnSaveInstanceState(r);//OnSaveInstanceState在此处调用
}
}
//由于keepShown为true,此处并没有调用Activity#performStop进入onStop调用,而是延迟到后面才执行
if (!keepShown) {
try {
// Now we are idle.
r.activity.performStop();
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
"Unable to stop activity "
+ r.intent.getComponent().toShortString()
+ ": " + e.toString(), e);
}
}
r.stopped = true;
}
r.paused = true;
}
}
StopInfo作用是向ActivityManagerService报告Activity已经Stop了。
/frameworks/base/core/java/android/app/ActivityThread.java
private static class StopInfo implements Runnable {
ActivityClientRecord activity;
Bundle state;
PersistableBundle persistentState;
CharSequence description;
@Override public void run() {
// Tell activity manager we have been stopped.
try {
if (DEBUG_MEMORY_TRIM) Slog.v(TAG, "Reporting activity stopped: " + activity);
ActivityManagerNative.getDefault().activityStopped(
activity.token, state, persistentState, description);
} catch (RemoteException ex) {
}
}
}
最后一步,Activity的状态完成从RESUMED->PAUSING->PAUSED->STOPPING->STOPPED的状态转换。
/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
@Override
public final void activityStopped(IBinder token, Bundle icicle,
PersistableBundle persistentState, CharSequence description) {
if (localLOGV) Slog.v(TAG, "Activity stopped: token=" + token);
// Refuse possible leaked file descriptors
if (icicle != null && icicle.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Bundle");
}
final long origId = Binder.clearCallingIdentity();
synchronized (this) {
ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r != null) {
r.task.stack.activityStoppedLocked(r, icicle, persistentState, description);
}
}
trimApplications();
Binder.restoreCallingIdentity(origId);
}
/frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
final void activityStoppedLocked(ActivityRecord r, Bundle icicle,
PersistableBundle persistentState, CharSequence description) {
if (r.state != ActivityState.STOPPING) {
Slog.i(TAG, "Activity reported stop, but no longer stopping: " + r);
mHandler.removeMessages(STOP_TIMEOUT_MSG, r);
return;
}
if (persistentState != null) {
r.persistentState = persistentState;
mService.notifyTaskPersisterLocked(r.task, false);
}
if (DEBUG_SAVED_STATE) Slog.i(TAG, "Saving icicle of " + r + ": " + icicle);
if (icicle != null) {
//关键点:将ActivityThread端的ActivityRecordClient的数据转移到ActivityManagerService端的ActivityRecord,icicle 为保存数据的Bundle,havestate为true表示这个Activity是带着保存的状态进入STOPPED状态的
// If icicle is null, this is happening due to a timeout, so we
// haven't really saved the state.
r.icicle = icicle;
r.haveState = true;
r.launchCount = 0;
r.updateThumbnailLocked(null, description);
}
if (!r.stopped) {
if (DEBUG_STATES) Slog.v(TAG, "Moving to STOPPED: " + r + " (stop complete)");
mHandler.removeMessages(STOP_TIMEOUT_MSG, r);
r.stopped = true;
//更新ActivityState的状态为STOPPED
r.state = ActivityState.STOPPED;
if (mActivityContainer.mActivityDisplay.mVisibleBehindActivity == r) {
mStackSupervisor.requestVisibleBehindLocked(r, false);
}
if (r.finishing) {
r.clearOptionsLocked();
} else {
if (r.configDestroy) {
destroyActivityLocked(r, true, "stop-config");
mStackSupervisor.resumeTopActivitiesLocked();
} else {
mStackSupervisor.updatePreviousProcessLocked(r);
}
}
}
}
&esmp; 在ActivityManagerService要关闭的情况下,stop掉的Activity还要进行sleep。
/frameworks/base/services/core/java/com/android/server/am/ActivityRecord.java
public void setSleeping(boolean _sleeping) {
if (sleeping == _sleeping) {
return;
}
if (app != null && app.thread != null) {
try {
app.thread.scheduleSleeping(appToken, _sleeping);
if (_sleeping && !mStackSupervisor.mGoingToSleepActivities.contains(this)) {
//更新mGoingToSleepActivities,这是checkReadyForSleepLocked检测的内容之一
mStackSupervisor.mGoingToSleepActivities.add(this);
}
sleeping = _sleeping;
} catch (RemoteException e) {
Slog.w(TAG, "Exception thrown when sleeping: " + intent.getComponent(), e);
}
}
}
/frameworks/base/core/java/android/app/ActivityThread.java
private void handleSleeping(IBinder token, boolean sleeping) {
ActivityClientRecord r = mActivities.get(token);
if (r == null) {
Log.w(TAG, "handleSleeping: no activity for token " + token);
return;
}
if (sleeping) {
if (!r.stopped && !r.isPreHoneycomb()) {
try {
// Now we are idle.
//之前没有执行Activity#onStop,改在这里执行
r.activity.performStop();
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
"Unable to stop activity "
+ r.intent.getComponent().toShortString()
+ ": " + e.toString(), e);
}
}
r.stopped = true;
}
// Make sure any pending writes are now committed.
if (!r.isPreHoneycomb()) {
QueuedWork.waitToFinish();
}
// Tell activity manager we slept.
try {
ActivityManagerNative.getDefault().activitySlept(r.token);
} catch (RemoteException ex) {
}
} else {
if (r.stopped && r.activity.mVisibleFromServer) {
r.activity.performRestart();
r.stopped = false;
}
}
}
/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public final void activitySlept(IBinder token) {
if (localLOGV) Slog.v(TAG, "Activity slept: token=" + token);
final long origId = Binder.clearCallingIdentity();
synchronized (this) {
final ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r != null) {
mStackSupervisor.activitySleptLocked(r);
}
}
Binder.restoreCallingIdentity(origId);
}
&esmp; activitySleptLocked中会再次调用checkReadyForSleepLocked是否可以对所有的ActivityRecord进行goToSleep操作了。前面有提到,checkReadyForSleepLocked会确保所有的Activity都已经是STOPPED状态且没有正在睡眠的Activity(mGoingToSleepActivities为空)时,调用ActivityStack#goToSleep使所有已经STOPPED的activity进入睡眠状态。
/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
void activitySleptLocked(ActivityRecord r) {
mGoingToSleepActivities.remove(r);
checkReadyForSleepLocked();
}
/frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
void goToSleep() {
ensureActivitiesVisibleLocked(null, 0);//调整activity的可见性
// Make sure any stopped but visible activities are now sleeping.
// This ensures that the activity's onStop() is called.
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = activities.get(activityNdx);
if (r.state == ActivityState.STOPPING || r.state == ActivityState.STOPPED) {
r.setSleeping(true);
}
}
}
}
当所有的前提条件都满足后,ActivityStackSuperVisor#checkReadyForSleepLocked会通过mService.notifyAll唤醒阻塞在ActivityStackSuperVisor#shutdownLocked的代码,从而完成ActivityManagerService的关闭流程。
总结下,当开始关闭ActivityManagerService时,当前Resume状态的Activity要经历从RESUMED->PAUSING->PAUSED->STOPPING->STOPPED的状态变迁,此外还要保证其他非Resume状态的也要是STOPPED状态。对于进入了STOPPED状态的activity,还要让他们进入睡眠状态。
MountService的关闭
/frameworks/base/services/core/java/com/android/server/MountService.java
if (state.equals(Environment.MEDIA_MOUNTED)) {
// Post a unmount message.
ShutdownCallBack ucb = new ShutdownCallBack(path, mountShutdownLatch);
mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
}
/frameworks/base/services/core/java/com/android/server/MountService.java
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case H_UNMOUNT_PM_UPDATE: {
if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_UPDATE");
UnmountCallBack ucb = (UnmountCallBack) msg.obj;
mForceUnmounts.add(ucb);
if (DEBUG_UNMOUNT) Slog.i(TAG, " registered = " + mUpdatingStatus);
// Register only if needed.
if (!mUpdatingStatus) {
if (DEBUG_UNMOUNT) Slog.i(TAG, "Updating external media status on PackageManager");
mUpdatingStatus = true;
mPms.updateExternalMediaStatus(false, true);
}
break;
}
最终会调到对H_UNMOUNT_PM_DONE来进行卸载已挂载设备的准备工作和进行卸载。
/frameworks/base/services/core/java/com/android/server/MountService.java
case H_UNMOUNT_PM_DONE: {
if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_DONE");
if (DEBUG_UNMOUNT) Slog.i(TAG, "Updated status. Processing requests");
mUpdatingStatus = false;
int size = mForceUnmounts.size();//mForceUnmounts保存了一些需要强行卸载的设备信息
int sizeArr[] = new int[size];
int sizeArrN = 0;
// Kill processes holding references first
ActivityManagerService ams = (ActivityManagerService)
ServiceManager.getService("activity");
for (int i = 0; i < size; i++) {
UnmountCallBack ucb = mForceUnmounts.get(i);
String path = ucb.path;
boolean done = false;
if (!ucb.force) {
done = true;
} else {//ucb.force为true,走此分支
int pids[] = getStorageUsers(path);
if (pids == null || pids.length == 0) {
done = true;
} else {
// Eliminate system process here?
//ActivityManagerService杀掉占用设备的进程
ams.killPids(pids, "unmount media", true);
// Confirm if file references have been freed.
pids = getStorageUsers(path);
if (pids == null || pids.length == 0) {
done = true;
}
}
}
if (!done && (ucb.retries < MAX_UNMOUNT_RETRIES)) {
// Retry again
//未能成功杀掉进程,则在30ms后重复杀进程动作,最多进行四次
Slog.i(TAG, "Retrying to kill storage users again");
mHandler.sendMessageDelayed(
mHandler.obtainMessage(H_UNMOUNT_PM_DONE,
ucb.retries++),
RETRY_UNMOUNT_DELAY);
} else {//卸载设备动作
if (ucb.retries >= MAX_UNMOUNT_RETRIES) {
Slog.i(TAG, "Failed to unmount media inspite of " +
MAX_UNMOUNT_RETRIES + " retries. Forcibly killing processes now");
}
sizeArr[sizeArrN++] = i;
mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS,
ucb));
}
}
// Remove already processed elements from list.
for (int i = (sizeArrN-1); i >= 0; i--) {
mForceUnmounts.remove(sizeArr[i]);
}
break;
}
case H_UNMOUNT_MS: {
//处理卸载设备的Message
if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_MS");
UnmountCallBack ucb = (UnmountCallBack) msg.obj;
ucb.handleFinished();
break;
}
ActivityManagerService杀进程
ActivityManagerService使用killPids来杀进程。
/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public boolean killPids(int[] pids, String pReason, boolean secure) {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("killPids only available to the system");
}
String reason = (pReason == null) ? "Unknown" : pReason;
// XXX Note: don't acquire main activity lock here, because the window
// manager calls in with its locks held.
boolean killed = false;
synchronized (mPidsSelfLocked) {
int[] types = new int[pids.length];
int worstType = 0;
for (int i=0; i<pids.length; i++) {
ProcessRecord proc = mPidsSelfLocked.get(pids[i]);
if (proc != null) {
int type = proc.setAdj;
types[i] = type;
if (type > worstType) {
worstType = type;
}
}
}
// If the worst oom_adj is somewhere in the cached proc LRU range,
// then constrain it so we will kill all cached procs.
if (worstType < ProcessList.CACHED_APP_MAX_ADJ
&& worstType > ProcessList.CACHED_APP_MIN_ADJ) {
worstType = ProcessList.CACHED_APP_MIN_ADJ;
}
// If this is not a secure call, don't let it kill processes that
// are important.
if (!secure && worstType < ProcessList.SERVICE_ADJ) {
worstType = ProcessList.SERVICE_ADJ;
}
Slog.w(TAG, "Killing processes " + reason + " at adjustment " + worstType);
for (int i=0; i<pids.length; i++) {
ProcessRecord proc = mPidsSelfLocked.get(pids[i]);
if (proc == null) {
continue;
}
int adj = proc.setAdj;
if (adj >= worstType && !proc.killedByAm) {
proc.kill(reason, true);
killed = true;
}
}
}
return killed;
}
杀进程的方法时向该Activity所在进程和同一个进程组的进程发送SIGKILL信号。
/frameworks/base/services/core/java/com/android/server/am/ProcessRecord.java
void kill(String reason, boolean noisy) {
if (!killedByAm) {
if (noisy) {
Slog.i(ActivityManagerService.TAG, "Killing " + toShortString() + " (adj " + setAdj
+ "): " + reason);
}
EventLog.writeEvent(EventLogTags.AM_KILL, userId, pid, processName, setAdj, reason);
Process.killProcessQuiet(pid);
Process.killProcessGroup(info.uid, pid);
if (!persistent) {
killed = true;
killedByAm = true;
}
}
}
杀进程后重启Activity
在启动一个Activity所在的进程时,会调用到ActivityManagerService#attachApplicationLocked。当中会为进程注册binder死亡回调,以监听binder死亡事件来进行善后处理。
/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
try {
AppDeathRecipient adr = new AppDeathRecipient(
app, pid, thread);
thread.asBinder().linkToDeath(adr, 0);
app.deathRecipient = adr;
} catch (RemoteException e) {
app.resetPackageList(mProcessStats);
startProcessLocked(app, "link fail", processName);
return false;
}
/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
private final class AppDeathRecipient implements IBinder.DeathRecipient {
final ProcessRecord mApp;
final int mPid;
final IApplicationThread mAppThread;
AppDeathRecipient(ProcessRecord app, int pid,
IApplicationThread thread) {
if (localLOGV) Slog.v(
TAG, "New death recipient " + this
+ " for thread " + thread.asBinder());
mApp = app;
mPid = pid;
mAppThread = thread;
}
@Override
public void binderDied() {
if (localLOGV) Slog.v(
TAG, "Death received in " + this
+ " for thread " + mAppThread.asBinder());
synchronized(ActivityManagerService.this) {
appDiedLocked(mApp, mPid, mAppThread);
}
}
重点关注下handleAppDiedLocked函数。
/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
final void appDiedLocked(ProcessRecord app) {
appDiedLocked(app, app.pid, app.thread);
}
final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread) {
// First check if this ProcessRecord is actually active for the pid.
synchronized (mPidsSelfLocked) {
ProcessRecord curProc = mPidsSelfLocked.get(pid);
if (curProc != app) {
Slog.w(TAG, "Spurious death for " + app + ", curProc for " + pid + ": " + curProc);
return;
}
}
BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
synchronized (stats) {
stats.noteProcessDiedLocked(app.info.uid, pid);
}
if (!app.killed) {
Process.killProcessQuiet(pid);
Process.killProcessGroup(app.info.uid, pid);
app.killed = true;
}
// Clean up already done if the process has been re-started.
if (app.pid == pid && app.thread != null &&
app.thread.asBinder() == thread.asBinder()) {
boolean doLowMem = app.instrumentationClass == null;
boolean doOomAdj = doLowMem;
if (!app.killedByAm) {
Slog.i(TAG, "Process " + app.processName + " (pid " + pid
+ ") has died");
mAllowLowerMemLevel = true;
} else {
// Note that we always want to do oom adj to update our state with the
// new number of procs.
mAllowLowerMemLevel = false;
doLowMem = false;
}
EventLog.writeEvent(EventLogTags.AM_PROC_DIED, app.userId, app.pid, app.processName);
if (DEBUG_CLEANUP) Slog.v(
TAG, "Dying app: " + app + ", pid: " + pid
+ ", thread: " + thread.asBinder());
handleAppDiedLocked(app, false, true);
if (doOomAdj) {
updateOomAdjLocked();
}
if (doLowMem) {
doLowMemReportIfNeededLocked(app);
}
} else if (app.pid != pid) {
// A new process has already been started.
Slog.i(TAG, "Process " + app.processName + " (pid " + pid
+ ") has died and restarted (pid " + app.pid + ").");
EventLog.writeEvent(EventLogTags.AM_PROC_DIED, app.userId, app.pid, app.processName);
} else if (DEBUG_PROCESSES) {
Slog.d(TAG, "Received spurious death notification for thread "
+ thread.asBinder());
}
}
handleAppDiedLocked主要是两部分:1.将对应ProcessRecord关联的数据从一些记录的数据结构中移除;2.启动一个Activity以确保有Activity在显示。
/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
private final void handleAppDiedLocked(ProcessRecord app,
boolean restarting, boolean allowRestart) {
int pid = app.pid;
boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1);
if (!kept && !restarting) {
removeLruProcessLocked(app);
if (pid > 0) {
ProcessList.remove(pid);
}
}
if (mProfileProc == app) {
clearProfilerLocked();
}
// Remove this application's activities from active lists.
//将进程的关联数据从相关数据结构中移除
boolean hasVisibleActivities = mStackSupervisor.handleAppDiedLocked(app);
app.activities.clear();
if (app.instrumentationClass != null) {
Slog.w(TAG, "Crash of app " + app.processName
+ " running instrumentation " + app.instrumentationClass);
Bundle info = new Bundle();
info.putString("shortMsg", "Process crashed.");
finishInstrumentationLocked(app, Activity.RESULT_CANCELED, info);
}
if (!restarting) {
//启动一个Activity以确保有Activity在显示
if (!mStackSupervisor.resumeTopActivitiesLocked()) {
// If there was nothing to resume, and we are not already
// restarting this process, but there is a visible activity that
// is hosted by the process... then make sure all visible
// activities are running, taking care of restarting this
// process.
if (hasVisibleActivities) {
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
}
}
}
}
先看第一部分:
/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
boolean handleAppDiedLocked(ProcessRecord app) {
boolean hasVisibleActivities = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
hasVisibleActivities |= stacks.get(stackNdx).handleAppDiedLocked(app);
}
}
return hasVisibleActivities;
}
/frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
boolean handleAppDiedLocked(ProcessRecord app) {
if (mPausingActivity != null && mPausingActivity.app == app) {
if (DEBUG_PAUSE || DEBUG_CLEANUP) Slog.v(TAG,
"App died while pausing: " + mPausingActivity);
mPausingActivity = null;
}
if (mLastPausedActivity != null && mLastPausedActivity.app == app) {
mLastPausedActivity = null;
mLastNoHistoryActivity = null;
}
return removeHistoryRecordsForAppLocked(app);
}
removeHistoryRecordsForAppLocked首先从一系列数据结构中移除了与入参的ProcessRecord关联的ActivityRecord记录。接下来是根据remove的值决定是否从TaskRecord移除ActivityRecord,从WindowManagerService移除WindowToken等,总之就是处理与Activity或Window管理相关的数据结构。
在以下几种情况remove的值为true,表示需要移除该ActivityRecord:1.haveState为false(表示这个ActivityRecord不需要保存状态)且stateNotNeeded为false(由android:stateNotNeeded指定,“true” 表示可在不考虑其之前状态情况下重新启动,“false"表示需要之前的状态,默认值"false”)或者该ActivityRecord是需要finish的;2.该ActivityRecord启动次数大于2且最后一次启动距离现在不超过60s。其他情况remove为false。之前提过,Activity在不是finish的情况下会调用OnSaveInstanceState来进行状态的保存,导致activityStoppedLocked执行时haveState为true,所以remove为false,所以ActivityRecord不会从TaskRecord或者ActivityStack移除,只是简单地将ActivityRecord的ProcessRecord置成true。
/frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
boolean removeHistoryRecordsForAppLocked(ProcessRecord app) {
//分别从mLRUActivities,mStackSupervisor.mStoppingActivities,mStackSupervisor.mGoingToSleepActivities,mStackSupervisor.mWaitingVisibleActivities,mStackSupervisor.mFinishingActivities移除与入参的ProcessRecord关联的ActivityRecord
removeHistoryRecordsForAppLocked(mLRUActivities, app, "mLRUActivities");
removeHistoryRecordsForAppLocked(mStackSupervisor.mStoppingActivities, app,
"mStoppingActivities");
removeHistoryRecordsForAppLocked(mStackSupervisor.mGoingToSleepActivities, app,
"mGoingToSleepActivities");
removeHistoryRecordsForAppLocked(mStackSupervisor.mWaitingVisibleActivities, app,
"mWaitingVisibleActivities");
removeHistoryRecordsForAppLocked(mStackSupervisor.mFinishingActivities, app,
"mFinishingActivities");
boolean hasVisibleActivities = false;
// Clean out the history list.
//遍历这个ActivityStack的所有ActivityRecord,找到与入参的ProcessRecord对应的ActivityRecord,根据remove的值决定是否移除与ActivityRecord相关的数据
int i = numActivities();
if (DEBUG_CLEANUP) Slog.v(
TAG, "Removing app " + app + " from history with " + i + " entries");
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = activities.get(activityNdx);
--i;
if (DEBUG_CLEANUP) Slog.v(
TAG, "Record #" + i + " " + r + ": app=" + r.app);
if (r.app == app) {
boolean remove;
if ((!r.haveState && !r.stateNotNeeded) || r.finishing) {
// Don't currently have state for the activity, or
// it is finishing -- always remove it.
remove = true;
} else if (r.launchCount > 2 &&
r.lastLaunchTime > (SystemClock.uptimeMillis()-60000)) {
// We have launched this activity too many times since it was
// able to run, so give up and remove it.
remove = true;
} else {
// The process may be gone, but the activity lives on!
remove = false;
}
if (remove) {
if (DEBUG_ADD_REMOVE || DEBUG_CLEANUP) {
RuntimeException here = new RuntimeException("here");
here.fillInStackTrace();
Slog.i(TAG, "Removing activity " + r + " from stack at " + i
+ ": haveState=" + r.haveState
+ " stateNotNeeded=" + r.stateNotNeeded
+ " finishing=" + r.finishing
+ " state=" + r.state, here);
}
if (!r.finishing) {
Slog.w(TAG, "Force removing " + r + ": app died, no saved state");
EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
r.userId, System.identityHashCode(r),
r.task.taskId, r.shortComponentName,
"proc died without state saved");
if (r.state == ActivityState.RESUMED) {
mService.updateUsageStats(r, false);
}
}
removeActivityFromHistoryLocked(r, "appDied");
} else {
// We have the current state for this activity, so
// it can be restarted later when needed.
if (localLOGV) Slog.v(
TAG, "Keeping entry, setting app to null");
if (r.visible) {
hasVisibleActivities = true;
}
if (DEBUG_APP) Slog.v(TAG, "Clearing app during removeHistory for activity "
+ r);
r.app = null;
r.nowVisible = false;
if (!r.haveState) {
if (DEBUG_SAVED_STATE) Slog.i(TAG,
"App died, clearing saved state of " + r);
r.icicle = null;
}
}
//将ActivityRecord的状态转变为DESTROYED
cleanUpActivityLocked(r, true, true);
}
}
}
return hasVisibleActivities;
}
再看第二部分:将栈顶的一个Activity启动,以确保有一个Activity在显示。
/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
boolean resumeTopActivitiesLocked() {
return resumeTopActivitiesLocked(null, null, null);
}
boolean resumeTopActivitiesLocked(ActivityStack targetStack, ActivityRecord target,
Bundle targetOptions) {
if (targetStack == null) {
targetStack = getFocusedStack();
}
// Do targetStack first.
boolean result = false;
if (isFrontStack(targetStack)) {
result = targetStack.resumeTopActivityLocked(target, targetOptions);
}
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
if (stack == targetStack) {
// Already started above.
continue;
}
if (isFrontStack(stack)) {
stack.resumeTopActivityLocked(null);
}
}
}
return result;
}
由于占用挂载设备资源的ActivityRecord并未从TaskRecord和ActivityStack清除,resumeTopActivityInnerLocked会将其重新启动。
/frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
final boolean resumeTopActivityInnerLocked(ActivityRecord prev, Bundle options) {
...
// Find the first activity that is not finishing.
//topRunningActivityLocked返回的是该ActivityStack最上面的的一个TaskRecord的最上面的一个ActivityRecord
final ActivityRecord next = topRunningActivityLocked(null);
...
} else {
// Whoops, need to restart this activity!
if (!next.hasBeenLaunched) {
next.hasBeenLaunched = true;
} else {
if (SHOW_APP_STARTING_PREVIEW) {
mWindowManager.setAppStartingWindow(
next.appToken, next.packageName, next.theme,
mService.compatibilityInfoForPackageLocked(
next.info.applicationInfo),
next.nonLocalizedLabel,
next.labelRes, next.icon, next.logo, next.windowFlags,
null, true);
}
if (DEBUG_SWITCH) Slog.v(TAG, "Restarting: " + next);
}
if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Restarting " + next);
mStackSupervisor.startSpecificActivityLocked(next, true, true);
}
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
由于多次杀掉app进程又会重启占用挂载设备资源的Activity,所以还会进行3次的ActivityManagerService#killPids,在此过程的现象是,Activity被干掉后又重启,所以会出现画面闪烁的现象。
于是,MountService会强制卸载设备。
MountService传给Vold的命令是:“volume unmount 设备路径 force”。
/frameworks/base/services/core/java/com/android/server/MountService.java
private int doUnmountVolume(String path, boolean force, boolean removeEncryption) {
if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) {
return VoldResponseCode.OpFailedVolNotMounted;
}
/*
* Force a GC to make sure AssetManagers in other threads of the
* system_server are cleaned up. We have to do this since AssetManager
* instances are kept as a WeakReference and it's possible we have files
* open on the external storage.
*/
Runtime.getRuntime().gc();
// Redundant probably. But no harm in updating state again.
mPms.updateExternalMediaStatus(false, false);
try {
final Command cmd = new Command("volume", "unmount", path);
if (removeEncryption) {
cmd.appendArg("force_and_revert");
} else if (force) {
cmd.appendArg("force");
}
mConnector.execute(cmd);
// We unmounted the volume. None of the asec containers are available now.
synchronized (mAsecMountSet) {
mAsecMountSet.clear();
}
return StorageResultCode.OperationSucceeded;
} catch (NativeDaemonConnectorException e) {
// Don't worry about mismatch in PackageManager since the
// call back will handle the status changes any way.
int code = e.getCode();
if (code == VoldResponseCode.OpFailedVolNotMounted) {
return StorageResultCode.OperationFailedStorageNotMounted;
} else if (code == VoldResponseCode.OpFailedStorageBusy) {
return StorageResultCode.OperationFailedStorageBusy;
} else {
return StorageResultCode.OperationFailedInternalError;
}
}
}
Vold层卸载设备的代码。retries表示进行卸载操作的可重复次数,这里为10次。每次卸载操作之间的时间间隔为1s。首先是调用umount来卸载,成功的直接返回。不成功的话,就会调用Process::killProcessesWithOpenFiles进行检查操作。Process::killProcessesWithOpenFiles在前8次都是在检查/proc/{pid}/目录下的内容,以打印出无法卸载的原因。第9次会发送SIGTERM信号到要杀死的进程,第10次会发送SIGKILL信号到要杀死的进程。
我们也知道占用资源的那个Activity杀死后会重启,关机有很大一部分耗时发生在这里。最后只得放弃卸载设备,直接走PowerManagerService的关机流程了。
/system/vold/Volume.cpp
int Volume::doUnmount(const char *path, bool force) {
int retries = 10;
if (mDebug) {
SLOGD("Unmounting {%s}, force = %d", path, force);
}
while (retries--) {
if (!umount(path) || errno == EINVAL || errno == ENOENT) {
SLOGI("%s sucessfully unmounted", path);
return 0;
}
int action = 0;
if (force) {
if (retries == 1) {
action = 2; // SIGKILL
} else if (retries == 2) {
action = 1; // SIGHUP
}
}
SLOGW("Failed to unmount %s (%s, retries %d, action %d)",
path, strerror(errno), retries, action);
Process::killProcessesWithOpenFiles(path, action);
usleep(1000*1000);
}
errno = EBUSY;
SLOGE("Giving up on unmount %s (%s)", path, strerror(errno));
return -1;
}
/system/vold/Process.cpp
void Process::killProcessesWithOpenFiles(const char *path, int action) {
DIR* dir;
struct dirent* de;
if (!(dir = opendir("/proc"))) {
SLOGE("opendir failed (%s)", strerror(errno));
return;
}
while ((de = readdir(dir))) {
int killed = 0;
int pid = getPid(de->d_name);
char name[PATH_MAX];
if (pid == -1)
continue;
getProcessName(pid, name, sizeof(name));
char openfile[PATH_MAX];
if (checkFileDescriptorSymLinks(pid, path, openfile, sizeof(openfile))) {
SLOGE("Process %s (%d) has open file %s", name, pid, openfile);
} else if (checkFileMaps(pid, path, openfile, sizeof(openfile))) {
SLOGE("Process %s (%d) has open filemap for %s", name, pid, openfile);
} else if (checkSymLink(pid, path, "cwd")) {
SLOGE("Process %s (%d) has cwd within %s", name, pid, path);
} else if (checkSymLink(pid, path, "root")) {
SLOGE("Process %s (%d) has chroot within %s", name, pid, path);
} else if (checkSymLink(pid, path, "exe")) {
SLOGE("Process %s (%d) has executable path within %s", name, pid, path);
} else {
continue;
}
if (action == 1) {
SLOGW("Sending SIGHUP to process %d", pid);
kill(pid, SIGTERM);
} else if (action == 2) {
SLOGE("Sending SIGKILL to process %d", pid);
kill(pid, SIGKILL);
}
}
closedir(dir);
}
解决方法
问题发生在播放视频的Activity在stop后没有finish。根据代码可知,仅仅是stop并不会将ActivityRecord移除出TaskRecord。但是如果是手动finish的话,就会移除出TaskRecord。这样,重启的Activity就不是播放视频的Activity了,而是文件浏览的Activity(这个Activity不占用挂载设备资源),也就不会触发关机时间过长的bug。所以,在写播放视频的Activity代码时,尽量重写onStop方法,并手动调用finish()函数。
/frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
boolean removeHistoryRecordsForAppLocked(ProcessRecord app) {
...
if (r.app == app) {
boolean remove;
if ((!r.haveState && !r.stateNotNeeded) || r.finishing) {
// Don't currently have state for the activity, or
// it is finishing -- always remove it.
remove = true;
}