diff options
Diffstat (limited to 'services/core')
7 files changed, 432 insertions, 131 deletions
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index 1ce073a..89e3f49 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -332,6 +332,7 @@ public class Watchdog extends Thread { final ArrayList<HandlerChecker> blockedCheckers; final String subject; final boolean allowRestart; + int debuggerWasConnected = 0; synchronized (this) { long timeout = CHECK_INTERVAL; // Make sure we (re)spin the checkers that have become idle within @@ -341,17 +342,27 @@ public class Watchdog extends Thread { hc.scheduleCheckLocked(); } + if (debuggerWasConnected > 0) { + debuggerWasConnected--; + } + // NOTE: We use uptimeMillis() here because we do not want to increment the time we // wait while asleep. If the device is asleep then the thing that we are waiting // to timeout on is asleep as well and won't have a chance to run, causing a false // positive on when to kill things. long start = SystemClock.uptimeMillis(); while (timeout > 0) { + if (Debug.isDebuggerConnected()) { + debuggerWasConnected = 2; + } try { wait(timeout); } catch (InterruptedException e) { Log.wtf(TAG, e); } + if (Debug.isDebuggerConnected()) { + debuggerWasConnected = 2; + } timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start); } @@ -450,7 +461,12 @@ public class Watchdog extends Thread { // Only kill the process if the debugger is not attached. if (Debug.isDebuggerConnected()) { + debuggerWasConnected = 2; + } + if (debuggerWasConnected >= 2) { Slog.w(TAG, "Debugger connected: Watchdog is *not* killing the system process"); + } else if (debuggerWasConnected > 0) { + Slog.w(TAG, "Debugger was connected: Watchdog is *not* killing the system process"); } else if (!allowRestart) { Slog.w(TAG, "Restart not allowed: Watchdog is *not* killing the system process"); } else { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index e794b83..296b0e2 100755 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -33,6 +33,7 @@ import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID; import android.Manifest; import android.app.AppOpsManager; +import android.app.ApplicationThreadNative; import android.app.IActivityContainer; import android.app.IActivityContainerCallback; import android.app.IAppTask; @@ -2831,8 +2832,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (proc.baseProcessTracker != null) { proc.baseProcessTracker.reportCachedKill(proc.pkgList, proc.lastCachedPss); } - killUnneededProcessLocked(proc, Long.toString(proc.lastCachedPss) - + "k from cached"); + proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true); } else if (proc != null && !keepIfLarge && mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) { @@ -2841,8 +2841,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (proc.baseProcessTracker != null) { proc.baseProcessTracker.reportCachedKill(proc.pkgList, proc.lastCachedPss); } - killUnneededProcessLocked(proc, Long.toString(proc.lastCachedPss) - + "k from cached"); + proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true); } } return proc; @@ -3318,7 +3317,8 @@ public final class ActivityManagerService extends ActivityManagerNative intent.setComponent(new ComponentName( ri.activityInfo.packageName, ri.activityInfo.name)); mStackSupervisor.startActivityLocked(null, intent, null, ri.activityInfo, - null, null, null, null, 0, 0, 0, null, 0, null, false, null, null); + null, null, null, null, 0, 0, 0, null, 0, null, false, null, null, + null); } } } @@ -3462,7 +3462,7 @@ public final class ActivityManagerService extends ActivityManagerNative // TODO: Switch to user app stacks here. return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType, null, null, resultTo, resultWho, requestCode, startFlags, profileFile, profileFd, - null, null, options, userId, null); + null, null, options, userId, null, null); } @Override @@ -3512,7 +3512,7 @@ public final class ActivityManagerService extends ActivityManagerNative int ret = mStackSupervisor.startActivityMayWait(null, targetUid, targetPackage, intent, resolvedType, null, null, resultTo, resultWho, requestCode, startFlags, null, null, null, null, options, UserHandle.getUserId(sourceRecord.app.uid), - null); + null, null); return ret; } catch (SecurityException e) { // XXX need to figure out how to propagate to original app. @@ -3542,7 +3542,7 @@ public final class ActivityManagerService extends ActivityManagerNative // TODO: Switch to user app stacks here. mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType, null, null, resultTo, resultWho, requestCode, startFlags, profileFile, profileFd, - res, null, options, userId, null); + res, null, options, userId, null, null); return res; } @@ -3557,7 +3557,7 @@ public final class ActivityManagerService extends ActivityManagerNative // TODO: Switch to user app stacks here. int ret = mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType, null, null, resultTo, resultWho, requestCode, startFlags, - null, null, null, config, options, userId, null); + null, null, null, config, options, userId, null, null); return ret; } @@ -3615,7 +3615,7 @@ public final class ActivityManagerService extends ActivityManagerNative // TODO: Switch to user app stacks here. return mStackSupervisor.startActivityMayWait(null, callingUid, callingPackage, intent, resolvedType, session, interactor, null, null, 0, startFlags, - profileFile, profileFd, null, null, options, userId, null); + profileFile, profileFd, null, null, options, userId, null, null); } @Override @@ -3713,7 +3713,7 @@ public final class ActivityManagerService extends ActivityManagerNative int res = mStackSupervisor.startActivityLocked(r.app.thread, intent, r.resolvedType, aInfo, null, null, resultTo != null ? resultTo.appToken : null, resultWho, requestCode, -1, r.launchedFromUid, r.launchedFromPackage, 0, - options, false, null, null); + options, false, null, null, null); Binder.restoreCallingIdentity(origId); r.finishing = wasFinishing; @@ -3732,36 +3732,42 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.w(TAG, msg); throw new SecurityException(msg); } + return startActivityFromRecentsInner(taskId, options); + } + + final int startActivityFromRecentsInner(int taskId, Bundle options) { + final TaskRecord task; final int callingUid; final String callingPackage; final Intent intent; final int userId; synchronized (this) { - final TaskRecord task = recentTaskForIdLocked(taskId); + task = recentTaskForIdLocked(taskId); if (task == null) { - throw new ActivityNotFoundException("Task " + taskId + " not found."); + throw new IllegalArgumentException("Task " + taskId + " not found."); } callingUid = task.mCallingUid; callingPackage = task.mCallingPackage; intent = task.intent; + intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY); userId = task.userId; } return startActivityInPackage(callingUid, callingPackage, intent, null, null, null, 0, 0, - options, userId, null); + options, userId, null, task); } final int startActivityInPackage(int uid, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, Bundle options, int userId, - IActivityContainer container) { + IActivityContainer container, TaskRecord inTask) { userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, false, ALLOW_FULL_ONLY, "startActivityInPackage", null); // TODO: Switch to user app stacks here. - int ret = mStackSupervisor.startActivityMayWait(null, uid, callingPackage, intent, resolvedType, - null, null, resultTo, resultWho, requestCode, startFlags, - null, null, null, null, options, userId, container); + int ret = mStackSupervisor.startActivityMayWait(null, uid, callingPackage, intent, + resolvedType, null, null, resultTo, resultWho, requestCode, startFlags, + null, null, null, null, options, userId, container, inTask); return ret; } @@ -4155,6 +4161,35 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override + public boolean releaseActivityInstance(IBinder token) { + synchronized(this) { + final long origId = Binder.clearCallingIdentity(); + try { + ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r.task == null || r.task.stack == null) { + return false; + } + return r.task.stack.safelyDestroyActivityLocked(r, "app-req"); + } finally { + Binder.restoreCallingIdentity(origId); + } + } + } + + @Override + public void releaseSomeActivities(IApplicationThread appInt) { + synchronized(this) { + final long origId = Binder.clearCallingIdentity(); + try { + ProcessRecord app = getRecordForAppLocked(appInt); + mStackSupervisor.releaseSomeActivitiesLocked(app, "low-mem"); + } finally { + Binder.restoreCallingIdentity(origId); + } + } + } + + @Override public boolean willActivityBeVisible(IBinder token) { synchronized(this) { ActivityStack stack = ActivityRecord.getStackLocked(token); @@ -4568,8 +4603,7 @@ public final class ActivityManagerService extends ActivityManagerNative // 0 == continue, -1 = kill process immediately int res = mController.appEarlyNotResponding(app.processName, app.pid, annotation); if (res < 0 && app.pid != MY_PID) { - Process.killProcess(app.pid); - Process.killProcessGroup(app.info.uid, app.pid); + app.kill("anr", true); } } catch (RemoteException e) { mController = null; @@ -4675,8 +4709,7 @@ public final class ActivityManagerService extends ActivityManagerNative int res = mController.appNotResponding(app.processName, app.pid, info.toString()); if (res != 0) { if (res < 0 && app.pid != MY_PID) { - Process.killProcess(app.pid); - Process.killProcessGroup(app.info.uid, app.pid); + app.kill("anr", true); } else { synchronized (this) { mServices.scheduleServiceTimeoutLocked(app); @@ -4696,7 +4729,7 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (this) { if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) { - killUnneededProcessLocked(app, "background ANR"); + app.kill("bg anr", true); return; } @@ -5420,8 +5453,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (app.isolated) { mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid); } - killUnneededProcessLocked(app, reason); - Process.killProcessGroup(app.info.uid, app.pid); + app.kill(reason, true); handleAppDiedLocked(app, true, allowRestart); removeLruProcessLocked(app); @@ -5469,7 +5501,7 @@ public final class ActivityManagerService extends ActivityManagerNative checkAppInLaunchingProvidersLocked(app, true); // Take care of any services that are waiting for the process. mServices.processStartTimedOutLocked(app); - killUnneededProcessLocked(app, "start timeout"); + app.kill("start timeout", true); if (mBackupTarget != null && mBackupTarget.app.pid == pid) { Slog.w(TAG, "Unattached app died before backup, skipping"); try { @@ -7915,17 +7947,6 @@ public final class ActivityManagerService extends ActivityManagerNative } } - private void killUnneededProcessLocked(ProcessRecord pr, String reason) { - if (!pr.killedByAm) { - Slog.i(TAG, "Killing " + pr.toShortString() + " (adj " + pr.setAdj + "): " + reason); - EventLog.writeEvent(EventLogTags.AM_KILL, pr.userId, pr.pid, - pr.processName, pr.setAdj, reason); - pr.killedByAm = true; - Process.killProcessQuiet(pr.pid); - Process.killProcessGroup(pr.info.uid, pr.pid); - } - } - private void cleanUpRemovedTaskLocked(TaskRecord tr, int flags) { tr.disposeThumbnail(); mRecentTasks.remove(tr); @@ -7969,7 +7990,7 @@ public final class ActivityManagerService extends ActivityManagerNative continue; } if (pr.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) { - killUnneededProcessLocked(pr, "remove task"); + pr.kill("remove task", true); } else { pr.waitingToKill = "remove task"; } @@ -8022,32 +8043,36 @@ public final class ActivityManagerService extends ActivityManagerNative if (DEBUG_STACK) Slog.d(TAG, "moveTaskToFront: moving taskId=" + taskId); synchronized(this) { - if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(), - Binder.getCallingUid(), "Task to front")) { - ActivityOptions.abort(options); + moveTaskToFrontLocked(taskId, flags, options); + } + } + + void moveTaskToFrontLocked(int taskId, int flags, Bundle options) { + if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(), + Binder.getCallingUid(), "Task to front")) { + ActivityOptions.abort(options); + return; + } + final long origId = Binder.clearCallingIdentity(); + try { + final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId); + if (task == null) { return; } - final long origId = Binder.clearCallingIdentity(); - try { - final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId); - if (task == null) { - return; - } - if (mStackSupervisor.isLockTaskModeViolation(task)) { - mStackSupervisor.showLockTaskToast(); - Slog.e(TAG, "moveTaskToFront: Attempt to violate Lock Task Mode"); - return; - } - final ActivityRecord prev = mStackSupervisor.topRunningActivityLocked(); - if (prev != null && prev.isRecentsActivity()) { - task.setTaskToReturnTo(ActivityRecord.RECENTS_ACTIVITY_TYPE); - } - mStackSupervisor.findTaskToMoveToFrontLocked(task, flags, options); - } finally { - Binder.restoreCallingIdentity(origId); + if (mStackSupervisor.isLockTaskModeViolation(task)) { + mStackSupervisor.showLockTaskToast(); + Slog.e(TAG, "moveTaskToFront: Attempt to violate Lock Task Mode"); + return; } - ActivityOptions.abort(options); + final ActivityRecord prev = mStackSupervisor.topRunningActivityLocked(); + if (prev != null && prev.isRecentsActivity()) { + task.setTaskToReturnTo(ActivityRecord.RECENTS_ACTIVITY_TYPE); + } + mStackSupervisor.findTaskToMoveToFrontLocked(task, flags, options); + } finally { + Binder.restoreCallingIdentity(origId); } + ActivityOptions.abort(options); } @Override @@ -10145,7 +10170,7 @@ public final class ActivityManagerService extends ActivityManagerNative } int adj = proc.setAdj; if (adj >= worstType && !proc.killedByAm) { - killUnneededProcessLocked(proc, reason); + proc.kill(reason, true); killed = true; } } @@ -10189,7 +10214,7 @@ public final class ActivityManagerService extends ActivityManagerNative final int adj = proc.setAdj; if (adj > belowAdj && !proc.killedByAm) { - killUnneededProcessLocked(proc, reason); + proc.kill(reason, true); killed = true; } } @@ -10308,8 +10333,8 @@ public final class ActivityManagerService extends ActivityManagerNative && proc.setProcState <= ActivityManager.PROCESS_STATE_SERVICE) { if (doKilling && proc.initialIdlePss != 0 && proc.lastPss > ((proc.initialIdlePss*3)/2)) { - killUnneededProcessLocked(proc, "idle maint (pss " + proc.lastPss - + " from " + proc.initialIdlePss + ")"); + proc.kill("idle maint (pss " + proc.lastPss + + " from " + proc.initialIdlePss + ")", true); } } } else if (proc.setProcState < ActivityManager.PROCESS_STATE_HOME) { @@ -10778,7 +10803,7 @@ public final class ActivityManagerService extends ActivityManagerNative } if (app.pid > 0 && app.pid != MY_PID) { handleAppCrashLocked(app, null, null, null); - killUnneededProcessLocked(app, "user request after error"); + app.kill("user request after error", true); } } } @@ -11345,8 +11370,11 @@ public final class ActivityManagerService extends ActivityManagerNative } else { Slog.w(TAG, "Force-killing crashed app " + name + " at watcher's request"); - Process.killProcess(pid); if (r != null) { + r.kill("crash", true); + } else { + // Huh. + Process.killProcess(pid); Process.killProcessGroup(uid, pid); } } @@ -13695,9 +13723,9 @@ public final class ActivityManagerService extends ActivityManagerNative if (!capp.persistent && capp.thread != null && capp.pid != 0 && capp.pid != MY_PID) { - killUnneededProcessLocked(capp, "depends on provider " + capp.kill("depends on provider " + cpr.name.flattenToShortString() - + " in dying proc " + (proc != null ? proc.processName : "??")); + + " in dying proc " + (proc != null ? proc.processName : "??"), true); } } else if (capp.thread != null && conn.provider.provider != null) { try { @@ -16553,8 +16581,7 @@ public final class ActivityManagerService extends ActivityManagerNative stats.reportExcessiveWakeLocked(app.info.uid, app.processName, realtimeSince, wtimeUsed); } - killUnneededProcessLocked(app, "excessive wake held " + wtimeUsed - + " during " + realtimeSince); + app.kill("excessive wake held " + wtimeUsed + " during " + realtimeSince, true); app.baseProcessTracker.reportExcessiveWake(app.pkgList); } else if (doCpuKills && uptimeSince > 0 && ((cputimeUsed*100)/uptimeSince) >= 25) { @@ -16562,8 +16589,7 @@ public final class ActivityManagerService extends ActivityManagerNative stats.reportExcessiveCpuLocked(app.info.uid, app.processName, uptimeSince, cputimeUsed); } - killUnneededProcessLocked(app, "excessive cpu " + cputimeUsed - + " during " + uptimeSince); + app.kill("excessive cpu " + cputimeUsed + " during " + uptimeSince, true); app.baseProcessTracker.reportExcessiveCpu(app.pkgList); } else { app.lastWakeTime = wtime; @@ -16598,7 +16624,7 @@ public final class ActivityManagerService extends ActivityManagerNative + " to " + app.curSchedGroup); if (app.waitingToKill != null && app.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) { - killUnneededProcessLocked(app, app.waitingToKill); + app.kill(app.waitingToKill, true); success = false; } else { if (true) { @@ -16978,19 +17004,19 @@ public final class ActivityManagerService extends ActivityManagerNative mNumCachedHiddenProcs++; numCached++; if (numCached > cachedProcessLimit) { - killUnneededProcessLocked(app, "cached #" + numCached); + app.kill("cached #" + numCached, true); } break; case ActivityManager.PROCESS_STATE_CACHED_EMPTY: if (numEmpty > ProcessList.TRIM_EMPTY_APPS && app.lastActivityTime < oldTime) { - killUnneededProcessLocked(app, "empty for " + app.kill("empty for " + ((oldTime + ProcessList.MAX_EMPTY_TIME - app.lastActivityTime) - / 1000) + "s"); + / 1000) + "s", true); } else { numEmpty++; if (numEmpty > emptyProcessLimit) { - killUnneededProcessLocked(app, "empty #" + numEmpty); + app.kill("empty #" + numEmpty, true); } } break; @@ -17006,7 +17032,7 @@ public final class ActivityManagerService extends ActivityManagerNative // definition not re-use the same process again, and it is // good to avoid having whatever code was running in them // left sitting around after no longer needed. - killUnneededProcessLocked(app, "isolated not needed"); + app.kill("isolated not needed", true); } if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME @@ -17234,11 +17260,7 @@ public final class ActivityManagerService extends ActivityManagerNative + (app.thread != null ? app.thread.asBinder() : null) + ")\n"); if (app.pid > 0 && app.pid != MY_PID) { - EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, - app.processName, app.setAdj, "empty"); - app.killedByAm = true; - Process.killProcessQuiet(app.pid); - Process.killProcessGroup(app.info.uid, app.pid); + app.kill("empty", false); } else { try { app.thread.scheduleExit(); @@ -18290,14 +18312,15 @@ public final class ActivityManagerService extends ActivityManagerNative long origId = Binder.clearCallingIdentity(); try { TaskRecord tr = recentTaskForIdLocked(mTaskId); - if (tr != null) { - // Only kill the process if we are not a new document - int flags = tr.getBaseIntent().getFlags(); - boolean isDocument = (flags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) == - Intent.FLAG_ACTIVITY_NEW_DOCUMENT; - removeTaskByIdLocked(mTaskId, - !isDocument ? ActivityManager.REMOVE_TASK_KILL_PROCESS : 0); - } + if (tr == null) { + throw new IllegalArgumentException("Unable to find task ID " + mTaskId); + } + // Only kill the process if we are not a new document + int flags = tr.getBaseIntent().getFlags(); + boolean isDocument = (flags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) == + Intent.FLAG_ACTIVITY_NEW_DOCUMENT; + removeTaskByIdLocked(mTaskId, + !isDocument ? ActivityManager.REMOVE_TASK_KILL_PROCESS : 0); } finally { Binder.restoreCallingIdentity(origId); } @@ -18312,17 +18335,64 @@ public final class ActivityManagerService extends ActivityManagerNative long origId = Binder.clearCallingIdentity(); try { TaskRecord tr = recentTaskForIdLocked(mTaskId); - if (tr != null) { - return createRecentTaskInfoFromTaskRecord(tr); + if (tr == null) { + throw new IllegalArgumentException("Unable to find task ID " + mTaskId); } + return createRecentTaskInfoFromTaskRecord(tr); } finally { Binder.restoreCallingIdentity(origId); } - return null; } } @Override + public void moveToFront() { + checkCaller(); + + final TaskRecord tr; + synchronized (ActivityManagerService.this) { + tr = recentTaskForIdLocked(mTaskId); + if (tr == null) { + throw new IllegalArgumentException("Unable to find task ID " + mTaskId); + } + if (tr.getRootActivity() != null) { + long origId = Binder.clearCallingIdentity(); + try { + moveTaskToFrontLocked(tr.taskId, 0, null); + return; + } finally { + Binder.restoreCallingIdentity(origId); + } + } + } + + startActivityFromRecentsInner(tr.taskId, null); + } + + @Override + public int startActivity(IBinder whoThread, String callingPackage, + Intent intent, String resolvedType, Bundle options) { + checkCaller(); + + int callingUser = UserHandle.getCallingUserId(); + TaskRecord tr; + IApplicationThread appThread; + synchronized (ActivityManagerService.this) { + tr = recentTaskForIdLocked(mTaskId); + if (tr == null) { + throw new IllegalArgumentException("Unable to find task ID " + mTaskId); + } + appThread = ApplicationThreadNative.asInterface(whoThread); + if (appThread == null) { + throw new IllegalArgumentException("Bad app thread " + appThread); + } + } + return mStackSupervisor.startActivityMayWait(appThread, -1, callingPackage, intent, + resolvedType, null, null, null, null, 0, 0, null, null, + null, null, options, callingUser, null, tr); + } + + @Override public void setExcludeFromRecents(boolean exclude) { checkCaller(); @@ -18330,14 +18400,15 @@ public final class ActivityManagerService extends ActivityManagerNative long origId = Binder.clearCallingIdentity(); try { TaskRecord tr = recentTaskForIdLocked(mTaskId); - if (tr != null) { - Intent intent = tr.getBaseIntent(); - if (exclude) { - intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); - } else { - intent.setFlags(intent.getFlags() - & ~Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); - } + if (tr == null) { + throw new IllegalArgumentException("Unable to find task ID " + mTaskId); + } + Intent intent = tr.getBaseIntent(); + if (exclude) { + intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + } else { + intent.setFlags(intent.getFlags() + & ~Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); } } finally { Binder.restoreCallingIdentity(origId); diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index fcbe71e..694142a 100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -1054,6 +1054,24 @@ final class ActivityRecord { return null; } + final boolean isDestroyable() { + if (finishing || app == null || state == ActivityState.DESTROYING + || state == ActivityState.DESTROYED) { + // This would be redundant. + return false; + } + if (task == null || task.stack == null || this == task.stack.mResumedActivity + || this == task.stack.mPausingActivity || !haveState || !stopped) { + // We're not ready for this kind of thing. + return false; + } + if (visible) { + // The user would notice this! + return false; + } + return true; + } + private static String createImageFilename(long createTime, int taskId) { return String.valueOf(taskId) + ACTIVITY_ICON_SUFFIX + createTime + TaskPersister.IMAGE_EXTENSION; diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 41ed4ce..fd1474f 100755 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -36,11 +36,13 @@ import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE; import static com.android.server.am.ActivityStackSupervisor.DEBUG_ADD_REMOVE; import static com.android.server.am.ActivityStackSupervisor.DEBUG_APP; import static com.android.server.am.ActivityStackSupervisor.DEBUG_CONTAINERS; +import static com.android.server.am.ActivityStackSupervisor.DEBUG_RELEASE; import static com.android.server.am.ActivityStackSupervisor.DEBUG_SAVED_STATE; import static com.android.server.am.ActivityStackSupervisor.DEBUG_SCREENSHOTS; import static com.android.server.am.ActivityStackSupervisor.DEBUG_STATES; import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID; +import android.util.ArraySet; import com.android.internal.app.IVoiceInteractor; import com.android.internal.os.BatteryStatsImpl; import com.android.server.Watchdog; @@ -2910,7 +2912,7 @@ final class ActivityStack { int res = mStackSupervisor.startActivityLocked(srec.app.thread, destIntent, null, aInfo, null, null, parent.appToken, null, 0, -1, parent.launchedFromUid, parent.launchedFromPackage, - 0, null, true, null, null); + 0, null, true, null, null, null); foundParentInTask = res == ActivityManager.START_SUCCESS; } catch (RemoteException e) { foundParentInTask = false; @@ -3058,15 +3060,10 @@ final class ActivityStack { if (!lastIsOpaque) { continue; } - // We can destroy this one if we have its icicle saved and - // it is not in the process of pausing/stopping/finishing. - if (r.app != null && r != mResumedActivity && r != mPausingActivity - && r.haveState && !r.visible && r.stopped - && r.state != ActivityState.DESTROYING - && r.state != ActivityState.DESTROYED) { + if (r.isDestroyable()) { if (DEBUG_SWITCH) Slog.v(TAG, "Destroying " + r + " in state " + r.state + " resumed=" + mResumedActivity - + " pausing=" + mPausingActivity); + + " pausing=" + mPausingActivity + " for reason " + reason); if (destroyActivityLocked(r, true, reason)) { activityRemoved = true; } @@ -3078,6 +3075,60 @@ final class ActivityStack { } } + final boolean safelyDestroyActivityLocked(ActivityRecord r, String reason) { + if (r.isDestroyable()) { + if (DEBUG_SWITCH) Slog.v(TAG, "Destroying " + r + " in state " + r.state + + " resumed=" + mResumedActivity + + " pausing=" + mPausingActivity + " for reason " + reason); + return destroyActivityLocked(r, true, reason); + } + return false; + } + + final int releaseSomeActivitiesLocked(ProcessRecord app, ArraySet<TaskRecord> tasks, + String reason) { + // Iterate over tasks starting at the back (oldest) first. + if (DEBUG_RELEASE) Slog.d(TAG, "Trying to release some activities in " + app); + int maxTasks = tasks.size() / 4; + if (maxTasks < 1) { + maxTasks = 1; + } + int numReleased = 0; + for (int taskNdx = 0; taskNdx < mTaskHistory.size() && maxTasks > 0; taskNdx++) { + final TaskRecord task = mTaskHistory.get(taskNdx); + if (!tasks.contains(task)) { + continue; + } + if (DEBUG_RELEASE) Slog.d(TAG, "Looking for activities to release in " + task); + int curNum = 0; + final ArrayList<ActivityRecord> activities = task.mActivities; + for (int actNdx = 0; actNdx < activities.size(); actNdx++) { + final ActivityRecord activity = activities.get(actNdx); + if (activity.app == app && activity.isDestroyable()) { + if (DEBUG_RELEASE) Slog.v(TAG, "Destroying " + activity + + " in state " + activity.state + " resumed=" + mResumedActivity + + " pausing=" + mPausingActivity + " for reason " + reason); + destroyActivityLocked(activity, true, reason); + if (activities.get(actNdx) != activity) { + // Was removed from list, back up so we don't miss the next one. + actNdx--; + } + curNum++; + } + } + if (curNum > 0) { + numReleased += curNum; + maxTasks--; + if (mTaskHistory.get(taskNdx) != task) { + // The entire task got removed, back up so we don't miss the next one. + taskNdx--; + } + } + } + if (DEBUG_RELEASE) Slog.d(TAG, "Done releasing: did " + numReleased + " activities"); + return numReleased; + } + /** * Destroy the current CLIENT SIDE instance of an activity. This may be * called both when actually finishing an activity, or when performing diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 8aec392..8058c05 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -84,6 +84,7 @@ import android.os.UserHandle; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.service.voice.IVoiceInteractionSession; +import android.util.ArraySet; import android.util.EventLog; import android.util.Slog; import android.util.SparseArray; @@ -115,10 +116,11 @@ public final class ActivityStackSupervisor implements DisplayListener { static final boolean DEBUG_APP = DEBUG || false; static final boolean DEBUG_CONTAINERS = DEBUG || false; static final boolean DEBUG_IDLE = DEBUG || false; - static final boolean DEBUG_VISIBLE_BEHIND = DEBUG || false; + static final boolean DEBUG_RELEASE = DEBUG || false; static final boolean DEBUG_SAVED_STATE = DEBUG || false; static final boolean DEBUG_SCREENSHOTS = DEBUG || false; static final boolean DEBUG_STATES = DEBUG || false; + static final boolean DEBUG_VISIBLE_BEHIND = DEBUG || false; public static final int HOME_STACK_ID = 0; @@ -781,7 +783,7 @@ public final class ActivityStackSupervisor implements DisplayListener { void startHomeActivity(Intent intent, ActivityInfo aInfo) { moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE); startActivityLocked(null, intent, null, aInfo, null, null, null, null, 0, 0, 0, null, 0, - null, false, null, null); + null, false, null, null, null); } final int startActivityMayWait(IApplicationThread caller, int callingUid, @@ -789,7 +791,7 @@ public final class ActivityStackSupervisor implements DisplayListener { IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, IBinder resultTo, String resultWho, int requestCode, int startFlags, String profileFile, ParcelFileDescriptor profileFd, WaitResult outResult, Configuration config, - Bundle options, int userId, IActivityContainer iContainer) { + Bundle options, int userId, IActivityContainer iContainer, TaskRecord inTask) { // Refuse possible leaked file descriptors if (intent != null && intent.hasFileDescriptors()) { throw new IllegalArgumentException("File descriptors passed in Intent"); @@ -899,7 +901,7 @@ public final class ActivityStackSupervisor implements DisplayListener { int res = startActivityLocked(caller, intent, resolvedType, aInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid, callingUid, callingPackage, startFlags, options, - componentSpecified, null, container); + componentSpecified, null, container, inTask); Binder.restoreCallingIdentity(origId); @@ -1014,7 +1016,7 @@ public final class ActivityStackSupervisor implements DisplayListener { } int res = startActivityLocked(caller, intent, resolvedTypes[i], aInfo, null, null, resultTo, null, -1, callingPid, callingUid, callingPackage, - 0, theseOptions, componentSpecified, outActivity, null); + 0, theseOptions, componentSpecified, outActivity, null, null); if (res < 0) { return res; } @@ -1241,7 +1243,8 @@ public final class ActivityStackSupervisor implements DisplayListener { IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid, String callingPackage, int startFlags, Bundle options, - boolean componentSpecified, ActivityRecord[] outActivity, ActivityContainer container) { + boolean componentSpecified, ActivityRecord[] outActivity, ActivityContainer container, + TaskRecord inTask) { int err = ActivityManager.START_SUCCESS; ProcessRecord callerApp = null; @@ -1453,7 +1456,7 @@ public final class ActivityStackSupervisor implements DisplayListener { doPendingActivityLaunchesLocked(false); err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor, - startFlags, true, options); + startFlags, true, options, inTask); if (err < 0) { // If someone asked to have the keyguard dismissed on the next @@ -1536,10 +1539,9 @@ public final class ActivityStackSupervisor implements DisplayListener { } } - final int startActivityUncheckedLocked(ActivityRecord r, - ActivityRecord sourceRecord, + final int startActivityUncheckedLocked(ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags, - boolean doResume, Bundle options) { + boolean doResume, Bundle options, TaskRecord inTask) { final Intent intent = r.intent; final int callingUid = r.launchedFromUid; @@ -1571,8 +1573,9 @@ public final class ActivityStackSupervisor implements DisplayListener { } } - final boolean launchTaskBehind = r.mLaunchTaskBehind && - (launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0; + final boolean launchTaskBehind = r.mLaunchTaskBehind + && !launchSingleTask && !launchSingleInstance + && (launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0; if (r.resultTo != null && (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { // For whatever reason this activity is being launched into a new @@ -1591,6 +1594,15 @@ public final class ActivityStackSupervisor implements DisplayListener { launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK; } + // If we are actually going to launch in to a new task, there are some cases where + // we further want to do multiple task. + if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { + if (launchTaskBehind + || r.info.documentLaunchMode == ActivityInfo.DOCUMENT_LAUNCH_ALWAYS) { + launchFlags |= Intent.FLAG_ACTIVITY_MULTIPLE_TASK; + } + } + // We'll invoke onUserLeaving before onPause only if the launching // activity did not explicitly state that this is an automated launch. mUserLeaving = (launchFlags & Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0; @@ -1624,7 +1636,7 @@ public final class ActivityStackSupervisor implements DisplayListener { if (sourceRecord == null) { // This activity is not being started from another... in this // case we -always- start a new task. - if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0) { + if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && inTask == null) { Slog.w(TAG, "startActivity called from non-Activity context; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent); launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK; @@ -1642,7 +1654,7 @@ public final class ActivityStackSupervisor implements DisplayListener { ActivityInfo newTaskInfo = null; Intent newTaskIntent = null; - final ActivityStack sourceStack; + ActivityStack sourceStack; if (sourceRecord != null) { if (sourceRecord.finishing) { // If the source is finishing, we can't further count it as our source. This @@ -1666,12 +1678,51 @@ public final class ActivityStackSupervisor implements DisplayListener { sourceStack = null; } - intent.setFlags(launchFlags); - boolean addingToTask = false; boolean movedHome = false; TaskRecord reuseTask = null; ActivityStack targetStack; + + intent.setFlags(launchFlags); + + // If the caller is not coming from another activity, but has given us an + // explicit task into which they would like us to launch the new activity, + // then let's see about doing that. + if (sourceRecord == null && inTask != null && inTask.stack != null) { + // If this task is empty, then we are adding the first activity -- it + // determines the root, and must be launching as a NEW_TASK. + if (inTask.getRootActivity() == null) { + if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0 + && !launchSingleInstance && !launchSingleTask) { + throw new IllegalStateException("Caller has inTask " + inTask + + " but target is not a new task"); + } else if (inTask.getBaseIntent() == null || !intent.getComponent().equals( + inTask.getBaseIntent().getComponent())) { + throw new IllegalStateException("Caller requested " + inTask + " is component " + + inTask.getBaseIntent() + " but starting " + intent); + } + inTask.setIntent(r); + + // If the task is not empty, then we are going to add the new activity on top + // of the task, so it can not be launching as a new task. + } else { + if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0 + || launchSingleInstance || launchSingleTask) { + throw new IllegalStateException("Caller has inTask " + inTask + + " but target is a new task"); + } + } + sourceStack = inTask.stack; + reuseTask = inTask; + } else { + inTask = null; + } + + // We may want to try to place the new activity in to an existing task. We always + // do this if the target activity is singleTask or singleInstance; we will also do + // this if NEW_TASK has been requested, and there is not an additional qualifier telling + // us to still place it in a new task: multi task, always doc mode, or being asked to + // launch this as a new task behind the current one. if (((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0 && (launchFlags & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0) || launchSingleInstance || launchSingleTask) { @@ -1908,8 +1959,13 @@ public final class ActivityStackSupervisor implements DisplayListener { Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r); return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION; } - newTask = true; - targetStack = adjustStackFocus(r, newTask); + if (inTask == null) { + // If we have an incoming task, we are just going to use that. + newTask = true; + targetStack = adjustStackFocus(r, newTask); + } else { + targetStack = inTask.stack; + } if (!launchTaskBehind) { targetStack.moveToFront(); } @@ -1986,6 +2042,20 @@ public final class ActivityStackSupervisor implements DisplayListener { if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in existing task " + r.task + " from source " + sourceRecord); + } else if (inTask != null) { + // The calling is asking that the new activity be started in an explicit + // task it has provided to us. + if (isLockTaskModeViolation(inTask)) { + Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r); + return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION; + } + targetStack = inTask.stack; + targetStack.moveToFront(); + mWindowManager.moveTaskToTop(targetStack.topTask().taskId); + r.setTask(inTask, null); + if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + + " in explicit task " + r.task); + } else { // This not being started from an existing activity, and not part // of a new task... just put it in the top task, though these days @@ -2023,7 +2093,7 @@ public final class ActivityStackSupervisor implements DisplayListener { while (!mPendingActivityLaunches.isEmpty()) { PendingActivityLaunch pal = mPendingActivityLaunches.remove(0); startActivityUncheckedLocked(pal.r, pal.sourceRecord, null, null, pal.startFlags, - doResume && mPendingActivityLaunches.isEmpty(), null); + doResume && mPendingActivityLaunches.isEmpty(), null, null); } } @@ -2726,6 +2796,64 @@ public final class ActivityStackSupervisor implements DisplayListener { } } + void releaseSomeActivitiesLocked(ProcessRecord app, String reason) { + // Examine all activities currently running in the process. + TaskRecord firstTask = null; + // Tasks is non-null only if two or more tasks are found. + ArraySet<TaskRecord> tasks = null; + if (DEBUG_RELEASE) Slog.d(TAG, "Trying to release some activities in " + app); + for (int i=0; i<app.activities.size(); i++) { + ActivityRecord r = app.activities.get(i); + // First, if we find an activity that is in the process of being destroyed, + // then we just aren't going to do anything for now; we want things to settle + // down before we try to prune more activities. + if (r.finishing || r.state == ActivityState.DESTROYING + || r.state == ActivityState.DESTROYED) { + if (DEBUG_RELEASE) Slog.d(TAG, "Abort release; already destroying: " + r); + return; + } + // Don't consider any activies that are currently not in a state where they + // can be destroyed. + if (r.visible || !r.stopped || !r.haveState + || r.state == ActivityState.RESUMED || r.state == ActivityState.PAUSING + || r.state == ActivityState.PAUSED || r.state == ActivityState.STOPPING) { + if (DEBUG_RELEASE) Slog.d(TAG, "Not releasing in-use activity: " + r); + continue; + } + if (r.task != null) { + if (DEBUG_RELEASE) Slog.d(TAG, "Collecting release task " + r.task + + " from " + r); + if (firstTask == null) { + firstTask = r.task; + } else if (firstTask != r.task) { + if (tasks == null) { + tasks = new ArraySet<>(); + tasks.add(firstTask); + } + tasks.add(r.task); + } + } + } + if (tasks == null) { + if (DEBUG_RELEASE) Slog.d(TAG, "Didn't find two or more tasks to release"); + return; + } + // If we have activities in multiple tasks that are in a position to be destroyed, + // let's iterate through the tasks and release the oldest one. + final int numDisplays = mActivityDisplays.size(); + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks; + // Step through all stacks starting from behind, to hit the oldest things first. + for (int stackNdx = 0; stackNdx < stacks.size(); stackNdx++) { + final ActivityStack stack = stacks.get(stackNdx); + // Try to release activities in this stack; if we manage to, we are done. + if (stack.releaseSomeActivitiesLocked(app, tasks, reason) > 0) { + return; + } + } + } + } + boolean switchUserLocked(int userId, UserStartedState uss) { mUserStackInFront.put(mCurrentUser, getFocusedStack().getStackId()); final int restoreStackId = mUserStackInFront.get(userId, HOME_STACK_ID); @@ -3499,7 +3627,7 @@ public final class ActivityStackSupervisor implements DisplayListener { mimeType = mService.getProviderMimeType(intent.getData(), userId); } return startActivityMayWait(null, -1, null, intent, mimeType, null, null, null, null, 0, 0, null, - null, null, null, null, userId, this); + null, null, null, null, userId, this, null); } @Override diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index 98999e9..433ab60 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -254,7 +254,7 @@ final class PendingIntentRecord extends IIntentSender.Stub { } else { owner.startActivityInPackage(uid, key.packageName, finalIntent, resolvedType, resultTo, resultWho, requestCode, 0, - options, userId, container); + options, userId, container, null); } } catch (RuntimeException e) { Slog.w(ActivityManagerService.TAG, diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index f1bcb60..0817dd8 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -17,6 +17,8 @@ package com.android.server.am; import android.util.ArraySet; +import android.util.EventLog; +import android.util.Slog; import com.android.internal.app.ProcessStats; import com.android.internal.os.BatteryStatsImpl; @@ -502,6 +504,21 @@ final class ProcessRecord { return adj; } + 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) { + killedByAm = true; + } + } + } + public String toShortString() { if (shortStringName != null) { return shortStringName; |