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

Commit fb9ec66e authored by Makoto Onuki's avatar Makoto Onuki Committed by Android (Google) Code Review
Browse files

Merge "Implement force-all-apps-standly in job scheduler."

parents c99d6c19 9be0140c
Loading
Loading
Loading
Loading
+3 −9
Original line number Diff line number Diff line
@@ -527,8 +527,7 @@ public final class PowerManager {
            ServiceType.SOUND,
            ServiceType.BATTERY_STATS,
            ServiceType.DATA_SAVER,
            ServiceType.FORCE_ALL_APPS_STANDBY_JOBS,
            ServiceType.FORCE_ALL_APPS_STANDBY_ALARMS,
            ServiceType.FORCE_ALL_APPS_STANDBY,
            ServiceType.OPTIONAL_SENSORS,
    })
    public @interface ServiceType {
@@ -545,14 +544,9 @@ public final class PowerManager {
        int DATA_SAVER = 10;

        /**
         * Whether the job scheduler should force app standby on all apps on battery saver or not.
         * Whether to enable force-app-standby on all apps or not.
         */
        int FORCE_ALL_APPS_STANDBY_JOBS = 11;

        /**
         * Whether the alarm manager should force app standby on all apps on battery saver or not.
         */
        int FORCE_ALL_APPS_STANDBY_ALARMS = 12;
        int FORCE_ALL_APPS_STANDBY = 11;

        /**
         * Whether to disable non-essential sensors. (e.g. edge sensors.)
+386 −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;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.AppOpsManager.PackageOps;
import android.app.IUidObserver;
import android.content.Context;
import android.os.Handler;
import android.os.PowerManager.ServiceType;
import android.os.PowerManagerInternal;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseBooleanArray;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
import com.android.internal.util.Preconditions;

import java.util.List;

/**
 * Class to track OP_RUN_ANY_IN_BACKGROUND, UID foreground state and "force all app standby".
 *
 * TODO Clean up cache when a user is deleted.
 * TODO Add unit tests. b/68769804.
 */
public class ForceAppStandbyTracker {
    private static final String TAG = "ForceAppStandbyTracker";

    @GuardedBy("ForceAppStandbyTracker.class")
    private static ForceAppStandbyTracker sInstance;

    private final Object mLock = new Object();
    private final Context mContext;

    AppOpsManager mAppOpsManager;
    IAppOpsService mAppOpsService;
    PowerManagerInternal mPowerManagerInternal;

    private final Handler mCallbackHandler;

    /**
     * Pair of (uid (not user-id), packageName) with OP_RUN_ANY_IN_BACKGROUND *not* allowed.
     */
    @GuardedBy("mLock")
    final ArraySet<Pair<Integer, String>> mForcedAppStandbyUidPackages = new ArraySet<>();

    @GuardedBy("mLock")
    final SparseBooleanArray mForegroundUids = new SparseBooleanArray();

    @GuardedBy("mLock")
    final ArraySet<Listener> mListeners = new ArraySet<>();

    @GuardedBy("mLock")
    boolean mStarted;

    @GuardedBy("mLock")
    boolean mForceAllAppsStandby;

    public static abstract class Listener {
        public void onRestrictionChanged(int uid, @Nullable String packageName) {
        }

        public void onGlobalRestrictionChanged() {
        }
    }

    private ForceAppStandbyTracker(Context context) {
        mContext = context;
        mCallbackHandler = FgThread.getHandler();
    }

    /**
     * Get the singleton instance.
     */
    public static synchronized ForceAppStandbyTracker getInstance(Context context) {
        if (sInstance == null) {
            sInstance = new ForceAppStandbyTracker(context);
        }
        return sInstance;
    }

    /**
     * Call it when the system is ready.
     */
    public void start() {
        synchronized (mLock) {
            if (mStarted) {
                return;
            }
            mStarted = true;

            mAppOpsManager = Preconditions.checkNotNull(
                    mContext.getSystemService(AppOpsManager.class));
            mAppOpsService = Preconditions.checkNotNull(
                    IAppOpsService.Stub.asInterface(
                            ServiceManager.getService(Context.APP_OPS_SERVICE)));
            mPowerManagerInternal = Preconditions.checkNotNull(
                    LocalServices.getService(PowerManagerInternal.class));

            try {
                ActivityManager.getService().registerUidObserver(new UidObserver(),
                        ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_IDLE
                                | ActivityManager.UID_OBSERVER_ACTIVE,
                        ActivityManager.PROCESS_STATE_UNKNOWN, null);
                mAppOpsService.startWatchingMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, null,
                        new AppOpsWatcher());
            } catch (RemoteException e) {
                // shouldn't happen.
            }

            mPowerManagerInternal.registerLowPowerModeObserver(
                    ServiceType.FORCE_ALL_APPS_STANDBY,
                    state -> updateForceAllAppsStandby(state.batterySaverEnabled));

            updateForceAllAppsStandby(
                    mPowerManagerInternal.getLowPowerState(ServiceType.FORCE_ALL_APPS_STANDBY)
                            .batterySaverEnabled);

            refreshForcedAppStandbyUidPackagesLocked();
        }
    }

    /**
     * Update {@link #mForcedAppStandbyUidPackages} with the current app ops state.
     */
    private void refreshForcedAppStandbyUidPackagesLocked() {
        final int op = AppOpsManager.OP_RUN_ANY_IN_BACKGROUND;

        mForcedAppStandbyUidPackages.clear();
        final List<PackageOps> ops = mAppOpsManager.getPackagesForOps(new int[] {op});

        if (ops == null) {
            return;
        }
        final int size = ops.size();
        for (int i = 0; i < size; i++) {
            final AppOpsManager.PackageOps pkg = ops.get(i);
            final List<AppOpsManager.OpEntry> entries = ops.get(i).getOps();

            for (int j = 0; j < entries.size(); j++) {
                AppOpsManager.OpEntry ent = entries.get(j);
                if (ent.getOp() != op) {
                    continue;
                }
                if (ent.getMode() != AppOpsManager.MODE_ALLOWED) {
                    mForcedAppStandbyUidPackages.add(Pair.create(
                            pkg.getUid(), pkg.getPackageName()));
                }
            }
        }
    }

    boolean isRunAnyInBackgroundAppOpRestricted(int uid, @NonNull String packageName) {
        try {
            return mAppOpsService.checkOperation(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
                    uid, packageName) != AppOpsManager.MODE_ALLOWED;
        } catch (RemoteException e) {
            return false; // shouldn't happen.
        }
    }

    private int findForcedAppStandbyUidPackageIndexLocked(int uid, @NonNull String packageName) {
        // TODO Maybe we should switch to indexOf(Pair.create()) if the array size is too big.
        final int size = mForcedAppStandbyUidPackages.size();
        for (int i = 0; i < size; i++) {
            final Pair<Integer, String> pair = mForcedAppStandbyUidPackages.valueAt(i);

            if ((pair.first == uid) && packageName.equals(pair.second)) {
                return i;
            }
        }
        return -1;
    }

    /**
     * @return whether a uid package-name pair is in mForcedAppStandbyUidPackages.
     */
    boolean isUidPackageRestrictedLocked(int uid, @NonNull String packageName) {
        return findForcedAppStandbyUidPackageIndexLocked(uid, packageName) >= 0;
    }

    boolean updateRestrictedUidPackageLocked(int uid, @NonNull String packageName,
            boolean restricted) {
        final int index =  findForcedAppStandbyUidPackageIndexLocked(uid, packageName);
        final boolean wasRestricted = index >= 0;
        if (wasRestricted == restricted) {
            return false;
        }
        if (restricted) {
            mForcedAppStandbyUidPackages.add(Pair.create(uid, packageName));
        } else {
            mForcedAppStandbyUidPackages.removeAt(index);
        }
        return true;
    }

    void uidToForeground(int uid) {
        synchronized (mLock) {
            if (!UserHandle.isApp(uid)) {
                return;
            }
            // TODO This can be optimized by calling indexOfKey and sharing the index for get and
            // put.
            if (mForegroundUids.get(uid)) {
                return;
            }
            mForegroundUids.put(uid, true);
            notifyForUidPackage(uid, null);
        }
    }

    void uidToBackground(int uid, boolean remove) {
        synchronized (mLock) {
            if (!UserHandle.isApp(uid)) {
                return;
            }
            // TODO This can be optimized by calling indexOfKey and sharing the index for get and
            // put.
            if (!mForegroundUids.get(uid)) {
                return;
            }
            if (remove) {
                mForegroundUids.delete(uid);
            } else {
                mForegroundUids.put(uid, false);
            }
            notifyForUidPackage(uid, null);
        }
    }

    // Event handlers

    final class UidObserver extends IUidObserver.Stub {
        @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
        }

        @Override public void onUidGone(int uid, boolean disabled) {
            uidToBackground(uid, /*remove=*/ true);
        }

        @Override public void onUidActive(int uid) {
            uidToForeground(uid);
        }

        @Override public void onUidIdle(int uid, boolean disabled) {
            // Just to avoid excessive memcpy, don't remove from the array in this case.
            uidToBackground(uid, /*remove=*/ false);
        }

        @Override public void onUidCachedChanged(int uid, boolean cached) {
        }
    };

    private final class AppOpsWatcher extends IAppOpsCallback.Stub {
        @Override
        public void opChanged(int op, int uid, String packageName) throws RemoteException {
            synchronized (mLock) {
                final boolean restricted = isRunAnyInBackgroundAppOpRestricted(uid, packageName);

                if (updateRestrictedUidPackageLocked(uid, packageName, restricted)) {
                    notifyForUidPackage(uid, packageName);
                }
            }
        }
    }

    private Listener[] cloneListeners() {
        synchronized (mLock) {
            return mListeners.toArray(new Listener[mListeners.size()]);
        }
    }

    void notifyForUidPackage(int uid, String packageName) {
        mCallbackHandler.post(() -> {
            for (Listener l : cloneListeners()) {
                l.onRestrictionChanged(uid, packageName);
            }
        });
    }

    void notifyGlobal() {
        mCallbackHandler.post(() -> {
            for (Listener l : cloneListeners()) {
                l.onGlobalRestrictionChanged();
            }
        });
    }

    void updateForceAllAppsStandby(boolean forceAllAppsStandby) {
        synchronized (mLock) {
            if (mForceAllAppsStandby == forceAllAppsStandby) {
                return;
            }
            mForceAllAppsStandby = forceAllAppsStandby;
            Slog.i(TAG, "Force all app standby: " + mForceAllAppsStandby);
            notifyGlobal();
        }
    }

    // Public interface.

    /**
     * Register a new listener.
     */
    public void addListener(@NonNull Listener listener) {
        synchronized (mLock) {
            mListeners.add(listener);
        }
    }

    /**
     * Whether force-app-standby is effective for a UID package-name.
     */
    public boolean isRestricted(int uid, @NonNull String packageName) {
        if (isInForeground(uid)) {
            return false;
        }
        synchronized (mLock) {
            if (mForceAllAppsStandby) {
                return true;
            }
            return isUidPackageRestrictedLocked(uid, packageName);
        }
    }

    /** For dumpsys -- otherwise the callers don't need to know it. */
    public boolean isInForeground(int uid) {
        if (!UserHandle.isApp(uid)) {
            return true;
        }
        synchronized (mLock) {
            return mForegroundUids.get(uid);
        }
    }

    /** For dumpsys -- otherwise the callers don't need to know it. */
    public boolean isForceAllAppsStandbyEnabled() {
        synchronized (mLock) {
            return mForceAllAppsStandby;
        }
    }

    /** For dumpsys -- otherwise the callers don't need to know it. */
    public boolean isRunAnyInBackgroundAppOpsAllowed(int uid, @NonNull String packageName) {
        synchronized (mLock) {
            return !isUidPackageRestrictedLocked(uid, packageName);
        }
    }

    /** For dumpsys -- otherwise the callers don't need to know it. */
    public SparseBooleanArray getForegroudUids() {
        synchronized (mLock) {
            return mForegroundUids.clone();
        }
    }

    /** For dumpsys -- otherwise the callers don't need to know it. */
    public ArraySet<Pair<Integer, String>> getRestrictedUidPackages() {
        synchronized (mLock) {
            return new ArraySet(mForcedAppStandbyUidPackages);
        }
    }
}
+0 −9
Original line number Diff line number Diff line
@@ -622,24 +622,15 @@ 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) {
+127 −157

File changed.

Preview size limit exceeded, changes collapsed.

+6 −21
Original line number Diff line number Diff line
@@ -29,9 +29,9 @@ import android.util.ArrayMap;
import android.util.KeyValueListParser;
import android.util.Slog;

import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.R;

import java.io.PrintWriter;
import java.util.ArrayList;
@@ -62,8 +62,7 @@ public class BatterySaverPolicy extends ContentObserver {
    private static final String KEY_ADJUST_BRIGHTNESS_FACTOR = "adjust_brightness_factor";
    private static final String KEY_FULLBACKUP_DEFERRED = "fullbackup_deferred";
    private static final String KEY_KEYVALUE_DEFERRED = "keyvaluebackup_deferred";
    private static final String KEY_FORCE_ALL_APPS_STANDBY_JOBS = "force_all_apps_standby_jobs";
    private static final String KEY_FORCE_ALL_APPS_STANDBY_ALARMS = "force_all_apps_standby_alarms";
    private static final String KEY_FORCE_ALL_APPS_STANDBY = "force_all_apps_standby";
    private static final String KEY_OPTIONAL_SENSORS_DISABLED = "optional_sensors_disabled";

    private static final String KEY_SCREEN_ON_FILE_PREFIX = "file-on:";
@@ -156,14 +155,9 @@ public class BatterySaverPolicy extends ContentObserver {
    private float mAdjustBrightnessFactor;

    /**
     * Whether to put all apps in the stand-by mode or not for job scheduler.
     * Whether to put all apps in the stand-by mode.
     */
    private boolean mForceAllAppsStandbyJobs;

    /**
     * Whether to put all apps in the stand-by mode or not for alarms.
     */
    private boolean mForceAllAppsStandbyAlarms;
    private boolean mForceAllAppsStandby;

    /**
     * Weather to show non-essential sensors (e.g. edge sensors) or not.
@@ -297,9 +291,7 @@ public class BatterySaverPolicy extends ContentObserver {
        mAdjustBrightnessDisabled = parser.getBoolean(KEY_ADJUST_BRIGHTNESS_DISABLED, false);
        mAdjustBrightnessFactor = parser.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR, 0.5f);
        mDataSaverDisabled = parser.getBoolean(KEY_DATASAVER_DISABLED, true);
        mForceAllAppsStandbyJobs = parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY_JOBS, true);
        mForceAllAppsStandbyAlarms =
                parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY_ALARMS, true);
        mForceAllAppsStandby = parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY, true);
        mOptionalSensorsDisabled = parser.getBoolean(KEY_OPTIONAL_SENSORS_DISABLED, true);

        // Get default value from Settings.Secure
@@ -387,12 +379,6 @@ public class BatterySaverPolicy extends ContentObserver {
                case ServiceType.VIBRATION:
                    return builder.setBatterySaverEnabled(mVibrationDisabled)
                            .build();
                case ServiceType.FORCE_ALL_APPS_STANDBY_JOBS:
                    return builder.setBatterySaverEnabled(mForceAllAppsStandbyJobs)
                            .build();
                case ServiceType.FORCE_ALL_APPS_STANDBY_ALARMS:
                    return builder.setBatterySaverEnabled(mForceAllAppsStandbyAlarms)
                            .build();
                case ServiceType.OPTIONAL_SENSORS:
                    return builder.setBatterySaverEnabled(mOptionalSensorsDisabled)
                            .build();
@@ -428,8 +414,7 @@ public class BatterySaverPolicy extends ContentObserver {
            pw.println("  " + KEY_ADJUST_BRIGHTNESS_DISABLED + "=" + mAdjustBrightnessDisabled);
            pw.println("  " + KEY_ADJUST_BRIGHTNESS_FACTOR + "=" + mAdjustBrightnessFactor);
            pw.println("  " + KEY_GPS_MODE + "=" + mGpsMode);
            pw.println("  " + KEY_FORCE_ALL_APPS_STANDBY_JOBS + "=" + mForceAllAppsStandbyJobs);
            pw.println("  " + KEY_FORCE_ALL_APPS_STANDBY_ALARMS + "=" + mForceAllAppsStandbyAlarms);
            pw.println("  " + KEY_FORCE_ALL_APPS_STANDBY + "=" + mForceAllAppsStandby);
            pw.println("  " + KEY_OPTIONAL_SENSORS_DISABLED + "=" + mOptionalSensorsDisabled);
            pw.println();

Loading