// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package org.chromium.content.browser; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.BatteryManager; import android.os.Build; import android.util.Log; import org.chromium.base.CalledByNative; import org.chromium.base.JNINamespace; import org.chromium.base.VisibleForTesting; /** * Android implementation of the battery status APIs. */ @JNINamespace("content") class BatteryStatusManager { private static final String TAG = "BatteryStatusManager"; // A reference to the application context in order to acquire the SensorService. private final Context mAppContext; private final IntentFilter mFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { BatteryStatusManager.this.onReceive(intent); } }; // Non-zero if and only if we're listening for events. // To avoid race conditions on the C++ side, access must be synchronized. private long mNativePtr; // The lock to access the mNativePtr. private final Object mNativePtrLock = new Object(); private boolean mEnabled = false; protected BatteryStatusManager(Context context) { mAppContext = context.getApplicationContext(); } @CalledByNative static BatteryStatusManager getInstance(Context appContext) { return new BatteryStatusManager(appContext); } /** * Start listening for intents * @return True on success. */ @CalledByNative boolean start(long nativePtr) { synchronized (mNativePtrLock) { if (!mEnabled && mAppContext.registerReceiver(mReceiver, mFilter) != null) { // success mNativePtr = nativePtr; mEnabled = true; } } return mEnabled; } /** * Stop listening to intents. */ @CalledByNative void stop() { synchronized (mNativePtrLock) { if (mEnabled) { mAppContext.unregisterReceiver(mReceiver); mNativePtr = 0; mEnabled = false; } } } @VisibleForTesting void onReceive(Intent intent) { if (!intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) { Log.e(TAG, "Unexpected intent."); return; } boolean present = ignoreBatteryPresentState() ? true : intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, false); int pluggedStatus = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); if (!present || pluggedStatus == -1) { // No battery or no plugged status: return default values. gotBatteryStatus(true, 0, Double.POSITIVE_INFINITY, 1); return; } boolean charging = pluggedStatus != 0; int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1); boolean batteryFull = status == BatteryManager.BATTERY_STATUS_FULL; int current = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); int max = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); double level = (double) current / (double) max; if (level < 0 || level > 1) { // Sanity check, assume default value in this case. level = 1.0; } // Currently Android does not provide charging/discharging time, as a work-around // we could compute it manually based on level delta. // TODO(timvolodine): add proper projection for chargingTime, dischargingTime // (see crbug.com/401553). double chargingTime = (charging & batteryFull) ? 0 : Double.POSITIVE_INFINITY; double dischargingTime = Double.POSITIVE_INFINITY; gotBatteryStatus(charging, chargingTime, dischargingTime, level); } /** * Returns whether the BatteryStatusManager should ignore the battery present state. * It is required for some devices that incorrectly set the EXTRA_PRESENT property. */ protected boolean ignoreBatteryPresentState() { // BatteryManager.EXTRA_PRESENT appears to be unreliable on Galaxy Nexus, // Android 4.2.1, it always reports false. See crbug.com/384348. return Build.MODEL.equals("Galaxy Nexus"); } protected void gotBatteryStatus(boolean charging, double chargingTime, double dischargingTime, double level) { synchronized (mNativePtrLock) { if (mNativePtr != 0) { nativeGotBatteryStatus(mNativePtr, charging, chargingTime, dischargingTime, level); } } } /** * Native JNI call * see content/browser/battery_status/battery_status_manager.cc */ private native void nativeGotBatteryStatus(long nativeBatteryStatusManagerAndroid, boolean charging, double chargingTime, double dischargingTime, double level); }