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 androidx.work.impl.constraints.trackers;
17
18import android.content.Context;
19import android.content.Intent;
20import android.content.IntentFilter;
21import android.os.BatteryManager;
22import android.support.annotation.NonNull;
23import android.support.annotation.RestrictTo;
24import android.util.Log;
25
26/**
27 * Tracks whether or not the device's battery level is low.
28 * @hide
29 */
30@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
31public class BatteryNotLowTracker extends BroadcastReceiverConstraintTracker<Boolean> {
32
33    private static final String TAG = "BatteryNotLowTracker";
34
35    /**
36     * {@see https://android.googlesource.com/platform/frameworks/base/+/oreo-release/services/core/java/com/android/server/BatteryService.java#111}
37     */
38    static final int BATTERY_PLUGGED_NONE = 0;
39
40    /**
41     * {@see https://android.googlesource.com/platform/frameworks/base/+/oreo-release/core/res/res/values/config.xml#986}
42     */
43    static final float BATTERY_LOW_PERCENTAGE = 0.15f;
44
45    /**
46     * Create an instance of {@link BatteryNotLowTracker}.
47     * @param context The application {@link Context}
48     */
49    public BatteryNotLowTracker(Context context) {
50        super(context);
51    }
52
53    /**
54     * Based on BatteryService#shouldSendBatteryLowLocked(), but this ignores the previous plugged
55     * state - cannot guarantee the last plugged state because this isn't always tracking.
56     *
57     * {@see https://android.googlesource.com/platform/frameworks/base/+/oreo-release/services/core/java/com/android/server/BatteryService.java#268}
58     */
59    @Override
60    public Boolean getInitialState() {
61        IntentFilter intentFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
62        Intent intent = mAppContext.registerReceiver(null, intentFilter);
63        if (intent == null) {
64            Log.e(TAG, "getInitialState - null intent received");
65            return null;
66        }
67        int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, BATTERY_PLUGGED_NONE);
68        int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
69        int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
70        int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
71        float batteryPercentage = level / (float) scale;
72
73        return (plugged != BATTERY_PLUGGED_NONE
74                || status == BatteryManager.BATTERY_STATUS_UNKNOWN
75                || batteryPercentage > BATTERY_LOW_PERCENTAGE);
76    }
77
78    @Override
79    public IntentFilter getIntentFilter() {
80        IntentFilter intentFilter = new IntentFilter();
81        intentFilter.addAction(Intent.ACTION_BATTERY_OKAY);
82        intentFilter.addAction(Intent.ACTION_BATTERY_LOW);
83        return intentFilter;
84    }
85
86    @Override
87    public void onBroadcastReceive(Context context, @NonNull Intent intent) {
88        if (intent.getAction() == null) {
89            return;
90        }
91
92        Log.d(TAG, String.format("Received %s", intent.getAction()));
93
94        switch (intent.getAction()) {
95            case Intent.ACTION_BATTERY_OKAY:
96                setState(true);
97                break;
98
99            case Intent.ACTION_BATTERY_LOW:
100                setState(false);
101                break;
102        }
103    }
104}
105