1/*
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;
24import android.os.Bundle;
25import android.os.Handler;
26import android.os.PowerManager;
27import android.util.Log;
28import com.android.systemui.DemoMode;
29
30import java.io.FileDescriptor;
31import java.io.PrintWriter;
32import java.util.ArrayList;
33
34/**
35 * Default implementation of a {@link BatteryController}. This controller monitors for battery
36 * level change events that are broadcasted by the system.
37 */
38public class BatteryControllerImpl extends BroadcastReceiver implements BatteryController {
39    private static final String TAG = "BatteryController";
40
41    public static final String ACTION_LEVEL_TEST = "com.android.systemui.BATTERY_LEVEL_TEST";
42
43    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
44
45    private final ArrayList<BatteryController.BatteryStateChangeCallback> mChangeCallbacks = new ArrayList<>();
46    private final PowerManager mPowerManager;
47    private final Handler mHandler;
48    private final Context mContext;
49
50    protected int mLevel;
51    protected boolean mPluggedIn;
52    protected boolean mCharging;
53    protected boolean mCharged;
54    protected boolean mPowerSave;
55    private boolean mTestmode = false;
56
57    public BatteryControllerImpl(Context context) {
58        mContext = context;
59        mHandler = new Handler();
60        mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
61
62        registerReceiver();
63        updatePowerSave();
64    }
65
66    private void registerReceiver() {
67        IntentFilter filter = new IntentFilter();
68        filter.addAction(Intent.ACTION_BATTERY_CHANGED);
69        filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
70        filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING);
71        filter.addAction(ACTION_LEVEL_TEST);
72        mContext.registerReceiver(this, filter);
73    }
74
75    @Override
76    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
77        pw.println("BatteryController state:");
78        pw.print("  mLevel="); pw.println(mLevel);
79        pw.print("  mPluggedIn="); pw.println(mPluggedIn);
80        pw.print("  mCharging="); pw.println(mCharging);
81        pw.print("  mCharged="); pw.println(mCharged);
82        pw.print("  mPowerSave="); pw.println(mPowerSave);
83    }
84
85    @Override
86    public void setPowerSaveMode(boolean powerSave) {
87        mPowerManager.setPowerSaveMode(powerSave);
88    }
89
90    @Override
91    public void addStateChangedCallback(BatteryController.BatteryStateChangeCallback cb) {
92        mChangeCallbacks.add(cb);
93        cb.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
94        cb.onPowerSaveChanged(mPowerSave);
95    }
96
97    @Override
98    public void removeStateChangedCallback(BatteryController.BatteryStateChangeCallback cb) {
99        mChangeCallbacks.remove(cb);
100    }
101
102    @Override
103    public void onReceive(final Context context, Intent intent) {
104        final String action = intent.getAction();
105        if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
106            if (mTestmode && !intent.getBooleanExtra("testmode", false)) return;
107            mLevel = (int)(100f
108                    * intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
109                    / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));
110            mPluggedIn = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
111
112            final int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
113                    BatteryManager.BATTERY_STATUS_UNKNOWN);
114            mCharged = status == BatteryManager.BATTERY_STATUS_FULL;
115            mCharging = mCharged || status == BatteryManager.BATTERY_STATUS_CHARGING;
116
117            fireBatteryLevelChanged();
118        } else if (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)) {
119            updatePowerSave();
120        } else if (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING)) {
121            setPowerSave(intent.getBooleanExtra(PowerManager.EXTRA_POWER_SAVE_MODE, false));
122        } else if (action.equals(ACTION_LEVEL_TEST)) {
123            mTestmode = true;
124            mHandler.post(new Runnable() {
125                int curLevel = 0;
126                int incr = 1;
127                int saveLevel = mLevel;
128                boolean savePlugged = mPluggedIn;
129                Intent dummy = new Intent(Intent.ACTION_BATTERY_CHANGED);
130                @Override
131                public void run() {
132                    if (curLevel < 0) {
133                        mTestmode = false;
134                        dummy.putExtra("level", saveLevel);
135                        dummy.putExtra("plugged", savePlugged);
136                        dummy.putExtra("testmode", false);
137                    } else {
138                        dummy.putExtra("level", curLevel);
139                        dummy.putExtra("plugged", incr > 0 ? BatteryManager.BATTERY_PLUGGED_AC
140                                : 0);
141                        dummy.putExtra("testmode", true);
142                    }
143                    context.sendBroadcast(dummy);
144
145                    if (!mTestmode) return;
146
147                    curLevel += incr;
148                    if (curLevel == 100) {
149                        incr *= -1;
150                    }
151                    mHandler.postDelayed(this, 200);
152                }
153            });
154        }
155    }
156
157    @Override
158    public boolean isPowerSave() {
159        return mPowerSave;
160    }
161
162    private void updatePowerSave() {
163        setPowerSave(mPowerManager.isPowerSaveMode());
164    }
165
166    private void setPowerSave(boolean powerSave) {
167        if (powerSave == mPowerSave) return;
168        mPowerSave = powerSave;
169        if (DEBUG) Log.d(TAG, "Power save is " + (mPowerSave ? "on" : "off"));
170        firePowerSaveChanged();
171    }
172
173    protected void fireBatteryLevelChanged() {
174        final int N = mChangeCallbacks.size();
175        for (int i = 0; i < N; i++) {
176            mChangeCallbacks.get(i).onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
177        }
178    }
179
180    private void firePowerSaveChanged() {
181        final int N = mChangeCallbacks.size();
182        for (int i = 0; i < N; i++) {
183            mChangeCallbacks.get(i).onPowerSaveChanged(mPowerSave);
184        }
185    }
186
187    private boolean mDemoMode;
188
189    @Override
190    public void dispatchDemoCommand(String command, Bundle args) {
191        if (!mDemoMode && command.equals(COMMAND_ENTER)) {
192            mDemoMode = true;
193            mContext.unregisterReceiver(this);
194        } else if (mDemoMode && command.equals(COMMAND_EXIT)) {
195            mDemoMode = false;
196            registerReceiver();
197            updatePowerSave();
198        } else if (mDemoMode && command.equals(COMMAND_BATTERY)) {
199           String level = args.getString("level");
200           String plugged = args.getString("plugged");
201           if (level != null) {
202               mLevel = Math.min(Math.max(Integer.parseInt(level), 0), 100);
203           }
204           if (plugged != null) {
205               mPluggedIn = Boolean.parseBoolean(plugged);
206           }
207            fireBatteryLevelChanged();
208        }
209    }
210}
211