Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 package org.mozilla.gecko;
8 import android.content.BroadcastReceiver;
9 import android.content.Context;
10 import android.content.Intent;
11 import android.content.IntentFilter;
12 import android.os.BatteryManager;
13 import android.os.Build;
14 import android.os.SystemClock;
15 import android.util.Log;
17 public class GeckoBatteryManager extends BroadcastReceiver {
18 private static final String LOGTAG = "GeckoBatteryManager";
20 // Those constants should be keep in sync with the ones in:
21 // dom/battery/Constants.h
22 private final static double kDefaultLevel = 1.0;
23 private final static boolean kDefaultCharging = true;
24 private final static double kDefaultRemainingTime = 0.0;
25 private final static double kUnknownRemainingTime = -1.0;
27 private static long sLastLevelChange = 0;
28 private static boolean sNotificationsEnabled = false;
29 private static double sLevel = kDefaultLevel;
30 private static boolean sCharging = kDefaultCharging;
31 private static double sRemainingTime = kDefaultRemainingTime;
33 private static GeckoBatteryManager sInstance = new GeckoBatteryManager();
35 private final IntentFilter mFilter;
36 private Context mApplicationContext;
37 private boolean mIsEnabled;
39 public static GeckoBatteryManager getInstance() {
40 return sInstance;
41 }
43 private GeckoBatteryManager() {
44 mFilter = new IntentFilter();
45 mFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
46 }
48 public synchronized void start(final Context context) {
49 if (mIsEnabled) {
50 Log.w(LOGTAG, "Already started!");
51 return;
52 }
54 mApplicationContext = context.getApplicationContext();
55 // registerReceiver will return null if registering fails.
56 if (mApplicationContext.registerReceiver(this, mFilter) == null) {
57 Log.e(LOGTAG, "Registering receiver failed");
58 } else {
59 mIsEnabled = true;
60 }
61 }
63 public synchronized void stop() {
64 if (!mIsEnabled) {
65 Log.w(LOGTAG, "Already stopped!");
66 return;
67 }
69 mApplicationContext.unregisterReceiver(this);
70 mApplicationContext = null;
71 mIsEnabled = false;
72 }
74 @Override
75 public void onReceive(Context context, Intent intent) {
76 if (!intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) {
77 Log.e(LOGTAG, "Got an unexpected intent!");
78 return;
79 }
81 boolean previousCharging = isCharging();
82 double previousLevel = getLevel();
84 // NOTE: it might not be common (in 2012) but technically, Android can run
85 // on a device that has no battery so we want to make sure it's not the case
86 // before bothering checking for battery state.
87 // However, the Galaxy Nexus phone advertizes itself as battery-less which
88 // force us to special-case the logic.
89 // See the Google bug: https://code.google.com/p/android/issues/detail?id=22035
90 if (intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, false) ||
91 Build.MODEL.equals("Galaxy Nexus")) {
92 int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
93 if (plugged == -1) {
94 sCharging = kDefaultCharging;
95 Log.e(LOGTAG, "Failed to get the plugged status!");
96 } else {
97 // Likely, if plugged > 0, it's likely plugged and charging but the doc
98 // isn't clear about that.
99 sCharging = plugged != 0;
100 }
102 if (sCharging != previousCharging) {
103 sRemainingTime = kUnknownRemainingTime;
104 // The new remaining time is going to take some time to show up but
105 // it's the best way to show a not too wrong value.
106 sLastLevelChange = 0;
107 }
109 // We need two doubles because sLevel is a double.
110 double current = (double)intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
111 double max = (double)intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
112 if (current == -1 || max == -1) {
113 Log.e(LOGTAG, "Failed to get battery level!");
114 sLevel = kDefaultLevel;
115 } else {
116 sLevel = current / max;
117 }
119 if (sLevel == 1.0 && sCharging) {
120 sRemainingTime = kDefaultRemainingTime;
121 } else if (sLevel != previousLevel) {
122 // Estimate remaining time.
123 if (sLastLevelChange != 0) {
124 // Use elapsedRealtime() because we want to track time across device sleeps.
125 long currentTime = SystemClock.elapsedRealtime();
126 long dt = (currentTime - sLastLevelChange) / 1000;
127 double dLevel = sLevel - previousLevel;
129 if (sCharging) {
130 if (dLevel < 0) {
131 Log.w(LOGTAG, "When charging, level should increase!");
132 sRemainingTime = kUnknownRemainingTime;
133 } else {
134 sRemainingTime = Math.round(dt / dLevel * (1.0 - sLevel));
135 }
136 } else {
137 if (dLevel > 0) {
138 Log.w(LOGTAG, "When discharging, level should decrease!");
139 sRemainingTime = kUnknownRemainingTime;
140 } else {
141 sRemainingTime = Math.round(dt / -dLevel * sLevel);
142 }
143 }
145 sLastLevelChange = currentTime;
146 } else {
147 // That's the first time we got an update, we can't do anything.
148 sLastLevelChange = SystemClock.elapsedRealtime();
149 }
150 }
151 } else {
152 sLevel = kDefaultLevel;
153 sCharging = kDefaultCharging;
154 sRemainingTime = kDefaultRemainingTime;
155 }
157 /*
158 * We want to inform listeners if the following conditions are fulfilled:
159 * - we have at least one observer;
160 * - the charging state or the level has changed.
161 *
162 * Note: no need to check for a remaining time change given that it's only
163 * updated if there is a level change or a charging change.
164 *
165 * The idea is to prevent doing all the way to the DOM code in the child
166 * process to finally not send an event.
167 */
168 if (sNotificationsEnabled &&
169 (previousCharging != isCharging() || previousLevel != getLevel())) {
170 GeckoAppShell.notifyBatteryChange(getLevel(), isCharging(), getRemainingTime());
171 }
172 }
174 public static boolean isCharging() {
175 return sCharging;
176 }
178 public static double getLevel() {
179 return sLevel;
180 }
182 public static double getRemainingTime() {
183 return sRemainingTime;
184 }
186 public static void enableNotifications() {
187 sNotificationsEnabled = true;
188 }
190 public static void disableNotifications() {
191 sNotificationsEnabled = false;
192 }
194 public static double[] getCurrentInformation() {
195 return new double[] { getLevel(), isCharging() ? 1.0 : 0.0, getRemainingTime() };
196 }
197 }