Add the thread-safe protection for PowerAllowlistBackend if possible

Add some thread-safe protection if possible, since the
PowerAllowlistBackend is designed as single instance, but multiple
modules in the Settings will use it, both in the background and main
thread to cause the potential race conditions.

Fix: 340029244
Test: presubmit
Merged-In: I3b9e76889db7807496e38371d735ca41160be88e
Change-Id: I5470558d2c3f330fb014d0f59891647633d2195d
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
index 8fd4e91..c0eaaaa 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
@@ -32,6 +32,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 
+import androidx.annotation.GuardedBy;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.telephony.SmsApplication;
@@ -52,13 +53,22 @@
 
     private static PowerAllowlistBackend sInstance;
 
+    private final Object mAllowlistedAppsLock = new Object();
+    private final Object mSysAllowlistedAppsLock = new Object();
+    private final Object mDefaultActiveAppsLock = new Object();
+
     private final Context mAppContext;
     private final IDeviceIdleController mDeviceIdleService;
+
+    @GuardedBy("mAllowlistedAppsLock")
     private final ArraySet<String> mAllowlistedApps = new ArraySet<>();
+    @GuardedBy("mSysAllowlistedAppsLock")
     private final ArraySet<String> mSysAllowlistedApps = new ArraySet<>();
+    @GuardedBy("mDefaultActiveAppsLock")
     private final ArraySet<String> mDefaultActiveApps = new ArraySet<>();
 
-    public PowerAllowlistBackend(Context context) {
+    @VisibleForTesting
+    PowerAllowlistBackend(Context context) {
         this(context, IDeviceIdleController.Stub.asInterface(
                 ServiceManager.getService(DEVICE_IDLE_SERVICE)));
     }
@@ -71,22 +81,28 @@
     }
 
     public int getAllowlistSize() {
-        return mAllowlistedApps.size();
+        synchronized (mAllowlistedAppsLock) {
+            return mAllowlistedApps.size();
+        }
     }
 
     /**
     * Check if target package is in System allow list
     */
     public boolean isSysAllowlisted(String pkg) {
-        return mSysAllowlistedApps.contains(pkg);
+        synchronized (mSysAllowlistedAppsLock) {
+            return mSysAllowlistedApps.contains(pkg);
+        }
     }
 
     /**
      * Check if target package is in allow list
      */
     public boolean isAllowlisted(String pkg, int uid) {
-        if (mAllowlistedApps.contains(pkg)) {
-            return true;
+        synchronized (mAllowlistedAppsLock) {
+            if (mAllowlistedApps.contains(pkg)) {
+                return true;
+            }
         }
 
         if (isDefaultActiveApp(pkg, uid)) {
@@ -103,9 +119,10 @@
         // Additionally, check if pkg is default dialer/sms. They are considered essential apps and
         // should be automatically allowlisted (otherwise user may be able to set restriction on
         // them, leading to bad device behavior.)
-
-        if (mDefaultActiveApps.contains(pkg)) {
-            return true;
+        synchronized (mDefaultActiveAppsLock) {
+            if (mDefaultActiveApps.contains(pkg)) {
+                return true;
+            }
         }
 
         final DevicePolicyManager devicePolicyManager = mAppContext.getSystemService(
@@ -165,10 +182,12 @@
      * Add app into power save allow list.
      * @param pkg packageName
      */
-    public void addApp(String pkg) {
+    public synchronized void addApp(String pkg) {
         try {
             mDeviceIdleService.addPowerSaveWhitelistApp(pkg);
-            mAllowlistedApps.add(pkg);
+            synchronized (mAllowlistedAppsLock) {
+                mAllowlistedApps.add(pkg);
+            }
         } catch (RemoteException e) {
             Log.w(TAG, "Unable to reach IDeviceIdleController", e);
         }
@@ -178,10 +197,12 @@
      * Remove package from power save allow list.
      * @param pkg
      */
-    public void removeApp(String pkg) {
+    public synchronized void removeApp(String pkg) {
         try {
             mDeviceIdleService.removePowerSaveWhitelistApp(pkg);
-            mAllowlistedApps.remove(pkg);
+            synchronized (mAllowlistedAppsLock) {
+                mAllowlistedApps.remove(pkg);
+            }
         } catch (RemoteException e) {
             Log.w(TAG, "Unable to reach IDeviceIdleController", e);
         }
@@ -191,21 +212,31 @@
      * Refresh all of lists
      */
     @VisibleForTesting
-    public void refreshList() {
-        mSysAllowlistedApps.clear();
-        mAllowlistedApps.clear();
-        mDefaultActiveApps.clear();
+    public synchronized void refreshList() {
+        synchronized (mSysAllowlistedAppsLock) {
+            mSysAllowlistedApps.clear();
+        }
+        synchronized (mAllowlistedAppsLock) {
+            mAllowlistedApps.clear();
+        }
+        synchronized (mDefaultActiveAppsLock) {
+            mDefaultActiveApps.clear();
+        }
         if (mDeviceIdleService == null) {
             return;
         }
         try {
             final String[] allowlistedApps = mDeviceIdleService.getFullPowerWhitelist();
-            for (String app : allowlistedApps) {
-                mAllowlistedApps.add(app);
+            synchronized (mAllowlistedAppsLock) {
+                for (String app : allowlistedApps) {
+                    mAllowlistedApps.add(app);
+                }
             }
             final String[] sysAllowlistedApps = mDeviceIdleService.getSystemPowerWhitelist();
-            for (String app : sysAllowlistedApps) {
-                mSysAllowlistedApps.add(app);
+            synchronized (mSysAllowlistedAppsLock) {
+                for (String app : sysAllowlistedApps) {
+                    mSysAllowlistedApps.add(app);
+                }
             }
             final boolean hasTelephony = mAppContext.getPackageManager().hasSystemFeature(
                     PackageManager.FEATURE_TELEPHONY);
@@ -216,14 +247,18 @@
 
             if (hasTelephony) {
                 if (defaultSms != null) {
-                    mDefaultActiveApps.add(defaultSms.getPackageName());
+                    synchronized (mDefaultActiveAppsLock) {
+                        mDefaultActiveApps.add(defaultSms.getPackageName());
+                    }
                 }
                 if (!TextUtils.isEmpty(defaultDialer)) {
-                    mDefaultActiveApps.add(defaultDialer);
+                    synchronized (mDefaultActiveAppsLock) {
+                        mDefaultActiveApps.add(defaultDialer);
+                    }
                 }
             }
-        } catch (RemoteException e) {
-            Log.w(TAG, "Unable to reach IDeviceIdleController", e);
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to invoke refreshList()", e);
         }
     }
 
@@ -232,10 +267,11 @@
      * @return a PowerAllowlistBackend object
      */
     public static PowerAllowlistBackend getInstance(Context context) {
-        if (sInstance == null) {
-            sInstance = new PowerAllowlistBackend(context);
+        synchronized (PowerAllowlistBackend.class) {
+            if (sInstance == null) {
+                sInstance = new PowerAllowlistBackend(context);
+            }
+            return sInstance;
         }
-        return sInstance;
     }
-
 }