BatteryStatusManager.java revision 116680a4aac90f2aa7413d9095a592090648e557
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
95       if (!present) {
96           // No battery, return default values.
97           gotBatteryStatus(true, 0, Double.POSITIVE_INFINITY, 1);
98           return;
99       }
100
101       int current = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
102       int max = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
103       double level = (double)current / (double)max;
104       if (level < 0 || level > 1) {
105           // Sanity check, assume default value in this case.
106           level = 1.0;
107       }
108
109       int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
110       boolean charging = !(status == BatteryManager.BATTERY_STATUS_DISCHARGING);
111
112       // TODO(timvolodine) : add proper projection for chargingTime, dischargingTime.
113       double chargingTime = (status == BatteryManager.BATTERY_STATUS_FULL) ?
114               0 : Double.POSITIVE_INFINITY;
115       double dischargingTime = Double.POSITIVE_INFINITY;
116
117       gotBatteryStatus(charging, chargingTime, dischargingTime, level);
118    }
119
120    /**
121     * Returns whether the BatteryStatusManager should ignore the battery present state.
122     * It is required for some devices that incorrectly set the EXTRA_PRESENT property.
123     */
124    protected boolean ignoreBatteryPresentState() {
125        // BatteryManager.EXTRA_PRESENT appears to be unreliable on Galaxy Nexus,
126        // Android 4.2.1, it always reports false. See crbug.com/384348.
127        return Build.MODEL.equals("Galaxy Nexus");
128    }
129
130    protected void gotBatteryStatus(boolean charging, double chargingTime,
131            double dischargingTime, double level) {
132        synchronized (mNativePtrLock) {
133            if (mNativePtr != 0) {
134                nativeGotBatteryStatus(mNativePtr, charging, chargingTime, dischargingTime, level);
135            }
136        }
137    }
138
139    /**
140     * Native JNI call
141     * see content/browser/battery_status/battery_status_manager.cc
142     */
143    private native void nativeGotBatteryStatus(long nativeBatteryStatusManagerAndroid,
144            boolean charging, double chargingTime, double dischargingTime, double level);
145}
146