blob: ddcfbf6f3c311f27f4eb2535ec2fe1296ec3a763 [file] [log] [blame]
Anthony Chenda62fdcd52016-04-06 16:15:14 -07001/*
2 * Copyright (C) 2016 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 */
16
17package com.android.systemui.statusbar.policy;
18
19import android.content.BroadcastReceiver;
20import android.content.Context;
21import android.content.Intent;
22import android.content.IntentFilter;
23import android.os.BatteryManager;
Jason Monk98d7c7a2016-04-12 13:08:31 -040024import android.os.Bundle;
Anthony Chenda62fdcd52016-04-06 16:15:14 -070025import android.os.Handler;
26import android.os.PowerManager;
Lucas Dupin92a62e52018-01-30 17:22:20 -080027import android.os.PowerSaveState;
Anthony Chenda62fdcd52016-04-06 16:15:14 -070028import android.util.Log;
Lucas Dupin92a62e52018-01-30 17:22:20 -080029
30import com.android.internal.annotations.VisibleForTesting;
Makoto Onuki16a0dd22018-03-20 10:40:37 -070031import com.android.settingslib.fuelgauge.BatterySaverUtils;
Evan Laird4bf21df2018-10-22 14:24:32 -040032import com.android.settingslib.utils.PowerUtil;
33import com.android.systemui.Dependency;
34import com.android.systemui.power.EnhancedEstimates;
35import com.android.systemui.power.Estimate;
Anthony Chenda62fdcd52016-04-06 16:15:14 -070036
37import java.io.FileDescriptor;
38import java.io.PrintWriter;
Evan Laird4bf21df2018-10-22 14:24:32 -040039import java.text.NumberFormat;
Anthony Chenda62fdcd52016-04-06 16:15:14 -070040import java.util.ArrayList;
41
42/**
43 * Default implementation of a {@link BatteryController}. This controller monitors for battery
44 * level change events that are broadcasted by the system.
45 */
46public class BatteryControllerImpl extends BroadcastReceiver implements BatteryController {
47 private static final String TAG = "BatteryController";
48
49 public static final String ACTION_LEVEL_TEST = "com.android.systemui.BATTERY_LEVEL_TEST";
50
51 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Evan Laird4bf21df2018-10-22 14:24:32 -040052 private static final int UPDATE_GRANULARITY_MSEC = 1000 * 60;
Anthony Chenda62fdcd52016-04-06 16:15:14 -070053
Evan Laird4bf21df2018-10-22 14:24:32 -040054 private final EnhancedEstimates mEstimates = Dependency.get(EnhancedEstimates.class);
Anthony Chenda62fdcd52016-04-06 16:15:14 -070055 private final ArrayList<BatteryController.BatteryStateChangeCallback> mChangeCallbacks = new ArrayList<>();
56 private final PowerManager mPowerManager;
57 private final Handler mHandler;
Jason Monk98d7c7a2016-04-12 13:08:31 -040058 private final Context mContext;
Anthony Chenda62fdcd52016-04-06 16:15:14 -070059
60 protected int mLevel;
61 protected boolean mPluggedIn;
62 protected boolean mCharging;
63 protected boolean mCharged;
64 protected boolean mPowerSave;
Lucas Dupin92a62e52018-01-30 17:22:20 -080065 protected boolean mAodPowerSave;
Anthony Chenda62fdcd52016-04-06 16:15:14 -070066 private boolean mTestmode = false;
Jason Monk159dfb72016-09-30 09:41:03 -040067 private boolean mHasReceivedBattery = false;
Evan Laird4bf21df2018-10-22 14:24:32 -040068 private Estimate mEstimate;
69 private long mLastEstimateTimestamp = -1;
Anthony Chenda62fdcd52016-04-06 16:15:14 -070070
71 public BatteryControllerImpl(Context context) {
Lucas Dupin92a62e52018-01-30 17:22:20 -080072 this(context, context.getSystemService(PowerManager.class));
73 }
74
75 @VisibleForTesting
76 BatteryControllerImpl(Context context, PowerManager powerManager) {
Jason Monk98d7c7a2016-04-12 13:08:31 -040077 mContext = context;
Anthony Chenda62fdcd52016-04-06 16:15:14 -070078 mHandler = new Handler();
Lucas Dupin92a62e52018-01-30 17:22:20 -080079 mPowerManager = powerManager;
Anthony Chenda62fdcd52016-04-06 16:15:14 -070080
Jason Monk98d7c7a2016-04-12 13:08:31 -040081 registerReceiver();
82 updatePowerSave();
Evan Laird4bf21df2018-10-22 14:24:32 -040083 updateEstimate();
Jason Monk98d7c7a2016-04-12 13:08:31 -040084 }
85
86 private void registerReceiver() {
Anthony Chenda62fdcd52016-04-06 16:15:14 -070087 IntentFilter filter = new IntentFilter();
88 filter.addAction(Intent.ACTION_BATTERY_CHANGED);
89 filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
90 filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING);
91 filter.addAction(ACTION_LEVEL_TEST);
Jason Monk98d7c7a2016-04-12 13:08:31 -040092 mContext.registerReceiver(this, filter);
Anthony Chenda62fdcd52016-04-06 16:15:14 -070093 }
94
95 @Override
96 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
97 pw.println("BatteryController state:");
98 pw.print(" mLevel="); pw.println(mLevel);
99 pw.print(" mPluggedIn="); pw.println(mPluggedIn);
100 pw.print(" mCharging="); pw.println(mCharging);
101 pw.print(" mCharged="); pw.println(mCharged);
102 pw.print(" mPowerSave="); pw.println(mPowerSave);
103 }
104
105 @Override
106 public void setPowerSaveMode(boolean powerSave) {
Makoto Onuki16a0dd22018-03-20 10:40:37 -0700107 BatterySaverUtils.setPowerSaveMode(mContext, powerSave, /*needFirstTimeWarning*/ true);
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700108 }
109
110 @Override
Jason Monk88529052016-11-04 13:29:58 -0400111 public void addCallback(BatteryController.BatteryStateChangeCallback cb) {
Jason Monk324a28f2016-07-12 13:34:12 -0400112 synchronized (mChangeCallbacks) {
113 mChangeCallbacks.add(cb);
114 }
Jason Monk159dfb72016-09-30 09:41:03 -0400115 if (!mHasReceivedBattery) return;
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700116 cb.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
117 cb.onPowerSaveChanged(mPowerSave);
118 }
119
120 @Override
Jason Monk88529052016-11-04 13:29:58 -0400121 public void removeCallback(BatteryController.BatteryStateChangeCallback cb) {
Jason Monk324a28f2016-07-12 13:34:12 -0400122 synchronized (mChangeCallbacks) {
123 mChangeCallbacks.remove(cb);
124 }
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700125 }
126
127 @Override
128 public void onReceive(final Context context, Intent intent) {
129 final String action = intent.getAction();
130 if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
131 if (mTestmode && !intent.getBooleanExtra("testmode", false)) return;
Jason Monk159dfb72016-09-30 09:41:03 -0400132 mHasReceivedBattery = true;
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700133 mLevel = (int)(100f
134 * intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
135 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));
136 mPluggedIn = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
137
138 final int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
139 BatteryManager.BATTERY_STATUS_UNKNOWN);
140 mCharged = status == BatteryManager.BATTERY_STATUS_FULL;
141 mCharging = mCharged || status == BatteryManager.BATTERY_STATUS_CHARGING;
142
143 fireBatteryLevelChanged();
144 } else if (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)) {
145 updatePowerSave();
146 } else if (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING)) {
147 setPowerSave(intent.getBooleanExtra(PowerManager.EXTRA_POWER_SAVE_MODE, false));
148 } else if (action.equals(ACTION_LEVEL_TEST)) {
149 mTestmode = true;
150 mHandler.post(new Runnable() {
151 int curLevel = 0;
152 int incr = 1;
153 int saveLevel = mLevel;
154 boolean savePlugged = mPluggedIn;
155 Intent dummy = new Intent(Intent.ACTION_BATTERY_CHANGED);
156 @Override
157 public void run() {
158 if (curLevel < 0) {
159 mTestmode = false;
160 dummy.putExtra("level", saveLevel);
161 dummy.putExtra("plugged", savePlugged);
162 dummy.putExtra("testmode", false);
163 } else {
164 dummy.putExtra("level", curLevel);
165 dummy.putExtra("plugged", incr > 0 ? BatteryManager.BATTERY_PLUGGED_AC
166 : 0);
167 dummy.putExtra("testmode", true);
168 }
169 context.sendBroadcast(dummy);
170
171 if (!mTestmode) return;
172
173 curLevel += incr;
174 if (curLevel == 100) {
175 incr *= -1;
176 }
177 mHandler.postDelayed(this, 200);
178 }
179 });
180 }
181 }
182
183 @Override
184 public boolean isPowerSave() {
185 return mPowerSave;
186 }
187
Lucas Dupin92a62e52018-01-30 17:22:20 -0800188 @Override
189 public boolean isAodPowerSave() {
190 return mAodPowerSave;
191 }
192
Evan Laird4bf21df2018-10-22 14:24:32 -0400193 @Override
194 public String getEstimatedTimeRemainingString() {
195 if (mEstimate == null
196 || System.currentTimeMillis() > mLastEstimateTimestamp + UPDATE_GRANULARITY_MSEC) {
197 updateEstimate();
198 }
199 // Estimates may not exist yet even if we've checked
200 if (mEstimate == null) {
201 return null;
202 }
203 final String percentage = NumberFormat.getPercentInstance().format((double) mLevel / 100.0);
204 return PowerUtil.getBatteryRemainingShortStringFormatted(
205 mContext, mEstimate.estimateMillis);
206 }
207
208 private void updateEstimate() {
209 mEstimate = mEstimates.getEstimate();
210 mLastEstimateTimestamp = System.currentTimeMillis();
211 }
212
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700213 private void updatePowerSave() {
214 setPowerSave(mPowerManager.isPowerSaveMode());
215 }
216
217 private void setPowerSave(boolean powerSave) {
218 if (powerSave == mPowerSave) return;
219 mPowerSave = powerSave;
Lucas Dupin92a62e52018-01-30 17:22:20 -0800220
221 // AOD power saving setting might be different from PowerManager power saving mode.
222 PowerSaveState state = mPowerManager.getPowerSaveState(PowerManager.ServiceType.AOD);
223 mAodPowerSave = state.batterySaverEnabled;
224
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700225 if (DEBUG) Log.d(TAG, "Power save is " + (mPowerSave ? "on" : "off"));
226 firePowerSaveChanged();
227 }
228
229 protected void fireBatteryLevelChanged() {
Jason Monk324a28f2016-07-12 13:34:12 -0400230 synchronized (mChangeCallbacks) {
231 final int N = mChangeCallbacks.size();
232 for (int i = 0; i < N; i++) {
233 mChangeCallbacks.get(i).onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
234 }
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700235 }
236 }
237
238 private void firePowerSaveChanged() {
Jason Monk324a28f2016-07-12 13:34:12 -0400239 synchronized (mChangeCallbacks) {
240 final int N = mChangeCallbacks.size();
241 for (int i = 0; i < N; i++) {
242 mChangeCallbacks.get(i).onPowerSaveChanged(mPowerSave);
243 }
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700244 }
245 }
Jason Monk98d7c7a2016-04-12 13:08:31 -0400246
247 private boolean mDemoMode;
248
249 @Override
250 public void dispatchDemoCommand(String command, Bundle args) {
251 if (!mDemoMode && command.equals(COMMAND_ENTER)) {
252 mDemoMode = true;
253 mContext.unregisterReceiver(this);
254 } else if (mDemoMode && command.equals(COMMAND_EXIT)) {
255 mDemoMode = false;
256 registerReceiver();
257 updatePowerSave();
258 } else if (mDemoMode && command.equals(COMMAND_BATTERY)) {
Evan Laird706d9682017-05-30 15:03:29 -0400259 String level = args.getString("level");
260 String plugged = args.getString("plugged");
261 String powerSave = args.getString("powersave");
262 if (level != null) {
263 mLevel = Math.min(Math.max(Integer.parseInt(level), 0), 100);
264 }
265 if (plugged != null) {
266 mPluggedIn = Boolean.parseBoolean(plugged);
267 }
268 if (powerSave != null) {
269 mPowerSave = powerSave.equals("true");
270 firePowerSaveChanged();
271 }
Jason Monk98d7c7a2016-04-12 13:08:31 -0400272 fireBatteryLevelChanged();
273 }
274 }
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700275}