diff --git a/res/values/strings.xml b/res/values/strings.xml index 0dae688f470..1eb58eee9a9 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -5434,6 +5434,10 @@ Manage battery usage + + Allow background usage + + Enable for real-time updates, disable to save battery Unrestricted diff --git a/res/xml/power_background_usage_detail.xml b/res/xml/power_background_usage_detail.xml new file mode 100644 index 00000000000..fb089fdfa11 --- /dev/null +++ b/res/xml/power_background_usage_detail.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/xml/power_usage_detail.xml b/res/xml/power_usage_detail.xml index 7b92f998e4c..f3b30b6323f 100644 --- a/res/xml/power_usage_detail.xml +++ b/res/xml/power_usage_detail.xml @@ -50,30 +50,11 @@ android:title="@string/manager_battery_usage_category_title" android:key="manage_battery_usage_category"> - - - - - + - - \ 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 82917d271fc..5262ba9224b 100644 --- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java +++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java @@ -27,10 +27,13 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; import android.os.UserHandle; +import android.text.TextUtils; import android.util.Log; import android.view.View; +import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; +import androidx.preference.Preference; import com.android.settings.R; import com.android.settings.SettingsActivity; @@ -45,14 +48,12 @@ import com.android.settings.fuelgauge.batteryusage.BatteryDiffEntry; import com.android.settings.fuelgauge.batteryusage.BatteryEntry; import com.android.settings.overlay.FeatureFactory; import com.android.settings.widget.EntityHeaderController; -import com.android.settingslib.HelpUtils; +import com.android.settingslib.PrimarySwitchPreference; import com.android.settingslib.applications.AppUtils; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.instrumentation.Instrumentable; -import com.android.settingslib.widget.FooterPreference; import com.android.settingslib.widget.LayoutPreference; -import com.android.settingslib.widget.SelectorWithWidgetPreference; import java.util.ArrayList; import java.util.List; @@ -67,8 +68,8 @@ import java.util.concurrent.Executors; */ public class AdvancedPowerUsageDetail extends DashboardFragment implements ButtonActionDialogFragment.AppButtonsDialogListener, - SelectorWithWidgetPreference.OnClickListener { - + Preference.OnPreferenceClickListener, + Preference.OnPreferenceChangeListener { public static final String TAG = "AdvancedPowerDetail"; public static final String EXTRA_UID = "extra_uid"; public static final String EXTRA_PACKAGE_NAME = "extra_package_name"; @@ -85,19 +86,16 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements public static final String EXTRA_POWER_USAGE_AMOUNT = "extra_power_usage_amount"; 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 String PACKAGE_NAME_NONE = "none"; - - private static final String HEADER_SUMMARY_FORMAT = "%s\n(%s)"; + private static final String KEY_ALLOW_BACKGROUND_USAGE = "allow_background_usage"; private static final int REQUEST_UNINSTALL = 0; private static final int REQUEST_REMOVE_DEVICE_ADMIN = 1; private final ExecutorService mExecutor = Executors.newSingleThreadExecutor(); + private AppButtonsPreferenceController mAppButtonsPreferenceController; + private PowerUsageTimeController mPowerUsageTimeController; + @VisibleForTesting LayoutPreference mHeaderPreference; @VisibleForTesting @@ -107,13 +105,7 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements @VisibleForTesting BatteryOptimizeUtils mBatteryOptimizeUtils; @VisibleForTesting - FooterPreference mFooterPreference; - @VisibleForTesting - SelectorWithWidgetPreference mRestrictedPreference; - @VisibleForTesting - SelectorWithWidgetPreference mOptimizePreference; - @VisibleForTesting - SelectorWithWidgetPreference mUnrestrictedPreference; + PrimarySwitchPreference mAllowBackgroundUsagePreference; @VisibleForTesting @BatteryOptimizeUtils.OptimizationMode int mOptimizationMode = BatteryOptimizeUtils.MODE_UNKNOWN; @@ -122,9 +114,6 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements @VisibleForTesting StringBuilder mLogStringBuilder; - private AppButtonsPreferenceController mAppButtonsPreferenceController; - private PowerUsageTimeController mPowerUsageTimeController; - // A wrapper class to carry LaunchBatteryDetailPage required arguments. private static final class LaunchBatteryDetailPageArgs { private String mUsagePercent; @@ -209,7 +198,7 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements args.putString(EXTRA_ANOMALY_HINT_PREF_KEY, launchArgs.mAnomalyHintPrefKey); args.putString(EXTRA_ANOMALY_HINT_TEXT, launchArgs.mAnomalyHintText); final int userId = launchArgs.mIsUserEntry ? ActivityManager.getCurrentUser() - : UserHandle.getUserId(launchArgs.mUid); + : UserHandle.getUserId(launchArgs.mUid); new SubSettingLauncher(context) .setDestination(AdvancedPowerUsageDetail.class.getName()) @@ -257,7 +246,7 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements super.onCreate(icicle); final String packageName = getArguments().getString(EXTRA_PACKAGE_NAME); - onCreateForTriState(packageName); + onCreateBackgroundUsageState(packageName); mHeaderPreference = findPreference(KEY_PREF_HEADER); if (packageName != null) { @@ -271,10 +260,10 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements initHeader(); mOptimizationMode = mBatteryOptimizeUtils.getAppOptimizationMode(); - initPreferenceForTriState(getContext()); + initFooter(); mExecutor.execute(() -> { - String packageName = - getLoggingPackageName(getContext(), mBatteryOptimizeUtils.getPackageName()); + final String packageName = BatteryUtils + .getLoggingPackageName(getContext(), mBatteryOptimizeUtils.getPackageName()); FeatureFactory.getFeatureFactory().getMetricsFeatureProvider() .action( getContext(), @@ -288,11 +277,10 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements public void onPause() { super.onPause(); - final int selectedPreference = getSelectedPreference(); - notifyBackupManager(); - mLogStringBuilder.append(", onPause mode = ").append(selectedPreference); - logMetricCategory(selectedPreference); + final int currentOptimizeMode = mBatteryOptimizeUtils.getAppOptimizationMode(); + mLogStringBuilder.append(", onPause mode = ").append(currentOptimizeMode); + logMetricCategory(currentOptimizeMode); mExecutor.execute(() -> { BatteryOptimizeLogUtils.writeLog( @@ -302,7 +290,7 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements mBatteryOptimizeUtils.getPackageName(), UserHandle.myUserId()), mLogStringBuilder.toString()); }); - Log.d(TAG, "Leave with mode: " + selectedPreference); + Log.d(TAG, "Leave with mode: " + currentOptimizeMode); } @VisibleForTesting @@ -353,33 +341,28 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements } @VisibleForTesting - void initPreferenceForTriState(Context context) { + void initFooter() { final String stateString; - final String footerString; + final String detailInfoString; + final Context context = getContext(); if (mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()) { // Present optimized only string when the package name is invalid. stateString = context.getString(R.string.manager_battery_usage_optimized_only); - footerString = context.getString( - R.string.manager_battery_usage_footer_limited, stateString); + detailInfoString = + 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_only); - footerString = context.getString( - R.string.manager_battery_usage_footer_limited, stateString); + detailInfoString = + 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(footerString); - final Intent helpIntent = HelpUtils.getHelpIntent(context, context.getString( - R.string.help_url_app_usage_settings), /*backupContext=*/ ""); - if (helpIntent != null) { - mFooterPreference.setLearnMoreAction(v -> - startActivityForResult(helpIntent, /*requestCode=*/ 0)); - mFooterPreference.setLearnMoreText( - context.getString(R.string.manager_battery_usage_link_a11y)); + detailInfoString = + context.getString( + R.string.manager_battery_usage_allow_background_usage_summary); } + mAllowBackgroundUsagePreference.setSummary(detailInfoString); } @Override @@ -412,9 +395,7 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements controllers.add(mPowerUsageTimeController); } controllers.add(mAppButtonsPreferenceController); - controllers.add(new UnrestrictedPreferenceController(context, uid, packageName)); - controllers.add(new OptimizedPreferenceController(context, uid, packageName)); - controllers.add(new RestrictedPreferenceController(context, uid, packageName)); + controllers.add(new AllowBackgroundPreferenceController(context, uid, packageName)); return controllers; } @@ -435,34 +416,45 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements } @Override - public void onRadioButtonClicked(SelectorWithWidgetPreference selected) { - final String selectedKey = selected.getKey(); - updatePreferenceState(mUnrestrictedPreference, selectedKey); - updatePreferenceState(mOptimizePreference, selectedKey); - updatePreferenceState(mRestrictedPreference, selectedKey); - mBatteryOptimizeUtils.setAppUsageState(getSelectedPreference(), Action.APPLY); + public boolean onPreferenceClick(Preference preference) { + if (!(preference instanceof PrimarySwitchPreference) + || !TextUtils.equals(preference.getKey(), KEY_ALLOW_BACKGROUND_USAGE)) { + return false; + } + PowerBackgroundUsageDetail.startPowerBackgroundUsageDetailPage( + getContext(), getArguments()); + return true; } - private void updatePreferenceState(SelectorWithWidgetPreference preference, - String selectedKey) { - preference.setChecked(selectedKey.equals(preference.getKey())); + @Override + public boolean onPreferenceChange(@NonNull Preference preference, Object newValue) { + if (!(preference instanceof PrimarySwitchPreference) + || !TextUtils.equals(preference.getKey(), KEY_ALLOW_BACKGROUND_USAGE)) { + return false; + } + if (newValue instanceof Boolean) { + final boolean isAllowBackgroundUsage = (boolean) newValue; + mBatteryOptimizeUtils.setAppUsageState( + isAllowBackgroundUsage + ? BatteryOptimizeUtils.MODE_OPTIMIZED + : BatteryOptimizeUtils.MODE_RESTRICTED, + Action.APPLY); + } + return true; } - private void logMetricCategory(int selectedKey) { - if (selectedKey == mOptimizationMode) { + private void logMetricCategory(int currentOptimizeMode) { + if (currentOptimizeMode == mOptimizationMode) { return; } - int metricCategory = 0; - switch (selectedKey) { + switch (currentOptimizeMode) { case BatteryOptimizeUtils.MODE_UNRESTRICTED: - metricCategory = SettingsEnums.ACTION_APP_BATTERY_USAGE_UNRESTRICTED; - break; case BatteryOptimizeUtils.MODE_OPTIMIZED: - metricCategory = SettingsEnums.ACTION_APP_BATTERY_USAGE_OPTIMIZED; + metricCategory = SettingsEnums.ACTION_APP_BATTERY_USAGE_ALLOW_BACKGROUND; break; case BatteryOptimizeUtils.MODE_RESTRICTED: - metricCategory = SettingsEnums.ACTION_APP_BATTERY_USAGE_RESTRICTED; + metricCategory = SettingsEnums.ACTION_APP_BATTERY_USAGE_DISABLE_BACKGROUND; break; } if (metricCategory == 0) { @@ -470,8 +462,8 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements } int finalMetricCategory = metricCategory; mExecutor.execute(() -> { - String packageName = - getLoggingPackageName(getContext(), mBatteryOptimizeUtils.getPackageName()); + String packageName = BatteryUtils + .getLoggingPackageName(getContext(), mBatteryOptimizeUtils.getPackageName()); FeatureFactory.getFeatureFactory().getMetricsFeatureProvider() .action( /* attribution */ SettingsEnums.OPEN_APP_BATTERY_USAGE, @@ -482,33 +474,15 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements }); } - private void onCreateForTriState(String packageName) { - mUnrestrictedPreference = findPreference(KEY_PREF_UNRESTRICTED); - mOptimizePreference = findPreference(KEY_PREF_OPTIMIZED); - mRestrictedPreference = findPreference(KEY_PREF_RESTRICTED); - mFooterPreference = findPreference(KEY_FOOTER_PREFERENCE); - mUnrestrictedPreference.setOnClickListener(this); - mOptimizePreference.setOnClickListener(this); - mRestrictedPreference.setOnClickListener(this); - - mBatteryOptimizeUtils = new BatteryOptimizeUtils( - getContext(), getArguments().getInt(EXTRA_UID), packageName); - } - - private int getSelectedPreference() { - if (mRestrictedPreference.isChecked()) { - return BatteryOptimizeUtils.MODE_RESTRICTED; - } else if (mUnrestrictedPreference.isChecked()) { - return BatteryOptimizeUtils.MODE_UNRESTRICTED; - } else if (mOptimizePreference.isChecked()) { - return BatteryOptimizeUtils.MODE_OPTIMIZED; - } else { - return BatteryOptimizeUtils.MODE_UNKNOWN; + private void onCreateBackgroundUsageState(String packageName) { + mAllowBackgroundUsagePreference = findPreference(KEY_ALLOW_BACKGROUND_USAGE); + if (mAllowBackgroundUsagePreference != null) { + mAllowBackgroundUsagePreference.setOnPreferenceClickListener(this); + mAllowBackgroundUsagePreference.setOnPreferenceChangeListener(this); } - } - private static String getLoggingPackageName(Context context, String originalPackingName) { - return BatteryUtils.isAppInstalledFromGooglePlayStore(context, originalPackingName) - ? originalPackingName : PACKAGE_NAME_NONE; + mBatteryOptimizeUtils = + new BatteryOptimizeUtils( + getContext(), getArguments().getInt(EXTRA_UID), packageName); } } diff --git a/src/com/android/settings/fuelgauge/RestrictedPreferenceController.java b/src/com/android/settings/fuelgauge/AllowBackgroundPreferenceController.java similarity index 53% rename from src/com/android/settings/fuelgauge/RestrictedPreferenceController.java rename to src/com/android/settings/fuelgauge/AllowBackgroundPreferenceController.java index 7db77f15e40..d722badbaaf 100644 --- a/src/com/android/settings/fuelgauge/RestrictedPreferenceController.java +++ b/src/com/android/settings/fuelgauge/AllowBackgroundPreferenceController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2023 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. @@ -17,50 +17,45 @@ package com.android.settings.fuelgauge; 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.PrimarySwitchPreference; import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.widget.SelectorWithWidgetPreference; +import com.android.settingslib.widget.MainSwitchPreference; -public class RestrictedPreferenceController extends AbstractPreferenceController +/** Controller to update the app background usage state */ +public class AllowBackgroundPreferenceController extends AbstractPreferenceController implements PreferenceControllerMixin { - private static final String TAG = "RESTRICTED_PREF"; + private static final String TAG = "AllowBackgroundPreferenceController"; + + @VisibleForTesting static final String KEY_ALLOW_BACKGROUND_USAGE = "allow_background_usage"; - @VisibleForTesting String KEY_RESTRICTED_PREF = "restricted_pref"; @VisibleForTesting BatteryOptimizeUtils mBatteryOptimizeUtils; - public RestrictedPreferenceController(Context context, int uid, String packageName) { + public AllowBackgroundPreferenceController(Context context, int uid, String packageName) { super(context); mBatteryOptimizeUtils = new BatteryOptimizeUtils(context, uid, packageName); } + private void setChecked(Preference preference, boolean checked) { + if (preference instanceof PrimarySwitchPreference) { + ((PrimarySwitchPreference) preference).setChecked(checked); + } else if (preference instanceof MainSwitchPreference) { + ((MainSwitchPreference) preference).setChecked(checked); + } + } + @Override public void updateState(Preference preference) { + preference.setEnabled(mBatteryOptimizeUtils.isOptimizeModeMutable()); - if (mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()) { - Log.d(TAG, "disable preference for " + mBatteryOptimizeUtils.getPackageName()); - preference.setEnabled(false); - return; - } else { - preference.setEnabled(true); - } - - if (mBatteryOptimizeUtils.getAppOptimizationMode() - == BatteryOptimizeUtils.MODE_RESTRICTED) { - Log.d(TAG, "is restricted states"); - ((SelectorWithWidgetPreference) preference).setChecked(true); - } else { - ((SelectorWithWidgetPreference) preference).setChecked(false); - if (mBatteryOptimizeUtils.isSystemOrDefaultApp()) { - Log.d(TAG, "is system or default app, disable pref"); - preference.setEnabled(false); - } - } + final boolean isAllowBackground = mBatteryOptimizeUtils.getAppOptimizationMode() + != BatteryOptimizeUtils.MODE_RESTRICTED; + setChecked(preference, isAllowBackground); } @Override @@ -70,7 +65,7 @@ public class RestrictedPreferenceController extends AbstractPreferenceController @Override public String getPreferenceKey() { - return KEY_RESTRICTED_PREF; + return KEY_ALLOW_BACKGROUND_USAGE; } @Override diff --git a/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java b/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java index 7b3a6ad33dc..003f77194bf 100644 --- a/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java +++ b/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java @@ -146,6 +146,22 @@ public class BatteryOptimizeUtils { return isSystemOrDefaultApp(mContext, mPowerAllowListBackend, mPackageName, mUid); } + /** + * Return {@code true} if the optimization mode of this package can be changed + */ + public boolean isOptimizeModeMutable() { + return !isDisabledForOptimizeModeOnly() && !isSystemOrDefaultApp(); + } + + /** + * Return {@code true} if the optimization mode is mutable and current state is not restricted + */ + public boolean isSelectorPreferenceEnabled() { + // Enable the preference if apps are not set into restricted mode, otherwise disable it + return isOptimizeModeMutable() + && getAppOptimizationMode() != BatteryOptimizeUtils.MODE_RESTRICTED; + } + /** * Gets the list of installed applications. */ diff --git a/src/com/android/settings/fuelgauge/BatteryUtils.java b/src/com/android/settings/fuelgauge/BatteryUtils.java index 3b958aec105..c38af073f0a 100644 --- a/src/com/android/settings/fuelgauge/BatteryUtils.java +++ b/src/com/android/settings/fuelgauge/BatteryUtils.java @@ -88,6 +88,7 @@ public class BatteryUtils { public static final String BYPASS_DOCK_DEFENDER_ACTION = "battery.dock.defender.bypass"; private static final String GOOGLE_PLAY_STORE_PACKAGE = "com.android.vending"; + private static final String PACKAGE_NAME_NONE = "none"; @Retention(RetentionPolicy.SOURCE) @IntDef({StatusType.SCREEN_USAGE, @@ -140,6 +141,12 @@ public class BatteryUtils { FeatureFactory.getFeatureFactory().getPowerUsageFeatureProvider(); } + /** For test to reset single instance. */ + @VisibleForTesting + public void reset() { + sInstance = null; + } + public long getProcessTimeMs(@StatusType int type, @Nullable BatteryStats.Uid uid, int which) { if (uid == null) { @@ -616,6 +623,12 @@ public class BatteryUtils { && GOOGLE_PLAY_STORE_PACKAGE.equals(installSourceInfo.getInitiatingPackageName()); } + /** Gets the logging package name. */ + public static String getLoggingPackageName(Context context, String originalPackingName) { + return BatteryUtils.isAppInstalledFromGooglePlayStore(context, originalPackingName) + ? originalPackingName : PACKAGE_NAME_NONE; + } + /** Gets the latest sticky battery intent from the Android system. */ public static Intent getBatteryIntent(Context context) { return com.android.settingslib.fuelgauge.BatteryUtils.getBatteryIntent(context); diff --git a/src/com/android/settings/fuelgauge/OptimizedPreferenceController.java b/src/com/android/settings/fuelgauge/OptimizedPreferenceController.java index ca75b0e8d22..3fed00cd171 100644 --- a/src/com/android/settings/fuelgauge/OptimizedPreferenceController.java +++ b/src/com/android/settings/fuelgauge/OptimizedPreferenceController.java @@ -17,7 +17,6 @@ package com.android.settings.fuelgauge; import android.content.Context; -import android.util.Log; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; @@ -31,8 +30,10 @@ public class OptimizedPreferenceController extends AbstractPreferenceController private static final String TAG = "OPTIMIZED_PREF"; - @VisibleForTesting String KEY_OPTIMIZED_PREF = "optimized_pref"; - @VisibleForTesting BatteryOptimizeUtils mBatteryOptimizeUtils; + @VisibleForTesting + static final String KEY_OPTIMIZED_PREF = "optimized_preference"; + @VisibleForTesting + BatteryOptimizeUtils mBatteryOptimizeUtils; public OptimizedPreferenceController(Context context, int uid, String packageName) { super(context); @@ -46,24 +47,12 @@ public class OptimizedPreferenceController extends AbstractPreferenceController @Override public void updateState(Preference preference) { - if (mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()) { - Log.d(TAG, "disable preference for " + mBatteryOptimizeUtils.getPackageName()); - preference.setEnabled(true); - ((SelectorWithWidgetPreference) preference).setChecked(true); - return; - } + preference.setEnabled(mBatteryOptimizeUtils.isSelectorPreferenceEnabled()); - if (mBatteryOptimizeUtils.getAppOptimizationMode() - == BatteryOptimizeUtils.MODE_OPTIMIZED) { - Log.d(TAG, "is optimized states"); - ((SelectorWithWidgetPreference) preference).setChecked(true); - } else { - ((SelectorWithWidgetPreference) preference).setChecked(false); - if (mBatteryOptimizeUtils.isSystemOrDefaultApp()) { - Log.d(TAG, "is system or default app, disable pref"); - preference.setEnabled(false); - } - } + final boolean isOptimized = mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly() + || mBatteryOptimizeUtils.getAppOptimizationMode() + == BatteryOptimizeUtils.MODE_OPTIMIZED; + ((SelectorWithWidgetPreference) preference).setChecked(isOptimized); } @Override diff --git a/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java b/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java new file mode 100644 index 00000000000..3bf4562aee5 --- /dev/null +++ b/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java @@ -0,0 +1,351 @@ +/* + * Copyright (C) 2023 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.BatteryOptimizeHistoricalLogEntry.Action; + +import android.app.Activity; +import android.app.backup.BackupManager; +import android.app.settings.SettingsEnums; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.UserHandle; +import android.text.TextUtils; +import android.util.Log; +import android.view.View; +import android.widget.Switch; + +import androidx.annotation.VisibleForTesting; + +import com.android.settings.R; +import com.android.settings.core.SubSettingLauncher; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.overlay.FeatureFactory; +import com.android.settings.widget.EntityHeaderController; +import com.android.settingslib.HelpUtils; +import com.android.settingslib.applications.AppUtils; +import com.android.settingslib.applications.ApplicationsState; +import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.widget.FooterPreference; +import com.android.settingslib.widget.LayoutPreference; +import com.android.settingslib.widget.MainSwitchPreference; +import com.android.settingslib.widget.OnMainSwitchChangeListener; +import com.android.settingslib.widget.SelectorWithWidgetPreference; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * Allow background usage fragment for each app + */ +public class PowerBackgroundUsageDetail extends DashboardFragment implements + SelectorWithWidgetPreference.OnClickListener, + OnMainSwitchChangeListener { + private static final String TAG = "PowerBackgroundUsageDetail"; + + public static final String EXTRA_UID = "extra_uid"; + public static final String EXTRA_PACKAGE_NAME = "extra_package_name"; + public static final String EXTRA_LABEL = "extra_label"; + public static final String EXTRA_POWER_USAGE_AMOUNT = "extra_power_usage_amount"; + public static final String EXTRA_ICON_ID = "extra_icon_id"; + private static final String KEY_PREF_HEADER = "header_view"; + private static final String KEY_PREF_UNRESTRICTED = "unrestricted_preference"; + private static final String KEY_PREF_OPTIMIZED = "optimized_preference"; + private static final String KEY_ALLOW_BACKGROUND_USAGE = "allow_background_usage"; + private static final String KEY_FOOTER_PREFERENCE = "app_usage_footer_preference"; + + private final ExecutorService mExecutor = Executors.newSingleThreadExecutor(); + + @VisibleForTesting + LayoutPreference mHeaderPreference; + @VisibleForTesting + ApplicationsState mState; + @VisibleForTesting + ApplicationsState.AppEntry mAppEntry; + @VisibleForTesting + BatteryOptimizeUtils mBatteryOptimizeUtils; + @VisibleForTesting + SelectorWithWidgetPreference mOptimizePreference; + @VisibleForTesting + SelectorWithWidgetPreference mUnrestrictedPreference; + @VisibleForTesting + MainSwitchPreference mMainSwitchPreference; + @VisibleForTesting + FooterPreference mFooterPreference; + @VisibleForTesting + BackupManager mBackupManager; + @VisibleForTesting + StringBuilder mLogStringBuilder; + @VisibleForTesting + @BatteryOptimizeUtils.OptimizationMode + int mOptimizationMode = BatteryOptimizeUtils.MODE_UNKNOWN; + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + mState = ApplicationsState.getInstance(getActivity().getApplication()); + } + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + final String packageName = getArguments().getString(EXTRA_PACKAGE_NAME); + onCreateBackgroundUsageState(packageName); + mHeaderPreference = findPreference(KEY_PREF_HEADER); + + if (packageName != null) { + mAppEntry = mState.getEntry(packageName, UserHandle.myUserId()); + } + } + + @Override + public void onResume() { + super.onResume(); + initHeader(); + mOptimizationMode = mBatteryOptimizeUtils.getAppOptimizationMode(); + initFooter(); + mExecutor.execute(() -> { + String packageName = BatteryUtils + .getLoggingPackageName(getContext(), mBatteryOptimizeUtils.getPackageName()); + FeatureFactory.getFeatureFactory().getMetricsFeatureProvider() + .action( + getContext(), + SettingsEnums.OPEN_APP_BATTERY_USAGE, + packageName); + }); + mLogStringBuilder = new StringBuilder("onResume mode = ").append(mOptimizationMode); + } + + @Override + public void onPause() { + super.onPause(); + + notifyBackupManager(); + final int currentOptimizeMode = mBatteryOptimizeUtils.getAppOptimizationMode(); + mLogStringBuilder.append(", onPause mode = ").append(currentOptimizeMode); + logMetricCategory(currentOptimizeMode); + + mExecutor.execute(() -> { + BatteryOptimizeLogUtils.writeLog( + getContext().getApplicationContext(), + Action.LEAVE, + BatteryOptimizeLogUtils.getPackageNameWithUserId( + mBatteryOptimizeUtils.getPackageName(), UserHandle.myUserId()), + mLogStringBuilder.toString()); + }); + Log.d(TAG, "Leave with mode: " + currentOptimizeMode); + } + + @Override + public void onRadioButtonClicked(SelectorWithWidgetPreference selected) { + final String selectedKey = selected == null ? null : selected.getKey(); + updateSelectorPreferenceState(mUnrestrictedPreference, selectedKey); + updateSelectorPreferenceState(mOptimizePreference, selectedKey); + mBatteryOptimizeUtils.setAppUsageState(getSelectedPreference(), Action.APPLY); + } + + @Override + public void onSwitchChanged(Switch switchView, boolean isChecked) { + mMainSwitchPreference.setChecked(isChecked); + updateSelectorPreference(isChecked); + } + + @Override + public int getMetricsCategory() { + return SettingsEnums.FUELGAUGE_POWER_USAGE_MANAGE_BACKGROUND; + } + + @Override + protected List createPreferenceControllers(Context context) { + final List controllers = new ArrayList<>(); + final Bundle bundle = getArguments(); + final int uid = bundle.getInt(EXTRA_UID, 0); + final String packageName = bundle.getString(EXTRA_PACKAGE_NAME); + + controllers.add(new AllowBackgroundPreferenceController(context, uid, packageName)); + controllers.add(new OptimizedPreferenceController(context, uid, packageName)); + controllers.add(new UnrestrictedPreferenceController(context, uid, packageName)); + + return controllers; + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.power_background_usage_detail; + } + + @Override + protected String getLogTag() { + return TAG; + } + + @VisibleForTesting + void updateSelectorPreference(boolean isEnabled) { + mOptimizePreference.setEnabled(isEnabled); + mUnrestrictedPreference.setEnabled(isEnabled); + onRadioButtonClicked(isEnabled ? mOptimizePreference : null); + } + + @VisibleForTesting + void notifyBackupManager() { + if (mOptimizationMode != mBatteryOptimizeUtils.getAppOptimizationMode()) { + final BackupManager backupManager = mBackupManager != null + ? mBackupManager : new BackupManager(getContext()); + backupManager.dataChanged(); + } + } + + @VisibleForTesting + int getSelectedPreference() { + if (!mMainSwitchPreference.isChecked()) { + return BatteryOptimizeUtils.MODE_RESTRICTED; + } else if (mUnrestrictedPreference.isChecked()) { + return BatteryOptimizeUtils.MODE_UNRESTRICTED; + } else if (mOptimizePreference.isChecked()) { + return BatteryOptimizeUtils.MODE_OPTIMIZED; + } else { + return BatteryOptimizeUtils.MODE_UNKNOWN; + } + } + + static void startPowerBackgroundUsageDetailPage( + Context context, Bundle args) { + new SubSettingLauncher(context) + .setDestination(PowerBackgroundUsageDetail.class.getName()) + .setArguments(args) + .setSourceMetricsCategory(SettingsEnums.FUELGAUGE_POWER_USAGE_MANAGE_BACKGROUND) + .launch(); + } + + @VisibleForTesting + void initHeader() { + final View appSnippet = mHeaderPreference.findViewById(R.id.entity_header); + final Activity context = getActivity(); + final Bundle bundle = getArguments(); + EntityHeaderController controller = EntityHeaderController + .newInstance(context, this, appSnippet) + .setButtonActions(EntityHeaderController.ActionType.ACTION_NONE, + EntityHeaderController.ActionType.ACTION_NONE); + + if (mAppEntry == null) { + controller.setLabel(bundle.getString(EXTRA_LABEL)); + + final int iconId = bundle.getInt(EXTRA_ICON_ID, 0); + if (iconId == 0) { + controller.setIcon(context.getPackageManager().getDefaultActivityIcon()); + } else { + controller.setIcon(context.getDrawable(bundle.getInt(EXTRA_ICON_ID))); + } + } else { + mState.ensureIcon(mAppEntry); + controller.setLabel(mAppEntry); + controller.setIcon(mAppEntry); + controller.setIsInstantApp(AppUtils.isInstant(mAppEntry.info)); + } + + controller.done(true /* rebindActions */); + } + + @VisibleForTesting + void initFooter() { + final String stateString; + final String footerString; + final Context context = getContext(); + + if (mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()) { + // Present optimized only string when the package name is invalid. + stateString = context.getString(R.string.manager_battery_usage_optimized_only); + 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_only); + 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(footerString); + final Intent helpIntent = HelpUtils.getHelpIntent(context, context.getString( + R.string.help_url_app_usage_settings), /*backupContext=*/ ""); + if (helpIntent != null) { + mFooterPreference.setLearnMoreAction(v -> + startActivityForResult(helpIntent, /*requestCode=*/ 0)); + mFooterPreference.setLearnMoreText( + context.getString(R.string.manager_battery_usage_link_a11y)); + } + } + + private void onCreateBackgroundUsageState(String packageName) { + mOptimizePreference = findPreference(KEY_PREF_OPTIMIZED); + mUnrestrictedPreference = findPreference(KEY_PREF_UNRESTRICTED); + mMainSwitchPreference = findPreference(KEY_ALLOW_BACKGROUND_USAGE); + mFooterPreference = findPreference(KEY_FOOTER_PREFERENCE); + + mOptimizePreference.setOnClickListener(this); + mUnrestrictedPreference.setOnClickListener(this); + mMainSwitchPreference.addOnSwitchChangeListener(this); + + mBatteryOptimizeUtils = new BatteryOptimizeUtils( + getContext(), getArguments().getInt(EXTRA_UID), packageName); + } + + private void updateSelectorPreferenceState(SelectorWithWidgetPreference preference, + String selectedKey) { + preference.setChecked(TextUtils.equals(selectedKey, preference.getKey())); + } + + private void logMetricCategory(int currentOptimizeMode) { + if (currentOptimizeMode == mOptimizationMode) { + return; + } + int metricCategory = 0; + switch (currentOptimizeMode) { + case BatteryOptimizeUtils.MODE_UNRESTRICTED: + metricCategory = SettingsEnums.ACTION_APP_BATTERY_USAGE_UNRESTRICTED; + break; + case BatteryOptimizeUtils.MODE_OPTIMIZED: + metricCategory = SettingsEnums.ACTION_APP_BATTERY_USAGE_OPTIMIZED; + break; + case BatteryOptimizeUtils.MODE_RESTRICTED: + metricCategory = SettingsEnums.ACTION_APP_BATTERY_USAGE_RESTRICTED; + break; + } + if (metricCategory == 0) { + return; + } + int finalMetricCategory = metricCategory; + mExecutor.execute(() -> { + String packageName = BatteryUtils + .getLoggingPackageName(getContext(), mBatteryOptimizeUtils.getPackageName()); + FeatureFactory.getFeatureFactory().getMetricsFeatureProvider() + .action( + /* attribution */ SettingsEnums.OPEN_APP_BATTERY_USAGE, + /* action */ finalMetricCategory, + /* pageId */ SettingsEnums.OPEN_APP_BATTERY_USAGE, + packageName, + getArguments().getInt(EXTRA_POWER_USAGE_AMOUNT)); + }); + } +} diff --git a/src/com/android/settings/fuelgauge/UnrestrictedPreferenceController.java b/src/com/android/settings/fuelgauge/UnrestrictedPreferenceController.java index 4578723ce5e..b06b7e2d08b 100644 --- a/src/com/android/settings/fuelgauge/UnrestrictedPreferenceController.java +++ b/src/com/android/settings/fuelgauge/UnrestrictedPreferenceController.java @@ -17,7 +17,6 @@ package com.android.settings.fuelgauge; import android.content.Context; -import android.util.Log; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; @@ -31,7 +30,9 @@ public class UnrestrictedPreferenceController extends AbstractPreferenceControll private static final String TAG = "UNRESTRICTED_PREF"; - @VisibleForTesting String KEY_UNRESTRICTED_PREF = "unrestricted_pref"; + @VisibleForTesting + static final String KEY_UNRESTRICTED_PREF = "unrestricted_preference"; + @VisibleForTesting BatteryOptimizeUtils mBatteryOptimizeUtils; public UnrestrictedPreferenceController(Context context, int uid, String packageName) { @@ -41,26 +42,11 @@ public class UnrestrictedPreferenceController extends AbstractPreferenceControll @Override public void updateState(Preference preference) { + preference.setEnabled(mBatteryOptimizeUtils.isSelectorPreferenceEnabled()); - if (mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()) { - Log.d(TAG, "disable preference for " + mBatteryOptimizeUtils.getPackageName()); - preference.setEnabled(false); - return; - } else { - preference.setEnabled(true); - } - - if (mBatteryOptimizeUtils.getAppOptimizationMode() - == BatteryOptimizeUtils.MODE_UNRESTRICTED) { - Log.d(TAG, "is unrestricted states"); - ((SelectorWithWidgetPreference) preference).setChecked(true); - } else { - ((SelectorWithWidgetPreference) preference).setChecked(false); - if (mBatteryOptimizeUtils.isSystemOrDefaultApp()) { - Log.d(TAG, "is system or default app, disable pref"); - preference.setEnabled(false); - } - } + final boolean isUnrestricted = mBatteryOptimizeUtils.getAppOptimizationMode() + == BatteryOptimizeUtils.MODE_UNRESTRICTED; + ((SelectorWithWidgetPreference) preference).setChecked(isUnrestricted); } @Override diff --git a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java index 9131051ccd1..80486cb3b41 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java @@ -17,6 +17,7 @@ package com.android.settings.fuelgauge; import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS; +import static com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action; import static com.google.common.truth.Truth.assertThat; @@ -53,13 +54,12 @@ import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.ShadowActivityManager; import com.android.settings.testutils.shadow.ShadowEntityHeaderController; import com.android.settings.widget.EntityHeaderController; +import com.android.settingslib.PrimarySwitchPreference; import com.android.settingslib.applications.AppUtils; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.instantapps.InstantAppDataProvider; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; -import com.android.settingslib.widget.FooterPreference; import com.android.settingslib.widget.LayoutPreference; -import com.android.settingslib.widget.SelectorWithWidgetPreference; import org.junit.After; import org.junit.Before; @@ -95,9 +95,7 @@ public class AdvancedPowerUsageDetailTest { private static final long FOREGROUND_SERVICE_TIME_MS = 444; private static final long FOREGROUND_TIME_MS = FOREGROUND_ACTIVITY_TIME_MS + FOREGROUND_SERVICE_TIME_MS; - 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_ALLOW_BACKGROUND_USAGE = "allow_background_usage"; @Mock(answer = Answers.RETURNS_DEEP_STUBS) private FragmentActivity mActivity; @@ -127,10 +125,7 @@ public class AdvancedPowerUsageDetailTest { private BackupManager mBackupManager; private Context mContext; - private FooterPreference mFooterPreference; - private SelectorWithWidgetPreference mRestrictedPreference; - private SelectorWithWidgetPreference mOptimizePreference; - private SelectorWithWidgetPreference mUnrestrictedPreference; + private PrimarySwitchPreference mAllowBackgroundUsagePreference; private AdvancedPowerUsageDetail mFragment; private SettingsActivity mTestActivity; private FakeFeatureFactory mFeatureFactory; @@ -198,14 +193,9 @@ public class AdvancedPowerUsageDetailTest { nullable(UserHandle.class)); doAnswer(callable).when(mActivity).startActivity(captor.capture()); - mFooterPreference = new FooterPreference(mContext); - mRestrictedPreference = new SelectorWithWidgetPreference(mContext); - mOptimizePreference = new SelectorWithWidgetPreference(mContext); - mUnrestrictedPreference = new SelectorWithWidgetPreference(mContext); - mFragment.mFooterPreference = mFooterPreference; - mFragment.mRestrictedPreference = mRestrictedPreference; - mFragment.mOptimizePreference = mOptimizePreference; - mFragment.mUnrestrictedPreference = mUnrestrictedPreference; + mAllowBackgroundUsagePreference = new PrimarySwitchPreference(mContext); + mAllowBackgroundUsagePreference.setKey(KEY_ALLOW_BACKGROUND_USAGE); + mFragment.mAllowBackgroundUsagePreference = mAllowBackgroundUsagePreference; } @After @@ -307,70 +297,60 @@ public class AdvancedPowerUsageDetailTest { } @Test - public void initPreferenceForTriState_isValidPackageName_hasCorrectString() { + public void initFooter_isValidPackageName_hasCorrectString() { when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(true); - mFragment.initPreferenceForTriState(mContext); + mFragment.initFooter(); - assertThat(mFooterPreference.getTitle().toString()) + assertThat(mAllowBackgroundUsagePreference.getSummary().toString()) .isEqualTo("This app requires optimized battery usage."); } @Test - public void initPreferenceForTriState_isSystemOrDefaultApp_hasCorrectString() { + public void initFooter_isSystemOrDefaultApp_hasCorrectString() { when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); - mFragment.initPreferenceForTriState(mContext); + mFragment.initFooter(); - assertThat(mFooterPreference.getTitle() - .toString()).isEqualTo("This app requires unrestricted battery usage."); + assertThat(mAllowBackgroundUsagePreference.getSummary().toString()) + .isEqualTo("This app requires unrestricted battery usage."); } @Test - public void initPreferenceForTriState_hasCorrectString() { + public void initFooter_hasCorrectString() { when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(false); - mFragment.initPreferenceForTriState(mContext); + mFragment.initFooter(); - assertThat(mFooterPreference.getTitle().toString()) - .isEqualTo("Changing how an app uses your battery can affect its performance."); - } - - @Test - public void onRadioButtonClicked_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(); + assertThat(mAllowBackgroundUsagePreference.getSummary().toString()) + .isEqualTo("Enable for real-time updates, disable to save battery"); } @Test public void onPause_optimizationModeChanged_logPreference() throws PackageManager.NameNotFoundException, InterruptedException { final String packageName = "testPackageName"; - final int mode = BatteryOptimizeUtils.MODE_RESTRICTED; - mFragment.mOptimizationMode = mode; - when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(mode); + final int restrictedMode = BatteryOptimizeUtils.MODE_RESTRICTED; + final int optimizedMode = BatteryOptimizeUtils.MODE_OPTIMIZED; + mFragment.mOptimizationMode = restrictedMode; + when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(restrictedMode); when(mBatteryOptimizeUtils.getPackageName()).thenReturn(packageName); when(mContext.getPackageManager()).thenReturn(mPackageManager); when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(mInstallSourceInfo); when(mInstallSourceInfo.getInitiatingPackageName()).thenReturn("com.android.vending"); - mOptimizePreference.setKey(KEY_PREF_OPTIMIZED); - mFragment.onRadioButtonClicked(mOptimizePreference); + mFragment.onPreferenceChange(mAllowBackgroundUsagePreference, true); + verify(mBatteryOptimizeUtils).setAppUsageState(optimizedMode, Action.APPLY); + when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(optimizedMode); mFragment.onPause(); TimeUnit.SECONDS.sleep(1); verify(mMetricsFeatureProvider) .action( SettingsEnums.OPEN_APP_BATTERY_USAGE, - SettingsEnums.ACTION_APP_BATTERY_USAGE_OPTIMIZED, + SettingsEnums.ACTION_APP_BATTERY_USAGE_ALLOW_BACKGROUND, SettingsEnums.OPEN_APP_BATTERY_USAGE, packageName, /* consumed battery */ 0); @@ -379,15 +359,20 @@ public class AdvancedPowerUsageDetailTest { @Test public void onPause_optimizationModeIsNotChanged_notInvokeLogging() throws PackageManager.NameNotFoundException, InterruptedException { - final int mode = BatteryOptimizeUtils.MODE_OPTIMIZED; - mFragment.mOptimizationMode = mode; - when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(mode); + final int restrictedMode = BatteryOptimizeUtils.MODE_RESTRICTED; + final int optimizedMode = BatteryOptimizeUtils.MODE_OPTIMIZED; + mFragment.mOptimizationMode = restrictedMode; + when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(restrictedMode); when(mContext.getPackageManager()).thenReturn(mPackageManager); when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(mInstallSourceInfo); when(mInstallSourceInfo.getInitiatingPackageName()).thenReturn("com.android.vending"); - mOptimizePreference.setKey(KEY_PREF_OPTIMIZED); - mFragment.onRadioButtonClicked(mOptimizePreference); + mFragment.onPreferenceChange(mAllowBackgroundUsagePreference, true); + verify(mBatteryOptimizeUtils).setAppUsageState(optimizedMode, Action.APPLY); + when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(optimizedMode); + mFragment.onPreferenceChange(mAllowBackgroundUsagePreference, false); + verify(mBatteryOptimizeUtils).setAppUsageState(restrictedMode, Action.APPLY); + when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(restrictedMode); mFragment.onPause(); TimeUnit.SECONDS.sleep(1); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/AllowBackgroundPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/AllowBackgroundPreferenceControllerTest.java new file mode 100644 index 00000000000..be80e1e4060 --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/AllowBackgroundPreferenceControllerTest.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2023 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.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.pm.PackageManager; + +import com.android.settingslib.widget.MainSwitchPreference; + +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 AllowBackgroundPreferenceControllerTest { + private static final int UID = 12345; + private static final String PACKAGE_NAME = "com.android.app"; + + private AllowBackgroundPreferenceController mController; + private MainSwitchPreference mMainSwitchPreference; + private BatteryOptimizeUtils mBatteryOptimizeUtils; + + @Mock private PackageManager mMockPackageManager; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + Context context = spy(RuntimeEnvironment.application); + BatteryUtils.getInstance(context).reset(); + doReturn(UID) + .when(mMockPackageManager) + .getPackageUid(PACKAGE_NAME, PackageManager.GET_META_DATA); + + mController = new AllowBackgroundPreferenceController(context, UID, PACKAGE_NAME); + mMainSwitchPreference = new MainSwitchPreference(RuntimeEnvironment.application); + mBatteryOptimizeUtils = spy(new BatteryOptimizeUtils(context, UID, PACKAGE_NAME)); + mController.mBatteryOptimizeUtils = mBatteryOptimizeUtils; + } + + @Test + public void testUpdateState_isValidPackage_prefEnabled() { + when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); + when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(false); + + mController.updateState(mMainSwitchPreference); + + assertThat(mBatteryOptimizeUtils.isOptimizeModeMutable()).isTrue(); + assertThat(mMainSwitchPreference.isEnabled()).isTrue(); + } + + @Test + public void testUpdateState_invalidPackage_prefDisabled() { + when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(true); + when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(false); + + mController.updateState(mMainSwitchPreference); + + assertThat(mBatteryOptimizeUtils.isOptimizeModeMutable()).isFalse(); + assertThat(mMainSwitchPreference.isEnabled()).isFalse(); + } + + @Test + public void testUpdateState_isSystemOrDefaultAppAndRestrictedStates_prefChecked() { + when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); + when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); + when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn( + BatteryOptimizeUtils.MODE_RESTRICTED); + + mController.updateState(mMainSwitchPreference); + + assertThat(mMainSwitchPreference.isEnabled()).isFalse(); + assertThat(mMainSwitchPreference.isChecked()).isFalse(); + } + + @Test + public void testUpdateState_isSystemOrDefaultApp_prefUnchecked() { + when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); + when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); + when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn( + BatteryOptimizeUtils.MODE_OPTIMIZED); + + mController.updateState(mMainSwitchPreference); + + assertThat(mMainSwitchPreference.isEnabled()).isFalse(); + assertThat(mMainSwitchPreference.isChecked()).isTrue(); + } + + @Test + public void testUpdateState_isRestrictedStates_prefChecked() { + when(mBatteryOptimizeUtils.isOptimizeModeMutable()).thenReturn(true); + when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn( + BatteryOptimizeUtils.MODE_RESTRICTED); + + mController.updateState(mMainSwitchPreference); + + assertThat(mMainSwitchPreference.isEnabled()).isTrue(); + assertThat(mMainSwitchPreference.isChecked()).isFalse(); + } + + @Test + public void testUpdateState_prefUnchecked() { + when(mBatteryOptimizeUtils.isOptimizeModeMutable()).thenReturn(true); + when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn( + BatteryOptimizeUtils.MODE_OPTIMIZED); + + mController.updateState(mMainSwitchPreference); + + assertThat(mMainSwitchPreference.isEnabled()).isTrue(); + assertThat(mMainSwitchPreference.isChecked()).isTrue(); + } + + @Test + public void testHandlePreferenceTreeClick_samePrefKey_verifyAction() { + mMainSwitchPreference.setKey( + AllowBackgroundPreferenceController.KEY_ALLOW_BACKGROUND_USAGE); + mController.handlePreferenceTreeClick(mMainSwitchPreference); + + assertThat(mController.handlePreferenceTreeClick(mMainSwitchPreference)).isTrue(); + } + + @Test + public void testHandlePreferenceTreeClick_incorrectPrefKey_noAction() { + assertThat(mController.handlePreferenceTreeClick(mMainSwitchPreference)).isFalse(); + } +} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBackupHelperTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBackupHelperTest.java index b8c72eeef14..350d2efcd4e 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBackupHelperTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBackupHelperTest.java @@ -121,6 +121,7 @@ public final class BatteryBackupHelperTest { mContext = spy(RuntimeEnvironment.application); mStringWriter = new StringWriter(); mPrintWriter = new PrintWriter(mStringWriter); + BatteryUtils.getInstance(mContext).reset(); doReturn(mContext).when(mContext).getApplicationContext(); doReturn(mAppOpsManager).when(mContext).getSystemService(AppOpsManager.class); doReturn(mUserManager).when(mContext).getSystemService(UserManager.class); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsMigrateCheckerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsMigrateCheckerTest.java index e2058e7c1ab..bab19e56be1 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsMigrateCheckerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsMigrateCheckerTest.java @@ -31,10 +31,8 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.os.UserHandle; -import android.os.UserManager; import com.android.settings.TestUtils; -import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry; import com.android.settings.fuelgauge.batterysaver.BatterySaverScheduleRadioButtonsController; import org.junit.After; @@ -75,6 +73,7 @@ public final class BatterySettingsMigrateCheckerTest { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); + BatteryUtils.getInstance(mContext).reset(); doReturn(mContext).when(mContext).getApplicationContext(); doReturn(mPackageManager).when(mContext).getPackageManager(); doReturn(UID).when(mPackageManager) diff --git a/tests/robotests/src/com/android/settings/fuelgauge/OptimizedPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/OptimizedPreferenceControllerTest.java index 71bb998e4b8..bfed1498113 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/OptimizedPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/OptimizedPreferenceControllerTest.java @@ -18,8 +18,13 @@ package com.android.settings.fuelgauge; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; +import android.content.Context; +import android.content.pm.PackageManager; + import com.android.settingslib.widget.SelectorWithWidgetPreference; import org.junit.Before; @@ -37,34 +42,41 @@ public class OptimizedPreferenceControllerTest { private OptimizedPreferenceController mController; private SelectorWithWidgetPreference mPreference; + private BatteryOptimizeUtils mBatteryOptimizeUtils; - @Mock BatteryOptimizeUtils mockBatteryOptimizeUtils; + @Mock PackageManager mMockPackageManager; @Before - public void setUp() { + public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mController = new OptimizedPreferenceController( - RuntimeEnvironment.application, UID, PACKAGE_NAME); + Context context = spy(RuntimeEnvironment.application); + BatteryUtils.getInstance(context).reset(); + doReturn(UID) + .when(mMockPackageManager) + .getPackageUid(PACKAGE_NAME, PackageManager.GET_META_DATA); + + mController = new OptimizedPreferenceController(context, UID, PACKAGE_NAME); mPreference = new SelectorWithWidgetPreference(RuntimeEnvironment.application); - mController.mBatteryOptimizeUtils = mockBatteryOptimizeUtils; + mBatteryOptimizeUtils = spy(new BatteryOptimizeUtils(context, UID, PACKAGE_NAME)); + mController.mBatteryOptimizeUtils = mBatteryOptimizeUtils; } @Test public void testUpdateState_invalidPackage_prefEnabled() { - when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(true); + when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(true); mController.updateState(mPreference); - assertThat(mPreference.isEnabled()).isTrue(); + assertThat(mPreference.isEnabled()).isFalse(); assertThat(mPreference.isChecked()).isTrue(); } @Test public void testUpdateState_isSystemOrDefaultAppAndOptimizeStates_prefChecked() { - when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mockBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); - when(mockBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn( + when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); + when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); + when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn( BatteryOptimizeUtils.MODE_OPTIMIZED); mController.updateState(mPreference); @@ -74,8 +86,8 @@ public class OptimizedPreferenceControllerTest { @Test public void testUpdateState_isSystemOrDefaultApp_prefUnchecked() { - when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mockBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); + when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); + when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); mController.updateState(mPreference); @@ -85,8 +97,8 @@ public class OptimizedPreferenceControllerTest { @Test public void testUpdateState_isOptimizedStates_prefChecked() { - when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mockBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn( + when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); + when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn( BatteryOptimizeUtils.MODE_OPTIMIZED); mController.updateState(mPreference); @@ -96,7 +108,7 @@ public class OptimizedPreferenceControllerTest { @Test public void testUpdateState_prefUnchecked() { - when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); + when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); mController.updateState(mPreference); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetailTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetailTest.java new file mode 100644 index 00000000000..e6caf787cdc --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetailTest.java @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2023 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.SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS; +import static com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.AppOpsManager; +import android.app.backup.BackupManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.UserHandle; +import android.widget.Switch; + +import androidx.fragment.app.FragmentActivity; +import androidx.loader.app.LoaderManager; + +import com.android.settings.SettingsActivity; +import com.android.settings.fuelgauge.batteryusage.BatteryEntry; +import com.android.settings.testutils.shadow.ShadowActivityManager; +import com.android.settings.testutils.shadow.ShadowEntityHeaderController; +import com.android.settings.widget.EntityHeaderController; +import com.android.settingslib.applications.AppUtils; +import com.android.settingslib.applications.ApplicationsState; +import com.android.settingslib.applications.instantapps.InstantAppDataProvider; +import com.android.settingslib.widget.FooterPreference; +import com.android.settingslib.widget.LayoutPreference; +import com.android.settingslib.widget.MainSwitchPreference; +import com.android.settingslib.widget.SelectorWithWidgetPreference; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.stubbing.Answer; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.util.ReflectionHelpers; + +@RunWith(RobolectricTestRunner.class) +@Config(shadows = { + ShadowEntityHeaderController.class, + ShadowActivityManager.class, + com.android.settings.testutils.shadow.ShadowFragment.class, +}) +public class PowerBackgroundUsageDetailTest { + private static final String APP_LABEL = "app label"; + private static final String SUMMARY = "summary"; + private static final int ICON_ID = 123; + private static final int UID = 1; + private static final String KEY_PREF_UNRESTRICTED = "unrestricted_preference"; + private static final String KEY_PREF_OPTIMIZED = "optimized_preference"; + private static final String KEY_ALLOW_BACKGROUND_USAGE = "allow_background_usage"; + + private Context mContext; + private PowerBackgroundUsageDetail mFragment; + private FooterPreference mFooterPreference; + private MainSwitchPreference mMainSwitchPreference; + private SelectorWithWidgetPreference mOptimizePreference; + private SelectorWithWidgetPreference mUnrestrictedPreference; + private SettingsActivity mTestActivity; + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private FragmentActivity mActivity; + @Mock + private EntityHeaderController mEntityHeaderController; + @Mock + private BatteryOptimizeUtils mBatteryOptimizeUtils; + @Mock + private LayoutPreference mHeaderPreference; + @Mock + private ApplicationsState mState; + @Mock + private Bundle mBundle; + @Mock + private LoaderManager mLoaderManager; + @Mock + private ApplicationsState.AppEntry mAppEntry; + @Mock + private BatteryEntry mBatteryEntry; + @Mock + private BackupManager mBackupManager; + @Mock + private PackageManager mPackageManager; + @Mock + private AppOpsManager mAppOpsManager; + @Mock + private Switch mMockSwitch; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mContext = spy(RuntimeEnvironment.application); + when(mContext.getPackageName()).thenReturn("foo"); + + mFragment = spy(new PowerBackgroundUsageDetail()); + doReturn(mContext).when(mFragment).getContext(); + doReturn(mActivity).when(mFragment).getActivity(); + doReturn(SUMMARY).when(mFragment).getString(anyInt()); + doReturn(APP_LABEL).when(mBundle).getString(nullable(String.class)); + when(mFragment.getArguments()).thenReturn(mBundle); + doReturn(mLoaderManager).when(mFragment).getLoaderManager(); + + ShadowEntityHeaderController.setUseMock(mEntityHeaderController); + doReturn(mEntityHeaderController).when(mEntityHeaderController) + .setButtonActions(anyInt(), anyInt()); + doReturn(mEntityHeaderController).when(mEntityHeaderController) + .setIcon(nullable(Drawable.class)); + doReturn(mEntityHeaderController).when(mEntityHeaderController).setIcon(nullable( + ApplicationsState.AppEntry.class)); + doReturn(mEntityHeaderController).when(mEntityHeaderController) + .setLabel(nullable(String.class)); + doReturn(mEntityHeaderController).when(mEntityHeaderController) + .setLabel(nullable(String.class)); + doReturn(mEntityHeaderController).when(mEntityHeaderController) + .setLabel(nullable(ApplicationsState.AppEntry.class)); + doReturn(mEntityHeaderController).when(mEntityHeaderController) + .setSummary(nullable(String.class)); + + when(mBatteryEntry.getUid()).thenReturn(UID); + when(mBatteryEntry.getLabel()).thenReturn(APP_LABEL); + mBatteryEntry.mIconId = ICON_ID; + + mFragment.mHeaderPreference = mHeaderPreference; + mFragment.mState = mState; + mFragment.mBatteryOptimizeUtils = mBatteryOptimizeUtils; + mFragment.mBackupManager = mBackupManager; + mAppEntry.info = mock(ApplicationInfo.class); + + mTestActivity = spy(new SettingsActivity()); + doReturn(mPackageManager).when(mTestActivity).getPackageManager(); + doReturn(mPackageManager).when(mActivity).getPackageManager(); + doReturn(mAppOpsManager).when(mTestActivity).getSystemService(Context.APP_OPS_SERVICE); + + final ArgumentCaptor captor = ArgumentCaptor.forClass(Intent.class); + + Answer callable = invocation -> { + mBundle = captor.getValue().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS); + System.out.println("mBundle = " + mBundle); + return null; + }; + doAnswer(callable).when(mActivity).startActivityAsUser(captor.capture(), + nullable(UserHandle.class)); + doAnswer(callable).when(mActivity).startActivity(captor.capture()); + + mFooterPreference = spy(new FooterPreference(mContext)); + mMainSwitchPreference = spy(new MainSwitchPreference(mContext)); + mMainSwitchPreference.setKey(KEY_ALLOW_BACKGROUND_USAGE); + mOptimizePreference = spy(new SelectorWithWidgetPreference(mContext)); + mOptimizePreference.setKey(KEY_PREF_OPTIMIZED); + mUnrestrictedPreference = spy(new SelectorWithWidgetPreference(mContext)); + mUnrestrictedPreference.setKey(KEY_PREF_UNRESTRICTED); + mFragment.mFooterPreference = mFooterPreference; + mFragment.mMainSwitchPreference = mMainSwitchPreference; + mFragment.mOptimizePreference = mOptimizePreference; + mFragment.mUnrestrictedPreference = mUnrestrictedPreference; + } + + @After + public void reset() { + ShadowEntityHeaderController.reset(); + } + + @Test + public void initHeader_NoAppEntry_BuildByBundle() { + mFragment.mAppEntry = null; + mFragment.initHeader(); + + verify(mEntityHeaderController).setIcon(nullable(Drawable.class)); + verify(mEntityHeaderController).setLabel(APP_LABEL); + } + + @Test + public void initHeader_HasAppEntry_BuildByAppEntry() { + ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", + new InstantAppDataProvider() { + @Override + public boolean isInstantApp(ApplicationInfo info) { + return false; + } + }); + mFragment.mAppEntry = mAppEntry; + mFragment.initHeader(); + + verify(mEntityHeaderController).setIcon(mAppEntry); + verify(mEntityHeaderController).setLabel(mAppEntry); + verify(mEntityHeaderController).setIsInstantApp(false); + } + + @Test + public void initHeader_HasAppEntry_InstantApp() { + ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", + new InstantAppDataProvider() { + @Override + public boolean isInstantApp(ApplicationInfo info) { + return true; + } + }); + mFragment.mAppEntry = mAppEntry; + mFragment.initHeader(); + + verify(mEntityHeaderController).setIcon(mAppEntry); + verify(mEntityHeaderController).setLabel(mAppEntry); + verify(mEntityHeaderController).setIsInstantApp(true); + } + + @Test + public void initFooter_hasCorrectString() { + when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); + when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(false); + + mFragment.initFooter(); + + assertThat(mFooterPreference.getTitle().toString()) + .isEqualTo("Changing how an app uses your battery can affect its performance."); + } + + @Test + public void onSwitchChanged_fromUnrestrictedModeSetDisabled_becomeRestrictedMode() { + final int restrictedMode = BatteryOptimizeUtils.MODE_RESTRICTED; + final int optimizedMode = BatteryOptimizeUtils.MODE_OPTIMIZED; + mFragment.mOptimizationMode = optimizedMode; + + mFragment.onSwitchChanged(mMockSwitch, /*isChecked=*/ false); + + verify(mOptimizePreference).setEnabled(false); + verify(mUnrestrictedPreference).setEnabled(false); + verify(mFragment).onRadioButtonClicked(null); + verify(mMainSwitchPreference).setChecked(false); + assertThat(mFragment.getSelectedPreference()).isEqualTo(restrictedMode); + verify(mBatteryOptimizeUtils).setAppUsageState(restrictedMode, Action.APPLY); + } + + @Test + public void onSwitchChanged_fromRestrictedModeSetEnabled_becomeOptimizedMode() { + final int restrictedMode = BatteryOptimizeUtils.MODE_RESTRICTED; + final int optimizedMode = BatteryOptimizeUtils.MODE_OPTIMIZED; + mFragment.mOptimizationMode = restrictedMode; + + mFragment.onSwitchChanged(mMockSwitch, /*isChecked=*/ true); + + verify(mOptimizePreference).setEnabled(true); + verify(mUnrestrictedPreference).setEnabled(true); + verify(mFragment).onRadioButtonClicked(mOptimizePreference); + verify(mMainSwitchPreference).setChecked(true); + verify(mOptimizePreference).setChecked(true); + assertThat(mFragment.getSelectedPreference()).isEqualTo(optimizedMode); + verify(mBatteryOptimizeUtils).setAppUsageState(optimizedMode, Action.APPLY); + } +} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/RestrictedPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/RestrictedPreferenceControllerTest.java deleted file mode 100644 index bcddbc2500b..00000000000 --- a/tests/robotests/src/com/android/settings/fuelgauge/RestrictedPreferenceControllerTest.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * 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.when; - -import com.android.settingslib.widget.SelectorWithWidgetPreference; - -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 SelectorWithWidgetPreference mPreference; - - @Mock BatteryOptimizeUtils mockBatteryOptimizeUtils; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - mController = new RestrictedPreferenceController( - RuntimeEnvironment.application, UID, PACKAGE_NAME); - mPreference = new SelectorWithWidgetPreference(RuntimeEnvironment.application); - mController.mBatteryOptimizeUtils = mockBatteryOptimizeUtils; - } - - @Test - public void testUpdateState_isValidPackage_prefEnabled() { - when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - - mController.updateState(mPreference); - - assertThat(mPreference.isEnabled()).isTrue(); - } - - @Test - public void testUpdateState_invalidPackage_prefDisabled() { - when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(true); - - mController.updateState(mPreference); - - assertThat(mPreference.isEnabled()).isFalse(); - } - - @Test - public void testUpdateState_isSystemOrDefaultAppAndRestrictedStates_prefChecked() { - when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mockBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); - when(mockBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn( - BatteryOptimizeUtils.MODE_RESTRICTED); - - mController.updateState(mPreference); - - assertThat(mPreference.isChecked()).isTrue(); - } - - @Test - public void testUpdateState_isSystemOrDefaultApp_prefUnchecked() { - when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mockBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); - - mController.updateState(mPreference); - - assertThat(mPreference.isChecked()).isFalse(); - assertThat(mPreference.isEnabled()).isFalse(); - } - - @Test - public void testUpdateState_isRestrictedStates_prefChecked() { - when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mockBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn( - BatteryOptimizeUtils.MODE_RESTRICTED); - - mController.updateState(mPreference); - - assertThat(mPreference.isChecked()).isTrue(); - } - - @Test - public void testUpdateState_prefUnchecked() { - when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - - mController.updateState(mPreference); - - assertThat(mPreference.isChecked()).isFalse(); - } - - @Test - public void testHandlePreferenceTreeClick_samePrefKey_verifyAction() { - mPreference.setKey(mController.KEY_RESTRICTED_PREF); - mController.handlePreferenceTreeClick(mPreference); - - assertThat(mController.handlePreferenceTreeClick(mPreference)).isTrue(); - } - - @Test - public void testHandlePreferenceTreeClick_incorrectPrefKey_noAction() { - assertThat(mController.handlePreferenceTreeClick(mPreference)).isFalse(); - } -} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/UnrestrictedPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/UnrestrictedPreferenceControllerTest.java index 9bed9bad152..5489196fc0c 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/UnrestrictedPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/UnrestrictedPreferenceControllerTest.java @@ -18,8 +18,13 @@ package com.android.settings.fuelgauge; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; +import android.content.Context; +import android.content.pm.PackageManager; + import com.android.settingslib.widget.SelectorWithWidgetPreference; import org.junit.Before; @@ -37,42 +42,53 @@ public class UnrestrictedPreferenceControllerTest { private UnrestrictedPreferenceController mController; private SelectorWithWidgetPreference mPreference; + private BatteryOptimizeUtils mBatteryOptimizeUtils; - @Mock BatteryOptimizeUtils mockBatteryOptimizeUtils; + @Mock PackageManager mMockPackageManager; @Before - public void setUp() { + public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mController = new UnrestrictedPreferenceController( - RuntimeEnvironment.application, UID, PACKAGE_NAME); + Context context = spy(RuntimeEnvironment.application); + BatteryUtils.getInstance(context).reset(); + doReturn(UID) + .when(mMockPackageManager) + .getPackageUid(PACKAGE_NAME, PackageManager.GET_META_DATA); + + mController = new UnrestrictedPreferenceController(context, UID, PACKAGE_NAME); mPreference = new SelectorWithWidgetPreference(RuntimeEnvironment.application); - mController.mBatteryOptimizeUtils = mockBatteryOptimizeUtils; + mBatteryOptimizeUtils = spy(new BatteryOptimizeUtils(context, UID, PACKAGE_NAME)); + mController.mBatteryOptimizeUtils = mBatteryOptimizeUtils; } @Test public void testUpdateState_isValidPackage_prefEnabled() { - when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); + when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); + when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(false); mController.updateState(mPreference); + assertThat(mBatteryOptimizeUtils.isOptimizeModeMutable()).isTrue(); assertThat(mPreference.isEnabled()).isTrue(); } @Test public void testUpdateState_invalidPackage_prefDisabled() { - when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(true); + when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(true); + when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(false); mController.updateState(mPreference); + assertThat(mBatteryOptimizeUtils.isOptimizeModeMutable()).isFalse(); assertThat(mPreference.isEnabled()).isFalse(); } @Test public void testUpdateState_isSystemOrDefaultAppAndUnrestrictedStates_prefChecked() { - when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mockBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); - when(mockBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn( + when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); + when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); + when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn( BatteryOptimizeUtils.MODE_UNRESTRICTED); mController.updateState(mPreference); @@ -82,32 +98,38 @@ public class UnrestrictedPreferenceControllerTest { @Test public void testUpdateState_isSystemOrDefaultApp_prefUnchecked() { - when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mockBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); + when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); + when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); + when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn( + BatteryOptimizeUtils.MODE_OPTIMIZED); mController.updateState(mPreference); - assertThat(mPreference.isChecked()).isFalse(); assertThat(mPreference.isEnabled()).isFalse(); + assertThat(mPreference.isChecked()).isFalse(); } @Test public void testUpdateState_isUnrestrictedStates_prefChecked() { - when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mockBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn( + when(mBatteryOptimizeUtils.isOptimizeModeMutable()).thenReturn(true); + when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn( BatteryOptimizeUtils.MODE_UNRESTRICTED); mController.updateState(mPreference); + assertThat(mPreference.isEnabled()).isTrue(); assertThat(mPreference.isChecked()).isTrue(); } @Test public void testUpdateState_prefUnchecked() { - when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); + when(mBatteryOptimizeUtils.isOptimizeModeMutable()).thenReturn(true); + when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn( + BatteryOptimizeUtils.MODE_OPTIMIZED); mController.updateState(mPreference); + assertThat(mPreference.isEnabled()).isTrue(); assertThat(mPreference.isChecked()).isFalse(); } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntryTest.java index ae726b75a1f..f43feecc628 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntryTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntryTest.java @@ -82,6 +82,7 @@ public final class BatteryDiffEntryTest { MockitoAnnotations.initMocks(this); ShadowUserHandle.reset(); mContext = spy(RuntimeEnvironment.application); + BatteryUtils.getInstance(mContext).reset(); doReturn(mContext).when(mContext).getApplicationContext(); doReturn(mMockUserManager).when(mContext).getSystemService(UserManager.class); doReturn(mMockPackageManager).when(mContext).getPackageManager(); @@ -461,9 +462,6 @@ public final class BatteryDiffEntryTest { @Test public void testIsUninstalledEntry_uninstalledApp_returnTrue() throws Exception { - doReturn(BatteryUtils.UID_NULL) - .when(mMockPackageManager) - .getPackageUid(PACKAGE_NAME, PackageManager.GET_META_DATA); final ContentValues values = getContentValuesWithType(ConvertUtils.CONSUMER_TYPE_UID_BATTERY); values.put(BatteryHistEntry.KEY_UID, UNINSTALLED_UID);