}
@Override
- public void onUidGone(int uid) {
+ public void onUidGone(int uid, boolean disabled) {
mListener.onUidImportance(uid, RunningAppProcessInfo.IMPORTANCE_GONE);
}
}
@Override
- public void onUidIdle(int uid) {
+ public void onUidIdle(int uid, boolean disabled) {
}
}
boolean supportsLocalVoiceInteraction() = 365;
void notifyPinnedStackAnimationEnded() = 366;
void removeStack(int stackId) = 367;
- void setLenientBackgroundCheck(boolean enabled) = 368;
+ void makePackageIdle(String packageName, int userId) = 368;
int getMemoryTrimLevel() = 369;
/**
* Resizes the pinned stack.
/**
* Report that there are no longer any processes running for a uid.
*/
- void onUidGone(int uid);
+ void onUidGone(int uid, boolean disabled);
/**
* Report that a uid is now active (no longer idle).
* Report that a uid is idle -- it has either been running in the background for
* a sufficient period of time, or all of its processes have gone away.
*/
- void onUidIdle(int uid);
+ void onUidIdle(int uid, boolean disabled);
}
public abstract boolean isPackageDataProtected(int userId, String packageName);
/**
+ * Returns {@code true} if a given package is installed as ephemeral. Otherwise, returns
+ * {@code false}.
+ */
+ public abstract boolean isPackageEphemeral(int userId, String packageName);
+
+ /**
* Gets whether the package was ever launched.
* @param packageName The package name.
* @param userId The user for which to check.
public static final String ALWAYS_FINISH_ACTIVITIES = "always_finish_activities";
/**
- * @hide
- * If not 0, the activity manager will implement a looser version of background
- * check that is more compatible with existing apps.
- */
- public static final String LENIENT_BACKGROUND_CHECK = "lenient_background_check";
-
- /**
* Use Dock audio output for media:
* 0 = disabled
* 1 = enabled
}
for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) {
final Alarm a = mPendingWhileIdleAlarms.get(i);
- try {
- if (a.uid == uid && ActivityManagerNative.getDefault().getAppStartMode(
- uid, a.packageName) == ActivityManager.APP_START_MODE_DISABLED) {
- // Don't set didRemove, since this doesn't impact the scheduled alarms.
- mPendingWhileIdleAlarms.remove(i);
- }
- } catch (RemoteException e) {
+ if (a.uid == uid) {
+ // Don't set didRemove, since this doesn't impact the scheduled alarms.
+ mPendingWhileIdleAlarms.remove(i);
}
}
@Override public void onUidStateChanged(int uid, int procState) throws RemoteException {
}
- @Override public void onUidGone(int uid) throws RemoteException {
+ @Override public void onUidGone(int uid, boolean disabled) throws RemoteException {
+ if (disabled) {
+ synchronized (mLock) {
+ removeForStoppedLocked(uid);
+ }
+ }
}
@Override public void onUidActive(int uid) throws RemoteException {
}
- @Override public void onUidIdle(int uid) throws RemoteException {
- synchronized (mLock) {
- removeForStoppedLocked(uid);
+ @Override public void onUidIdle(int uid, boolean disabled) throws RemoteException {
+ if (disabled) {
+ synchronized (mLock) {
+ removeForStoppedLocked(uid);
+ }
}
}
};
// Before going further -- if this app is not allowed to run in the
// background, then at this point we aren't going to let it period.
final int allowed = mAm.checkAllowBackgroundLocked(
- r.appInfo.uid, r.packageName, callingPid, true);
+ r.appInfo.uid, r.packageName, callingPid, false);
if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
Slog.w(TAG, "Background start not allowed: service "
+ service + " to " + r.name.flattenToShortString()
for (int i=services.mServicesByName.size()-1; i>=0; i--) {
ServiceRecord service = services.mServicesByName.valueAt(i);
if (service.appInfo.uid == uid && service.startRequested) {
- if (mAm.mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND,
+ if (service.appInfo.isEphemeralApp() ||
+ mAm.mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND,
uid, service.packageName) != AppOpsManager.MODE_ALLOWED) {
if (stopping == null) {
stopping = new ArrayList<>();
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL;
-import static android.provider.Settings.Global.LENIENT_BACKGROUND_CHECK;
import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
import static android.provider.Settings.System.FONT_SCALE;
-import static android.util.TypedValue.COMPLEX_UNIT_DIP;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.internal.util.XmlUtils.readBooleanAttribute;
String mOrigDebugApp = null;
boolean mOrigWaitForDebugger = false;
boolean mAlwaysFinishActivities = false;
- boolean mLenientBackgroundCheck = false;
boolean mForceResizableActivities;
boolean mSupportsMultiWindow;
boolean mSupportsFreeformWindowManagement;
if ((reg.which & ActivityManager.UID_OBSERVER_IDLE) != 0) {
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"UID idle uid=" + item.uid);
- observer.onUidIdle(item.uid);
+ observer.onUidIdle(item.uid, item.ephemeral);
}
if (VALIDATE_UID_STATES && i == 0) {
if (validateUid != null) {
if ((reg.which & ActivityManager.UID_OBSERVER_GONE) != 0) {
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"UID gone uid=" + item.uid);
- observer.onUidGone(item.uid);
+ observer.onUidGone(item.uid, item.ephemeral);
}
if (reg.lastProcStates != null) {
reg.lastProcStates.delete(item.uid);
public int getAppStartMode(int uid, String packageName) {
synchronized (this) {
- return checkAllowBackgroundLocked(uid, packageName, -1, true);
+ return checkAllowBackgroundLocked(uid, packageName, -1, false);
}
}
int checkAllowBackgroundLocked(int uid, String packageName, int callingPid,
- boolean allowWhenForeground) {
+ boolean alwaysRestrict) {
UidRecord uidRec = mActiveUids.get(uid);
- if (!mLenientBackgroundCheck) {
- if (!allowWhenForeground || uidRec == null
- || uidRec.curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) {
- if (mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND, uid,
- packageName) != AppOpsManager.MODE_ALLOWED) {
- return ActivityManager.APP_START_MODE_DELAYED;
- }
+ if (uidRec == null || alwaysRestrict || uidRec.idle) {
+ boolean ephemeral;
+ if (uidRec == null) {
+ ephemeral = getPackageManagerInternalLocked().isPackageEphemeral(
+ UserHandle.getUserId(uid), packageName);
+ } else {
+ ephemeral = uidRec.ephemeral;
}
- } else if (uidRec == null || uidRec.idle) {
- if (callingPid >= 0) {
- ProcessRecord proc;
- synchronized (mPidsSelfLocked) {
- proc = mPidsSelfLocked.get(callingPid);
+ if (ephemeral) {
+ // We are hard-core about ephemeral apps not running in the background.
+ return ActivityManager.APP_START_MODE_DISABLED;
+ } else {
+ if (callingPid >= 0) {
+ ProcessRecord proc;
+ synchronized (mPidsSelfLocked) {
+ proc = mPidsSelfLocked.get(callingPid);
+ }
+ if (proc != null && proc.curProcState
+ < ActivityManager.PROCESS_STATE_RECEIVER) {
+ // Whoever is instigating this is in the foreground, so we will allow it
+ // to go through.
+ return ActivityManager.APP_START_MODE_NORMAL;
+ }
}
- if (proc != null && proc.curProcState < ActivityManager.PROCESS_STATE_RECEIVER) {
- // Whoever is instigating this is in the foreground, so we will allow it
- // to go through.
- return ActivityManager.APP_START_MODE_NORMAL;
+ if (mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND, uid,
+ packageName) != AppOpsManager.MODE_ALLOWED) {
+ return ActivityManager.APP_START_MODE_DELAYED;
}
}
- if (mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND, uid, packageName)
- != AppOpsManager.MODE_ALLOWED) {
- return ActivityManager.APP_START_MODE_DELAYED;
- }
}
return ActivityManager.APP_START_MODE_NORMAL;
}
}
@Override
- public void setLenientBackgroundCheck(boolean enabled) {
- enforceCallingPermission(android.Manifest.permission.SET_PROCESS_LIMIT,
- "setLenientBackgroundCheck()");
-
- long ident = Binder.clearCallingIdentity();
- try {
- Settings.Global.putInt(
- mContext.getContentResolver(),
- Settings.Global.LENIENT_BACKGROUND_CHECK, enabled ? 1 : 0);
-
- synchronized (this) {
- mLenientBackgroundCheck = enabled;
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- @Override
public void setActivityController(IActivityController controller, boolean imAMonkey) {
enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
"setActivityController()");
final boolean waitForDebugger = Settings.Global.getInt(resolver, WAIT_FOR_DEBUGGER, 0) != 0;
final boolean alwaysFinishActivities =
Settings.Global.getInt(resolver, ALWAYS_FINISH_ACTIVITIES, 0) != 0;
- final boolean lenientBackgroundCheck =
- Settings.Global.getInt(resolver, LENIENT_BACKGROUND_CHECK, 0) != 0;
final boolean forceRtl = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_RTL, 0) != 0;
final boolean forceResizable = Settings.Global.getInt(
resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
mDebugApp = mOrigDebugApp = debugApp;
mWaitForDebugger = mOrigWaitForDebugger = waitForDebugger;
mAlwaysFinishActivities = alwaysFinishActivities;
- mLenientBackgroundCheck = lenientBackgroundCheck;
mSupportsLeanbackOnly = supportsLeanbackOnly;
mForceResizableActivities = forceResizable;
if (supportsMultiWindow || forceResizable) {
}
}
if (dumpPackage == null) {
- if (mAlwaysFinishActivities || mLenientBackgroundCheck) {
- pw.println(" mAlwaysFinishActivities=" + mAlwaysFinishActivities
- + " mLenientBackgroundCheck=" + mLenientBackgroundCheck);
+ if (mAlwaysFinishActivities) {
+ pw.println(" mAlwaysFinishActivities=" + mAlwaysFinishActivities);
}
if (mController != null) {
pw.println(" mController=" + mController
pendingChange.change = change;
pendingChange.processState = uidRec != null
? uidRec.setProcState : ActivityManager.PROCESS_STATE_NONEXISTENT;
+ pendingChange.ephemeral = uidRec.ephemeral;
// Directly update the power manager, since we sit on top of it and it is critical
// it be kept in sync (so wake locks will be held as soon as appropriate).
} else {
// Keeping this process, update its uid.
final UidRecord uidRec = app.uidRecord;
- if (uidRec != null && uidRec.curProcState > app.curProcState) {
- uidRec.curProcState = app.curProcState;
+ if (uidRec != null) {
+ uidRec.ephemeral = app.info.isEphemeralApp();
+ if (uidRec.curProcState > app.curProcState) {
+ uidRec.curProcState = app.curProcState;
+ }
}
}
}
}
+ @Override
+ public void makePackageIdle(String packageName, int userId) {
+ if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
+ != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: makePackageIdle() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + android.Manifest.permission.FORCE_STOP_PACKAGES;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ final int callingPid = Binder.getCallingPid();
+ userId = mUserController.handleIncomingUser(callingPid, Binder.getCallingUid(),
+ userId, true, ALLOW_FULL_ONLY, "makePackageIdle", null);
+ long callingId = Binder.clearCallingIdentity();
+ synchronized(this) {
+ try {
+ IPackageManager pm = AppGlobals.getPackageManager();
+ int pkgUid = -1;
+ try {
+ pkgUid = pm.getPackageUid(packageName, MATCH_UNINSTALLED_PACKAGES
+ | MATCH_DEBUG_TRIAGED_MISSING, UserHandle.USER_SYSTEM);
+ } catch (RemoteException e) {
+ }
+ if (pkgUid == -1) {
+ throw new IllegalArgumentException("Unknown package name " + packageName);
+ }
+
+ if (mLocalPowerManager != null) {
+ mLocalPowerManager.startUidChanges();
+ }
+ final int appId = UserHandle.getAppId(pkgUid);
+ final int N = mActiveUids.size();
+ for (int i=N-1; i>=0; i--) {
+ final UidRecord uidRec = mActiveUids.valueAt(i);
+ final long bgTime = uidRec.lastBackgroundTime;
+ if (bgTime > 0 && !uidRec.idle) {
+ if (UserHandle.getAppId(uidRec.uid) == appId) {
+ if (userId == UserHandle.USER_ALL ||
+ userId == UserHandle.getUserId(uidRec.uid)) {
+ uidRec.idle = true;
+ Slog.w(TAG, "Idling uid " + UserHandle.formatUid(uidRec.uid)
+ + " from package " + packageName + " user " + userId);
+ doStopUidLocked(uidRec.uid, uidRec);
+ }
+ }
+ }
+ }
+ } finally {
+ if (mLocalPowerManager != null) {
+ mLocalPowerManager.finishUidChanges();
+ }
+ Binder.restoreCallingIdentity(callingId);
+ }
+ }
+ }
+
final void idleUids() {
synchronized (this) {
final int N = mActiveUids.size();
return runKill(pw);
case "kill-all":
return runKillAll(pw);
+ case "make-idle":
+ return runMakeIdle(pw);
case "monitor":
return runMonitor(pw);
case "hang":
return runTrackAssociations(pw);
case "untrack-associations":
return runUntrackAssociations(pw);
- case "lenient-background-check":
- return runLenientBackgroundCheck(pw);
case "get-uid-state":
return getUidState(pw);
case "get-config":
return 0;
}
+ int runMakeIdle(PrintWriter pw) throws RemoteException {
+ int userId = UserHandle.USER_ALL;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ if (opt.equals("--user")) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ } else {
+ getErrPrintWriter().println("Error: Unknown option: " + opt);
+ return -1;
+ }
+ }
+ mInterface.makePackageIdle(getNextArgRequired(), userId);
+ return 0;
+ }
+
static final class MyActivityController extends IActivityController.Stub {
final IActivityManager mInterface;
final PrintWriter mPw;
return 0;
}
- int runLenientBackgroundCheck(PrintWriter pw) throws RemoteException {
- String arg = getNextArg();
- if (arg != null) {
- boolean state = Boolean.valueOf(arg) || "1".equals(arg);
- mInterface.setLenientBackgroundCheck(state);
- }
- synchronized (mInternal) {
- if (mInternal.mLenientBackgroundCheck) {
- pw.println("Lenient background check enabled");
- } else {
- pw.println("Lenient background check disabled");
- }
- }
- return 0;
- }
-
int getUidState(PrintWriter pw) throws RemoteException {
mInternal.enforceCallingPermission(android.Manifest.permission.DUMP,
"getUidState()");
pw.println(" Enable association tracking.");
pw.println(" untrack-associations");
pw.println(" Disable and clear association tracking.");
- pw.println(" lenient-background-check [<true|false>]");
- pw.println(" Optionally controls lenient background check mode, returns current mode.");
pw.println(" get-uid-state <UID>");
pw.println(" Gets the process state of an app given its <UID>.");
pw.println(" attach-agent <PROCESS> <FILE>");
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
}
if (!skip) {
final int allowed = mService.checkAllowBackgroundLocked(filter.receiverList.uid,
- filter.packageName, -1, true);
- if (allowed == ActivityManager.APP_START_MODE_DISABLED) {
+ filter.packageName, -1, false);
+ if (false && allowed == ActivityManager.APP_START_MODE_DISABLED) {
+ // XXX should we really not allow this? It means that while we are
+ // keeping an ephemeral app cached, its registered receivers will stop
+ // receiving broadcasts after it goes idle... so if it comes back to
+ // the foreground, it won't know what the current state of those broadcasts is.
Slog.w(TAG, "Background execution not allowed: receiving "
+ r.intent
+ " to " + filter.receiverList.app
if (!skip) {
final int allowed = mService.checkAllowBackgroundLocked(
info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, -1,
- false);
+ true);
if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
// We won't allow this receiver to be launched if the app has been
// completely disabled from launches, or it was not explicitly sent
int curProcState;
int setProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
long lastBackgroundTime;
+ boolean ephemeral;
boolean idle;
int numProcs;
int uid;
int change;
int processState;
+ boolean ephemeral;
}
ChangeItem pendingChange;
UserHandle.formatUid(sb, uid);
sb.append(' ');
sb.append(ProcessList.makeProcStateString(curProcState));
+ if (ephemeral) {
+ sb.append(" ephemeral");
+ }
if (lastBackgroundTime > 0) {
sb.append(" bg:");
TimeUtils.formatDuration(SystemClock.elapsedRealtime()-lastBackgroundTime, sb);
Slog.d(TAG, "Removing jobs for package " + pkgName
+ " in user " + userId);
}
- cancelJobsForUid(pkgUid, true);
+ cancelJobsForUid(pkgUid);
}
} catch (RemoteException|IllegalArgumentException e) {
/*
if (DEBUG) {
Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
}
- cancelJobsForUid(uidRemoved, true);
+ cancelJobsForUid(uidRemoved);
}
} else if (Intent.ACTION_USER_REMOVED.equals(action)) {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
updateUidState(uid, procState);
}
- @Override public void onUidGone(int uid) throws RemoteException {
+ @Override public void onUidGone(int uid, boolean disabled) throws RemoteException {
updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
+ if (disabled) {
+ cancelJobsForUid(uid);
+ }
}
@Override public void onUidActive(int uid) throws RemoteException {
}
- @Override public void onUidIdle(int uid) throws RemoteException {
- cancelJobsForUid(uid, false);
+ @Override public void onUidIdle(int uid, boolean disabled) throws RemoteException {
+ if (disabled) {
+ cancelJobsForUid(uid);
+ }
}
};
* This will remove the job from the master list, and cancel the job if it was staged for
* execution or being executed.
* @param uid Uid to check against for removal of a job.
- * @param forceAll If true, all jobs for the uid will be canceled; if false, only those
- * whose apps are stopped.
+ *
*/
- public void cancelJobsForUid(int uid, boolean forceAll) {
+ public void cancelJobsForUid(int uid) {
List<JobStatus> jobsForUid;
synchronized (mLock) {
jobsForUid = mJobs.getJobsByUid(uid);
}
for (int i=0; i<jobsForUid.size(); i++) {
JobStatus toRemove = jobsForUid.get(i);
- if (!forceAll) {
- String packageName = toRemove.getServiceComponent().getPackageName();
- try {
- if (ActivityManagerNative.getDefault().getAppStartMode(uid, packageName)
- != ActivityManager.APP_START_MODE_DISABLED) {
- continue;
- }
- } catch (RemoteException e) {
- }
- }
cancelJobImpl(toRemove, null);
}
}
long ident = Binder.clearCallingIdentity();
try {
- JobSchedulerService.this.cancelJobsForUid(uid, true);
+ JobSchedulerService.this.cancelJobsForUid(uid);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
- @Override public void onUidGone(int uid) throws RemoteException {
+ @Override public void onUidGone(int uid, boolean disabled) throws RemoteException {
synchronized (mUidRulesFirstLock) {
removeUidStateUL(uid);
}
@Override public void onUidActive(int uid) throws RemoteException {
}
- @Override public void onUidIdle(int uid) throws RemoteException {
+ @Override public void onUidIdle(int uid, boolean disabled) throws RemoteException {
}
};
}
@Override
+ public boolean isPackageEphemeral(int userId, String packageName) {
+ synchronized (mPackages) {
+ PackageParser.Package p = mPackages.get(packageName);
+ return p != null ? p.applicationInfo.isEphemeralApp() : false;
+ }
+ }
+
+ @Override
public boolean wasPackageEverLaunched(String packageName, int userId) {
synchronized (mPackages) {
return mSettings.wasPackageEverLaunchedLPr(packageName, userId);
}
@Override
- public void onUidGone(int uid) throws RemoteException {
+ public void onUidGone(int uid, boolean disabled) throws RemoteException {
handleOnUidStateChanged(uid, ActivityManager.PROCESS_STATE_NONEXISTENT);
}
}
@Override
- public void onUidIdle(int uid) throws RemoteException {
+ public void onUidIdle(int uid, boolean disabled) throws RemoteException {
}
};