Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit acd9589d authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Restricting background apps from running jobs"

parents 98bf0da8 3ac1daac
Loading
Loading
Loading
Loading
+12 −2
Original line number Diff line number Diff line
@@ -252,8 +252,10 @@ public class AppOpsManager {
    public static final int OP_INSTANT_APP_START_FOREGROUND = 68;
    /** @hide Answer incoming phone calls */
    public static final int OP_ANSWER_PHONE_CALLS = 69;
    /** @hide Run jobs when in background */
    public static final int OP_RUN_ANY_IN_BACKGROUND = 70;
    /** @hide */
    public static final int _NUM_OP = 70;
    public static final int _NUM_OP = 71;

    /** Access to coarse location information. */
    public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -492,7 +494,8 @@ public class AppOpsManager {
            OP_REQUEST_INSTALL_PACKAGES,
            OP_PICTURE_IN_PICTURE,
            OP_INSTANT_APP_START_FOREGROUND,
            OP_ANSWER_PHONE_CALLS
            OP_ANSWER_PHONE_CALLS,
            OP_RUN_ANY_IN_BACKGROUND,
    };

    /**
@@ -570,6 +573,7 @@ public class AppOpsManager {
            OPSTR_PICTURE_IN_PICTURE,
            OPSTR_INSTANT_APP_START_FOREGROUND,
            OPSTR_ANSWER_PHONE_CALLS,
            null, // OP_RUN_ANY_IN_BACKGROUND
    };

    /**
@@ -647,6 +651,7 @@ public class AppOpsManager {
            "PICTURE_IN_PICTURE",
            "INSTANT_APP_START_FOREGROUND",
            "ANSWER_PHONE_CALLS",
            "RUN_ANY_IN_BACKGROUND",
    };

    /**
@@ -724,6 +729,7 @@ public class AppOpsManager {
            null, // no permission for entering picture-in-picture on hide
            Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE,
            Manifest.permission.ANSWER_PHONE_CALLS,
            null, // no permission for OP_RUN_ANY_IN_BACKGROUND
    };

    /**
@@ -802,6 +808,7 @@ public class AppOpsManager {
            null, // ENTER_PICTURE_IN_PICTURE_ON_HIDE
            null, // INSTANT_APP_START_FOREGROUND
            null, // ANSWER_PHONE_CALLS
            null, // OP_RUN_ANY_IN_BACKGROUND
    };

    /**
@@ -879,6 +886,7 @@ public class AppOpsManager {
            false, // ENTER_PICTURE_IN_PICTURE_ON_HIDE
            false, // INSTANT_APP_START_FOREGROUND
            false, // ANSWER_PHONE_CALLS
            false, // OP_RUN_ANY_IN_BACKGROUND
    };

    /**
@@ -955,6 +963,7 @@ public class AppOpsManager {
            AppOpsManager.MODE_ALLOWED,  // OP_PICTURE_IN_PICTURE
            AppOpsManager.MODE_DEFAULT,  // OP_INSTANT_APP_START_FOREGROUND
            AppOpsManager.MODE_ALLOWED, // ANSWER_PHONE_CALLS
            AppOpsManager.MODE_ALLOWED,  // OP_RUN_ANY_IN_BACKGROUND
    };

    /**
@@ -1035,6 +1044,7 @@ public class AppOpsManager {
            false, // OP_PICTURE_IN_PICTURE
            false,
            false, // ANSWER_PHONE_CALLS
            false, // OP_RUN_ANY_IN_BACKGROUND
    };

    /**
+66 −2
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@ import android.util.SparseIntArray;
import android.util.TimeUtils;
import android.util.Xml;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
import com.android.internal.os.Zygote;
@@ -87,6 +88,11 @@ public class AppOpsService extends IAppOpsService.Stub {
    static final String TAG = "AppOps";
    static final boolean DEBUG = false;

    private static final int NO_VERSION = -1;
    /** Increment by one every time and add the corresponding upgrade logic in
     *  {@link #upgradeLocked(int)} below. The first version was 1 */
    private static final int CURRENT_VERSION = 1;

    // Write at most every 30 minutes.
    static final long WRITE_DELAY = DEBUG ? 1000 : 30*60*1000;

@@ -112,14 +118,16 @@ public class AppOpsService extends IAppOpsService.Stub {
        }
    };

    private final SparseArray<UidState> mUidStates = new SparseArray<>();
    @VisibleForTesting
    final SparseArray<UidState> mUidStates = new SparseArray<>();

    /*
     * These are app op restrictions imposed per user from various parties.
     */
    private final ArrayMap<IBinder, ClientRestrictionState> mOpUserRestrictions = new ArrayMap<>();

    private static final class UidState {
    @VisibleForTesting
    static final class UidState {
        public final int uid;
        public ArrayMap<String, Ops> pkgOps;
        public SparseIntArray opModes;
@@ -1398,6 +1406,7 @@ public class AppOpsService extends IAppOpsService.Stub {
    }

    void readState() {
        int oldVersion = NO_VERSION;
        synchronized (mFile) {
            synchronized (this) {
                FileInputStream stream;
@@ -1422,6 +1431,11 @@ public class AppOpsService extends IAppOpsService.Stub {
                        throw new IllegalStateException("no start tag found");
                    }

                    final String versionString = parser.getAttributeValue(null, "v");
                    if (versionString != null) {
                        oldVersion = Integer.parseInt(versionString);
                    }

                    int outerDepth = parser.getDepth();
                    while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                            && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
@@ -1464,6 +1478,55 @@ public class AppOpsService extends IAppOpsService.Stub {
                }
            }
        }
        synchronized (this) {
            upgradeLocked(oldVersion);
        }
    }

    private void upgradeRunAnyInBackgroundLocked() {
        for (int i = 0; i < mUidStates.size(); i++) {
            final UidState uidState = mUidStates.valueAt(i);
            if (uidState == null) {
                continue;
            }
            if (uidState.opModes != null) {
                final int idx = uidState.opModes.indexOfKey(AppOpsManager.OP_RUN_IN_BACKGROUND);
                if (idx >= 0) {
                    uidState.opModes.put(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
                            uidState.opModes.valueAt(idx));
                }
            }
            if (uidState.pkgOps == null) {
                continue;
            }
            for (int j = 0; j < uidState.pkgOps.size(); j++) {
                Ops ops = uidState.pkgOps.valueAt(j);
                if (ops != null) {
                    final Op op = ops.get(AppOpsManager.OP_RUN_IN_BACKGROUND);
                    if (op != null && op.mode != AppOpsManager.opToDefaultMode(op.op)) {
                        final Op copy = new Op(op.uid, op.packageName,
                                AppOpsManager.OP_RUN_ANY_IN_BACKGROUND);
                        copy.mode = op.mode;
                        ops.put(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, copy);
                    }
                }
            }
        }
    }

    private void upgradeLocked(int oldVersion) {
        if (oldVersion >= CURRENT_VERSION) {
            return;
        }
        Slog.d(TAG, "Upgrading app-ops xml from version " + oldVersion + " to " + CURRENT_VERSION);
        switch (oldVersion) {
            case NO_VERSION:
                upgradeRunAnyInBackgroundLocked();
                // fall through
            case 1:
                // for future upgrades
        }
        scheduleFastWriteLocked();
    }

    void readUidOps(XmlPullParser parser) throws NumberFormatException,
@@ -1613,6 +1676,7 @@ public class AppOpsService extends IAppOpsService.Stub {
                out.setOutput(stream, StandardCharsets.UTF_8.name());
                out.startDocument(null, true);
                out.startTag(null, "app-ops");
                out.attribute(null, "v", String.valueOf(CURRENT_VERSION));

                final int uidStateCount = mUidStates.size();
                for (int i = 0; i < uidStateCount; i++) {
+32 −2
Original line number Diff line number Diff line
@@ -80,6 +80,7 @@ import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.job.JobStore.JobStatusFunctor;
import com.android.server.job.controllers.AppIdleController;
import com.android.server.job.controllers.BackgroundJobsController;
import com.android.server.job.controllers.BatteryController;
import com.android.server.job.controllers.ConnectivityController;
import com.android.server.job.controllers.ContentObserverController;
@@ -140,6 +141,8 @@ public final class JobSchedulerService extends com.android.server.SystemService
    BatteryController mBatteryController;
    /** Need direct access to this for testing. */
    StorageController mStorageController;
    /** Need directly for sending uid state changes */
    private BackgroundJobsController mBackgroundJobsController;
    /**
     * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
     * when ready to execute them.
@@ -225,6 +228,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
        private static final String KEY_MAX_WORK_RESCHEDULE_COUNT = "max_work_reschedule_count";
        private static final String KEY_MIN_LINEAR_BACKOFF_TIME = "min_linear_backoff_time";
        private static final String KEY_MIN_EXP_BACKOFF_TIME = "min_exp_backoff_time";
        private static final String KEY_BG_JOBS_RESTRICTED = "bg_jobs_restricted";

        private static final int DEFAULT_MIN_IDLE_COUNT = 1;
        private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
@@ -240,6 +244,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
        private static final int DEFAULT_BG_MODERATE_JOB_COUNT = 4;
        private static final int DEFAULT_BG_LOW_JOB_COUNT = 1;
        private static final int DEFAULT_BG_CRITICAL_JOB_COUNT = 1;
        private static final boolean DEFAULT_BG_JOBS_RESTRICTED = false;
        private static final int DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT = Integer.MAX_VALUE;
        private static final int DEFAULT_MAX_WORK_RESCHEDULE_COUNT = Integer.MAX_VALUE;
        private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
@@ -333,6 +338,11 @@ public final class JobSchedulerService extends com.android.server.SystemService
         */
        long MIN_EXP_BACKOFF_TIME = DEFAULT_MIN_EXP_BACKOFF_TIME;

        /**
         * Runtime switch for throttling background jobs
         */
        boolean BACKGROUND_JOBS_RESTRICTED = DEFAULT_BG_JOBS_RESTRICTED;

        private ContentResolver mResolver;
        private final KeyValueListParser mParser = new KeyValueListParser(',');

@@ -411,6 +421,12 @@ public final class JobSchedulerService extends com.android.server.SystemService
                        DEFAULT_MIN_LINEAR_BACKOFF_TIME);
                MIN_EXP_BACKOFF_TIME = mParser.getLong(KEY_MIN_EXP_BACKOFF_TIME,
                        DEFAULT_MIN_EXP_BACKOFF_TIME);
                final boolean bgJobsRestricted = mParser.getBoolean(KEY_BG_JOBS_RESTRICTED,
                        DEFAULT_BG_JOBS_RESTRICTED);
                if (bgJobsRestricted != BACKGROUND_JOBS_RESTRICTED) {
                    mBackgroundJobsController.enableRestrictionsLocked(
                            BACKGROUND_JOBS_RESTRICTED = bgJobsRestricted);
                }
            }
        }

@@ -470,6 +486,9 @@ public final class JobSchedulerService extends com.android.server.SystemService

            pw.print("    "); pw.print(KEY_MIN_EXP_BACKOFF_TIME); pw.print("=");
            pw.print(MIN_EXP_BACKOFF_TIME); pw.println();

            pw.print("    "); pw.print(KEY_BG_JOBS_RESTRICTED); pw.print("=");
            pw.print(BACKGROUND_JOBS_RESTRICTED); pw.println();
        }
    }

@@ -613,15 +632,24 @@ public final class JobSchedulerService extends com.android.server.SystemService
            if (disabled) {
                cancelJobsForUid(uid, "uid gone");
            }
            synchronized (mLock) {
                mBackgroundJobsController.setUidActiveLocked(uid, false);
            }
        }

        @Override public void onUidActive(int uid) throws RemoteException {
            synchronized (mLock) {
                mBackgroundJobsController.setUidActiveLocked(uid, true);
            }
        }

        @Override public void onUidIdle(int uid, boolean disabled) {
            if (disabled) {
                cancelJobsForUid(uid, "app uid idle");
            }
            synchronized (mLock) {
                mBackgroundJobsController.setUidActiveLocked(uid, false);
            }
        }

        @Override public void onUidCachedChanged(int uid, boolean cached) {
@@ -917,6 +945,8 @@ public final class JobSchedulerService extends com.android.server.SystemService
        mControllers.add(mBatteryController);
        mStorageController = StorageController.get(this);
        mControllers.add(mStorageController);
        mBackgroundJobsController = BackgroundJobsController.get(this);
        mControllers.add(mBackgroundJobsController);
        mControllers.add(AppIdleController.get(this));
        mControllers.add(ContentObserverController.get(this));
        mControllers.add(DeviceIdleJobsController.get(this));
@@ -997,8 +1027,8 @@ public final class JobSchedulerService extends com.android.server.SystemService
            try {
                ActivityManager.getService().registerUidObserver(mUidObserver,
                        ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
                        | ActivityManager.UID_OBSERVER_IDLE, ActivityManager.PROCESS_STATE_UNKNOWN,
                        null);
                        | ActivityManager.UID_OBSERVER_IDLE | ActivityManager.UID_OBSERVER_ACTIVE,
                        ActivityManager.PROCESS_STATE_UNKNOWN, null);
            } catch (RemoteException e) {
                // ignored; both services live in system_server
            }
+314 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package com.android.server.job.controllers;

import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IDeviceIdleController;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;

import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
import com.android.internal.util.ArrayUtils;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.JobStore;

import java.io.PrintWriter;

public final class BackgroundJobsController extends StateController {

    private static final String LOG_TAG = "BackgroundJobsController";
    private static final boolean DEBUG = JobSchedulerService.DEBUG;

    // Singleton factory
    private static final Object sCreationLock = new Object();
    private static volatile BackgroundJobsController sController;

    /* Runtime switch to keep feature under wraps */
    private boolean mEnableSwitch;
    private final JobSchedulerService mJobSchedulerService;
    private final IAppOpsService mAppOpsService;
    private final IDeviceIdleController mDeviceIdleController;

    private final SparseBooleanArray mForegroundUids;
    private int[] mPowerWhitelistedAppIds;
    private int[] mTempWhitelistedAppIds;
    /**
     * Only tracks jobs for which source package app op RUN_ANY_IN_BACKGROUND is not ALLOWED.
     * Maps jobs to the sourceUid unlike the global {@link JobSchedulerService#mJobs JobStore}
     * which uses callingUid.
     */
    private SparseArray<ArraySet<JobStatus>> mTrackedJobs;

    public static BackgroundJobsController get(JobSchedulerService service) {
        synchronized (sCreationLock) {
            if (sController == null) {
                sController = new BackgroundJobsController(service, service.getContext(),
                        service.getLock());
            }
            return sController;
        }
    }

    private BroadcastReceiver mDozeWhitelistReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            synchronized (mLock) {
                try {
                    switch (intent.getAction()) {
                        case PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED:
                            mPowerWhitelistedAppIds = mDeviceIdleController.getAppIdWhitelist();
                            break;
                        case PowerManager.ACTION_POWER_SAVE_TEMP_WHITELIST_CHANGED:
                            mTempWhitelistedAppIds = mDeviceIdleController.getAppIdTempWhitelist();
                            break;
                    }
                } catch (RemoteException rexc) {
                    Slog.e(LOG_TAG, "Device idle controller not reachable");
                }
                if (checkAllTrackedJobsLocked()) {
                    mStateChangedListener.onControllerStateChanged();
                }
            }
        }
    };

    private BackgroundJobsController(JobSchedulerService service, Context context, Object lock) {
        super(service, context, lock);
        mJobSchedulerService = service;
        mAppOpsService = IAppOpsService.Stub.asInterface(
                ServiceManager.getService(Context.APP_OPS_SERVICE));
        mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
                ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));

        mForegroundUids = new SparseBooleanArray();
        mTrackedJobs = new SparseArray<>();
        try {
            mAppOpsService.startWatchingMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, null,
                    new AppOpsWatcher());
            mPowerWhitelistedAppIds = mDeviceIdleController.getAppIdWhitelist();
            mTempWhitelistedAppIds = mDeviceIdleController.getAppIdTempWhitelist();
        } catch (RemoteException rexc) {
            // Shouldn't happen as they are in the same process.
            Slog.e(LOG_TAG, "AppOps or DeviceIdle service not reachable", rexc);
        }
        IntentFilter powerWhitelistFilter = new IntentFilter();
        powerWhitelistFilter.addAction(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED);
        powerWhitelistFilter.addAction(PowerManager.ACTION_POWER_SAVE_TEMP_WHITELIST_CHANGED);
        context.registerReceiverAsUser(mDozeWhitelistReceiver, UserHandle.ALL, powerWhitelistFilter,
                null, null);

        mEnableSwitch = false;
    }

    @Override
    public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
        final int uid = jobStatus.getSourceUid();
        final String packageName = jobStatus.getSourcePackageName();
        try {
            final int mode = mAppOpsService.checkOperation(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
                    uid, packageName);
            if (mode == AppOpsManager.MODE_ALLOWED) {
                jobStatus.setBackgroundNotRestrictedConstraintSatisfied(true);
                return;
            }
        } catch (RemoteException rexc) {
            Slog.e(LOG_TAG, "Cannot reach app ops service", rexc);
        }
        jobStatus.setBackgroundNotRestrictedConstraintSatisfied(canRunJobLocked(uid));
        startTrackingJobLocked(jobStatus);
    }

    @Override
    public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
            boolean forUpdate) {
        stopTrackingJobLocked(jobStatus);
    }

    /* Called by JobSchedulerService to report uid state changes between active and idle */
    public void setUidActiveLocked(int uid, boolean active) {
        final boolean changed = (active != mForegroundUids.get(uid));
        if (!changed) {
            return;
        }
        if (DEBUG) {
            Slog.d(LOG_TAG, "uid " + uid + " going to " + (active ? "fg" : "bg"));
        }
        if (active) {
            mForegroundUids.put(uid, true);
        } else {
            mForegroundUids.delete(uid);
        }
        if (checkTrackedJobsForUidLocked(uid)) {
            mStateChangedListener.onControllerStateChanged();
        }
    }

    @Override
    public void dumpControllerStateLocked(final PrintWriter pw, final int filterUid) {
        pw.println("Background restrictions: global switch = " + mEnableSwitch);
        pw.print("Foreground uids: [");
        for (int i = 0; i < mForegroundUids.size(); i++) {
            if (mForegroundUids.valueAt(i)) pw.print(mForegroundUids.keyAt(i) + " ");
        }
        pw.println("]");
        mJobSchedulerService.getJobStore().forEachJob(new JobStore.JobStatusFunctor() {
            @Override
            public void process(JobStatus jobStatus) {
                if (!jobStatus.shouldDump(filterUid)) {
                    return;
                }
                final int uid = jobStatus.getSourceUid();
                pw.print("  #");
                jobStatus.printUniqueId(pw);
                pw.print(" from ");
                UserHandle.formatUid(pw, uid);
                pw.print(mForegroundUids.get(uid) ? " foreground" : " background");
                if (isWhitelistedLocked(uid)) {
                    pw.print(", whitelisted");
                }
                pw.print(": ");
                pw.print(jobStatus.getSourcePackageName());
                pw.print(" [background restrictions");
                final ArraySet<JobStatus> jobsForUid = mTrackedJobs.get(uid);
                pw.print(jobsForUid != null && jobsForUid.contains(jobStatus) ? " on]" : " off]");
                if ((jobStatus.satisfiedConstraints
                        & JobStatus.CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
                    pw.println(" RUNNABLE");
                } else {
                    pw.println(" WAITING");
                }
            }
        });
    }

    public void enableRestrictionsLocked(boolean enable) {
        mEnableSwitch = enable;
        Slog.d(LOG_TAG, "Background jobs restrictions switch changed to " + mEnableSwitch);
        if (checkAllTrackedJobsLocked()) {
            mStateChangedListener.onControllerStateChanged();
        }
    }

    void startTrackingJobLocked(JobStatus jobStatus) {
        final int uid = jobStatus.getSourceUid();
        ArraySet<JobStatus> jobsForUid = mTrackedJobs.get(uid);
        if (jobsForUid == null) {
            jobsForUid = new ArraySet<>();
            mTrackedJobs.put(uid, jobsForUid);
        }
        jobsForUid.add(jobStatus);
    }

    void stopTrackingJobLocked(JobStatus jobStatus) {
        final int uid = jobStatus.getSourceUid();
        ArraySet<JobStatus> jobsForUid = mTrackedJobs.get(uid);
        if (jobsForUid != null) {
            jobsForUid.remove(jobStatus);
        }
    }

    boolean checkAllTrackedJobsLocked() {
        boolean changed = false;
        for (int i = 0; i < mTrackedJobs.size(); i++) {
            changed |= checkTrackedJobsForUidLocked(mTrackedJobs.keyAt(i));
        }
        return changed;
    }

    private boolean checkTrackedJobsForUidLocked(int uid) {
        final ArraySet<JobStatus> jobsForUid = mTrackedJobs.get(uid);
        boolean changed = false;
        if (jobsForUid != null) {
            for (int i = 0; i < jobsForUid.size(); i++) {
                JobStatus jobStatus = jobsForUid.valueAt(i);
                changed |= jobStatus.setBackgroundNotRestrictedConstraintSatisfied(
                        canRunJobLocked(uid));
            }
        }
        return changed;
    }

    boolean isWhitelistedLocked(int uid) {
        return ArrayUtils.contains(mTempWhitelistedAppIds, UserHandle.getAppId(uid))
                || ArrayUtils.contains(mPowerWhitelistedAppIds, UserHandle.getAppId(uid));
    }

    boolean canRunJobLocked(int uid) {
        return !mEnableSwitch || mForegroundUids.get(uid) || isWhitelistedLocked(uid);
    }

    private final class AppOpsWatcher extends IAppOpsCallback.Stub {
        @Override
        public void opChanged(int op, int uid, String packageName) throws RemoteException {
            synchronized (mLock) {
                final int mode = mAppOpsService.checkOperation(op, uid, packageName);
                if (DEBUG) {
                    Slog.d(LOG_TAG,
                            "Appop changed for " + uid + ", " + packageName + " to " + mode);
                }
                final boolean shouldTrack = (mode != AppOpsManager.MODE_ALLOWED);
                UpdateTrackedJobsFunc updateTrackedJobs = new UpdateTrackedJobsFunc(uid,
                        packageName, shouldTrack);
                mJobSchedulerService.getJobStore().forEachJob(updateTrackedJobs);
                if (updateTrackedJobs.mChanged) {
                    mStateChangedListener.onControllerStateChanged();
                }
            }
        }
    }

    private final class UpdateTrackedJobsFunc implements JobStore.JobStatusFunctor {
        private final String mPackageName;
        private final int mUid;
        private final boolean mShouldTrack;
        private boolean mChanged = false;

        UpdateTrackedJobsFunc(int uid, String packageName, boolean shouldTrack) {
            mUid = uid;
            mPackageName = packageName;
            mShouldTrack = shouldTrack;
        }

        @Override
        public void process(JobStatus jobStatus) {
            final String packageName = jobStatus.getSourcePackageName();
            final int uid = jobStatus.getSourceUid();
            if (mUid != uid || !mPackageName.equals(packageName)) {
                return;
            }
            if (mShouldTrack) {
                mChanged |= jobStatus.setBackgroundNotRestrictedConstraintSatisfied(
                        canRunJobLocked(uid));
                startTrackingJobLocked(jobStatus);
            } else {
                mChanged |= jobStatus.setBackgroundNotRestrictedConstraintSatisfied(true);
                stopTrackingJobLocked(jobStatus);
            }
        }
    }
}
+10 −1

File changed.

Preview size limit exceeded, changes collapsed.

Loading