BatteryStatusManager.java revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package org.chromium.content.browser;
6
7import android.content.BroadcastReceiver;
8import android.content.Context;
9import android.content.Intent;
10import android.content.IntentFilter;
11import android.os.BatteryManager;
12import android.os.Build;
13import android.util.Log;
14
15import org.chromium.base.CalledByNative;
16import org.chromium.base.JNINamespace;
17import org.chromium.base.VisibleForTesting;
18
19/**
20 * Android implementation of the battery status APIs.
21 */
22@JNINamespace("content")
23class BatteryStatusManager {
24
25    private static final String TAG = "BatteryStatusManager";
26
27    // A reference to the application context in order to acquire the SensorService.
28    private final Context mAppContext;
29    private final IntentFilter mFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
30    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
31        @Override
32        public void onReceive(Context context, Intent intent) {
33            BatteryStatusManager.this.onReceive(intent);
34        }
35    };
36
37    // Non-zero if and only if we're listening for events.
38    // To avoid race conditions on the C++ side, access must be synchronized.
39    private long mNativePtr;
40    // The lock to access the mNativePtr.
41    private final Object mNativePtrLock = new Object();
42
43    private boolean mEnabled = false;
44
45    protected BatteryStatusManager(Context context) {
46        mAppContext = context.getApplicationContext();
47    }
48
49    @CalledByNative
50    static BatteryStatusManager getInstance(Context appContext) {
51        return new BatteryStatusManager(appContext);
52    }
53
54    /**
55     * Start listening for intents
56     * @return True on success.
57     */
58    @CalledByNative
59    boolean start(long nativePtr) {
60        synchronized (mNativePtrLock) {
61            if (!mEnabled && mAppContext.registerReceiver(mReceiver, mFilter) != null) {
62                // success
63                mNativePtr = nativePtr;
64                mEnabled = true;
65            }
66        }
67        return mEnabled;
68    }
69
70    /**
71     * Stop listening to intents.
72     */
73    @CalledByNative
74    void stop() {
75        synchronized (mNativePtrLock) {
76            if (mEnabled) {
77                mAppContext.unregisterReceiver(mReceiver);
78                mNativePtr = 0;
79                mEnabled = false;
80            }
81        }
82    }
83
84    @VisibleForTesting
85    void onReceive(Intent intent) {
86       if (!intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) {
87           Log.e(TAG, "Unexpected intent.");
88           return;
89       }
90
91       boolean present = ignoreBatteryPresentState() ?
92               true : intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, false);
93       int pluggedStatus = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
94
95       if (!present || pluggedStatus == -1) {
96           // No battery or no plugged status: return default values.
97           gotBatteryStatus(true, 0, Double.POSITIVE_INFINITY, 1);
98           return;
99       }
100
101       boolean charging = pluggedStatus != 0;
102       int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
103       boolean batteryFull = status == BatteryManager.BATTERY_STATUS_FULL;
104
105       int current = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
106       int max = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
107       double level = (double) current / (double) max;
108       if (level < 0 || level > 1) {
109           // Sanity check, assume default value in this case.
110           level = 1.0;
111       }
112
113       // Currently Android does not provide charging/discharging time, as a work-around
114       // we could compute it manually based on level delta.
115       // TODO(timvolodine): add proper projection for chargingTime, dischargingTime
116       // (see crbug.com/401553).
117       double chargingTime = (charging & batteryFull) ? 0 : Double.POSITIVE_INFINITY;
118       double dischargingTime = Double.POSITIVE_INFINITY;
119
120       gotBatteryStatus(charging, chargingTime, dischargingTime, level);
121    }
122
123    /**
124     * Returns whether the BatteryStatusManager should ignore the battery present state.
125     * It is required for some devices that incorrectly set the EXTRA_PRESENT property.
126     */
127    protected boolean ignoreBatteryPresentState() {
128        // BatteryManager.EXTRA_PRESENT appears to be unreliable on Galaxy Nexus,
129        // Android 4.2.1, it always reports false. See crbug.com/384348.
130        return Build.MODEL.equals("Galaxy Nexus");
131    }
132
133    protected void gotBatteryStatus(boolean charging, double chargingTime,
134            double dischargingTime, double level) {
135        synchronized (mNativePtrLock) {
136            if (mNativePtr != 0) {
137                nativeGotBatteryStatus(mNativePtr, charging, chargingTime, dischargingTime, level);
138            }
139        }
140    }
141
142    /**
143     * Native JNI call
144     * see content/browser/battery_status/battery_status_manager.cc
145     */
146    private native void nativeGotBatteryStatus(long nativeBatteryStatusManagerAndroid,
147            boolean charging, double chargingTime, double dischargingTime, double level);
148}
149