BatterySaverPolicy.java revision 3aaed2912be642d306fa223edcb58278b0e45795
1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.server.power;
17
18import android.content.ContentResolver;
19import android.content.Context;
20import android.database.ContentObserver;
21import android.net.Uri;
22import android.os.Handler;
23import android.os.PowerManager.ServiceType;
24import android.os.PowerSaveState;
25import android.provider.Settings;
26import android.provider.Settings.Global;
27import android.text.TextUtils;
28import android.util.ArrayMap;
29import android.util.KeyValueListParser;
30import android.util.Slog;
31
32import com.android.internal.annotations.GuardedBy;
33import com.android.internal.annotations.VisibleForTesting;
34import com.android.internal.R;
35
36import java.io.PrintWriter;
37import java.util.ArrayList;
38import java.util.List;
39
40/**
41 * Class to decide whether to turn on battery saver mode for specific service
42 *
43 * TODO: We should probably make {@link #mFilesForInteractive} and {@link #mFilesForNoninteractive}
44 * less flexible and just take a list of "CPU number - frequency" pairs. Being able to write
45 * anything under /sys/ and /proc/ is too loose.
46 *
47 * Test: atest BatterySaverPolicyTest
48 */
49public class BatterySaverPolicy extends ContentObserver {
50    private static final String TAG = "BatterySaverPolicy";
51
52    // Value of batterySaverGpsMode such that GPS isn't affected by battery saver mode.
53    public static final int GPS_MODE_NO_CHANGE = 0;
54    // Value of batterySaverGpsMode such that GPS is disabled when battery saver mode
55    // is enabled and the screen is off.
56    public static final int GPS_MODE_DISABLED_WHEN_SCREEN_OFF = 1;
57    // Secure setting for GPS behavior when battery saver mode is on.
58    public static final String SECURE_KEY_GPS_MODE = "batterySaverGpsMode";
59
60    private static final String KEY_GPS_MODE = "gps_mode";
61    private static final String KEY_VIBRATION_DISABLED = "vibration_disabled";
62    private static final String KEY_ANIMATION_DISABLED = "animation_disabled";
63    private static final String KEY_SOUNDTRIGGER_DISABLED = "soundtrigger_disabled";
64    private static final String KEY_FIREWALL_DISABLED = "firewall_disabled";
65    private static final String KEY_ADJUST_BRIGHTNESS_DISABLED = "adjust_brightness_disabled";
66    private static final String KEY_DATASAVER_DISABLED = "datasaver_disabled";
67    private static final String KEY_ADJUST_BRIGHTNESS_FACTOR = "adjust_brightness_factor";
68    private static final String KEY_FULLBACKUP_DEFERRED = "fullbackup_deferred";
69    private static final String KEY_KEYVALUE_DEFERRED = "keyvaluebackup_deferred";
70    private static final String KEY_FORCE_ALL_APPS_STANDBY_JOBS = "force_all_apps_standby_jobs";
71    private static final String KEY_FORCE_ALL_APPS_STANDBY_ALARMS = "force_all_apps_standby_alarms";
72    private static final String KEY_OPTIONAL_SENSORS_DISABLED = "optional_sensors_disabled";
73
74    private static final String KEY_FILE_FOR_INTERACTIVE_PREFIX = "file-on:";
75    private static final String KEY_FILE_FOR_NONINTERACTIVE_PREFIX = "file-off:";
76
77    private static String mSettings;
78    private static String mDeviceSpecificSettings;
79    private static String mDeviceSpecificSettingsSource; // For dump() only.
80
81    /**
82     * {@code true} if vibration is disabled in battery saver mode.
83     *
84     * @see Settings.Global#BATTERY_SAVER_CONSTANTS
85     * @see #KEY_VIBRATION_DISABLED
86     */
87    private boolean mVibrationDisabled;
88
89    /**
90     * {@code true} if animation is disabled in battery saver mode.
91     *
92     * @see Settings.Global#BATTERY_SAVER_CONSTANTS
93     * @see #KEY_ANIMATION_DISABLED
94     */
95    private boolean mAnimationDisabled;
96
97    /**
98     * {@code true} if sound trigger is disabled in battery saver mode
99     * in battery saver mode.
100     *
101     * @see Settings.Global#BATTERY_SAVER_CONSTANTS
102     * @see #KEY_SOUNDTRIGGER_DISABLED
103     */
104    private boolean mSoundTriggerDisabled;
105
106    /**
107     * {@code true} if full backup is deferred in battery saver mode.
108     *
109     * @see Settings.Global#BATTERY_SAVER_CONSTANTS
110     * @see #KEY_FULLBACKUP_DEFERRED
111     */
112    private boolean mFullBackupDeferred;
113
114    /**
115     * {@code true} if key value backup is deferred in battery saver mode.
116     *
117     * @see Settings.Global#BATTERY_SAVER_CONSTANTS
118     * @see #KEY_KEYVALUE_DEFERRED
119     */
120    private boolean mKeyValueBackupDeferred;
121
122    /**
123     * {@code true} if network policy firewall is disabled in battery saver mode.
124     *
125     * @see Settings.Global#BATTERY_SAVER_CONSTANTS
126     * @see #KEY_FIREWALL_DISABLED
127     */
128    private boolean mFireWallDisabled;
129
130    /**
131     * {@code true} if adjust brightness is disabled in battery saver mode.
132     *
133     * @see Settings.Global#BATTERY_SAVER_CONSTANTS
134     * @see #KEY_ADJUST_BRIGHTNESS_DISABLED
135     */
136    private boolean mAdjustBrightnessDisabled;
137
138    /**
139     * {@code true} if data saver is disabled in battery saver mode.
140     *
141     * @see Settings.Global#BATTERY_SAVER_CONSTANTS
142     * @see #KEY_DATASAVER_DISABLED
143     */
144    private boolean mDataSaverDisabled;
145
146    /**
147     * This is the flag to decide the gps mode in battery saver mode.
148     *
149     * @see Settings.Global#BATTERY_SAVER_CONSTANTS
150     * @see #KEY_GPS_MODE
151     */
152    private int mGpsMode;
153
154    /**
155     * This is the flag to decide the how much to adjust the screen brightness. This is
156     * the float value from 0 to 1 where 1 means don't change brightness.
157     *
158     * @see Settings.Global#BATTERY_SAVER_CONSTANTS
159     * @see #KEY_ADJUST_BRIGHTNESS_FACTOR
160     */
161    private float mAdjustBrightnessFactor;
162
163    /**
164     * Whether to put all apps in the stand-by mode or not for job scheduler.
165     */
166    private boolean mForceAllAppsStandbyJobs;
167
168    /**
169     * Whether to put all apps in the stand-by mode or not for alarms.
170     */
171    private boolean mForceAllAppsStandbyAlarms;
172
173    /**
174     * Weather to show non-essential sensors (e.g. edge sensors) or not.
175     */
176    private boolean mOptionalSensorsDisabled;
177
178    private final Object mLock = new Object();
179
180    @GuardedBy("mLock")
181    private Context mContext;
182
183    @GuardedBy("mLock")
184    private ContentResolver mContentResolver;
185
186    @GuardedBy("mLock")
187    private final List<BatterySaverPolicyListener> mListeners = new ArrayList<>();
188
189    /**
190     * List of [Filename -> content] that should be written when battery saver is activated
191     * and the device is interactive.
192     *
193     * We use this to change the max CPU frequencies.
194     */
195    @GuardedBy("mLock")
196    private ArrayMap<String, String> mFilesForInteractive;
197
198    /**
199     * List of [Filename -> content] that should be written when battery saver is activated
200     * and the device is non-interactive.
201     *
202     * We use this to change the max CPU frequencies.
203     */
204    @GuardedBy("mLock")
205    private ArrayMap<String, String> mFilesForNoninteractive;
206
207    public interface BatterySaverPolicyListener {
208        void onBatterySaverPolicyChanged(BatterySaverPolicy policy);
209    }
210
211    public BatterySaverPolicy(Handler handler) {
212        super(handler);
213    }
214
215    public void systemReady(Context context) {
216        synchronized (mLock) {
217            mContext = context;
218            mContentResolver = context.getContentResolver();
219
220            mContentResolver.registerContentObserver(Settings.Global.getUriFor(
221                    Settings.Global.BATTERY_SAVER_CONSTANTS), false, this);
222            mContentResolver.registerContentObserver(Settings.Global.getUriFor(
223                    Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS), false, this);
224        }
225        onChange(true, null);
226    }
227
228    public void addListener(BatterySaverPolicyListener listener) {
229        synchronized (mLock) {
230            mListeners.add(listener);
231        }
232    }
233
234    @VisibleForTesting
235    String getGlobalSetting(String key) {
236        return Settings.Global.getString(mContentResolver, key);
237    }
238
239    @VisibleForTesting
240    int getDeviceSpecificConfigResId() {
241        return R.string.config_batterySaverDeviceSpecificConfig;
242    }
243
244    @Override
245    public void onChange(boolean selfChange, Uri uri) {
246        final BatterySaverPolicyListener[] listeners;
247        synchronized (mLock) {
248            // Load the non-device-specific setting.
249            final String setting = getGlobalSetting(Settings.Global.BATTERY_SAVER_CONSTANTS);
250
251            // Load the device specific setting.
252            // We first check the global setting, and if it's empty or the string "null" is set,
253            // use the default value from config.xml.
254            String deviceSpecificSetting = getGlobalSetting(
255                    Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS);
256            mDeviceSpecificSettingsSource =
257                    Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS;
258
259            if (TextUtils.isEmpty(deviceSpecificSetting) || "null".equals(deviceSpecificSetting)) {
260                deviceSpecificSetting =
261                        mContext.getString(getDeviceSpecificConfigResId());
262                mDeviceSpecificSettingsSource = "(overlay)";
263            }
264
265            // Update.
266            updateConstantsLocked(setting, deviceSpecificSetting);
267
268            listeners = mListeners.toArray(new BatterySaverPolicyListener[mListeners.size()]);
269        }
270
271        // Notify the listeners.
272        for (BatterySaverPolicyListener listener : listeners) {
273            listener.onBatterySaverPolicyChanged(this);
274        }
275    }
276
277    @VisibleForTesting
278    void updateConstantsLocked(final String setting, final String deviceSpecificSetting) {
279        mSettings = setting;
280        mDeviceSpecificSettings = deviceSpecificSetting;
281
282        final KeyValueListParser parser = new KeyValueListParser(',');
283
284        // Non-device-specific parameters.
285        try {
286            parser.setString(setting);
287        } catch (IllegalArgumentException e) {
288            Slog.wtf(TAG, "Bad battery saver constants: " + setting);
289        }
290
291        mVibrationDisabled = parser.getBoolean(KEY_VIBRATION_DISABLED, true);
292        mAnimationDisabled = parser.getBoolean(KEY_ANIMATION_DISABLED, true);
293        mSoundTriggerDisabled = parser.getBoolean(KEY_SOUNDTRIGGER_DISABLED, true);
294        mFullBackupDeferred = parser.getBoolean(KEY_FULLBACKUP_DEFERRED, true);
295        mKeyValueBackupDeferred = parser.getBoolean(KEY_KEYVALUE_DEFERRED, true);
296        mFireWallDisabled = parser.getBoolean(KEY_FIREWALL_DISABLED, false);
297        mAdjustBrightnessDisabled = parser.getBoolean(KEY_ADJUST_BRIGHTNESS_DISABLED, false);
298        mAdjustBrightnessFactor = parser.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR, 0.5f);
299        mDataSaverDisabled = parser.getBoolean(KEY_DATASAVER_DISABLED, true);
300        mForceAllAppsStandbyJobs = parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY_JOBS, true);
301        mForceAllAppsStandbyAlarms =
302                parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY_ALARMS, true);
303        mOptionalSensorsDisabled = parser.getBoolean(KEY_OPTIONAL_SENSORS_DISABLED, true);
304
305        // Get default value from Settings.Secure
306        final int defaultGpsMode = Settings.Secure.getInt(mContentResolver, SECURE_KEY_GPS_MODE,
307                GPS_MODE_NO_CHANGE);
308        mGpsMode = parser.getInt(KEY_GPS_MODE, defaultGpsMode);
309
310        // Non-device-specific parameters.
311        try {
312            parser.setString(deviceSpecificSetting);
313        } catch (IllegalArgumentException e) {
314            Slog.wtf(TAG, "Bad device specific battery saver constants: "
315                    + deviceSpecificSetting);
316        }
317
318        mFilesForInteractive = collectParams(parser, KEY_FILE_FOR_INTERACTIVE_PREFIX);
319        mFilesForNoninteractive = collectParams(parser, KEY_FILE_FOR_NONINTERACTIVE_PREFIX);
320    }
321
322    private static ArrayMap<String, String> collectParams(
323            KeyValueListParser parser, String prefix) {
324        final ArrayMap<String, String> ret = new ArrayMap<>();
325
326        for (int i = parser.size() - 1; i >= 0; i--) {
327            final String key = parser.keyAt(i);
328            if (!key.startsWith(prefix)) {
329                continue;
330            }
331            final String path = key.substring(prefix.length());
332
333            if (!(path.startsWith("/sys/") || path.startsWith("/proc/"))) {
334                Slog.wtf(TAG, "Invalid path: " + path);
335                continue;
336            }
337
338            ret.put(path, parser.getString(key, ""));
339        }
340        return ret;
341    }
342
343    /**
344     * Get the {@link PowerSaveState} based on {@paramref type} and {@paramref realMode}.
345     * The result will have {@link PowerSaveState#batterySaverEnabled} and some other
346     * parameters when necessary.
347     *
348     * @param type     type of the service, one of {@link ServiceType}
349     * @param realMode whether the battery saver is on by default
350     * @return State data that contains battery saver data
351     */
352    public PowerSaveState getBatterySaverPolicy(@ServiceType int type, boolean realMode) {
353        synchronized (mLock) {
354            final PowerSaveState.Builder builder = new PowerSaveState.Builder()
355                    .setGlobalBatterySaverEnabled(realMode);
356            if (!realMode) {
357                return builder.setBatterySaverEnabled(realMode)
358                        .build();
359            }
360            switch (type) {
361                case ServiceType.GPS:
362                    return builder.setBatterySaverEnabled(realMode)
363                            .setGpsMode(mGpsMode)
364                            .build();
365                case ServiceType.ANIMATION:
366                    return builder.setBatterySaverEnabled(mAnimationDisabled)
367                            .build();
368                case ServiceType.FULL_BACKUP:
369                    return builder.setBatterySaverEnabled(mFullBackupDeferred)
370                            .build();
371                case ServiceType.KEYVALUE_BACKUP:
372                    return builder.setBatterySaverEnabled(mKeyValueBackupDeferred)
373                            .build();
374                case ServiceType.NETWORK_FIREWALL:
375                    return builder.setBatterySaverEnabled(!mFireWallDisabled)
376                            .build();
377                case ServiceType.SCREEN_BRIGHTNESS:
378                    return builder.setBatterySaverEnabled(!mAdjustBrightnessDisabled)
379                            .setBrightnessFactor(mAdjustBrightnessFactor)
380                            .build();
381                case ServiceType.DATA_SAVER:
382                    return builder.setBatterySaverEnabled(!mDataSaverDisabled)
383                            .build();
384                case ServiceType.SOUND:
385                    return builder.setBatterySaverEnabled(mSoundTriggerDisabled)
386                            .build();
387                case ServiceType.VIBRATION:
388                    return builder.setBatterySaverEnabled(mVibrationDisabled)
389                            .build();
390                case ServiceType.FORCE_ALL_APPS_STANDBY_JOBS:
391                    return builder.setBatterySaverEnabled(mForceAllAppsStandbyJobs)
392                            .build();
393                case ServiceType.FORCE_ALL_APPS_STANDBY_ALARMS:
394                    return builder.setBatterySaverEnabled(mForceAllAppsStandbyAlarms)
395                            .build();
396                case ServiceType.OPTIONAL_SENSORS:
397                    return builder.setBatterySaverEnabled(mOptionalSensorsDisabled)
398                            .build();
399                default:
400                    return builder.setBatterySaverEnabled(realMode)
401                            .build();
402            }
403        }
404    }
405
406    public ArrayMap<String, String> getFileValues(boolean interactive) {
407        synchronized (mLock) {
408            return interactive ? mFilesForInteractive : mFilesForNoninteractive;
409        }
410    }
411
412    public void dump(PrintWriter pw) {
413        synchronized (mLock) {
414            pw.println();
415            pw.println("Battery saver policy");
416            pw.println("  Settings " + Settings.Global.BATTERY_SAVER_CONSTANTS);
417            pw.println("  value: " + mSettings);
418            pw.println("  Settings " + mDeviceSpecificSettingsSource);
419            pw.println("  value: " + mDeviceSpecificSettings);
420
421            pw.println();
422            pw.println("  " + KEY_VIBRATION_DISABLED + "=" + mVibrationDisabled);
423            pw.println("  " + KEY_ANIMATION_DISABLED + "=" + mAnimationDisabled);
424            pw.println("  " + KEY_FULLBACKUP_DEFERRED + "=" + mFullBackupDeferred);
425            pw.println("  " + KEY_KEYVALUE_DEFERRED + "=" + mKeyValueBackupDeferred);
426            pw.println("  " + KEY_FIREWALL_DISABLED + "=" + mFireWallDisabled);
427            pw.println("  " + KEY_DATASAVER_DISABLED + "=" + mDataSaverDisabled);
428            pw.println("  " + KEY_ADJUST_BRIGHTNESS_DISABLED + "=" + mAdjustBrightnessDisabled);
429            pw.println("  " + KEY_ADJUST_BRIGHTNESS_FACTOR + "=" + mAdjustBrightnessFactor);
430            pw.println("  " + KEY_GPS_MODE + "=" + mGpsMode);
431            pw.println("  " + KEY_FORCE_ALL_APPS_STANDBY_JOBS + "=" + mForceAllAppsStandbyJobs);
432            pw.println("  " + KEY_FORCE_ALL_APPS_STANDBY_ALARMS + "=" + mForceAllAppsStandbyAlarms);
433            pw.println("  " + KEY_OPTIONAL_SENSORS_DISABLED + "=" + mOptionalSensorsDisabled);
434            pw.println();
435
436            pw.print("  Interactive File values:\n");
437            dumpMap(pw, "    ", mFilesForInteractive);
438            pw.println();
439
440            pw.print("  Noninteractive File values:\n");
441            dumpMap(pw, "    ", mFilesForNoninteractive);
442            pw.println();
443        }
444    }
445
446    private void dumpMap(PrintWriter pw, String prefix, ArrayMap<String, String> map) {
447        if (map == null) {
448            return;
449        }
450        final int size = map.size();
451        for (int i = 0; i < size; i++) {
452            pw.print(prefix);
453            pw.print(map.keyAt(i));
454            pw.print(": '");
455            pw.print(map.valueAt(i));
456            pw.println("'");
457        }
458    }
459}
460