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