Merge "Make it possible to check processed DeviceConfig flags." into main
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index e6ee975..2b27526 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -485,6 +485,32 @@
 
     private class ConstantsObserver implements DeviceConfig.OnPropertiesChangedListener,
             EconomyManagerInternal.TareStateChangeListener {
+        @Nullable
+        @GuardedBy("mLock")
+        private DeviceConfig.Properties mLastPropertiesPulled;
+        @GuardedBy("mLock")
+        private boolean mCacheConfigChanges = false;
+
+        @Nullable
+        @GuardedBy("mLock")
+        public String getValueLocked(String key) {
+            if (mLastPropertiesPulled == null) {
+                return null;
+            }
+            return mLastPropertiesPulled.getString(key, null);
+        }
+
+        @GuardedBy("mLock")
+        public void setCacheConfigChangesLocked(boolean enabled) {
+            if (enabled && !mCacheConfigChanges) {
+                mLastPropertiesPulled =
+                        DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER);
+            } else {
+                mLastPropertiesPulled = null;
+            }
+            mCacheConfigChanges = enabled;
+        }
+
         public void start() {
             DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
                     AppSchedulingModuleThread.getExecutor(), this);
@@ -513,11 +539,15 @@
             }
 
             synchronized (mLock) {
+                if (mCacheConfigChanges) {
+                    mLastPropertiesPulled =
+                            DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER);
+                }
                 for (String name : properties.getKeyset()) {
                     if (name == null) {
                         continue;
                     }
-                    if (DEBUG) {
+                    if (DEBUG || mCacheConfigChanges) {
                         Slog.d(TAG, "DeviceConfig " + name
                                 + " changed to " + properties.getString(name, null));
                     }
@@ -5554,6 +5584,18 @@
         }
     }
 
+    void setCacheConfigChanges(boolean enabled) {
+        synchronized (mLock) {
+            mConstantsObserver.setCacheConfigChangesLocked(enabled);
+        }
+    }
+
+    String getConfigValue(String key) {
+        synchronized (mLock) {
+            return mConstantsObserver.getValueLocked(key);
+        }
+    }
+
     int getStorageSeq() {
         synchronized (mLock) {
             return mStorageController.getTracker().getSeq();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
index 90b4630..43e2888 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
@@ -16,6 +16,7 @@
 
 package com.android.server.job;
 
+import android.Manifest;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.AppGlobals;
@@ -66,6 +67,8 @@
                     return getBatteryCharging(pw);
                 case "get-battery-not-low":
                     return getBatteryNotLow(pw);
+                case "get-config-value":
+                    return getConfigValue(pw);
                 case "get-estimated-download-bytes":
                     return getEstimatedNetworkBytes(pw, BYTE_OPTION_DOWNLOAD);
                 case "get-estimated-upload-bytes":
@@ -82,6 +85,8 @@
                     return getJobState(pw);
                 case "heartbeat":
                     return doHeartbeat(pw);
+                case "cache-config-changes":
+                    return cacheConfigChanges(pw);
                 case "reset-execution-quota":
                     return resetExecutionQuota(pw);
                 case "reset-schedule-quota":
@@ -100,13 +105,16 @@
     }
 
     private void checkPermission(String operation) throws Exception {
+        checkPermission(operation, Manifest.permission.CHANGE_APP_IDLE_STATE);
+    }
+
+    private void checkPermission(String operation, String permission) throws Exception {
         final int uid = Binder.getCallingUid();
         if (uid == 0) {
             // Root can do anything.
             return;
         }
-        final int perm = mPM.checkUidPermission(
-                "android.permission.CHANGE_APP_IDLE_STATE", uid);
+        final int perm = mPM.checkUidPermission(permission, uid);
         if (perm != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Uid " + uid
                     + " not permitted to " + operation);
@@ -339,7 +347,7 @@
     }
 
     private int getAconfigFlagState(PrintWriter pw) throws Exception {
-        checkPermission("get aconfig flag state");
+        checkPermission("get aconfig flag state", Manifest.permission.DUMP);
 
         final String flagName = getNextArgRequired();
 
@@ -390,6 +398,20 @@
         return 0;
     }
 
+    private int getConfigValue(PrintWriter pw) throws Exception {
+        checkPermission("get device config value", Manifest.permission.DUMP);
+
+        final String key = getNextArgRequired();
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            pw.println(mInternal.getConfigValue(key));
+            return 0;
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
     private int getEstimatedNetworkBytes(PrintWriter pw, int byteOption) throws Exception {
         checkPermission("get estimated bytes");
 
@@ -540,6 +562,28 @@
         return -1;
     }
 
+    private int cacheConfigChanges(PrintWriter pw) throws Exception {
+        checkPermission("change config caching", Manifest.permission.DUMP);
+        String opt = getNextArgRequired();
+        boolean enabled;
+        if ("on".equals(opt)) {
+            enabled = true;
+        } else if ("off".equals(opt)) {
+            enabled = false;
+        } else {
+            getErrPrintWriter().println("Error: unknown option " + opt);
+            return 1;
+        }
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            mInternal.setCacheConfigChanges(enabled);
+            pw.println("Config caching " + (enabled ? "enabled" : "disabled"));
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+        return 0;
+    }
+
     private int resetExecutionQuota(PrintWriter pw) throws Exception {
         checkPermission("reset execution quota");
 
@@ -726,6 +770,9 @@
         pw.println("         is null (no namespace).");
         pw.println("  heartbeat [num]");
         pw.println("    No longer used.");
+        pw.println("  cache-config-changes [on|off]");
+        pw.println("    Control caching the set of most recently processed config flags.");
+        pw.println("    Off by default.  Turning on makes get-config-value useful.");
         pw.println("  monitor-battery [on|off]");
         pw.println("    Control monitoring of all battery changes.  Off by default.  Turning");
         pw.println("    on makes get-battery-seq useful.");
@@ -738,6 +785,9 @@
         pw.println("    Return whether the battery is currently considered to be charging.");
         pw.println("  get-battery-not-low");
         pw.println("    Return whether the battery is currently considered to not be low.");
+        pw.println("  get-config-value KEY");
+        pw.println("    Return the most recently processed and cached config value for the KEY.");
+        pw.println("    Only useful if caching is turned on with cache-config-changes.");
         pw.println("  get-estimated-download-bytes [-u | --user USER_ID]"
                 + " [-n | --namespace NAMESPACE] PACKAGE JOB_ID");
         pw.println("    Return the most recent estimated download bytes for the job.");