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 5 package org.chromium.content.browser; 6 7 import android.content.BroadcastReceiver; 8 import android.content.Context; 9 import android.content.Intent; 10 import android.content.IntentFilter; 11 import android.os.BatteryManager; 12 import android.os.Build; 13 import android.util.Log; 14 15 import org.chromium.base.CalledByNative; 16 import org.chromium.base.JNINamespace; 17 import org.chromium.base.VisibleForTesting; 18 19 /** 20 * Android implementation of the battery status APIs. 21 */ 22 @JNINamespace("content") 23 class 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 BatteryStatusManager(Context context)45 protected BatteryStatusManager(Context context) { 46 mAppContext = context.getApplicationContext(); 47 } 48 49 @CalledByNative getInstance(Context appContext)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 start(long nativePtr)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 stop()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 onReceive(Intent intent)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 */ ignoreBatteryPresentState()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 gotBatteryStatus(boolean charging, double chargingTime, double dischargingTime, double level)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 */ nativeGotBatteryStatus(long nativeBatteryStatusManagerAndroid, boolean charging, double chargingTime, double dischargingTime, double level)146 private native void nativeGotBatteryStatus(long nativeBatteryStatusManagerAndroid, 147 boolean charging, double chargingTime, double dischargingTime, double level); 148 } 149