android.process.media crash后现象研究
在ActivityManagerService启动android.process.media进程时,在启动进程后会调用attachApplicationLocked,在其中利用Binder的linkToDeath方法来接收进程退出消息。
在进程crash后,如果进程中存在Service在运行或者有Provider等待运行则进程会被自动运行。
ActivityManagerService.java
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {...
// If this application record is still attached to a previous
// process, clean it up now.
if (app.thread != null) {
handleAppDiedLocked(app, true);
}
String processName = app.processName;
try {
thread.asBinder().linkToDeath(new AppDeathRecipient(
app, pid, thread), 0);
} catch (RemoteException e) {
app.resetPackageList();
startProcessLocked(app, "link fail", processName);
return false;
}
一旦监控的进程退出,则AppDeathRecipient.binderDied()将被调用。
private final class AppDeathRecipient implements IBinder.DeathRecipient {
final ProcessRecord mApp;
final int mPid;
final IApplicationThread mAppThread;
AppDeathRecipient(ProcessRecord app, int pid,
IApplicationThread thread) {
mApp = app; //待监听应用进程
mPid = pid; //被监听进程ID
mAppThread = thread; //被监听进程的binder句柄
}
public void binderDied() {
synchronized(ActivityManagerService.this) {
dyingPidLocked = mPid;
appDiedLocked(mApp, mPid, mAppThread); //调用appDiedLocked
dyingPidLocked = -1;
}
}
}
final void appDiedLocked(ProcessRecord app, int pid,
IApplicationThread thread) {...
mProcDeaths[0]++; //死亡的进程个数加1,代表自最近启动进程以来死亡的进程数,startProcessLocked中会清零
BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
synchronized (stats) {
stats.noteProcessDiedLocked(app.info.uid, pid); //从电池用量计数进程表中删除
}
// Clean up already done if the process has been re-started.
if (app.pid == pid && app.thread != null &&
app.thread.asBinder() == thread.asBinder()) {
if (!app.killedBackground) {
Slog.i(TAG, "Process " + app.processName + " (pid " + pid
+ ") has died.");
}
EventLog.writeEvent(EventLogTags.AM_PROC_DIED, app.pid, app.processName);
if (localLOGV) Slog.v(
TAG, "Dying app: " + app + ", pid: " + pid
+ ", thread: " + thread.asBinder());
boolean doLowMem = app.instrumentationClass == null;
handleAppDiedLocked(app, false);
if (doLowMem) {
// If there are no longer any background processes running,
// and the app that died was not running instrumentation,
// then tell everyone we are now low on memory.
boolean haveBg = false;
for (int i=mLruProcesses.size()-1; i>=0; i--) {
ProcessRecord rec = mLruProcesses.get(i);
if (rec.thread != null && rec.setAdj >= HIDDEN_APP_MIN_ADJ) {
haveBg = true;
break;
}
}
if (!haveBg) {
Slog.i(TAG, "Low Memory: No more background processes.");
EventLog.writeEvent(EventLogTags.AM_LOW_MEMORY, mLruProcesses.size());
long now = SystemClock.uptimeMillis();
for (int i=mLruProcesses.size()-1; i>=0; i--) {
ProcessRecord rec = mLruProcesses.get(i);
if (rec != app && rec.thread != null &&
(rec.lastLowMemory+GC_MIN_INTERVAL) <= now) {
// The low memory report is overriding any current
// state for a GC request. Make sure to do
// heavy/important/visible/foreground processes first.
if (rec.setAdj <= HEAVY_WEIGHT_APP_ADJ) {
rec.lastRequestedGc = 0;
} else {
rec.lastRequestedGc = rec.lastLowMemory;
}
rec.reportLowMemory = true;
rec.lastLowMemory = now;
mProcessesToGc.remove(rec);
addProcessToGcListLocked(rec);
}
}
scheduleAppGcsLocked();
}
}
} 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.pid, app.processName);
} else {
if (DEBUG_PROCESSES) {
Slog.d(TAG, "Received spurious death notification for thread "
+ thread.asBinder());
}
if (app.bad) {
// When the process is marked "bad" because of its frequent crashes,
// WindowManagerService may lose focus window and go into infinite loop.
// Find a top alive activity from mHistory to recover from the wrong loop.
mMainStack.resumeTopActivityLocked(null);
}
}
}
private final void handleAppDiedLocked(ProcessRecord app,
boolean restarting) {
cleanUpApplicationRecordLocked(app, restarting, -1);
if (!restarting) {
mLruProcesses.remove(app);
}
// Just in case...
if (mMainStack.mPausingActivity != null && mMainStack.mPausingActivity.app == app) {
if (DEBUG_PAUSE) Slog.v(TAG, "App died while pausing: " +mMainStack.mPausingActivity);
mMainStack.mPausingActivity = null;
}
if (mMainStack.mLastPausedActivity != null && mMainStack.mLastPausedActivity.app == app) {
mMainStack.mLastPausedActivity = null;
}
// Remove this application's activities from active lists.
mMainStack.removeHistoryRecordsForAppLocked(app);
boolean atTop = true;
boolean hasVisibleActivities = false;
// Clean out the history list.
int i = mMainStack.mHistory.size();
if (localLOGV) Slog.v(
TAG, "Removing app " + app + " from history with " + i + " entries");
while (i > 0) {
i--;
ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
if (localLOGV) Slog.v(
TAG, "Record #" + i + " " + r + ": app=" + r.app);
if (r.app == app) {
if ((!r.haveState && !r.stateNotNeeded) || r.finishing) {
if (localLOGV) Slog.v(
TAG, "Removing this entry! frozen=" + r.haveState
+ " finishing=" + r.finishing);
mMainStack.mHistory.remove(i);
sendHistoryChangeNoti(r.task.taskId);
r.inHistory = false;
mWindowManager.removeAppToken(r);
if (VALIDATE_TOKENS) {
mWindowManager.validateAppTokens(mMainStack.mHistory);
}
r.removeUriPermissionsLocked();
} 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;
}
r.app = null;
r.nowVisible = false;
if (!r.haveState) {
r.icicle = null;
}
}
r.stack.cleanUpActivityLocked(r, true);
r.state = ActivityState.STOPPED;
}
atTop = false;
}
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) {
if (!mMainStack.resumeTopActivityLocked(null)) {
// 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) {
mMainStack.ensureActivitiesVisibleLocked(null, 0);
}
}
}
}
在此决定是否重启该进程或进程中正在运行的Service,并清除所有Provider及Service记录
/**
* Main code for cleaning up a process when it has gone away. This is
* called both as a result of the process dying, or directly when stopping
* a process when running in single process mode.
*/
private final void cleanUpApplicationRecordLocked(ProcessRecord app,
boolean restarting, int index) {
if (index >= 0) {
mLruProcesses.remove(index);
}
mProcessesToGc.remove(app);
synchronized(app) {
if (!app.bad && (app.crashDialog != null)) {
app.crashDialog.dismiss();
}
app.crashDialog = null;
}
if (app.anrDialog != null) {
app.anrDialog.dismiss();
app.anrDialog = null;
}
if (app.waitDialog != null) {
app.waitDialog.dismiss();
app.waitDialog = null;
}
app.crashing = false;
app.notResponding = false;
app.resetPackageList();
app.thread = null;
app.forcingToForeground = null;
app.foregroundServices = false;
killServicesLocked(app, true);
boolean restart = false;
int NL = mLaunchingProviders.size();
// Remove published content providers.
if (!app.pubProviders.isEmpty()) {
Iterator<ContentProviderRecord> it = app.pubProviders.values().iterator();
while (it.hasNext()) {
ContentProviderRecord cpr = it.next();
cpr.provider = null;
cpr.app = null;
// See if someone is waiting for this provider... in which
// case we don't remove it, but just let it restart.
int i = 0;
if (!app.bad) {
for (; i<NL; i++) {
if (mLaunchingProviders.get(i) == cpr) {
restart = true;
break;
}
}
} else {
i = NL;
}
if (i >= NL) {
removeDyingProviderLocked(app, cpr);
NL = mLaunchingProviders.size();
}
}
app.pubProviders.clear();
}
// Take care of any launching providers waiting for this process.
if (checkAppInLaunchingProvidersLocked(app, false)) { //仅当有应用依赖与某Provider且此Provider尚未publish则会自动重启crash进程,所有Provider将会在进程运行时被启动
restart = true;
}
// Unregister from connected content providers.
if (!app.conProviders.isEmpty()) {
Iterator it = app.conProviders.keySet().iterator();
while (it.hasNext()) {
ContentProviderRecord cpr = (ContentProviderRecord)it.next();
cpr.clients.remove(app);
}
app.conProviders.clear();
}
skipCurrentReceiverLocked(app);
// Unregister any receivers.
if (app.receivers.size() > 0) {
Iterator<ReceiverList> it = app.receivers.iterator();
while (it.hasNext()) {
removeReceiverLocked(it.next());
}
app.receivers.clear();
}
// If the app is undergoing backup, tell the backup manager about it
if (mBackupTarget != null && app.pid == mBackupTarget.app.pid) {
if (DEBUG_BACKUP) Slog.d(TAG, "App " + mBackupTarget.appInfo + " died during backup");
try {
IBackupManager bm = IBackupManager.Stub.asInterface(
ServiceManager.getService(Context.BACKUP_SERVICE));
bm.agentDisconnected(app.info.packageName);
} catch (RemoteException e) {
// can't happen; backup manager is local
}
}
// If the caller is restarting this app, then leave it in its
// current lists and let the caller take care of it.
if (restarting) {
return;
}
if (!app.persistent) {
if (DEBUG_PROCESSES) Slog.v(TAG,
"Removing non-persistent process during cleanup: " + app);
mProcessNames.remove(app.processName, app.info.uid);
if (mHeavyWeightProcess == app) {
mHeavyWeightProcess = null;
mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
}
} else if (!app.removed) {
// This app is persistent, so we need to keep its record around.
// If it is not already on the pending app list, add it there
// and start a new process for it.
app.thread = null;
app.forcingToForeground = null;
app.foregroundServices = false;
if (mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
restart = true;
}
}
if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG,
"Clean-up removing on hold: " + app);
mProcessesOnHold.remove(app);
if (app == mHomeProcess) {
mHomeProcess = null;
mHomeTask = -1;
}
if (restart) {
// We have components that still need to be running in the
// process, so re-launch it.
mProcessNames.put(app.processName, app.info.uid, app);
startProcessLocked(app, "restart", app.processName); //重启crash的应用
} else if (app.pid > 0 && app.pid != MY_PID) {
// Goodbye!
synchronized (mPidsSelfLocked) {
if (dyingPidLocked == -1) {
mPidsSelfLocked.remove(app.pid);
} else {
mPidsSelfLocked.remove(dyingPidLocked);
}
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
}
// If the dyingPid matches app.pid, there is no race between deatch
// and re-birth, so it's ok to clear app.pid here
if ((dyingPidLocked == -1) || (dyingPidLocked == app.pid)) {
app.setPid(0);
} else {
if (DEBUG_PROCESSES) Slog.v(TAG, "hhmm - Race, skipped setting app.pid to 0" );
}
}
}
以下函数将清除进程app内的Service及所有连接,如果allowRestart为true且非多次crash(<2),则将发消息以重启该服务。
private final void killServicesLocked(ProcessRecord app,boolean allowRestart) {
// 当进程crash时,allowRestart必为true
// Clean up any connections this application has to other services.
if (app.connections.size() > 0) {
Iterator<ConnectionRecord> it = app.connections.iterator();
while (it.hasNext()) {
ConnectionRecord r = it.next();
removeConnectionLocked(r, app, null);
}
}
app.connections.clear();
if (app.services.size() != 0) { //存在Service否?
// Any services running in the application need to be placed
// back in the pending list.
Iterator<ServiceRecord> it = app.services.iterator();
while (it.hasNext()) {
ServiceRecord sr = it.next();
synchronized (sr.stats.getBatteryStats()) {
sr.stats.stopLaunchedLocked();
}
sr.app = null;
sr.executeNesting = 0;
if (mStoppingServices.remove(sr)) {
if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr);
}
boolean hasClients = sr.bindings.size() > 0;
if (hasClients) {
Iterator<IntentBindRecord> bindings
= sr.bindings.values().iterator();
while (bindings.hasNext()) {
IntentBindRecord b = bindings.next();
if (DEBUG_SERVICE) Slog.v(TAG, "Killing binding " + b
+ ": shouldUnbind=" + b.hasBound);
b.binder = null;
b.requested = b.received = b.hasBound = false;
}
}
if (sr.crashCount >= 2) { //如果多次crash
Slog.w(TAG, "Service crashed " + sr.crashCount
+ " times, stopping: " + sr);
EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH,
sr.crashCount, sr.shortName, app.pid);
bringDownServiceLocked(sr, true); //移除Service
} else if (!allowRestart) {
bringDownServiceLocked(sr, true);
} else {
boolean canceled = scheduleServiceRestartLocked(sr, true); //在此发起请求重起Service
// Should the service remain running? Note that in the
// extreme case of so many attempts to deliver a command
// that it failed, that we also will stop it here.
if (sr.startRequested && (sr.stopIfKilled || canceled)) {
if (sr.pendingStarts.size() == 0) {
sr.startRequested = false;
if (!hasClients) {
// Whoops, no reason to restart!
bringDownServiceLocked(sr, true);
}
}
}
}
}
if (!allowRestart) {
app.services.clear();
}
}
// Make sure we have no more records on the stopping list.
int i = mStoppingServices.size();
while (i > 0) {
i--;
ServiceRecord sr = mStoppingServices.get(i);
if (sr.app == app) {
mStoppingServices.remove(i);
if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr);
}
}
app.executingServices.clear();
}