diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index 67d711c79b15a..b1c8b511ac25b 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -63,6 +63,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; +import android.app.AppOpsManager; import android.app.usage.AppStandbyInfo; import android.app.usage.UsageEvents; import android.app.usage.UsageStatsManager.ForcedReasons; @@ -108,6 +109,7 @@ import android.util.IndentingPrintWriter; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; +import android.util.SparseIntArray; import android.util.SparseLongArray; import android.util.TimeUtils; import android.view.Display; @@ -116,6 +118,8 @@ import android.widget.Toast; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.app.IAppOpsCallback; +import com.android.internal.app.IAppOpsService; import com.android.internal.app.IBatteryStats; import com.android.internal.util.ArrayUtils; import com.android.internal.util.ConcurrentUtils; @@ -279,6 +283,13 @@ public class AppStandbyController @GuardedBy("mPendingIdleStateChecks") private final SparseLongArray mPendingIdleStateChecks = new SparseLongArray(); + /** + * Map of uids to their current app-op mode for + * {@link AppOpsManager#OPSTR_SYSTEM_EXEMPT_FROM_APP_STANDBY}. + */ + @GuardedBy("mSystemExemptionAppOpMode") + private final SparseIntArray mSystemExemptionAppOpMode = new SparseIntArray(); + // Cache the active network scorer queried from the network scorer service private volatile String mCachedNetworkScorer = null; // The last time the network scorer service was queried @@ -488,6 +499,7 @@ public class AppStandbyController private AppWidgetManager mAppWidgetManager; private PackageManager mPackageManager; + private AppOpsManager mAppOpsManager; Injector mInjector; private static class Pool { @@ -647,6 +659,28 @@ public class AppStandbyController settingsObserver.start(); mAppWidgetManager = mContext.getSystemService(AppWidgetManager.class); + mAppOpsManager = mContext.getSystemService(AppOpsManager.class); + IAppOpsService iAppOpsService = mInjector.getAppOpsService(); + try { + iAppOpsService.startWatchingMode( + AppOpsManager.OP_SYSTEM_EXEMPT_FROM_APP_STANDBY, + /*packageName=*/ null, + new IAppOpsCallback.Stub() { + @Override + public void opChanged(int op, int uid, String packageName) { + final int userId = UserHandle.getUserId(uid); + synchronized (mSystemExemptionAppOpMode) { + mSystemExemptionAppOpMode.delete(uid); + } + mHandler.obtainMessage( + MSG_CHECK_PACKAGE_IDLE_STATE, userId, uid, packageName) + .sendToTarget(); + } + }); + } catch (RemoteException e) { + // Should not happen. + Slog.wtf(TAG, "Failed start watching for app op", e); + } mInjector.registerDisplayListener(mDisplayListener, mHandler); synchronized (mAppIdleLock) { @@ -1417,6 +1451,23 @@ public class AppStandbyController return STANDBY_BUCKET_EXEMPTED; } + final int uid = UserHandle.getUid(userId, appId); + synchronized (mSystemExemptionAppOpMode) { + if (mSystemExemptionAppOpMode.indexOfKey(uid) >= 0) { + if (mSystemExemptionAppOpMode.get(uid) + == AppOpsManager.MODE_ALLOWED) { + return STANDBY_BUCKET_EXEMPTED; + } + } else { + int mode = mAppOpsManager.checkOpNoThrow( + AppOpsManager.OP_SYSTEM_EXEMPT_FROM_APP_STANDBY, uid, packageName); + mSystemExemptionAppOpMode.put(uid, mode); + if (mode == AppOpsManager.MODE_ALLOWED) { + return STANDBY_BUCKET_EXEMPTED; + } + } + } + if (mAppWidgetManager != null && mInjector.isBoundWidgetPackage(mAppWidgetManager, packageName, userId)) { return STANDBY_BUCKET_ACTIVE; @@ -2129,6 +2180,12 @@ public class AppStandbyController clearAppIdleForPackage(pkgName, userId); } } + synchronized (mSystemExemptionAppOpMode) { + if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { + mSystemExemptionAppOpMode.delete(UserHandle.getUid(userId, getAppId(pkgName))); + } + } + } } @@ -2524,6 +2581,11 @@ public class AppStandbyController } } + IAppOpsService getAppOpsService() { + return IAppOpsService.Stub.asInterface( + ServiceManager.getService(Context.APP_OPS_SERVICE)); + } + /** * Returns {@code true} if the supplied package is the wellbeing app. Otherwise, * returns {@code false}.