Merge "Ensure apps can't bypass FAS using UIJ." into udc-dev
This commit is contained in:
@@ -200,7 +200,10 @@ class JobNotificationCoordinator {
|
||||
// No more jobs using this notification. Apply the final job stop policy.
|
||||
// If the user attempted to stop the job/app, then always remove the notification
|
||||
// so the user doesn't get confused about the app state.
|
||||
// Similarly, if the user background restricted the app, remove the notification so
|
||||
// the user doesn't think the app is continuing to run in the background.
|
||||
if (details.jobEndNotificationPolicy == JOB_END_NOTIFICATION_POLICY_REMOVE
|
||||
|| stopReason == JobParameters.STOP_REASON_BACKGROUND_RESTRICTION
|
||||
|| stopReason == JobParameters.STOP_REASON_USER) {
|
||||
mNotificationManagerInternal.cancelNotification(
|
||||
packageName, packageName, details.appUid, details.appPid, /* tag */ null,
|
||||
|
||||
@@ -413,16 +413,22 @@ public final class JobServiceContext implements ServiceConnection {
|
||||
final Intent intent = new Intent().setComponent(job.getServiceComponent())
|
||||
.setFlags(Intent.FLAG_FROM_BACKGROUND);
|
||||
boolean binding = false;
|
||||
boolean startedWithForegroundFlag = false;
|
||||
try {
|
||||
final Context.BindServiceFlags bindFlags;
|
||||
if (job.shouldTreatAsUserInitiatedJob()) {
|
||||
if (job.shouldTreatAsUserInitiatedJob() && !job.isUserBgRestricted()) {
|
||||
// If the user has bg restricted the app, don't give the job FG privileges
|
||||
// such as bypassing data saver or getting the higher foreground proc state.
|
||||
// If we've gotten to this point, the app is most likely in the foreground,
|
||||
// so the job will run just fine while the user keeps the app in the foreground.
|
||||
bindFlags = Context.BindServiceFlags.of(
|
||||
Context.BIND_AUTO_CREATE
|
||||
| Context.BIND_ALMOST_PERCEPTIBLE
|
||||
| Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS
|
||||
| Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS
|
||||
| Context.BIND_NOT_APP_COMPONENT_USAGE);
|
||||
} else if (job.shouldTreatAsExpeditedJob()) {
|
||||
startedWithForegroundFlag = true;
|
||||
} else if (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob()) {
|
||||
bindFlags = Context.BindServiceFlags.of(
|
||||
Context.BIND_AUTO_CREATE
|
||||
| Context.BIND_NOT_FOREGROUND
|
||||
@@ -535,8 +541,11 @@ public final class JobServiceContext implements ServiceConnection {
|
||||
mAvailable = false;
|
||||
mStoppedReason = null;
|
||||
mStoppedTime = 0;
|
||||
// Wait until after bindService() returns a success value to set these so we don't
|
||||
// have JobStatus objects that aren't running but have these set to true.
|
||||
job.startedAsExpeditedJob = job.shouldTreatAsExpeditedJob();
|
||||
job.startedAsUserInitiatedJob = job.shouldTreatAsUserInitiatedJob();
|
||||
job.startedWithForegroundFlag = startedWithForegroundFlag;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.server.job.controllers;
|
||||
import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
|
||||
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManagerInternal;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
@@ -205,8 +206,32 @@ public final class BackgroundJobsController extends StateController {
|
||||
final int uid = jobStatus.getSourceUid();
|
||||
final String packageName = jobStatus.getSourcePackageName();
|
||||
|
||||
final boolean canRun = !mAppStateTracker.areJobsRestricted(uid, packageName,
|
||||
jobStatus.canRunInBatterySaver());
|
||||
final boolean isUserBgRestricted =
|
||||
!mActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled()
|
||||
&& !mAppStateTracker.isRunAnyInBackgroundAppOpsAllowed(uid, packageName);
|
||||
// If a job started with the foreground flag, it'll cause the UID to stay active
|
||||
// and thus cause areJobsRestricted() to always return false, so if
|
||||
// areJobsRestricted() returns false and the app is BG restricted and not TOP,
|
||||
// we need to stop any jobs that started with the foreground flag so they don't
|
||||
// keep the app in an elevated proc state. If we were to get in this situation,
|
||||
// then the user restricted the app after the job started, so it's best to stop
|
||||
// the job as soon as possible, especially since the job would be visible to the
|
||||
// user (with a notification and in Task Manager).
|
||||
// There are several other reasons that uidActive can be true for an app even if its
|
||||
// proc state is less important than BFGS.
|
||||
// JobScheduler has historically (at least up through UDC) allowed the app's jobs to run
|
||||
// when its UID was active, even if it's background restricted. This has been fine because
|
||||
// JobScheduler stops the job as soon as the UID becomes inactive and the jobs themselves
|
||||
// will not keep the UID active. The logic here is to ensure that special jobs
|
||||
// (e.g. user-initiated jobs) themselves do not keep the UID active when the app is
|
||||
// background restricted.
|
||||
final boolean shouldStopImmediately = jobStatus.startedWithForegroundFlag
|
||||
&& isUserBgRestricted
|
||||
&& mService.getUidProcState(uid)
|
||||
> ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
|
||||
final boolean canRun = !shouldStopImmediately
|
||||
&& !mAppStateTracker.areJobsRestricted(
|
||||
uid, packageName, jobStatus.canRunInBatterySaver());
|
||||
|
||||
final boolean isActive;
|
||||
if (activeState == UNKNOWN) {
|
||||
@@ -219,8 +244,7 @@ public final class BackgroundJobsController extends StateController {
|
||||
}
|
||||
boolean didChange =
|
||||
jobStatus.setBackgroundNotRestrictedConstraintSatisfied(nowElapsed, canRun,
|
||||
!mActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled()
|
||||
&& !mAppStateTracker.isRunAnyInBackgroundAppOpsAllowed(uid, packageName));
|
||||
isUserBgRestricted);
|
||||
didChange |= jobStatus.setUidActive(isActive);
|
||||
return didChange;
|
||||
}
|
||||
|
||||
@@ -1774,6 +1774,12 @@ public final class ConnectivityController extends RestrictingController implemen
|
||||
}
|
||||
pw.println();
|
||||
|
||||
if (mBackgroundMeteredAllowed.size() > 0) {
|
||||
pw.print("Background metered allowed: ");
|
||||
pw.println(mBackgroundMeteredAllowed);
|
||||
pw.println();
|
||||
}
|
||||
|
||||
pw.println("Current default network callbacks:");
|
||||
pw.increaseIndent();
|
||||
for (int i = 0; i < mCurrentDefaultNetworkCallbacks.size(); i++) {
|
||||
|
||||
@@ -430,6 +430,13 @@ public final class JobStatus {
|
||||
* when it started running. This isn't copied over when a job is rescheduled.
|
||||
*/
|
||||
public boolean startedAsUserInitiatedJob = false;
|
||||
/**
|
||||
* Whether this particular JobStatus instance started with the foreground flag
|
||||
* (or more accurately, did <b>not</b> have the
|
||||
* {@link android.content.Context#BIND_NOT_FOREGROUND} flag
|
||||
* included in its binding flags when started).
|
||||
*/
|
||||
public boolean startedWithForegroundFlag = false;
|
||||
|
||||
public boolean startedWithImmediacyPrivilege = false;
|
||||
|
||||
@@ -1606,6 +1613,10 @@ public final class JobStatus {
|
||||
* for any reason.
|
||||
*/
|
||||
public boolean shouldTreatAsUserInitiatedJob() {
|
||||
// isUserBgRestricted is intentionally excluded from this method. It should be fine to
|
||||
// treat the job as a UI job while the app is TOP, but just not in the background.
|
||||
// Instead of adding a proc state check here, the parts of JS that can make the distinction
|
||||
// and care about the distinction can do the check.
|
||||
return getJob().isUserInitiated()
|
||||
&& (getInternalFlags() & INTERNAL_FLAG_DEMOTED_BY_USER) == 0
|
||||
&& (getInternalFlags() & INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ) == 0;
|
||||
@@ -1653,6 +1664,11 @@ public final class JobStatus {
|
||||
&& (mDynamicConstraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) == 0);
|
||||
}
|
||||
|
||||
/** Returns whether or not the app is background restricted by the user (FAS). */
|
||||
public boolean isUserBgRestricted() {
|
||||
return mIsUserBgRestricted;
|
||||
}
|
||||
|
||||
/** @return true if the constraint was changed, false otherwise. */
|
||||
boolean setChargingConstraintSatisfied(final long nowElapsed, boolean state) {
|
||||
return setConstraintSatisfied(CONSTRAINT_CHARGING, nowElapsed, state);
|
||||
@@ -2802,6 +2818,12 @@ public final class JobStatus {
|
||||
}
|
||||
pw.decreaseIndent();
|
||||
|
||||
pw.print("Started with foreground flag: ");
|
||||
pw.println(startedWithForegroundFlag);
|
||||
if (mIsUserBgRestricted) {
|
||||
pw.println("User BG restricted");
|
||||
}
|
||||
|
||||
if (changedAuthorities != null) {
|
||||
pw.println("Changed authorities:");
|
||||
pw.increaseIndent();
|
||||
|
||||
@@ -607,6 +607,7 @@ interface IActivityManager {
|
||||
|
||||
void killPackageDependents(in String packageName, int userId);
|
||||
void makePackageIdle(String packageName, int userId);
|
||||
void setDeterministicUidIdle(boolean deterministic);
|
||||
int getMemoryTrimLevel();
|
||||
boolean isVrModePackageEnabled(in ComponentName packageName);
|
||||
void notifyLockedProfile(int userId);
|
||||
|
||||
@@ -1678,9 +1678,9 @@ final class ActivityManagerConstants extends ContentObserver {
|
||||
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
|
||||
KEY_KILL_BG_RESTRICTED_CACHED_IDLE_SETTLE_TIME,
|
||||
DEFAULT_KILL_BG_RESTRICTED_CACHED_IDLE_SETTLE_TIME_MS);
|
||||
if (mKillBgRestrictedAndCachedIdleSettleTimeMs != currentSettleTime) {
|
||||
mService.mHandler.removeMessages(
|
||||
ActivityManagerService.IDLE_UIDS_MSG);
|
||||
if (mKillBgRestrictedAndCachedIdleSettleTimeMs < currentSettleTime) {
|
||||
// Don't remove existing messages in case other IDLE_UIDS_MSG initiators use lower
|
||||
// delays, but send a new message if the settle time has decreased.
|
||||
mService.mHandler.sendEmptyMessageDelayed(
|
||||
ActivityManagerService.IDLE_UIDS_MSG,
|
||||
mKillBgRestrictedAndCachedIdleSettleTimeMs);
|
||||
|
||||
@@ -1526,6 +1526,8 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
*/
|
||||
int mBootPhase;
|
||||
|
||||
volatile boolean mDeterministicUidIdle = false;
|
||||
|
||||
@VisibleForTesting
|
||||
public WindowManagerService mWindowManager;
|
||||
WindowManagerInternal mWmInternal;
|
||||
@@ -16494,6 +16496,11 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDeterministicUidIdle(boolean deterministic) {
|
||||
mDeterministicUidIdle = deterministic;
|
||||
}
|
||||
|
||||
/** Make the currently active UIDs idle after a certain grace period. */
|
||||
final void idleUids() {
|
||||
synchronized (this) {
|
||||
|
||||
@@ -290,6 +290,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
|
||||
return runKillAll(pw);
|
||||
case "make-uid-idle":
|
||||
return runMakeIdle(pw);
|
||||
case "set-deterministic-uid-idle":
|
||||
return runSetDeterministicUidIdle(pw);
|
||||
case "monitor":
|
||||
return runMonitor(pw);
|
||||
case "watch-uids":
|
||||
@@ -1520,6 +1522,23 @@ final class ActivityManagerShellCommand extends ShellCommand {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int runSetDeterministicUidIdle(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;
|
||||
}
|
||||
}
|
||||
boolean deterministic = Boolean.parseBoolean(getNextArgRequired());
|
||||
mInterface.setDeterministicUidIdle(deterministic);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static final class MyActivityController extends IActivityController.Stub {
|
||||
final IActivityManager mInterface;
|
||||
final PrintWriter mPw;
|
||||
@@ -4271,6 +4290,11 @@ final class ActivityManagerShellCommand extends ShellCommand {
|
||||
pw.println(" make-uid-idle [--user <USER_ID> | all | current] <PACKAGE>");
|
||||
pw.println(" If the given application's uid is in the background and waiting to");
|
||||
pw.println(" become idle (not allowing background services), do that now.");
|
||||
pw.println(
|
||||
" set-deterministic-uid-idle [--user <USER_ID> | all | current] <true|false>");
|
||||
pw.println(" If true, sets the timing of making UIDs idle consistent and");
|
||||
pw.println(" deterministic. If false, the timing will be variable depending on");
|
||||
pw.println(" other activity on the device. The default is false.");
|
||||
pw.println(" monitor [--gdb <port>] [-p <TARGET>] [-s] [-c] [-k]");
|
||||
pw.println(" Start monitoring for crashes or ANRs.");
|
||||
pw.println(" --gdb: start gdbserv on the given port at crash/ANR");
|
||||
|
||||
@@ -1471,7 +1471,8 @@ public class OomAdjuster {
|
||||
if (!ActivityManager.isProcStateBackground(uidRec.getSetProcState())
|
||||
|| uidRec.isSetAllowListed()) {
|
||||
uidRec.setLastBackgroundTime(nowElapsed);
|
||||
if (!mService.mHandler.hasMessages(IDLE_UIDS_MSG)) {
|
||||
if (mService.mDeterministicUidIdle
|
||||
|| !mService.mHandler.hasMessages(IDLE_UIDS_MSG)) {
|
||||
// Note: the background settle time is in elapsed realtime, while
|
||||
// the handler time base is uptime. All this means is that we may
|
||||
// stop background uids later than we had intended, but that only
|
||||
@@ -3227,7 +3228,8 @@ public class OomAdjuster {
|
||||
// (for states debouncing to avoid from thrashing).
|
||||
state.setLastCanKillOnBgRestrictedAndIdleTime(nowElapsed);
|
||||
// Kick off the delayed checkup message if needed.
|
||||
if (!mService.mHandler.hasMessages(IDLE_UIDS_MSG)) {
|
||||
if (mService.mDeterministicUidIdle
|
||||
|| !mService.mHandler.hasMessages(IDLE_UIDS_MSG)) {
|
||||
mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
|
||||
mConstants.mKillBgRestrictedAndCachedIdleSettleTimeMs);
|
||||
}
|
||||
@@ -3346,6 +3348,7 @@ public class OomAdjuster {
|
||||
@GuardedBy("mService")
|
||||
void idleUidsLocked() {
|
||||
final int N = mActiveUids.size();
|
||||
mService.mHandler.removeMessages(IDLE_UIDS_MSG);
|
||||
if (N <= 0) {
|
||||
return;
|
||||
}
|
||||
@@ -3391,7 +3394,6 @@ public class OomAdjuster {
|
||||
}
|
||||
}
|
||||
if (nextTime > 0) {
|
||||
mService.mHandler.removeMessages(IDLE_UIDS_MSG);
|
||||
mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
|
||||
nextTime + mConstants.BACKGROUND_SETTLE_TIME - nowElapsed);
|
||||
}
|
||||
|
||||
@@ -5243,7 +5243,9 @@ public final class ProcessList {
|
||||
mAppsInBackgroundRestricted.add(app);
|
||||
final long future = killAppIfBgRestrictedAndCachedIdleLocked(
|
||||
app, nowElapsed);
|
||||
if (future > 0 && !mService.mHandler.hasMessages(IDLE_UIDS_MSG)) {
|
||||
if (future > 0
|
||||
&& (mService.mDeterministicUidIdle
|
||||
|| !mService.mHandler.hasMessages(IDLE_UIDS_MSG))) {
|
||||
mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
|
||||
future - nowElapsed);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user