/** @hide User operation call: given user id is the current user, can't be stopped. */
public static final int USER_OP_IS_CURRENT = -2;
+ /** @hide Process is a persistent system process. */
+ public static final int PROCESS_STATE_PERSISTENT = 0;
+
+ /** @hide Process is a persistent system process and is doing UI. */
+ public static final int PROCESS_STATE_PERSISTENT_UI = 1;
+
+ /** @hide Process is hosting the current top activity. */
+ public static final int PROCESS_STATE_TOP = 2;
+
+ /** @hide Process is important to the user, and something they are aware of. */
+ public static final int PROCESS_STATE_IMPORTANT_PERCEPTIBLE = 3;
+
+ /** @hide Process is important to the user, but not something they are aware of. */
+ public static final int PROCESS_STATE_IMPORTANT_BACKGROUND = 4;
+
+ /** @hide Process is in the background running a receiver. */
+ public static final int PROCESS_STATE_RECEIVER = 5;
+
+ /** @hide Process is in the background running a backup/restore operation. */
+ public static final int PROCESS_STATE_BACKUP = 6;
+
+ /** @hide Process is in the background running a service. */
+ public static final int PROCESS_STATE_SERVICE = 7;
+
+ /** @hide Process is in the background but hosts the home activity. */
+ public static final int PROCESS_STATE_HOME = 8;
+
+ /** @hide Process is in the background but hosts the last shown activity. */
+ public static final int PROCESS_STATE_LAST_ACTIVITY = 9;
+
+ /** @hide Process is being cached for later use. */
+ public static final int PROCESS_STATE_CACHED = 10;
+
/*package*/ ActivityManager(Context context, Handler handler) {
mContext = context;
mHandler = handler;
// Formatting for checkin service - update version if row format changes
private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 3;
+ private int mLastProcessState = -1;
+
private void updatePendingConfiguration(Configuration config) {
synchronized (mResourcesManager) {
if (mPendingConfiguration == null ||
queueOrSendMessage(H.SLEEPING, token, sleeping ? 1 : 0);
}
- public final void scheduleResumeActivity(IBinder token, boolean isForward) {
+ public final void scheduleResumeActivity(IBinder token, int processState,
+ boolean isForward) {
+ updateProcessState(processState, false);
queueOrSendMessage(H.RESUME_ACTIVITY, token, isForward ? 1 : 0);
}
// activity itself back to the activity manager. (matters more with ipc)
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
- Bundle state, List<ResultInfo> pendingResults,
+ int procState, Bundle state, List<ResultInfo> pendingResults,
List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
+
+ updateProcessState(procState, false);
+
ActivityClientRecord r = new ActivityClientRecord();
r.token = token;
public final void scheduleReceiver(Intent intent, ActivityInfo info,
CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
- boolean sync, int sendingUser) {
+ boolean sync, int sendingUser, int processState) {
+ updateProcessState(processState, false);
ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
sync, false, mAppThread.asBinder(), sendingUser);
r.info = info;
}
public final void scheduleCreateService(IBinder token,
- ServiceInfo info, CompatibilityInfo compatInfo) {
+ ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
+ updateProcessState(processState, false);
CreateServiceData s = new CreateServiceData();
s.token = token;
s.info = info;
}
public final void scheduleBindService(IBinder token, Intent intent,
- boolean rebind) {
+ boolean rebind, int processState) {
+ updateProcessState(processState, false);
BindServiceData s = new BindServiceData();
s.token = token;
s.intent = intent;
// applies transaction ordering per object for such calls.
public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
int resultCode, String dataStr, Bundle extras, boolean ordered,
- boolean sticky, int sendingUser) throws RemoteException {
+ boolean sticky, int sendingUser, int processState) throws RemoteException {
+ updateProcessState(processState, false);
receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
sticky, sendingUser);
}
public void scheduleTranslucentConversionComplete(IBinder token, boolean drawComplete) {
queueOrSendMessage(H.TRANSLUCENT_CONVERSION_COMPLETE, token, drawComplete ? 1 : 0);
}
+
+ public void setProcessState(int state) {
+ updateProcessState(state, true);
+ }
+
+ public void updateProcessState(int processState, boolean fromIpc) {
+ synchronized (this) {
+ if (mLastProcessState != processState) {
+ mLastProcessState = processState;
+
+ // Update Dalvik state here based on ActivityManager.PROCESS_STATE_* constants.
+ if (false) {
+ Slog.i(TAG, "******************* PROCESS STATE CHANGED TO: " + processState
+ + (fromIpc ? " (from ipc": ""));
+ }
+ }
+ }
+ }
}
private class H extends Handler {
{
data.enforceInterface(IApplicationThread.descriptor);
IBinder b = data.readStrongBinder();
+ int procState = data.readInt();
boolean isForward = data.readInt() != 0;
- scheduleResumeActivity(b, isForward);
+ scheduleResumeActivity(b, procState, isForward);
return true;
}
ActivityInfo info = ActivityInfo.CREATOR.createFromParcel(data);
Configuration curConfig = Configuration.CREATOR.createFromParcel(data);
CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
+ int procState = data.readInt();
Bundle state = data.readBundle();
List<ResultInfo> ri = data.createTypedArrayList(ResultInfo.CREATOR);
List<Intent> pi = data.createTypedArrayList(Intent.CREATOR);
ParcelFileDescriptor profileFd = data.readInt() != 0
? data.readFileDescriptor() : null;
boolean autoStopProfiler = data.readInt() != 0;
- scheduleLaunchActivity(intent, b, ident, info, curConfig, compatInfo, state, ri, pi,
- notResumed, isForward, profileName, profileFd, autoStopProfiler);
+ scheduleLaunchActivity(intent, b, ident, info, curConfig, compatInfo, procState, state,
+ ri, pi, notResumed, isForward, profileName, profileFd, autoStopProfiler);
return true;
}
Bundle resultExtras = data.readBundle();
boolean sync = data.readInt() != 0;
int sendingUser = data.readInt();
+ int processState = data.readInt();
scheduleReceiver(intent, info, compatInfo, resultCode, resultData,
- resultExtras, sync, sendingUser);
+ resultExtras, sync, sendingUser, processState);
return true;
}
IBinder token = data.readStrongBinder();
ServiceInfo info = ServiceInfo.CREATOR.createFromParcel(data);
CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
- scheduleCreateService(token, info, compatInfo);
+ int processState = data.readInt();
+ scheduleCreateService(token, info, compatInfo, processState);
return true;
}
IBinder token = data.readStrongBinder();
Intent intent = Intent.CREATOR.createFromParcel(data);
boolean rebind = data.readInt() != 0;
- scheduleBindService(token, intent, rebind);
+ int processState = data.readInt();
+ scheduleBindService(token, intent, rebind, processState);
return true;
}
boolean ordered = data.readInt() != 0;
boolean sticky = data.readInt() != 0;
int sendingUser = data.readInt();
+ int processState = data.readInt();
scheduleRegisteredReceiver(receiver, intent,
- resultCode, dataStr, extras, ordered, sticky, sendingUser);
+ resultCode, dataStr, extras, ordered, sticky, sendingUser, processState);
return true;
}
reply.writeNoException();
return true;
}
+
+ case SET_PROCESS_STATE_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ int state = data.readInt();
+ setProcessState(state);
+ reply.writeNoException();
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
data.recycle();
}
- public final void scheduleResumeActivity(IBinder token, boolean isForward)
+ public final void scheduleResumeActivity(IBinder token, int procState, boolean isForward)
throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeStrongBinder(token);
+ data.writeInt(procState);
data.writeInt(isForward ? 1 : 0);
mRemote.transact(SCHEDULE_RESUME_ACTIVITY_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
- Bundle state, List<ResultInfo> pendingResults,
+ int procState, Bundle state, List<ResultInfo> pendingResults,
List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler)
throws RemoteException {
info.writeToParcel(data, 0);
curConfig.writeToParcel(data, 0);
compatInfo.writeToParcel(data, 0);
+ data.writeInt(procState);
data.writeBundle(state);
data.writeTypedList(pendingResults);
data.writeTypedList(pendingNewIntents);
public final void scheduleReceiver(Intent intent, ActivityInfo info,
CompatibilityInfo compatInfo, int resultCode, String resultData,
- Bundle map, boolean sync, int sendingUser) throws RemoteException {
+ Bundle map, boolean sync, int sendingUser, int processState) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
intent.writeToParcel(data, 0);
data.writeBundle(map);
data.writeInt(sync ? 1 : 0);
data.writeInt(sendingUser);
+ data.writeInt(processState);
mRemote.transact(SCHEDULE_RECEIVER_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
}
public final void scheduleCreateService(IBinder token, ServiceInfo info,
- CompatibilityInfo compatInfo) throws RemoteException {
+ CompatibilityInfo compatInfo, int processState) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeStrongBinder(token);
info.writeToParcel(data, 0);
compatInfo.writeToParcel(data, 0);
+ data.writeInt(processState);
mRemote.transact(SCHEDULE_CREATE_SERVICE_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
}
- public final void scheduleBindService(IBinder token, Intent intent, boolean rebind)
- throws RemoteException {
+ public final void scheduleBindService(IBinder token, Intent intent, boolean rebind,
+ int processState) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeStrongBinder(token);
intent.writeToParcel(data, 0);
data.writeInt(rebind ? 1 : 0);
+ data.writeInt(processState);
mRemote.transact(SCHEDULE_BIND_SERVICE_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
int resultCode, String dataStr, Bundle extras, boolean ordered,
- boolean sticky, int sendingUser) throws RemoteException {
+ boolean sticky, int sendingUser, int processState) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeStrongBinder(receiver.asBinder());
data.writeInt(ordered ? 1 : 0);
data.writeInt(sticky ? 1 : 0);
data.writeInt(sendingUser);
+ data.writeInt(processState);
mRemote.transact(SCHEDULE_REGISTERED_RECEIVER_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
mRemote.transact(SCHEDULE_TRANSLUCENT_CONVERSION_COMPLETE_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
data.recycle();
}
+
+ @Override
+ public void setProcessState(int state) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ data.writeInt(state);
+ mRemote.transact(SET_PROCESS_STATE_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
}
int configChanges) throws RemoteException;
void scheduleWindowVisibility(IBinder token, boolean showWindow) throws RemoteException;
void scheduleSleeping(IBinder token, boolean sleeping) throws RemoteException;
- void scheduleResumeActivity(IBinder token, boolean isForward) throws RemoteException;
+ void scheduleResumeActivity(IBinder token, int procState, boolean isForward)
+ throws RemoteException;
void scheduleSendResult(IBinder token, List<ResultInfo> results) throws RemoteException;
void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
- Bundle state, List<ResultInfo> pendingResults,
+ int procState, Bundle state, List<ResultInfo> pendingResults,
List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler)
throws RemoteException;
int configChanges) throws RemoteException;
void scheduleReceiver(Intent intent, ActivityInfo info, CompatibilityInfo compatInfo,
int resultCode, String data, Bundle extras, boolean sync,
- int sendingUser) throws RemoteException;
+ int sendingUser, int processState) throws RemoteException;
static final int BACKUP_MODE_INCREMENTAL = 0;
static final int BACKUP_MODE_FULL = 1;
static final int BACKUP_MODE_RESTORE = 2;
void scheduleDestroyBackupAgent(ApplicationInfo app, CompatibilityInfo compatInfo)
throws RemoteException;
void scheduleCreateService(IBinder token, ServiceInfo info,
- CompatibilityInfo compatInfo) throws RemoteException;
+ CompatibilityInfo compatInfo, int processState) throws RemoteException;
void scheduleBindService(IBinder token,
- Intent intent, boolean rebind) throws RemoteException;
+ Intent intent, boolean rebind, int processState) throws RemoteException;
void scheduleUnbindService(IBinder token,
Intent intent) throws RemoteException;
void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,
throws RemoteException;
void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
int resultCode, String data, Bundle extras, boolean ordered,
- boolean sticky, int sendingUser) throws RemoteException;
+ boolean sticky, int sendingUser, int processState) throws RemoteException;
void scheduleLowMemory() throws RemoteException;
void scheduleActivityConfigurationChanged(IBinder token) throws RemoteException;
void profilerControl(boolean start, String path, ParcelFileDescriptor fd, int profileType)
throws RemoteException;
void scheduleTranslucentConversionComplete(IBinder token, boolean timeout)
throws RemoteException;
+ void setProcessState(int state) throws RemoteException;
String descriptor = "android.app.IApplicationThread";
int UNSTABLE_PROVIDER_DIED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+46;
int REQUEST_ACTIVITY_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+47;
int SCHEDULE_TRANSLUCENT_CONVERSION_COMPLETE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+48;
+ int SET_PROCESS_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+49;
}
if ((!i.requested || rebind) && i.apps.size() > 0) {
try {
bumpServiceExecutingLocked(r, "bind");
- r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind);
+ r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
+ r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
+ r.app.repProcState);
if (!rebind) {
i.requested = true;
}
r.stats.startLaunchedLocked();
}
mAm.ensurePackageDexOpt(r.serviceInfo.packageName);
+ app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
app.thread.scheduleCreateService(r, r.serviceInfo,
- mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo));
+ mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
+ app.repProcState);
r.postNotification();
created = true;
} finally {
} else {
oomAdj = Integer.toString(r.setAdj);
}
- String schedGroup;
+ char schedGroup;
switch (r.setSchedGroup) {
case Process.THREAD_GROUP_BG_NONINTERACTIVE:
- schedGroup = "B";
+ schedGroup = 'B';
break;
case Process.THREAD_GROUP_DEFAULT:
- schedGroup = "F";
+ schedGroup = 'F';
break;
default:
- schedGroup = Integer.toString(r.setSchedGroup);
+ schedGroup = '?';
break;
}
- String foreground;
+ char foreground;
if (r.foregroundActivities) {
- foreground = "A";
+ foreground = 'A';
} else if (r.foregroundServices) {
- foreground = "S";
+ foreground = 'S';
} else {
- foreground = " ";
+ foreground = ' ';
}
- pw.println(String.format("%s%s #%2d: adj=%s/%s%s trm=%2d %s (%s)",
- prefix, (r.persistent ? persistentLabel : normalLabel),
- (origList.size()-1)-list.get(i).second, oomAdj, schedGroup,
- foreground, r.trimMemoryLevel, r.toShortString(), r.adjType));
+ String procState;
+ switch (r.curProcState) {
+ case ActivityManager.PROCESS_STATE_PERSISTENT:
+ procState = "P ";
+ break;
+ case ActivityManager.PROCESS_STATE_PERSISTENT_UI:
+ procState = "PU";
+ break;
+ case ActivityManager.PROCESS_STATE_TOP:
+ procState = "T ";
+ break;
+ case ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE:
+ procState = "IP";
+ break;
+ case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND:
+ procState = "IB";
+ break;
+ case ActivityManager.PROCESS_STATE_RECEIVER:
+ procState = "R ";
+ break;
+ case ActivityManager.PROCESS_STATE_BACKUP:
+ procState = "BU";
+ break;
+ case ActivityManager.PROCESS_STATE_SERVICE:
+ procState = "S ";
+ break;
+ case ActivityManager.PROCESS_STATE_HOME:
+ procState = "H ";
+ break;
+ case ActivityManager.PROCESS_STATE_LAST_ACTIVITY:
+ procState = "LA";
+ break;
+ case ActivityManager.PROCESS_STATE_CACHED:
+ procState = "C ";
+ break;
+ default:
+ procState = "??";
+ break;
+ }
+ pw.print(prefix);
+ pw.print(r.persistent ? persistentLabel : normalLabel);
+ pw.print(" #");
+ int num = (origList.size()-1)-list.get(i).second;
+ if (num < 10) pw.print(' ');
+ pw.print(num);
+ pw.print(": ");
+ pw.print(oomAdj);
+ pw.print(' ');
+ pw.print(schedGroup);
+ pw.print('/');
+ pw.print(foreground);
+ pw.print('/');
+ pw.print(procState);
+ pw.print(" trm:");
+ if (r.trimMemoryLevel < 10) pw.print(' ');
+ pw.print(r.trimMemoryLevel);
+ pw.print(' ');
+ pw.print(r.toShortString());
+ pw.print(" (");
+ pw.print(r.adjType);
+ pw.println(')');
if (r.adjSource != null || r.adjTarget != null) {
pw.print(prefix);
pw.print(" ");
if (app.thread == null) {
app.adjSeq = mAdjSeq;
app.curSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ app.curProcState = ActivityManager.PROCESS_STATE_CACHED;
return (app.curAdj=app.curRawAdj=ProcessList.CACHED_APP_MAX_ADJ);
}
app.foregroundActivities = false;
app.keeping = true;
app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;
+ app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT;
// System process can do UI, and when they do we want to have
// them trim their memory after the user leaves the UI. To
// facilitate this, here we need to determine whether or not it
}
}
}
+ if (!app.systemNoUi) {
+ app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT_UI;
+ }
return (app.curAdj=app.maxAdj);
}
// important to least, and assign an appropriate OOM adjustment.
int adj;
int schedGroup;
+ int procState;
boolean foregroundActivities = false;
boolean interesting = false;
BroadcastQueue queue;
foregroundActivities = true;
interesting = true;
app.hasActivities = true;
+ procState = ActivityManager.PROCESS_STATE_TOP;
} else if (app.instrumentationClass != null) {
// Don't want to kill running instrumentation.
adj = ProcessList.FOREGROUND_APP_ADJ;
schedGroup = Process.THREAD_GROUP_DEFAULT;
app.adjType = "instrumentation";
interesting = true;
+ procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE;
} else if ((queue = isReceivingBroadcast(app)) != null) {
// An app that is currently receiving a broadcast also
// counts as being in the foreground for OOM killer purposes.
schedGroup = (queue == mFgBroadcastQueue)
? Process.THREAD_GROUP_DEFAULT : Process.THREAD_GROUP_BG_NONINTERACTIVE;
app.adjType = "broadcast";
+ procState = ActivityManager.PROCESS_STATE_RECEIVER;
} else if (app.executingServices.size() > 0) {
// An app that is currently executing a service callback also
// counts as being in the foreground.
adj = ProcessList.FOREGROUND_APP_ADJ;
schedGroup = Process.THREAD_GROUP_DEFAULT;
app.adjType = "exec-service";
+ procState = ActivityManager.PROCESS_STATE_SERVICE;
} else {
// Assume process is cached (has activities); we will correct
// later if this is not the case.
schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
app.cached = true;
app.adjType = "cch-act";
+ procState = ActivityManager.PROCESS_STATE_CACHED;
}
boolean hasStoppingActivities = false;
adj = ProcessList.VISIBLE_APP_ADJ;
app.adjType = "visible";
}
+ if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE) {
+ procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE;
+ }
schedGroup = Process.THREAD_GROUP_DEFAULT;
app.cached = false;
app.hasActivities = true;
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
app.adjType = "pausing";
}
+ if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE) {
+ procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE;
+ }
app.cached = false;
foregroundActivities = true;
} else if (r.state == ActivityState.STOPPING) {
// We will apply the actual adjustment later, because
// we want to allow this process to immediately go through
// any memory trimming that is in effect.
+ if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) {
+ procState = ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
+ }
app.cached = false;
foregroundActivities = true;
hasStoppingActivities = true;
if (app.foregroundServices) {
// The user is aware of this app, so make it visible.
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+ procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE;
app.cached = false;
app.adjType = "fg-service";
schedGroup = Process.THREAD_GROUP_DEFAULT;
} else if (app.forcingToForeground != null) {
// The user is aware of this app, so make it visible.
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+ procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE;
app.cached = false;
app.adjType = "force-fg";
app.adjSource = app.forcingToForeground;
app.adjType = "heavy";
}
- if (adj > ProcessList.HOME_APP_ADJ && app == mHomeProcess) {
- // This process is hosting what we currently consider to be the
- // home app, so we don't want to let it go into the background.
- adj = ProcessList.HOME_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
- app.cached = false;
- app.adjType = "home";
+ if (app == mHomeProcess) {
+ if (adj > ProcessList.HOME_APP_ADJ) {
+ // This process is hosting what we currently consider to be the
+ // home app, so we don't want to let it go into the background.
+ adj = ProcessList.HOME_APP_ADJ;
+ schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ app.cached = false;
+ app.adjType = "home";
+ }
+ if (procState > ActivityManager.PROCESS_STATE_HOME) {
+ procState = ActivityManager.PROCESS_STATE_HOME;
+ }
}
- if (adj > ProcessList.PREVIOUS_APP_ADJ && app == mPreviousProcess
- && app.activities.size() > 0) {
- // This was the previous process that showed UI to the user.
- // We want to try to keep it around more aggressively, to give
- // a good experience around switching between two apps.
- adj = ProcessList.PREVIOUS_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
- app.cached = false;
- app.adjType = "previous";
+ if (app == mPreviousProcess && app.activities.size() > 0) {
+ if (adj > ProcessList.PREVIOUS_APP_ADJ) {
+ // This was the previous process that showed UI to the user.
+ // We want to try to keep it around more aggressively, to give
+ // a good experience around switching between two apps.
+ adj = ProcessList.PREVIOUS_APP_ADJ;
+ schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ app.cached = false;
+ app.adjType = "previous";
+ }
+ if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
+ procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
+ }
}
if (false) Slog.i(TAG, "OOM " + app + ": initial adj=" + adj
if (adj > ProcessList.BACKUP_APP_ADJ) {
if (DEBUG_BACKUP) Slog.v(TAG, "oom BACKUP_APP_ADJ for " + app);
adj = ProcessList.BACKUP_APP_ADJ;
+ if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) {
+ procState = ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
+ }
app.adjType = "backup";
app.cached = false;
}
+ if (procState > ActivityManager.PROCESS_STATE_BACKUP) {
+ procState = ActivityManager.PROCESS_STATE_BACKUP;
+ }
}
if (app.services.size() != 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
ServiceRecord s = jt.next();
if (s.startRequested) {
app.hasStartedServices = true;
+ if (procState > ActivityManager.PROCESS_STATE_SERVICE) {
+ procState = ActivityManager.PROCESS_STATE_SERVICE;
+ }
if (app.hasShownUi && app != mHomeProcess) {
// If this process has shown some UI, let it immediately
// go to the LRU list because it may be pretty heavy with
}
if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) {
ProcessRecord client = cr.binding.client;
- int clientAdj = adj;
int myCachedAdj = cachedAdj;
if (myCachedAdj > client.cachedAdj) {
if (client.cachedAdj >= ProcessList.VISIBLE_APP_ADJ) {
myEmptyAdj = ProcessList.VISIBLE_APP_ADJ;
}
}
- clientAdj = computeOomAdjLocked(client, myCachedAdj,
+ int clientAdj = computeOomAdjLocked(client, myCachedAdj,
myClientCachedAdj, myEmptyAdj, TOP_APP, true, doingAll);
+ int clientProcState = client.curProcState;
String adjType = null;
if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
// Not doing bind OOM management, so treat
}
app.cached = false;
clientAdj = adj;
+ clientProcState = procState;
} else {
if (now >= (s.lastActivity
+ ActiveServices.MAX_SERVICE_INACTIVITY)) {
// LRU list as a cached process (with activities),
// we don't want the service it is connected to
// to go into the empty LRU and quickly get killed,
- // because I'll we'll do is just end up restarting
+ // because all we'll do is just end up restarting
// the service.
app.hasClientActivities |= client.hasActivities;
}
adjType = "service";
}
}
+ if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
+ if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
+ schedGroup = Process.THREAD_GROUP_DEFAULT;
+ }
+ if (clientProcState <
+ ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE) {
+ clientProcState =
+ ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE;
+ }
+ } else {
+ if (clientProcState <
+ ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) {
+ clientProcState =
+ ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
+ }
+ }
+ if (procState > clientProcState) {
+ procState = clientProcState;
+ }
if (adjType != null) {
app.adjType = adjType;
app.adjTypeCode = ActivityManager.RunningAppProcessInfo
app.adjSourceOom = clientAdj;
app.adjTarget = s.name;
}
- if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
- if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
- schedGroup = Process.THREAD_GROUP_DEFAULT;
- }
- }
}
final ActivityRecord a = cr.activity;
if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
app.adjSourceOom = clientAdj;
app.adjTarget = cpr.name;
}
+ if (procState > client.curProcState) {
+ procState = client.curProcState;
+ }
if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
schedGroup = Process.THREAD_GROUP_DEFAULT;
}
app.adjType = "provider";
app.adjTarget = cpr.name;
}
+ if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE) {
+ procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE;
+ }
}
}
}
}
+ app.curProcState = procState;
+
int importance = app.memImportance;
if (importance == 0 || adj != app.curAdj || schedGroup != app.curSchedGroup) {
app.curAdj = adj;
}
private final boolean updateOomAdjLocked(ProcessRecord app, int cachedAdj,
- int clientCachedAdj, int emptyAdj, ProcessRecord TOP_APP, boolean doingAll, long now) {
+ int clientCachedAdj, int emptyAdj, ProcessRecord TOP_APP, boolean doingAll,
+ boolean doingProcessState, long now) {
app.cachedAdj = cachedAdj;
app.clientCachedAdj = clientCachedAdj;
app.emptyAdj = emptyAdj;
}
}
}
+ if (app.repProcState != app.curProcState) {
+ app.repProcState = app.curProcState;
+ if (!doingProcessState && app.thread != null) {
+ try {
+ if (false) {
+ //RuntimeException h = new RuntimeException("here");
+ Slog.i(TAG, "Sending new process state " + app.repProcState
+ + " to " + app /*, h*/);
+ }
+ app.thread.setProcessState(app.repProcState);
+ } catch (RemoteException e) {
+ }
+ }
+ }
return success;
}
}
final boolean updateOomAdjLocked(ProcessRecord app) {
+ return updateOomAdjLocked(app, false);
+ }
+
+ final boolean updateOomAdjLocked(ProcessRecord app, boolean doingProcessState) {
final ActivityRecord TOP_ACT = resumedAppLocked();
final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
int curAdj = app.curAdj;
mAdjSeq++;
boolean success = updateOomAdjLocked(app, app.cachedAdj, app.clientCachedAdj,
- app.emptyAdj, TOP_APP, false, SystemClock.uptimeMillis());
+ app.emptyAdj, TOP_APP, false, doingProcessState, SystemClock.uptimeMillis());
final boolean nowCached = app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ
&& app.curAdj <= ProcessList.CACHED_APP_MAX_ADJ;
if (nowCached != wasCached) {
//Slog.i(TAG, "OOM " + app + ": cur cached=" + curCachedAdj);
app.setAdjChanged = false;
updateOomAdjLocked(app, curCachedAdj, curClientCachedAdj, curEmptyAdj, TOP_APP,
- true, now);
+ true, false, now);
changed |= app.setAdjChanged;
if (!app.killedBackground) {
if (app.curRawAdj == curCachedAdj && app.hasActivities) {
destroyActivityLocked(r, true, false, "stop-config");
mStackSupervisor.resumeTopActivitiesLocked();
} else {
- // Now that this process has stopped, we may want to consider
- // it to be the previous app to try to keep around in case
- // the user wants to return to it.
- ProcessRecord fgApp = null;
- if (mResumedActivity != null) {
- fgApp = mResumedActivity.app;
- } else if (mPausingActivity != null) {
- fgApp = mPausingActivity.app;
- }
- if (r.app != null && fgApp != null && r.app != fgApp
- && r.lastVisibleTime > mService.mPreviousProcessVisibleTime
- && r.app != mService.mHomeProcess) {
- mService.mPreviousProcess = r.app;
- mService.mPreviousProcessVisibleTime = r.lastVisibleTime;
- }
+ mStackSupervisor.updatePreviousProcessLocked(r);
}
}
}
next.sleeping = false;
mService.showAskCompatModeDialogLocked(next);
next.app.pendingUiClean = true;
- next.app.thread.scheduleResumeActivity(next.appToken,
+ next.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_TOP);
+ next.app.thread.scheduleResumeActivity(next.appToken, next.app.repProcState,
mService.isNextTransitionForward());
mStackSupervisor.checkReadyForSleepLocked();
}
}
}
+ app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_TOP);
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
System.identityHashCode(r), r.info,
- new Configuration(mService.mConfiguration),
- r.compat, r.icicle, results, newIntents, !andResume,
+ new Configuration(mService.mConfiguration), r.compat,
+ app.repProcState, r.icicle, results, newIntents, !andResume,
mService.isNextTransitionForward(), profileFile, profileFd,
profileAutoStop);
return didSomething;
}
+ void updatePreviousProcessLocked(ActivityRecord r) {
+ // Now that this process has stopped, we may want to consider
+ // it to be the previous app to try to keep around in case
+ // the user wants to return to it.
+
+ // First, found out what is currently the foreground app, so that
+ // we don't blow away the previous app if this activity is being
+ // hosted by the process that is actually still the foreground.
+ ProcessRecord fgApp = null;
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ if (isFrontStack(stack)) {
+ if (stack.mResumedActivity != null) {
+ fgApp = stack.mResumedActivity.app;
+ } else if (stack.mPausingActivity != null) {
+ fgApp = stack.mPausingActivity.app;
+ }
+ break;
+ }
+ }
+
+ // Now set this one as the previous process, only if that really
+ // makes sense to.
+ if (r.app != null && fgApp != null && r.app != fgApp
+ && r.lastVisibleTime > mService.mPreviousProcessVisibleTime
+ && r.app != mService.mHomeProcess) {
+ mService.mPreviousProcess = r.app;
+ mService.mPreviousProcessVisibleTime = r.lastVisibleTime;
+ }
+ }
+
boolean resumeTopActivitiesLocked() {
return resumeTopActivitiesLocked(null, null, null);
}
r.receiver = app.thread.asBinder();
r.curApp = app;
app.curReceiver = r;
+ app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
mService.updateLruProcessLocked(app, true);
// Tell the application to launch this receiver.
mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
- r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId);
+ r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
+ app.repProcState);
if (DEBUG_BROADCAST) Slog.v(TAG,
"Process cur broadcast " + r + " DELIVERED for app " + app);
started = true;
// If we have an app thread, do the call through that so it is
// correctly ordered with other one-way calls.
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
- data, extras, ordered, sticky, sendingUser);
+ data, extras, ordered, sticky, sendingUser, app.repProcState);
} else {
receiver.performReceive(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
// are already core system stuff so don't matter for this.
r.curApp = filter.receiverList.app;
filter.receiverList.app.curReceiver = r;
- mService.updateOomAdjLocked();
+ mService.updateOomAdjLocked(r.curApp, true);
}
}
try {
int setSchedGroup; // Last set to background scheduling class
int trimMemoryLevel; // Last selected memory trimming level
int memImportance; // Importance constant computed from curAdj
+ int curProcState = -1; // Currently computed process state: ActivityManager.PROCESS_STATE_*
+ int repProcState = -1; // Last reported process state
boolean serviceb; // Process currently is on the service B list
boolean keeping; // Actively running code so don't kill due to that?
boolean setIsForeground; // Running foreground UI when last set?
pw.print(" setSchedGroup="); pw.print(setSchedGroup);
pw.print(" systemNoUi="); pw.print(systemNoUi);
pw.print(" trimMemoryLevel="); pw.println(trimMemoryLevel);
+ pw.print(prefix); pw.print("curProcState="); pw.print(curProcState);
+ pw.print(" repProcState="); pw.println(repProcState);
pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq);
pw.print(" lruSeq="); pw.print(lruSeq);
pw.print(" lastPssTime="); pw.println(lastPssTime);
return setAdj;
}
+ public void forceProcessStateUpTo(int newState) {
+ if (repProcState > newState) {
+ curProcState = repProcState = newState;
+ }
+ }
+
public void setProcessTrackerState(ProcessRecord TOP_APP, int memFactor, long now,
ProcessList plist) {
int state = this == TOP_APP ? ProcessTracker.STATE_TOP