Merge "Tweak the layout of top app bar" into sc-dev
diff --git a/res/xml/power_usage_detail.xml b/res/xml/power_usage_detail.xml
index 95c1038..0258f8d 100644
--- a/res/xml/power_usage_detail.xml
+++ b/res/xml/power_usage_detail.xml
@@ -34,6 +34,30 @@
         android:title="@string/battery_detail_manage_title"
         settings:allowDividerAbove="true">
 
+        <com.android.settingslib.widget.RadioButtonPreference
+            android:key="unrestricted_pref"
+            android:summary="@string/manager_battery_usage_unrestricted_summary"
+            android:title="@string/manager_battery_usage_unrestricted_title"
+            settings:controller="com.android.settings.fuelgauge.UnrestrictedPreferenceController"/>
+
+        <com.android.settingslib.widget.RadioButtonPreference
+            android:key="optimized_pref"
+            android:summary="@string/manager_battery_usage_optimized_summary"
+            android:title="@string/manager_battery_usage_optimized_title"
+            settings:controller="com.android.settings.fuelgauge.OptimizedPreferenceController"/>
+
+        <com.android.settingslib.widget.RadioButtonPreference
+            android:key="restricted_pref"
+            android:summary="@string/manager_battery_usage_restricted_summary"
+            android:title="@string/restricted_true_label"
+            settings:controller="com.android.settings.fuelgauge.RestrictedPreferenceController"/>
+
+    </PreferenceCategory>
+
+    <PreferenceCategory
+        android:title="@string/battery_detail_manage_title"
+        settings:allowDividerAbove="true">
+
         <com.android.settingslib.RestrictedPreference
             android:key="background_activity"
             android:title="@string/background_activity_title"
@@ -63,4 +87,11 @@
 
     </PreferenceCategory>
 
+    <com.android.settingslib.widget.FooterPreference
+        android:order="100"
+        android:key="app_usage_footer_preference"
+        android:title="@string/manager_battery_usage_footer"
+        android:selectable="true"
+        settings:searchable="false"/>
+
 </PreferenceScreen>
\ No newline at end of file
diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
index f4bdfb3..f28ae00 100644
--- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
+++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
@@ -25,6 +25,7 @@
 import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.os.UserHandle;
+import android.text.Html;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.View;
@@ -48,6 +49,7 @@
 import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.utils.StringUtil;
 import com.android.settingslib.widget.LayoutPreference;
+import com.android.settingslib.widget.RadioButtonPreference;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -60,7 +62,7 @@
  */
 public class AdvancedPowerUsageDetail extends DashboardFragment implements
         ButtonActionDialogFragment.AppButtonsDialogListener,
-        BatteryTipPreferenceController.BatteryTipListener {
+        BatteryTipPreferenceController.BatteryTipListener, RadioButtonPreference.OnClickListener {
 
     public static final String TAG = "AdvancedPowerDetail";
     public static final String EXTRA_UID = "extra_uid";
@@ -75,6 +77,10 @@
     private static final String KEY_PREF_FOREGROUND = "app_usage_foreground";
     private static final String KEY_PREF_BACKGROUND = "app_usage_background";
     private static final String KEY_PREF_HEADER = "header_view";
+    private static final String KEY_PREF_UNRESTRICTED = "unrestricted_pref";
+    private static final String KEY_PREF_OPTIMIZED = "optimized_pref";
+    private static final String KEY_PREF_RESTRICTED = "restricted_pref";
+    private static final String KEY_FOOTER_PREFERENCE = "app_usage_footer_preference";
 
     private static final int REQUEST_UNINSTALL = 0;
     private static final int REQUEST_REMOVE_DEVICE_ADMIN = 1;
@@ -87,13 +93,26 @@
     ApplicationsState.AppEntry mAppEntry;
     @VisibleForTesting
     BatteryUtils mBatteryUtils;
+    @VisibleForTesting
+    BatteryOptimizeUtils mBatteryOptimizeUtils;
 
     @VisibleForTesting
     Preference mForegroundPreference;
     @VisibleForTesting
     Preference mBackgroundPreference;
+    @VisibleForTesting
+    Preference mFooterPreference;
+    @VisibleForTesting
+    RadioButtonPreference mRestrictedPreference;
+    @VisibleForTesting
+    RadioButtonPreference mOptimizePreference;
+    @VisibleForTesting
+    RadioButtonPreference mUnrestrictedPreference;
     private AppButtonsPreferenceController mAppButtonsPreferenceController;
     private BackgroundActivityPreferenceController mBackgroundActivityPreferenceController;
+    private UnrestrictedPreferenceController mUnrestrictedPreferenceController;
+    private OptimizedPreferenceController mOptimizedPreferenceController;
+    private RestrictedPreferenceController mRestrictedPreferenceController;
 
     private String mPackageName;
 
@@ -174,8 +193,19 @@
         mPackageName = getArguments().getString(EXTRA_PACKAGE_NAME);
         mForegroundPreference = findPreference(KEY_PREF_FOREGROUND);
         mBackgroundPreference = findPreference(KEY_PREF_BACKGROUND);
+        mFooterPreference = findPreference(KEY_FOOTER_PREFERENCE);
         mHeaderPreference = (LayoutPreference) findPreference(KEY_PREF_HEADER);
 
+        mUnrestrictedPreference  = findPreference(KEY_PREF_UNRESTRICTED);
+        mOptimizePreference  = findPreference(KEY_PREF_OPTIMIZED);
+        mRestrictedPreference  = findPreference(KEY_PREF_RESTRICTED);
+        mUnrestrictedPreference.setOnClickListener(this);
+        mOptimizePreference.setOnClickListener(this);
+        mRestrictedPreference.setOnClickListener(this);
+
+        mBatteryOptimizeUtils = new BatteryOptimizeUtils(
+                getContext(), getArguments().getInt(EXTRA_UID), mPackageName);
+
         if (mPackageName != null) {
             mAppEntry = mState.getEntry(mPackageName, UserHandle.myUserId());
         }
@@ -241,6 +271,26 @@
                                 backgroundTimeMs,
                                 /* withSeconds */ false,
                                 /* collapseTimeUnit */ false)));
+
+        final String stateString;
+        final String footerString;
+        //TODO(b/178197718) Update strings
+        if (!mBatteryOptimizeUtils.isValidPackageName()) {
+            //Present optimized only string when the package name is invalid.
+            stateString = context.getString(R.string.manager_battery_usage_optimized_title);
+            footerString = context.getString(
+                    R.string.manager_battery_usage_footer_limited, stateString);
+        } else if (mBatteryOptimizeUtils.isSystemOrDefaultApp()) {
+            //Present unrestricted only string when the package is system or default active app.
+            stateString = context.getString(R.string.manager_battery_usage_unrestricted_title);
+            footerString = context.getString(
+                    R.string.manager_battery_usage_footer_limited, stateString);
+        } else {
+            //Present default string to normal app.
+            footerString = context.getString(R.string.manager_battery_usage_footer);
+
+        }
+        mFooterPreference.setTitle(Html.fromHtml(footerString, Html.FROM_HTML_MODE_COMPACT));
     }
 
     @Override
@@ -274,6 +324,15 @@
                 (SettingsActivity) getActivity(), this, getSettingsLifecycle(), packageName, mState,
                 REQUEST_UNINSTALL, REQUEST_REMOVE_DEVICE_ADMIN);
         controllers.add(mAppButtonsPreferenceController);
+        mUnrestrictedPreferenceController =
+                new UnrestrictedPreferenceController(context, uid, packageName);
+        mOptimizedPreferenceController =
+                new OptimizedPreferenceController(context, uid, packageName);
+        mRestrictedPreferenceController =
+                new RestrictedPreferenceController(context, uid, packageName);
+        controllers.add(mUnrestrictedPreferenceController);
+        controllers.add(mOptimizedPreferenceController);
+        controllers.add(mRestrictedPreferenceController);
 
         return controllers;
     }
@@ -298,4 +357,15 @@
         mBackgroundActivityPreferenceController.updateSummary(
                 findPreference(mBackgroundActivityPreferenceController.getPreferenceKey()));
     }
+
+    @Override
+    public void onRadioButtonClicked(RadioButtonPreference selected) {
+        updatePreferenceState(mUnrestrictedPreference, selected.getKey());
+        updatePreferenceState(mOptimizePreference, selected.getKey());
+        updatePreferenceState(mRestrictedPreference, selected.getKey());
+    }
+
+    private void updatePreferenceState(RadioButtonPreference preference, String selectedKey) {
+        preference.setChecked(selectedKey.equals(preference.getKey()));
+    }
 }
diff --git a/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java b/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java
new file mode 100644
index 0000000..1184a77
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2021 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.settings.fuelgauge;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settingslib.fuelgauge.PowerAllowlistBackend;
+
+/** A utility class for application usage operation. */
+public class BatteryOptimizeUtils {
+    private static final String TAG = "BatteryOptimizeUtils";
+
+    @VisibleForTesting AppOpsManager mAppOpsManager;
+    @VisibleForTesting BatteryUtils mBatteryUtils;
+    @VisibleForTesting PowerAllowlistBackend mPowerAllowListBackend;
+    private final String mPackageName;
+    private final int mUid;
+
+    private int mMode;
+    private boolean mAllowListed;
+
+    /**
+     * Usage type of application.
+     */
+    public enum AppUsageState {
+        UNKNOWN,
+        RESTRICTED,
+        UNRESTRICTED,
+        OPTIMIZED,
+    }
+
+    public BatteryOptimizeUtils(Context context, int uid, String packageName) {
+        mUid = uid;
+        mPackageName = packageName;
+        mAppOpsManager = context.getSystemService(AppOpsManager.class);
+        mBatteryUtils = BatteryUtils.getInstance(context);
+        mPowerAllowListBackend = PowerAllowlistBackend.getInstance(context);
+        mMode = mAppOpsManager
+                .checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mPackageName);
+        mAllowListed = mPowerAllowListBackend.isAllowlisted(mPackageName);
+    }
+
+    public AppUsageState getAppUsageState() {
+        refreshState();
+        if (!mAllowListed && mMode == AppOpsManager.MODE_IGNORED) {
+            return AppUsageState.RESTRICTED;
+        } else if (mAllowListed && mMode == AppOpsManager.MODE_ALLOWED) {
+            return AppUsageState.UNRESTRICTED;
+        } else if (!mAllowListed && mMode == AppOpsManager.MODE_ALLOWED) {
+            return AppUsageState.OPTIMIZED;
+        } else {
+            Log.d(TAG, "get unknown app usage state.");
+            return AppUsageState.UNKNOWN;
+        }
+    }
+
+    public void setAppUsageState(AppUsageState state) {
+        switch (state) {
+            case RESTRICTED:
+                mBatteryUtils.setForceAppStandby(mUid, mPackageName, AppOpsManager.MODE_IGNORED);
+                mPowerAllowListBackend.removeApp(mPackageName);
+                break;
+            case UNRESTRICTED:
+                mBatteryUtils.setForceAppStandby(mUid, mPackageName, AppOpsManager.MODE_ALLOWED);
+                mPowerAllowListBackend.addApp(mPackageName);
+                break;
+            case OPTIMIZED:
+                mBatteryUtils.setForceAppStandby(mUid, mPackageName, AppOpsManager.MODE_ALLOWED);
+                mPowerAllowListBackend.removeApp(mPackageName);
+                break;
+            default:
+                Log.d(TAG, "set unknown app usage state.");
+        }
+    }
+
+    /**
+     * Return {@code true} if package name is valid (can get an uid).
+     */
+    public boolean isValidPackageName() {
+        return mBatteryUtils.getPackageUid(mPackageName) != BatteryUtils.UID_NULL;
+    }
+
+    /**
+     * Return {@code true} if this package is system or default active app.
+     */
+    public boolean isSystemOrDefaultApp() {
+        mPowerAllowListBackend.refreshList();
+
+        return mPowerAllowListBackend.isSysAllowlisted(mPackageName)
+                || mPowerAllowListBackend.isDefaultActiveApp(mPackageName);
+    }
+
+    private void refreshState() {
+        mPowerAllowListBackend.refreshList();
+        mAllowListed = mPowerAllowListBackend.isAllowlisted(mPackageName);
+        mMode = mAppOpsManager
+                .checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mPackageName);
+        Log.d(TAG, String.format("refresh %s state, allowlisted = %s, mode = %d",
+                mPackageName,
+                mAllowListed,
+                mMode));
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/OptimizedPreferenceController.java b/src/com/android/settings/fuelgauge/OptimizedPreferenceController.java
new file mode 100644
index 0000000..b2da356
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/OptimizedPreferenceController.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2021 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.settings.fuelgauge;
+
+import static com.android.settings.fuelgauge.BatteryOptimizeUtils.AppUsageState.OPTIMIZED;
+
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.widget.RadioButtonPreference;
+
+public class OptimizedPreferenceController extends AbstractPreferenceController
+        implements PreferenceControllerMixin {
+
+    private static final String TAG = "OPTIMIZED_PREF";
+
+    @VisibleForTesting String KEY_OPTIMIZED_PREF = "optimized_pref";
+    @VisibleForTesting BatteryOptimizeUtils mBatteryOptimizeUtils;
+
+    public OptimizedPreferenceController(Context context, int uid, String packageName) {
+        super(context);
+        mBatteryOptimizeUtils = new BatteryOptimizeUtils(context, uid, packageName);
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        if (!mBatteryOptimizeUtils.isValidPackageName()) {
+            Log.d(TAG, "invalid package name, optimized states only");
+            preference.setEnabled(true);
+            ((RadioButtonPreference) preference).setChecked(true);
+            return;
+        }
+
+        if (mBatteryOptimizeUtils.isSystemOrDefaultApp()) {
+            Log.d(TAG, "is system or default app, disable pref");
+            ((RadioButtonPreference) preference).setChecked(false);
+            preference.setEnabled(false);
+        } else if (mBatteryOptimizeUtils.getAppUsageState() == OPTIMIZED) {
+            Log.d(TAG, "is optimized states");
+            ((RadioButtonPreference) preference).setChecked(true);
+        } else {
+            ((RadioButtonPreference) preference).setChecked(false);
+        }
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_OPTIMIZED_PREF;
+    }
+
+    @Override
+    public boolean handlePreferenceTreeClick(Preference preference) {
+        if (!KEY_OPTIMIZED_PREF.equals(preference.getKey())) {
+            return false;
+        }
+
+        mBatteryOptimizeUtils.setAppUsageState(OPTIMIZED);
+        Log.d(TAG, "Set optimized");
+        return true;
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/RestrictedPreferenceController.java b/src/com/android/settings/fuelgauge/RestrictedPreferenceController.java
new file mode 100644
index 0000000..b52af57
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/RestrictedPreferenceController.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2021 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.settings.fuelgauge;
+
+import static com.android.settings.fuelgauge.BatteryOptimizeUtils.AppUsageState.RESTRICTED;
+
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.widget.RadioButtonPreference;
+
+public class RestrictedPreferenceController extends AbstractPreferenceController
+        implements PreferenceControllerMixin {
+
+    private static final String TAG = "RESTRICTED_PREF";
+
+    @VisibleForTesting String KEY_RESTRICTED_PREF = "restricted_pref";
+    @VisibleForTesting BatteryOptimizeUtils mBatteryOptimizeUtils;
+
+    public RestrictedPreferenceController(Context context, int uid, String packageName) {
+        super(context);
+        mBatteryOptimizeUtils = new BatteryOptimizeUtils(context, uid, packageName);
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+
+        if (!mBatteryOptimizeUtils.isValidPackageName()) {
+            Log.d(TAG, "invalid package name, disable pref");
+            preference.setEnabled(false);
+            return;
+        } else {
+            preference.setEnabled(true);
+        }
+
+        if (mBatteryOptimizeUtils.isSystemOrDefaultApp()) {
+            Log.d(TAG, "is system or default app, disable pref");
+            ((RadioButtonPreference) preference).setChecked(false);
+            preference.setEnabled(false);
+        } else if (mBatteryOptimizeUtils.getAppUsageState() == RESTRICTED) {
+            Log.d(TAG, "is restricted states");
+            ((RadioButtonPreference) preference).setChecked(true);
+        } else {
+            ((RadioButtonPreference) preference).setChecked(false);
+        }
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_RESTRICTED_PREF;
+    }
+
+    @Override
+    public boolean handlePreferenceTreeClick(Preference preference) {
+        if (!KEY_RESTRICTED_PREF.equals(preference.getKey())) {
+            return false;
+        }
+
+        mBatteryOptimizeUtils.setAppUsageState(RESTRICTED);
+        Log.d(TAG, "Set restricted");
+        return true;
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/UnrestrictedPreferenceController.java b/src/com/android/settings/fuelgauge/UnrestrictedPreferenceController.java
new file mode 100644
index 0000000..36141c5
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/UnrestrictedPreferenceController.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 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.settings.fuelgauge;
+
+import static com.android.settings.fuelgauge.BatteryOptimizeUtils.AppUsageState.UNRESTRICTED;
+
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.widget.RadioButtonPreference;
+
+public class UnrestrictedPreferenceController extends AbstractPreferenceController
+        implements PreferenceControllerMixin {
+
+    private static final String TAG = "UNRESTRICTED_PREF";
+
+    @VisibleForTesting String KEY_UNRESTRICTED_PREF = "unrestricted_pref";
+    @VisibleForTesting BatteryOptimizeUtils mBatteryOptimizeUtils;
+
+    public UnrestrictedPreferenceController(Context context, int uid, String packageName) {
+        super(context);
+        mBatteryOptimizeUtils = new BatteryOptimizeUtils(context, uid, packageName);
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+
+        if (!mBatteryOptimizeUtils.isValidPackageName()) {
+            Log.d(TAG, "invalid package name, disable pref");
+            preference.setEnabled(false);
+            return;
+        } else {
+            preference.setEnabled(true);
+        }
+
+        if (mBatteryOptimizeUtils.isSystemOrDefaultApp()) {
+            Log.d(TAG, "is system or default app, unrestricted states only");
+            ((RadioButtonPreference) preference).setChecked(true);
+        } else if (mBatteryOptimizeUtils.getAppUsageState() == UNRESTRICTED) {
+            Log.d(TAG, "is unrestricted states");
+            ((RadioButtonPreference) preference).setChecked(true);
+        } else {
+            ((RadioButtonPreference) preference).setChecked(false);
+        }
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_UNRESTRICTED_PREF;
+    }
+
+    @Override
+    public boolean handlePreferenceTreeClick(Preference preference) {
+        if (!KEY_UNRESTRICTED_PREF.equals(preference.getKey())) {
+            return false;
+        }
+
+        mBatteryOptimizeUtils.setAppUsageState(UNRESTRICTED);
+        Log.d(TAG, "Set unrestricted");
+        return true;
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetailsTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetailsTest.java
index 197fdff..c5ea21f 100644
--- a/tests/robotests/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetailsTest.java
+++ b/tests/robotests/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetailsTest.java
@@ -34,7 +34,6 @@
 
 import com.google.common.collect.ImmutableList;
 
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
@@ -80,7 +79,6 @@
     }
 
     @Test
-    @Ignore
     public void getPreferenceSummary_appOpNotAllowed_returnsNotAllowed() {
         shadowOf(mUserManager).addUser(
                 PERSONAL_PROFILE_ID, "personal-profile"/* name */, 0/* flags */);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java
index 7f76c70..820607f 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java
@@ -23,6 +23,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.doAnswer;
@@ -58,6 +59,7 @@
 import com.android.settingslib.applications.instantapps.InstantAppDataProvider;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.widget.LayoutPreference;
+import com.android.settingslib.widget.RadioButtonPreference;
 
 import org.junit.After;
 import org.junit.Before;
@@ -96,6 +98,9 @@
     private static final long PROCSTATE_TOP_TIME_US = PROCSTATE_TOP_TIME_MS * 1000;
     private static final long PHONE_FOREGROUND_TIME_MS = 250 * 1000;
     private static final long PHONE_BACKGROUND_TIME_MS = 0;
+    private static final String KEY_PREF_UNRESTRICTED = "unrestricted_pref";
+    private static final String KEY_PREF_OPTIMIZED = "optimized_pref";
+    private static final String KEY_PREF_RESTRICTED = "restricted_pref";
 
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private FragmentActivity mActivity;
@@ -121,9 +126,15 @@
     private BatteryStats.Timer mForegroundActivityTimer;
     @Mock
     private BatteryUtils mBatteryUtils;
+    @Mock
+    private BatteryOptimizeUtils mBatteryOptimizeUtils;
     private Context mContext;
     private Preference mForegroundPreference;
     private Preference mBackgroundPreference;
+    private Preference mFooterPreference;
+    private RadioButtonPreference mRestrictedPreference;
+    private RadioButtonPreference mOptimizePreference;
+    private RadioButtonPreference mUnrestrictedPreference;
     private AdvancedPowerUsageDetail mFragment;
     private SettingsActivity mTestActivity;
 
@@ -170,6 +181,7 @@
         mFragment.mHeaderPreference = mHeaderPreference;
         mFragment.mState = mState;
         mFragment.mBatteryUtils = new BatteryUtils(RuntimeEnvironment.application);
+        mFragment.mBatteryOptimizeUtils = mBatteryOptimizeUtils;
         mAppEntry.info = mock(ApplicationInfo.class);
 
         mTestActivity = spy(new SettingsActivity());
@@ -194,8 +206,16 @@
 
         mForegroundPreference = new Preference(mContext);
         mBackgroundPreference = new Preference(mContext);
+        mFooterPreference = new Preference(mContext);
+        mRestrictedPreference = new RadioButtonPreference(mContext);
+        mOptimizePreference = new RadioButtonPreference(mContext);
+        mUnrestrictedPreference = new RadioButtonPreference(mContext);
         mFragment.mForegroundPreference = mForegroundPreference;
         mFragment.mBackgroundPreference = mBackgroundPreference;
+        mFragment.mFooterPreference = mFooterPreference;
+        mFragment.mRestrictedPreference = mRestrictedPreference;
+        mFragment.mOptimizePreference = mOptimizePreference;
+        mFragment.mUnrestrictedPreference = mUnrestrictedPreference;
     }
 
     @After
@@ -352,4 +372,48 @@
         assertThat(mForegroundPreference.getSummary().toString()).isEqualTo("Used for 0 min");
         assertThat(mBackgroundPreference.getSummary().toString()).isEqualTo("Active for 0 min");
     }
+
+    @Test
+    public void testInitPreference_isValidPackageName_hasCorrectString() {
+        when(mBatteryOptimizeUtils.isValidPackageName()).thenReturn(false);
+
+        mFragment.initPreference();
+
+        assertThat(mFooterPreference.getTitle().toString())
+                .isEqualTo("This app requires Optimized battery usage.");
+    }
+
+    @Test
+    public void testInitPreference_isSystemOrDefaultApp_hasCorrectString() {
+        when(mBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
+        when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true);
+
+        mFragment.initPreference();
+
+        assertThat(mFooterPreference.getTitle()
+                .toString()).isEqualTo("This app requires Unrestricted battery usage.");
+    }
+
+    @Test
+    public void testInitPreference_hasCorrectString() {
+        when(mBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
+        when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(false);
+
+        mFragment.initPreference();
+
+        assertThat(mFooterPreference.getTitle().toString())
+                .isEqualTo("Changing how an app uses your battery can affect its performance.");
+    }
+
+    @Test
+    public void testOnRadioButtonClicked_clickOptimizePref_optimizePreferenceChecked() {
+        mOptimizePreference.setKey(KEY_PREF_OPTIMIZED);
+        mRestrictedPreference.setKey(KEY_PREF_RESTRICTED);
+        mUnrestrictedPreference.setKey(KEY_PREF_UNRESTRICTED);
+        mFragment.onRadioButtonClicked(mOptimizePreference);
+
+        assertThat(mOptimizePreference.isChecked()).isTrue();
+        assertThat(mRestrictedPreference.isChecked()).isFalse();
+        assertThat(mUnrestrictedPreference.isChecked()).isFalse();
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryOptimizeUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryOptimizeUtilsTest.java
new file mode 100644
index 0000000..89d66be
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryOptimizeUtilsTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2021 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.settings.fuelgauge;
+
+import static com.android.settings.fuelgauge.BatteryOptimizeUtils.AppUsageState.OPTIMIZED;
+import static com.android.settings.fuelgauge.BatteryOptimizeUtils.AppUsageState.RESTRICTED;
+import static com.android.settings.fuelgauge.BatteryOptimizeUtils.AppUsageState.UNRESTRICTED;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+
+import com.android.settingslib.fuelgauge.PowerAllowlistBackend;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class BatteryOptimizeUtilsTest {
+
+    private static final int UID = 12345;
+    private static final String PACKAGE_NAME = "com.android.app";
+
+    @Mock BatteryUtils mockBatteryUtils;
+    @Mock AppOpsManager mockAppOpsManager;
+    @Mock PowerAllowlistBackend mockBackend;
+
+    private Context mContext;
+    private BatteryOptimizeUtils mBatteryOptimizeUtils;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+        mBatteryOptimizeUtils = spy(new BatteryOptimizeUtils(mContext, UID, PACKAGE_NAME));
+        mBatteryOptimizeUtils.mAppOpsManager = mockAppOpsManager;
+        mBatteryOptimizeUtils.mBatteryUtils = mockBatteryUtils;
+        mBatteryOptimizeUtils.mPowerAllowListBackend = mockBackend;
+    }
+
+    @Test
+    public void testGetAppUsageState_returnRestricted() {
+        when(mockBackend.isAllowlisted(anyString())).thenReturn(false);
+        when(mockAppOpsManager.checkOpNoThrow(anyInt(), anyInt(), anyString()))
+                .thenReturn(AppOpsManager.MODE_IGNORED);
+
+        assertThat(mBatteryOptimizeUtils.getAppUsageState()).isEqualTo(RESTRICTED);
+    }
+
+    @Test
+    public void testGetAppUsageState_returnUnrestricted() {
+        when(mockBackend.isAllowlisted(anyString())).thenReturn(true);
+        when(mockAppOpsManager.checkOpNoThrow(anyInt(), anyInt(), anyString()))
+                .thenReturn(AppOpsManager.MODE_ALLOWED);
+
+        assertThat(mBatteryOptimizeUtils.getAppUsageState()).isEqualTo(UNRESTRICTED);
+    }
+
+    @Test
+    public void testGetAppUsageState_returnOptimized() {
+        when(mockBackend.isAllowlisted(anyString())).thenReturn(false);
+        when(mockAppOpsManager.checkOpNoThrow(anyInt(), anyInt(), anyString()))
+                .thenReturn(AppOpsManager.MODE_ALLOWED);
+
+        assertThat(mBatteryOptimizeUtils.getAppUsageState()).isEqualTo(OPTIMIZED);
+    }
+
+    @Test
+    public void testIsSystemOrDefaultApp_isSystemOrDefaultApp_returnTrue() {
+        when(mockBackend.isAllowlisted(anyString())).thenReturn(true);
+        when(mockBackend.isDefaultActiveApp(anyString())).thenReturn(true);
+
+        assertThat(mBatteryOptimizeUtils.isSystemOrDefaultApp()).isTrue();
+    }
+
+    @Test
+    public void testIsSystemOrDefaultApp_notSystemOrDefaultApp_returnFalse() {
+        assertThat(mBatteryOptimizeUtils.isSystemOrDefaultApp()).isFalse();
+    }
+
+    @Test
+    public void testIsValidPackageName_InvalidPackageName_returnFalse() {
+        final BatteryOptimizeUtils testBatteryOptimizeUtils =
+                new BatteryOptimizeUtils(mContext, UID, null);
+
+        assertThat(testBatteryOptimizeUtils.isValidPackageName()).isFalse();
+    }
+
+    @Test
+    public void testIsValidPackageName_validPackageName_returnTrue() {
+        assertThat(mBatteryOptimizeUtils.isValidPackageName()).isTrue();
+    }
+
+    @Test
+    public void testSetAppUsageState_Restricted_verifyAction() {
+        mBatteryOptimizeUtils.setAppUsageState(RESTRICTED);
+
+        verify(mockBatteryUtils).setForceAppStandby(UID,
+                PACKAGE_NAME, AppOpsManager.MODE_IGNORED);
+        verify(mockBackend).removeApp(PACKAGE_NAME);
+    }
+
+    @Test
+    public void  testSetAppUsageState_Unrestricted_verifyAction() {
+        mBatteryOptimizeUtils.setAppUsageState(UNRESTRICTED);
+
+        verify(mockBatteryUtils).setForceAppStandby(UID,
+                PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
+        verify(mockBackend).addApp(PACKAGE_NAME);
+    }
+
+    @Test
+    public void  testSetAppUsageState_Optimized_verifyAction() {
+        mBatteryOptimizeUtils.setAppUsageState(OPTIMIZED);
+
+        verify(mockBatteryUtils).setForceAppStandby(UID,
+                PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
+        verify(mockBackend).removeApp(PACKAGE_NAME);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/OptimizedPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/OptimizedPreferenceControllerTest.java
new file mode 100644
index 0000000..874618d
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/OptimizedPreferenceControllerTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2021 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.settings.fuelgauge;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import com.android.settingslib.widget.RadioButtonPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class OptimizedPreferenceControllerTest {
+    private static final int UID = 12345;
+    private static final String PACKAGE_NAME = "com.android.app";
+
+    private OptimizedPreferenceController mController;
+    private RadioButtonPreference mPreference;
+
+    @Mock BatteryOptimizeUtils mockBatteryOptimizeUtils;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mController = new OptimizedPreferenceController(
+                RuntimeEnvironment.application, UID, PACKAGE_NAME);
+        mPreference = new RadioButtonPreference(RuntimeEnvironment.application);
+        mController.mBatteryOptimizeUtils = mockBatteryOptimizeUtils;
+    }
+
+    @Test
+    public void testUpdateState_invalidPackage_prefEnabled() {
+        when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(false);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isEnabled()).isTrue();
+        assertThat(mPreference.isChecked()).isTrue();
+    }
+
+    @Test
+    public void testUpdateState_isSystemOrDefaultApp_prefUnchecked() {
+        when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
+        when(mockBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isChecked()).isFalse();
+        assertThat(mPreference.isEnabled()).isFalse();
+    }
+
+    @Test
+    public void testUpdateState_isOptimizedStates_prefChecked() {
+        when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
+        when(mockBatteryOptimizeUtils.getAppUsageState()).thenReturn(
+                BatteryOptimizeUtils.AppUsageState.OPTIMIZED);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isChecked()).isTrue();
+    }
+
+    @Test
+    public void testUpdateState_prefUnchecked() {
+        when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isChecked()).isFalse();
+    }
+
+    @Test
+    public void testHandlePreferenceTreeClick_samePrefKey_verifyAction() {
+        mPreference.setKey(mController.KEY_OPTIMIZED_PREF);
+        mController.handlePreferenceTreeClick(mPreference);
+
+        verify(mockBatteryOptimizeUtils).setAppUsageState(
+                BatteryOptimizeUtils.AppUsageState.OPTIMIZED);
+    }
+
+    @Test
+    public void testHandlePreferenceTreeClick_incorrectPrefKey_noAction() {
+        mController.handlePreferenceTreeClick(mPreference);
+
+        verifyZeroInteractions(mockBatteryOptimizeUtils);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/RestrictedPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/RestrictedPreferenceControllerTest.java
new file mode 100644
index 0000000..2e17404
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/RestrictedPreferenceControllerTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2021 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.settings.fuelgauge;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import com.android.settingslib.widget.RadioButtonPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class RestrictedPreferenceControllerTest {
+    private static final int UID = 12345;
+    private static final String PACKAGE_NAME = "com.android.app";
+
+    private RestrictedPreferenceController mController;
+    private RadioButtonPreference mPreference;
+
+    @Mock BatteryOptimizeUtils mockBatteryOptimizeUtils;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mController = new RestrictedPreferenceController(
+                RuntimeEnvironment.application, UID, PACKAGE_NAME);
+        mPreference = new RadioButtonPreference(RuntimeEnvironment.application);
+        mController.mBatteryOptimizeUtils = mockBatteryOptimizeUtils;
+    }
+
+    @Test
+    public void testUpdateState_isValidPackage_prefEnabled() {
+        when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isEnabled()).isTrue();
+    }
+
+    @Test
+    public void testUpdateState_invalidPackage_prefDisabled() {
+        when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(false);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isEnabled()).isFalse();
+    }
+
+    @Test
+    public void testUpdateState_isSystemOrDefaultApp_prefChecked() {
+        when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
+        when(mockBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isChecked()).isFalse();
+        assertThat(mPreference.isEnabled()).isFalse();
+    }
+
+    @Test
+    public void testUpdateState_isRestrictedStates_prefChecked() {
+        when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
+        when(mockBatteryOptimizeUtils.getAppUsageState()).thenReturn(
+                BatteryOptimizeUtils.AppUsageState.RESTRICTED);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isChecked()).isTrue();
+    }
+
+    @Test
+    public void testUpdateState_prefUnchecked() {
+        when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isChecked()).isFalse();
+    }
+
+    @Test
+    public void testHandlePreferenceTreeClick_samePrefKey_verifyAction() {
+        mPreference.setKey(mController.KEY_RESTRICTED_PREF);
+        mController.handlePreferenceTreeClick(mPreference);
+
+        verify(mockBatteryOptimizeUtils).setAppUsageState(
+                BatteryOptimizeUtils.AppUsageState.RESTRICTED);
+    }
+
+    @Test
+    public void testHandlePreferenceTreeClick_incorrectPrefKey_noAction() {
+        mController.handlePreferenceTreeClick(mPreference);
+
+        verifyZeroInteractions(mockBatteryOptimizeUtils);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/UnrestrictedPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/UnrestrictedPreferenceControllerTest.java
new file mode 100644
index 0000000..63cf760
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/UnrestrictedPreferenceControllerTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2021 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.settings.fuelgauge;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import com.android.settingslib.widget.RadioButtonPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class UnrestrictedPreferenceControllerTest {
+    private static final int UID = 12345;
+    private static final String PACKAGE_NAME = "com.android.app";
+
+    private UnrestrictedPreferenceController mController;
+    private RadioButtonPreference mPreference;
+
+    @Mock BatteryOptimizeUtils mockBatteryOptimizeUtils;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mController = new UnrestrictedPreferenceController(
+                RuntimeEnvironment.application, UID, PACKAGE_NAME);
+        mPreference = new RadioButtonPreference(RuntimeEnvironment.application);
+        mController.mBatteryOptimizeUtils = mockBatteryOptimizeUtils;
+    }
+
+    @Test
+    public void testUpdateState_isValidPackage_prefEnabled() {
+        when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isEnabled()).isTrue();
+    }
+
+    @Test
+    public void testUpdateState_invalidPackage_prefDisabled() {
+        when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(false);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isEnabled()).isFalse();
+    }
+
+    @Test
+    public void testUpdateState_isSystemOrDefaultApp_prefChecked() {
+        when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
+        when(mockBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isChecked()).isTrue();
+    }
+
+    @Test
+    public void testUpdateState_isUnrestrictedStates_prefChecked() {
+        when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
+        when(mockBatteryOptimizeUtils.getAppUsageState()).thenReturn(
+                BatteryOptimizeUtils.AppUsageState.UNRESTRICTED);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isChecked()).isTrue();
+    }
+
+    @Test
+    public void testUpdateState_prefUnchecked() {
+        when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isChecked()).isFalse();
+    }
+
+    @Test
+    public void testHandlePreferenceTreeClick_samePrefKey_verifyAction() {
+        mPreference.setKey(mController.KEY_UNRESTRICTED_PREF);
+        mController.handlePreferenceTreeClick(mPreference);
+
+        verify(mockBatteryOptimizeUtils).setAppUsageState(
+                BatteryOptimizeUtils.AppUsageState.UNRESTRICTED);
+    }
+
+    @Test
+    public void testHandlePreferenceTreeClick_incorrectPrefKey_noAction() {
+        mController.handlePreferenceTreeClick(mPreference);
+
+        verifyZeroInteractions(mockBatteryOptimizeUtils);
+    }
+}