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

Commit 2bf60697 authored by YK Hung's avatar YK Hung
Browse files

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
Change-Id: I3b9e76889db7807496e38371d735ca41160be88e
parent 63c2e779
Loading
Loading
Loading
Loading
+78 −51
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;

import androidx.annotation.GuardedBy;
import androidx.annotation.VisibleForTesting;

import com.android.internal.telephony.SmsApplication;
@@ -56,13 +57,22 @@ public class PowerAllowlistBackend {

    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)));
    }
@@ -75,24 +85,25 @@ public class PowerAllowlistBackend {
    }

    public int getAllowlistSize() {
        synchronized (mAllowlistedAppsLock) {
            return mAllowlistedApps.size();
        }
    }

    /**
    * Check if target package is in System allow list
    */
    /** Check if target package is in System allow list */
    public boolean isSysAllowlisted(String pkg) {
        synchronized (mSysAllowlistedAppsLock) {
            return mSysAllowlistedApps.contains(pkg);
        }
    }

    /**
     * Check if target package is in allow list
     */
    /** Check if target package is in allow list */
    public boolean isAllowlisted(String pkg, int uid) {
        synchronized (mAllowlistedAppsLock) {
            if (mAllowlistedApps.contains(pkg)) {
                return true;
            }

        }
        if (isDefaultActiveApp(pkg, uid)) {
            return true;
        }
@@ -100,17 +111,17 @@ public class PowerAllowlistBackend {
        return false;
    }

    /**
     * Check if it is default active app in multiple area(i.e. SMS, Dialer, Device admin..)
     */
    /** Check if it is default active app in multiple area */
    public boolean isDefaultActiveApp(String pkg, int uid) {
        // 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.)

        synchronized (mDefaultActiveAppsLock) {
            if (mDefaultActiveApps.contains(pkg)) {
                return true;
            }
        }

        final DevicePolicyManager devicePolicyManager = mAppContext.getSystemService(
                DevicePolicyManager.class);
@@ -143,9 +154,7 @@ public class PowerAllowlistBackend {
                DEFAULT_SYSTEM_EXEMPT_POWER_RESTRICTIONS_ENABLED);
    }

    /**
     * Check if target package is in allow list except idle app
     */
    /** Check if target package is in allow list except idle app */
    public boolean isAllowlistedExceptIdle(String pkg) {
        try {
            return mDeviceIdleService.isPowerSaveWhitelistExceptIdleApp(pkg);
@@ -156,6 +165,7 @@ public class PowerAllowlistBackend {
    }

    /**
     * Check if target package is in allow list except idle app
     *
     * @param pkgs a list of packageName
     * @return true when one of package is in allow list
@@ -174,20 +184,21 @@ public class PowerAllowlistBackend {
    }

    /**
     * Add app into power save allow list.
     * Add app into power save allow list
     *
     * @param pkg packageName of the app
     */
    // TODO: Fix all callers to pass in UID
    public void addApp(String pkg) {
        addApp(pkg, Process.INVALID_UID);
    }

    /**
     * Add app into power save allow list.
     * Add app into power save allow list
     *
     * @param pkg packageName of the app
     * @param uid uid of the app
     */
    public void addApp(String pkg, int uid) {
    public synchronized void addApp(String pkg, int uid) {
        try {
            if (android.app.Flags.appRestrictionsApi()) {
                if (uid == Process.INVALID_UID) {
@@ -204,7 +215,9 @@ public class PowerAllowlistBackend {
            }

            mDeviceIdleService.addPowerSaveWhitelistApp(pkg);
            synchronized (mAllowlistedAppsLock) {
                mAllowlistedApps.add(pkg);
            }
        } catch (RemoteException e) {
            Log.w(TAG, "Unable to reach IDeviceIdleController", e);
        } catch (NameNotFoundException e) {
@@ -213,7 +226,8 @@ public class PowerAllowlistBackend {
    }

    /**
     * Remove package from power save allow list.
     * Remove package from power save allow list
     *
     * @param pkg packageName of the app
     */
    public void removeApp(String pkg) {
@@ -222,10 +236,11 @@ public class PowerAllowlistBackend {

    /**
     * Remove package from power save allow list.
     *
     * @param pkg packageName of the app
     * @param uid uid of the app
     */
    public void removeApp(String pkg, int uid) {
    public synchronized void removeApp(String pkg, int uid) {
        try {
            if (android.app.Flags.appRestrictionsApi()) {
                if (uid == Process.INVALID_UID) {
@@ -241,7 +256,9 @@ public class PowerAllowlistBackend {
            }

            mDeviceIdleService.removePowerSaveWhitelistApp(pkg);
            synchronized (mAllowlistedAppsLock) {
                mAllowlistedApps.remove(pkg);
            }
        } catch (RemoteException e) {
            Log.w(TAG, "Unable to reach IDeviceIdleController", e);
        } catch (NameNotFoundException e) {
@@ -249,26 +266,34 @@ public class PowerAllowlistBackend {
        }
    }

    /**
     * Refresh all of lists
     */
    /** Refresh all of lists */
    @VisibleForTesting
    public void refreshList() {
    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();
            synchronized (mAllowlistedAppsLock) {
                for (String app : allowlistedApps) {
                    mAllowlistedApps.add(app);
                }
            }
            final String[] sysAllowlistedApps = mDeviceIdleService.getSystemPowerWhitelist();
            synchronized (mSysAllowlistedAppsLock) {
                for (String app : sysAllowlistedApps) {
                    mSysAllowlistedApps.add(app);
                }
            }
            final boolean hasTelephony = mAppContext.getPackageManager().hasSystemFeature(
                    PackageManager.FEATURE_TELEPHONY);
            final ComponentName defaultSms = SmsApplication.getDefaultSmsApplication(mAppContext,
@@ -278,26 +303,28 @@ public class PowerAllowlistBackend {

            if (hasTelephony) {
                if (defaultSms != null) {
                    synchronized (mDefaultActiveAppsLock) {
                        mDefaultActiveApps.add(defaultSms.getPackageName());
                    }
                }
                if (!TextUtils.isEmpty(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);
        }
    }

    /**
     * @param context
     * @return a PowerAllowlistBackend object
     */
    /** Get the {@link PowerAllowlistBackend} instance */
    public static PowerAllowlistBackend getInstance(Context context) {
        synchronized (PowerAllowlistBackend.class) {
            if (sInstance == null) {
                sInstance = new PowerAllowlistBackend(context);
            }
            return sInstance;
        }

    }
}