blob: b3e85383806990f3b934aa116512e9a7f282eced [file] [log] [blame]
Makoto Onuki66a78122017-11-14 15:03:21 -08001/*
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.batterysaver;
17
18import android.Manifest;
19import android.content.BroadcastReceiver;
20import android.content.Context;
21import android.content.Intent;
22import android.content.IntentFilter;
23import android.hardware.power.V1_0.PowerHint;
24import android.os.Handler;
25import android.os.Looper;
26import android.os.Message;
27import android.os.PowerManager;
28import android.os.PowerManagerInternal.LowPowerModeListener;
29import android.os.PowerSaveState;
30import android.os.UserHandle;
31import android.util.ArrayMap;
32import android.util.Slog;
33import android.widget.Toast;
34
35import com.android.internal.annotations.GuardedBy;
36import com.android.internal.util.Preconditions;
37import com.android.server.power.BatterySaverPolicy;
38import com.android.server.power.BatterySaverPolicy.BatterySaverPolicyListener;
39import com.android.server.power.PowerManagerService;
40
41import java.util.ArrayList;
42
43/**
44 * Responsible for battery saver mode transition logic.
45 */
46public class BatterySaverController implements BatterySaverPolicyListener {
47 static final String TAG = "BatterySaverController";
48
49 static final boolean DEBUG = false; // DO NOT MERGE WITH TRUE
50
51 private final Object mLock = new Object();
52 private final Context mContext;
53 private final MyHandler mHandler;
54 private final FileUpdater mFileUpdater;
55
56 private PowerManager mPowerManager;
57
58 private final BatterySaverPolicy mBatterySaverPolicy;
59
60 @GuardedBy("mLock")
61 private final ArrayList<LowPowerModeListener> mListeners = new ArrayList<>();
62
63 @GuardedBy("mLock")
64 private boolean mEnabled;
65
66 /**
67 * Keep track of the previous enabled state, which we use to decide when to send broadcasts,
68 * which we don't want to send only when the screen state changes.
69 */
70 @GuardedBy("mLock")
71 private boolean mWasEnabled;
72
73 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
74 @Override
75 public void onReceive(Context context, Intent intent) {
76 switch (intent.getAction()) {
77 case Intent.ACTION_SCREEN_ON:
78 case Intent.ACTION_SCREEN_OFF:
79 mHandler.postStateChanged();
80 break;
81 }
82 }
83 };
84
85 /**
86 * Constructor.
87 */
88 public BatterySaverController(Context context, Looper looper, BatterySaverPolicy policy) {
89 mContext = context;
90 mHandler = new MyHandler(looper);
91 mBatterySaverPolicy = policy;
92 mBatterySaverPolicy.addListener(this);
93 mFileUpdater = new FileUpdater(context);
94 }
95
96 /**
97 * Add a listener.
98 */
99 public void addListener(LowPowerModeListener listener) {
100 synchronized (mLock) {
101 mListeners.add(listener);
102 }
103 }
104
105 /**
106 * Called by {@link PowerManagerService} on system ready..
107 */
108 public void systemReady() {
109 final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
110 filter.addAction(Intent.ACTION_SCREEN_OFF);
111 mContext.registerReceiver(mReceiver, filter);
112 }
113
114 private PowerManager getPowerManager() {
115 if (mPowerManager == null) {
116 mPowerManager =
117 Preconditions.checkNotNull(mContext.getSystemService(PowerManager.class));
118 }
119 return mPowerManager;
120 }
121
122 @Override
123 public void onBatterySaverPolicyChanged(BatterySaverPolicy policy) {
124 mHandler.postStateChanged();
125 }
126
127 private class MyHandler extends Handler {
128 private final int MSG_STATE_CHANGED = 1;
129
130 public MyHandler(Looper looper) {
131 super(looper);
132 }
133
134 public void postStateChanged() {
135 obtainMessage(MSG_STATE_CHANGED).sendToTarget();
136 }
137
138 @Override
139 public void dispatchMessage(Message msg) {
140 switch (msg.what) {
141 case MSG_STATE_CHANGED:
142 handleBatterySaverStateChanged();
143 break;
144 }
145 }
146 }
147
148 /**
149 * Called by {@link PowerManagerService} to update the battery saver stete.
150 */
151 public void enableBatterySaver(boolean enable) {
152 synchronized (mLock) {
153 if (mEnabled == enable) {
154 return;
155 }
156 mEnabled = enable;
157
158 mHandler.postStateChanged();
159 }
160 }
161
162 /**
163 * Dispatch power save events to the listeners.
164 *
165 * This is always called on the handler thread.
166 */
167 void handleBatterySaverStateChanged() {
168 final LowPowerModeListener[] listeners;
169
170 final boolean wasEnabled;
171 final boolean enabled;
172 final boolean isScreenOn = getPowerManager().isInteractive();
173 final ArrayMap<String, String> fileValues;
174
175 synchronized (mLock) {
176 Slog.i(TAG, "Battery saver enabled: screen on=" + isScreenOn);
177
178 listeners = mListeners.toArray(new LowPowerModeListener[mListeners.size()]);
179 wasEnabled = mWasEnabled;
180 enabled = mEnabled;
181
182 if (enabled) {
183 fileValues = mBatterySaverPolicy.getFileValues(isScreenOn);
184 } else {
185 fileValues = null;
186 }
187 }
188
189 PowerManagerService.powerHintInternal(PowerHint.LOW_POWER, enabled ? 1 : 0);
190
191 if (enabled) {
192 // STOPSHIP Remove the toast.
193 Toast.makeText(mContext,
194 com.android.internal.R.string.battery_saver_warning,
195 Toast.LENGTH_LONG).show();
196 }
197
198 if (fileValues == null || fileValues.size() == 0) {
199 mFileUpdater.restoreDefault();
200 } else {
201 mFileUpdater.writeFiles(fileValues);
202 }
203
204 if (enabled != wasEnabled) {
205 if (DEBUG) {
206 Slog.i(TAG, "Sending broadcasts for mode: " + enabled);
207 }
208
209 // Send the broadcasts and notify the listeners. We only do this when the battery saver
210 // mode changes, but not when only the screen state changes.
211 Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING)
212 .putExtra(PowerManager.EXTRA_POWER_SAVE_MODE, enabled)
213 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
214 mContext.sendBroadcast(intent);
215
216 intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
217 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
218 mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
219
220 // Send internal version that requires signature permission.
221 intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL);
222 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
223 mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
224 Manifest.permission.DEVICE_POWER);
225
226
227 for (LowPowerModeListener listener : listeners) {
228 final PowerSaveState result =
229 mBatterySaverPolicy.getBatterySaverPolicy(
230 listener.getServiceType(), enabled);
231 listener.onLowPowerModeChanged(result);
232 }
233 }
234
235 synchronized (mLock) {
236 mWasEnabled = enabled;
237 }
238 }
239}