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

Commit a786f00f authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Refactor battery saver logic + add "per device" setting"

parents 0620c306 66a7812b
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -9477,6 +9477,16 @@ public final class Settings {
         */
        public static final String BATTERY_SAVER_CONSTANTS = "battery_saver_constants";

        /**
         * Battery Saver device specific settings
         * This is encoded as a key=value list, separated by commas.
         * See {@link com.android.server.power.BatterySaverPolicy} for the details.
         *
         * @hide
         */
        public static final String BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS =
                "battery_saver_device_specific_constants";

        /**
         * Battery anomaly detection specific settings
         * This is encoded as a key=value list, separated by commas.
+14 −0
Original line number Diff line number Diff line
@@ -147,4 +147,18 @@ public class KeyValueListParser {
        }
        return def;
    }

    /**
     * @return the number of keys.
     */
    public int size() {
        return mValues.size();
    }

    /**
     * @return the key at {@code index}. Use with {@link #size()} to enumerate all key-value pairs.
     */
    public String keyAt(int index) {
        return mValues.keyAt(index);
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -3164,4 +3164,6 @@

    <!-- Corner radius of system dialogs -->
    <dimen name="config_dialogCornerRadius">2dp</dimen>

    <string translatable="false" name="config_batterySaverDeviceSpecificConfig"></string>
</resources>
+1 −0
Original line number Diff line number Diff line
@@ -3150,4 +3150,5 @@

  <!-- From media projection -->
  <java-symbol type="string" name="config_mediaProjectionPermissionDialogComponent" />
  <java-symbol type="string" name="config_batterySaverDeviceSpecificConfig" />
</resources>
+219 −57
Original line number Diff line number Diff line
@@ -15,25 +15,31 @@
 */
package com.android.server.power;

import android.annotation.IntDef;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
import android.os.PowerSaveState;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.KeyValueListParser;
import android.util.Slog;
import android.os.PowerSaveState;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.R;

import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;

/**
 * Class to decide whether to turn on battery saver mode for specific service
 *
 * Test: atest BatterySaverPolicyTest
 */
public class BatterySaverPolicy extends ContentObserver {
    private static final String TAG = "BatterySaverPolicy";
@@ -60,7 +66,12 @@ public class BatterySaverPolicy extends ContentObserver {
    private static final String KEY_FORCE_ALL_APPS_STANDBY_ALARMS = "force_all_apps_standby_alarms";
    private static final String KEY_OPTIONAL_SENSORS_DISABLED = "optional_sensors_disabled";

    private final KeyValueListParser mParser = new KeyValueListParser(',');
    private static final String KEY_SCREEN_ON_FILE_PREFIX = "file-on:";
    private static final String KEY_SCREEN_OFF_FILE_PREFIX = "file-off:";

    private static String mSettings;
    private static String mDeviceSpecificSettings;
    private static String mDeviceSpecificSettingsSource; // For dump() only.

    /**
     * {@code true} if vibration is disabled in battery saver mode.
@@ -159,55 +170,174 @@ public class BatterySaverPolicy extends ContentObserver {
     */
    private boolean mOptionalSensorsDisabled;

    private final Object mLock = new Object();

    @GuardedBy("mLock")
    private Context mContext;

    @GuardedBy("mLock")
    private ContentResolver mContentResolver;

    @GuardedBy("mLock")
    private final ArrayList<BatterySaverPolicyListener> mListeners = new ArrayList<>();

    /**
     * List of [Filename -> content] that should be written when battery saver is activated
     * and the screen is on.
     *
     * We use this to change the max CPU frequencies.
     */
    @GuardedBy("mLock")
    private ArrayMap<String, String> mScreenOnFiles;

    /**
     * List of [Filename -> content] that should be written when battery saver is activated
     * and the screen is off.
     *
     * We use this to change the max CPU frequencies.
     */
    @GuardedBy("mLock")
    private ArrayMap<String, String> mScreenOffFiles;

    public interface BatterySaverPolicyListener {
        void onBatterySaverPolicyChanged(BatterySaverPolicy policy);
    }

    public BatterySaverPolicy(Handler handler) {
        super(handler);
    }

    public void start(ContentResolver contentResolver) {
        mContentResolver = contentResolver;
    public void systemReady(Context context) {
        synchronized (mLock) {
            mContext = context;
            mContentResolver = context.getContentResolver();

            mContentResolver.registerContentObserver(Settings.Global.getUriFor(
                    Settings.Global.BATTERY_SAVER_CONSTANTS), false, this);
            mContentResolver.registerContentObserver(Settings.Global.getUriFor(
                    Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS), false, this);
        }
        onChange(true, null);
    }

    public void addListener(BatterySaverPolicyListener listener) {
        synchronized (mLock) {
            mListeners.add(listener);
        }
    }

    @VisibleForTesting
    String getGlobalSetting(String key) {
        return Settings.Global.getString(mContentResolver, key);
    }

    @VisibleForTesting
    int getDeviceSpecificConfigResId() {
        return R.string.config_batterySaverDeviceSpecificConfig;
    }

    @VisibleForTesting
    void onChangeForTest() {
        onChange(true, null);
    }

    @Override
    public void onChange(boolean selfChange, Uri uri) {
        final String value = Settings.Global.getString(mContentResolver,
                Settings.Global.BATTERY_SAVER_CONSTANTS);
        updateConstants(value);
        final BatterySaverPolicyListener[] listeners;
        synchronized (mLock) {
            // Load the non-device-specific setting.
            final String setting = getGlobalSetting(Settings.Global.BATTERY_SAVER_CONSTANTS);

            // Load the device specific setting.
            // We first check the global setting, and if it's empty or the string "null" is set,
            // use the default value from config.xml.
            String deviceSpecificSetting = getGlobalSetting(
                    Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS);
            mDeviceSpecificSettingsSource =
                    Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS;

            if (TextUtils.isEmpty(deviceSpecificSetting) || "null".equals(deviceSpecificSetting)) {
                deviceSpecificSetting =
                        mContext.getString(getDeviceSpecificConfigResId());
                mDeviceSpecificSettingsSource = "(overlay)";
            }

            // Update.
            updateConstantsLocked(setting, deviceSpecificSetting);

            listeners = mListeners.toArray(new BatterySaverPolicyListener[mListeners.size()]);
        }

        // Notify the listeners.
        for (BatterySaverPolicyListener listener : listeners) {
            listener.onBatterySaverPolicyChanged(this);
        }
    }

    @VisibleForTesting
    void updateConstants(final String value) {
        synchronized (BatterySaverPolicy.this) {
    void updateConstantsLocked(final String setting, final String deviceSpecificSetting) {
        mSettings = setting;
        mDeviceSpecificSettings = deviceSpecificSetting;

        final KeyValueListParser parser = new KeyValueListParser(',');

        // Non-device-specific parameters.
        try {
                mParser.setString(value);
            parser.setString(setting);
        } catch (IllegalArgumentException e) {
                Slog.e(TAG, "Bad battery saver constants");
            }

            mVibrationDisabled = mParser.getBoolean(KEY_VIBRATION_DISABLED, true);
            mAnimationDisabled = mParser.getBoolean(KEY_ANIMATION_DISABLED, true);
            mSoundTriggerDisabled = mParser.getBoolean(KEY_SOUNDTRIGGER_DISABLED, true);
            mFullBackupDeferred = mParser.getBoolean(KEY_FULLBACKUP_DEFERRED, true);
            mKeyValueBackupDeferred = mParser.getBoolean(KEY_KEYVALUE_DEFERRED, true);
            mFireWallDisabled = mParser.getBoolean(KEY_FIREWALL_DISABLED, false);
            mAdjustBrightnessDisabled = mParser.getBoolean(KEY_ADJUST_BRIGHTNESS_DISABLED, false);
            mAdjustBrightnessFactor = mParser.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR, 0.5f);
            mDataSaverDisabled = mParser.getBoolean(KEY_DATASAVER_DISABLED, true);
            mForceAllAppsStandbyJobs = mParser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY_JOBS, true);
            Slog.wtf(TAG, "Bad battery saver constants: " + setting);
        }

        mVibrationDisabled = parser.getBoolean(KEY_VIBRATION_DISABLED, true);
        mAnimationDisabled = parser.getBoolean(KEY_ANIMATION_DISABLED, true);
        mSoundTriggerDisabled = parser.getBoolean(KEY_SOUNDTRIGGER_DISABLED, true);
        mFullBackupDeferred = parser.getBoolean(KEY_FULLBACKUP_DEFERRED, true);
        mKeyValueBackupDeferred = parser.getBoolean(KEY_KEYVALUE_DEFERRED, true);
        mFireWallDisabled = parser.getBoolean(KEY_FIREWALL_DISABLED, false);
        mAdjustBrightnessDisabled = parser.getBoolean(KEY_ADJUST_BRIGHTNESS_DISABLED, false);
        mAdjustBrightnessFactor = parser.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR, 0.5f);
        mDataSaverDisabled = parser.getBoolean(KEY_DATASAVER_DISABLED, true);
        mForceAllAppsStandbyJobs = parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY_JOBS, true);
        mForceAllAppsStandbyAlarms =
                    mParser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY_ALARMS, true);
            mOptionalSensorsDisabled = mParser.getBoolean(KEY_OPTIONAL_SENSORS_DISABLED, true);
                parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY_ALARMS, true);
        mOptionalSensorsDisabled = parser.getBoolean(KEY_OPTIONAL_SENSORS_DISABLED, true);

        // Get default value from Settings.Secure
        final int defaultGpsMode = Settings.Secure.getInt(mContentResolver, SECURE_KEY_GPS_MODE,
                GPS_MODE_NO_CHANGE);
            mGpsMode = mParser.getInt(KEY_GPS_MODE, defaultGpsMode);
        mGpsMode = parser.getInt(KEY_GPS_MODE, defaultGpsMode);

        // Non-device-specific parameters.
        try {
            parser.setString(deviceSpecificSetting);
        } catch (IllegalArgumentException e) {
            Slog.wtf(TAG, "Bad device specific battery saver constants: "
                    + deviceSpecificSetting);
        }

        mScreenOnFiles = collectParams(parser, KEY_SCREEN_ON_FILE_PREFIX);
        mScreenOffFiles = collectParams(parser, KEY_SCREEN_OFF_FILE_PREFIX);
    }

    private static ArrayMap<String, String> collectParams(
            KeyValueListParser parser, String prefix) {
        final ArrayMap<String, String> ret = new ArrayMap<>();

        for (int i = parser.size() - 1; i >= 0; i--) {
            final String key = parser.keyAt(i);
            if (!key.startsWith(prefix)) {
                continue;
            }
            final String path = key.substring(prefix.length());

            if (!(path.startsWith("/sys/") || path.startsWith("/proc"))) {
                Slog.wtf(TAG, "Invalid path: " + path);
                continue;
            }

            ret.put(path, parser.getString(key, ""));
        }
        return ret;
    }

    /**
@@ -220,7 +350,7 @@ public class BatterySaverPolicy extends ContentObserver {
     * @return State data that contains battery saver data
     */
    public PowerSaveState getBatterySaverPolicy(@ServiceType int type, boolean realMode) {
        synchronized (BatterySaverPolicy.this) {
        synchronized (mLock) {
            final PowerSaveState.Builder builder = new PowerSaveState.Builder()
                    .setGlobalBatterySaverEnabled(realMode);
            if (!realMode) {
@@ -273,12 +403,20 @@ public class BatterySaverPolicy extends ContentObserver {
        }
    }

    public ArrayMap<String, String> getFileValues(boolean screenOn) {
        synchronized (mLock) {
            return screenOn ? mScreenOnFiles : mScreenOffFiles;
        }
    }

    public void dump(PrintWriter pw) {
        synchronized (mLock) {
            pw.println();
            pw.println("Battery saver policy");
            pw.println("  Settings " + Settings.Global.BATTERY_SAVER_CONSTANTS);
        pw.println("  value: " + Settings.Global.getString(mContentResolver,
                Settings.Global.BATTERY_SAVER_CONSTANTS));
            pw.println("  value: " + mSettings);
            pw.println("  Settings " + mDeviceSpecificSettingsSource);
            pw.println("  value: " + mDeviceSpecificSettings);

            pw.println();
            pw.println("  " + KEY_VIBRATION_DISABLED + "=" + mVibrationDisabled);
@@ -293,5 +431,29 @@ public class BatterySaverPolicy extends ContentObserver {
            pw.println("  " + KEY_FORCE_ALL_APPS_STANDBY_JOBS + "=" + mForceAllAppsStandbyJobs);
            pw.println("  " + KEY_FORCE_ALL_APPS_STANDBY_ALARMS + "=" + mForceAllAppsStandbyAlarms);
            pw.println("  " + KEY_OPTIONAL_SENSORS_DISABLED + "=" + mOptionalSensorsDisabled);
            pw.println();

            pw.print("  Screen On Files:\n");
            dumpMap(pw, "    ", mScreenOnFiles);
            pw.println();

            pw.print("  Screen Off Files:\n");
            dumpMap(pw, "    ", mScreenOffFiles);
            pw.println();
        }
    }

    private void dumpMap(PrintWriter pw, String prefix, ArrayMap<String, String> map) {
        if (map == null) {
            return;
        }
        final int size = map.size();
        for (int i = 0; i < size; i++) {
            pw.print(prefix);
            pw.print(map.keyAt(i));
            pw.print(": '");
            pw.print(map.valueAt(i));
            pw.println("'");
        }
    }
}
Loading