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

Commit 0cd24ade authored by ykhung's avatar ykhung
Browse files

Add allowlist mechanism for battery optimization mode

Add a mechanism to add package name into the allowlist to avoid users
change the battery optimization modes for specific apps in the list

https://screenshot.googleplex.com/8hrHCcTh5bNYXqp

Bug: 281566984
Test: make test RunSettingsRoboTests ROBOTEST_FILTER=com.android.settings.fuelgauge.*
Change-Id: I8efa6a55646d761f5bee3667a59b38ab68c74bc1
parent b71dd78a
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ message BatteryOptimizeHistoricalLogEntry {
    RESET = 3;
    RESTORE = 4;
    BACKUP = 5;
    FORCE_RESET = 6;
  }

  optional string package_name = 1;
+4 −0
Original line number Diff line number Diff line
@@ -526,6 +526,10 @@
        <item>content://com.android.settings.slices/intent/media_output_indicator</item>
    </string-array>

    <!-- List containing the apps cannot be changed the battery optimize modes -->
    <string-array name="config_disable_optimization_mode_apps" translatable="false">
    </string-array>

    <!-- Uri to query non-public Slice Uris. -->
    <string name="config_non_public_slice_query_uri" translatable="false"></string>

+30 −15
Original line number Diff line number Diff line
@@ -91,11 +91,12 @@ public final class BatteryBackupHelper implements BackupHelper {

    @Override
    public void restoreEntity(BackupDataInputStream data) {
        BatterySettingsMigrateChecker.verifyConfiguration(mContext);
        BatterySettingsMigrateChecker.verifySaverConfiguration(mContext);
        if (!isOwner() || data == null || data.size() == 0) {
            Log.w(TAG, "ignore restoreEntity() for non-owner or empty data");
            return;
        }

        if (KEY_OPTIMIZATION_LIST.equals(data.getKey())) {
            final int dataSize = data.size();
            final byte[] dataBytes = new byte[dataSize];
@@ -105,7 +106,10 @@ public final class BatteryBackupHelper implements BackupHelper {
                Log.e(TAG, "failed to load BackupDataInputStream", e);
                return;
            }
            restoreOptimizationMode(dataBytes);
            final int restoreCount = restoreOptimizationMode(dataBytes);
            if (restoreCount > 0) {
                BatterySettingsMigrateChecker.verifyOptimizationModes(mContext);
            }
        }
    }

@@ -175,17 +179,17 @@ public final class BatteryBackupHelper implements BackupHelper {
    }

    @VisibleForTesting
    void restoreOptimizationMode(byte[] dataBytes) {
    int restoreOptimizationMode(byte[] dataBytes) {
        final long timestamp = System.currentTimeMillis();
        final String dataContent = new String(dataBytes, StandardCharsets.UTF_8);
        if (dataContent == null || dataContent.isEmpty()) {
            Log.w(TAG, "no data found in the restoreOptimizationMode()");
            return;
            return 0;
        }
        final String[] appConfigurations = dataContent.split(BatteryBackupHelper.DELIMITER);
        if (appConfigurations == null || appConfigurations.length == 0) {
            Log.w(TAG, "no data found from the split() processing");
            return;
            return 0;
        }
        int restoreCount = 0;
        for (int index = 0; index < appConfigurations.length; index++) {
@@ -217,6 +221,7 @@ public final class BatteryBackupHelper implements BackupHelper {
        }
        Log.d(TAG, String.format("restoreOptimizationMode() count=%d in %d/ms",
                restoreCount, (System.currentTimeMillis() - timestamp)));
        return restoreCount;
    }

    /** Dump the app optimization mode backup history data. */
@@ -225,6 +230,23 @@ public final class BatteryBackupHelper implements BackupHelper {
                getSharedPreferences(context), writer);
    }

    static boolean isOwner() {
        return UserHandle.myUserId() == UserHandle.USER_SYSTEM;
    }

    static BatteryOptimizeUtils newBatteryOptimizeUtils(
            Context context, String packageName, BatteryOptimizeUtils testOptimizeUtils) {
        final int uid = BatteryUtils.getInstance(context).getPackageUid(packageName);
        if (uid == BatteryUtils.UID_NULL) {
            return null;
        }
        final BatteryOptimizeUtils batteryOptimizeUtils =
                testOptimizeUtils != null
                        ? testOptimizeUtils /*testing only*/
                        : new BatteryOptimizeUtils(context, uid, packageName);
        return batteryOptimizeUtils;
    }

    @VisibleForTesting
    static SharedPreferences getSharedPreferences(Context context) {
        return context.getSharedPreferences(
@@ -233,14 +255,11 @@ public final class BatteryBackupHelper implements BackupHelper {

    private void restoreOptimizationMode(
            String packageName, @BatteryOptimizeUtils.OptimizationMode int mode) {
        final int uid = BatteryUtils.getInstance(mContext).getPackageUid(packageName);
        if (uid == BatteryUtils.UID_NULL) {
        final BatteryOptimizeUtils batteryOptimizeUtils =
                newBatteryOptimizeUtils(mContext, packageName, mBatteryOptimizeUtils);
        if (batteryOptimizeUtils == null) {
            return;
        }
        final BatteryOptimizeUtils batteryOptimizeUtils =
                mBatteryOptimizeUtils != null
                        ? mBatteryOptimizeUtils /*testing only*/
                        : new BatteryOptimizeUtils(mContext, uid, packageName);
        batteryOptimizeUtils.setAppUsageState(
                mode, BatteryOptimizeHistoricalLogEntry.Action.RESTORE);
        Log.d(TAG, String.format("restore:%s mode=%d", packageName, mode));
@@ -294,8 +313,4 @@ public final class BatteryBackupHelper implements BackupHelper {
            Log.e(TAG, "writeBackupData() is failed for " + dataKey, e);
        }
    }

    private static boolean isOwner() {
        return UserHandle.myUserId() == UserHandle.USER_SYSTEM;
    }
}
+8 −0
Original line number Diff line number Diff line
@@ -31,11 +31,14 @@ import android.util.Log;

import androidx.annotation.VisibleForTesting;

import com.android.settings.R;
import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action;
import com.android.settingslib.fuelgauge.PowerAllowlistBackend;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.List;

/** A utility class for application usage operation. */
public class BatteryOptimizeUtils {
@@ -214,6 +217,11 @@ public class BatteryOptimizeUtils {
                || powerAllowlistBackend.isDefaultActiveApp(packageName, uid);
    }

    static List<String> getAllowList(Context context) {
        return Arrays.asList(context.getResources().getStringArray(
                R.array.config_disable_optimization_mode_apps));
    }

    private static void setAppUsageStateInternal(
            Context context, @OptimizationMode int mode, int uid, String packageName,
            BatteryUtils batteryUtils, PowerAllowlistBackend powerAllowlistBackend,
+39 −2
Original line number Diff line number Diff line
@@ -23,16 +23,27 @@ import android.content.Intent;
import android.provider.Settings;
import android.util.Log;

import androidx.annotation.VisibleForTesting;

import com.android.settings.R;
import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry;
import com.android.settings.fuelgauge.batterysaver.BatterySaverScheduleRadioButtonsController;
import com.android.settingslib.fuelgauge.BatterySaverUtils;

import java.util.List;

/** Execute battery settings migration tasks in the device booting stage. */
public final class BatterySettingsMigrateChecker extends BroadcastReceiver {
    private static final String TAG = "BatterySettingsMigrateChecker";

    @VisibleForTesting
    static BatteryOptimizeUtils sBatteryOptimizeUtils = null;

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent != null && Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
        if (intent != null
                && Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())
                && BatteryBackupHelper.isOwner()) {
            verifyConfiguration(context);
        }
    }
@@ -40,9 +51,35 @@ public final class BatterySettingsMigrateChecker extends BroadcastReceiver {
    static void verifyConfiguration(Context context) {
        context = context.getApplicationContext();
        verifySaverConfiguration(context);
        verifyOptimizationModes(context);
    }

    /** Avoid users set important apps into the unexpected battery optimize modes */
    static void verifyOptimizationModes(Context context) {
        Log.d(TAG, "invoke verifyOptimizationModes()");
        verifyOptimizationModes(context, BatteryOptimizeUtils.getAllowList(context));
    }

    @VisibleForTesting
    static void verifyOptimizationModes(Context context, List<String> allowList) {
        allowList.forEach(packageName -> {
            final BatteryOptimizeUtils batteryOptimizeUtils =
                    BatteryBackupHelper.newBatteryOptimizeUtils(context, packageName,
                            /* testOptimizeUtils */ sBatteryOptimizeUtils);
            if (batteryOptimizeUtils == null) {
                return;
            }
            if (batteryOptimizeUtils.getAppOptimizationMode() !=
                    BatteryOptimizeUtils.MODE_OPTIMIZED) {
                Log.w(TAG, "Reset optimization mode for: " + packageName);
                batteryOptimizeUtils.setAppUsageState(BatteryOptimizeUtils.MODE_OPTIMIZED,
                        BatteryOptimizeHistoricalLogEntry.Action.FORCE_RESET);
            }
        });
    }

    private static void verifySaverConfiguration(Context context) {
    static void verifySaverConfiguration(Context context) {
        Log.d(TAG, "invoke verifySaverConfiguration()");
        final ContentResolver resolver = context.getContentResolver();
        final int threshold = Settings.Global.getInt(resolver,
                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
Loading