Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5558b79e authored by Jeff Brown's avatar Jeff Brown Committed by Android (Google) Code Review
Browse files

Merge "Fix some synchronization issues in BatteryService." into jb-mr1-dev

parents 6bcd7320 a4d8204e
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -117,4 +117,8 @@ public class BatteryManager {
    public static final int BATTERY_PLUGGED_USB = 2;
    /** Power source is wireless. */
    public static final int BATTERY_PLUGGED_WIRELESS = 4;

    /** @hide */
    public static final int BATTERY_PLUGGED_ANY =
            BATTERY_PLUGGED_AC | BATTERY_PLUGGED_USB | BATTERY_PLUGGED_WIRELESS;
}
+167 −137
Original line number Diff line number Diff line
@@ -40,11 +40,9 @@ import android.util.Slog;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;


/**
@@ -69,12 +67,12 @@ import java.util.Arrays;
 * a degree Centigrade</p>
 * <p>&quot;technology&quot; - String, the type of battery installed, e.g. "Li-ion"</p>
 */
public class BatteryService extends Binder {
public final class BatteryService extends Binder {
    private static final String TAG = BatteryService.class.getSimpleName();

    private static final boolean LOCAL_LOGV = false;
    private static final boolean DEBUG = false;

    static final int BATTERY_SCALE = 100;    // battery capacity is a percentage
    private static final int BATTERY_SCALE = 100;    // battery capacity is a percentage

    // Used locally for determining when to make a last ditch effort to log
    // discharge stats before the device dies.
@@ -92,6 +90,9 @@ public class BatteryService extends Binder {
    private final Context mContext;
    private final IBatteryStats mBatteryStats;

    private final Object mLock = new Object();

    /* Begin native fields: All of these fields are set by native code. */
    private boolean mAcOnline;
    private boolean mUsbOnline;
    private boolean mWirelessOnline;
@@ -103,7 +104,7 @@ public class BatteryService extends Binder {
    private int mBatteryTemperature;
    private String mBatteryTechnology;
    private boolean mBatteryLevelCritical;
    private int mInvalidCharger;
    /* End native fields. */

    private int mLastBatteryStatus;
    private int mLastBatteryHealth;
@@ -112,6 +113,8 @@ public class BatteryService extends Binder {
    private int mLastBatteryVoltage;
    private int mLastBatteryTemperature;
    private boolean mLastBatteryLevelCritical;

    private int mInvalidCharger;
    private int mLastInvalidCharger;

    private int mLowBatteryWarningLevel;
@@ -128,6 +131,8 @@ public class BatteryService extends Binder {

    private boolean mSentLowBatteryBroadcast = false;

    private native void native_update();

    public BatteryService(Context context, LightsService lights) {
        mContext = context;
        mLed = new Led(context, lights);
@@ -146,83 +151,74 @@ public class BatteryService extends Binder {

        // watch for invalid charger messages if the invalid_charger switch exists
        if (new File("/sys/devices/virtual/switch/invalid_charger/state").exists()) {
            mInvalidChargerObserver.startObserving("DEVPATH=/devices/virtual/switch/invalid_charger");
            mInvalidChargerObserver.startObserving(
                    "DEVPATH=/devices/virtual/switch/invalid_charger");
        }

        // set initial status
        update();
        synchronized (mLock) {
            updateLocked();
        }
    }

    public final boolean isPowered() {
        // assume we are powered if battery state is unknown so the "stay on while plugged in" option will work.
        return (mAcOnline || mUsbOnline || mWirelessOnline
                || mBatteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN);
    void systemReady() {
        // check our power situation now that it is safe to display the shutdown dialog.
        synchronized (mLock) {
            shutdownIfNoPowerLocked();
            shutdownIfOverTempLocked();
        }
    }

    /**
     * Returns true if the device is plugged into any of the specified plug types.
     */
    public boolean isPowered(int plugTypeSet) {
        synchronized (mLock) {
            return isPoweredLocked(plugTypeSet);
        }
    }

    public final boolean isPowered(int plugTypeSet) {
    private boolean isPoweredLocked(int plugTypeSet) {
        // assume we are powered if battery state is unknown so
        // the "stay on while plugged in" option will work.
        if (mBatteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {
            return true;
        }
        if (plugTypeSet == 0) {
            return false;
        }
        int plugTypeBit = 0;
        if (mAcOnline) {
            plugTypeBit |= BatteryManager.BATTERY_PLUGGED_AC;
        }
        if (mUsbOnline) {
            plugTypeBit |= BatteryManager.BATTERY_PLUGGED_USB;
        }
        if (mWirelessOnline) {
            plugTypeBit |= BatteryManager.BATTERY_PLUGGED_WIRELESS;
        }
        return (plugTypeSet & plugTypeBit) != 0;
    }

    public final int getPlugType() {
        return mPlugType;
        if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_AC) != 0 && mAcOnline) {
            return true;
        }

    private UEventObserver mPowerSupplyObserver = new UEventObserver() {
        @Override
        public void onUEvent(UEventObserver.UEvent event) {
            update();
        if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_USB) != 0 && mUsbOnline) {
            return true;
        }
    };

    private UEventObserver mInvalidChargerObserver = new UEventObserver() {
        @Override
        public void onUEvent(UEventObserver.UEvent event) {
            int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;
            if (mInvalidCharger != invalidCharger) {
                mInvalidCharger = invalidCharger;
                update();
        if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0 && mWirelessOnline) {
            return true;
        }
        return false;
    }
    };

    // returns battery level as a percentage
    public final int getBatteryLevel() {
    /**
     * Returns battery level as a percentage.
     */
    public int getBatteryLevel() {
        synchronized (mLock) {
            return mBatteryLevel;
        }
    }

    // true if battery level is below the first warning threshold
    public final boolean isBatteryLow() {
    /**
     * Returns true if battery level is below the first warning threshold.
     */
    public boolean isBatteryLow() {
        synchronized (mLock) {
            return mBatteryPresent && mBatteryLevel <= mLowBatteryWarningLevel;
        }

    void systemReady() {
        // check our power situation now that it is safe to display the shutdown dialog.
        shutdownIfNoPower();
        shutdownIfOverTemp();
    }

    private final void shutdownIfNoPower() {
    private void shutdownIfNoPowerLocked() {
        // shut down gracefully if our battery is critically low and we are not powered.
        // wait until the system has booted before attempting to display the shutdown dialog.
        if (mBatteryLevel == 0 && !isPowered() && ActivityManagerNative.isSystemReady()) {
        if (mBatteryLevel == 0 && !isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)
                && ActivityManagerNative.isSystemReady()) {
            Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
            intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -230,7 +226,7 @@ public class BatteryService extends Binder {
        }
    }

    private final void shutdownIfOverTemp() {
    private void shutdownIfOverTempLocked() {
        // shut down gracefully if temperature is too high (> 68.0C by default)
        // wait until the system has booted before attempting to display the
        // shutdown dialog.
@@ -243,18 +239,19 @@ public class BatteryService extends Binder {
        }
    }

    private native void native_update();

    private synchronized final void update() {
    private void updateLocked() {
        // Update the values of mAcOnline, et. all.
        native_update();
        processValues();

        // Process the new values.
        processValuesLocked();
    }

    private void processValues() {
    private void processValuesLocked() {
        boolean logOutlier = false;
        long dischargeDuration = 0;

        mBatteryLevelCritical = mBatteryLevel <= mCriticalBatteryLevel;
        mBatteryLevelCritical = (mBatteryLevel <= mCriticalBatteryLevel);
        if (mAcOnline) {
            mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
        } else if (mUsbOnline) {
@@ -265,6 +262,22 @@ public class BatteryService extends Binder {
            mPlugType = BATTERY_PLUGGED_NONE;
        }

        if (DEBUG) {
            Slog.d(TAG, "Processing new values: "
                    + "mAcOnline=" + mAcOnline
                    + ", mUsbOnline=" + mUsbOnline
                    + ", mWirelessOnline=" + mWirelessOnline
                    + ", mBatteryStatus=" + mBatteryStatus
                    + ", mBatteryHealth=" + mBatteryHealth
                    + ", mBatteryPresent=" + mBatteryPresent
                    + ", mBatteryLevel=" + mBatteryLevel
                    + ", mBatteryTechnology=" + mBatteryTechnology
                    + ", mBatteryVoltage=" + mBatteryVoltage
                    + ", mBatteryTemperature=" + mBatteryTemperature
                    + ", mBatteryLevelCritical=" + mBatteryLevelCritical
                    + ", mPlugType=" + mPlugType);
        }

        // Let the battery stats keep track of the current level.
        try {
            mBatteryStats.setBatteryState(mBatteryStatus, mBatteryHealth,
@@ -274,8 +287,8 @@ public class BatteryService extends Binder {
            // Should never happen.
        }

        shutdownIfNoPower();
        shutdownIfOverTemp();
        shutdownIfNoPowerLocked();
        shutdownIfOverTempLocked();

        if (mBatteryStatus != mLastBatteryStatus ||
                mBatteryHealth != mLastBatteryHealth ||
@@ -342,7 +355,7 @@ public class BatteryService extends Binder {
                    && mBatteryLevel <= mLowBatteryWarningLevel
                    && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);

            sendIntent();
            sendIntentLocked();

            // Separate broadcast is sent for power connected / not connected
            // since the standard intent will not wake any applications and some
@@ -373,7 +386,7 @@ public class BatteryService extends Binder {

            // This needs to be done after sendIntent() so that we get the lastest battery stats.
            if (logOutlier && dischargeDuration != 0) {
                logOutlier(dischargeDuration);
                logOutlierLocked(dischargeDuration);
            }

            mLastBatteryStatus = mBatteryStatus;
@@ -388,13 +401,13 @@ public class BatteryService extends Binder {
        }
    }

    private final void sendIntent() {
    private void sendIntentLocked() {
        //  Pack up the values and broadcast them to everyone
        Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
                | Intent.FLAG_RECEIVER_REPLACE_PENDING);

        int icon = getIcon(mBatteryLevel);
        int icon = getIconLocked(mBatteryLevel);

        intent.putExtra(BatteryManager.EXTRA_STATUS, mBatteryStatus);
        intent.putExtra(BatteryManager.EXTRA_HEALTH, mBatteryHealth);
@@ -408,22 +421,22 @@ public class BatteryService extends Binder {
        intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryTechnology);
        intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);

        if (false) {
            Slog.d(TAG, "level:" + mBatteryLevel +
                    " scale:" + BATTERY_SCALE + " status:" + mBatteryStatus +
                    " health:" + mBatteryHealth +  " present:" + mBatteryPresent +
                    " voltage: " + mBatteryVoltage +
                    " temperature: " + mBatteryTemperature +
                    " technology: " + mBatteryTechnology +
                    " AC powered:" + mAcOnline + " USB powered:" + mUsbOnline +
                    " Wireless powered:" + mWirelessOnline +
                    " icon:" + icon  + " invalid charger:" + mInvalidCharger);
        if (DEBUG) {
            Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED.  level:" + mBatteryLevel +
                    ", scale:" + BATTERY_SCALE + ", status:" + mBatteryStatus +
                    ", health:" + mBatteryHealth +  ", present:" + mBatteryPresent +
                    ", voltage: " + mBatteryVoltage +
                    ", temperature: " + mBatteryTemperature +
                    ", technology: " + mBatteryTechnology +
                    ", AC powered:" + mAcOnline + ", USB powered:" + mUsbOnline +
                    ", Wireless powered:" + mWirelessOnline +
                    ", icon:" + icon  + ", invalid charger:" + mInvalidCharger);
        }

        ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);
    }

    private final void logBatteryStats() {
    private void logBatteryStatsLocked() {
        IBinder batteryInfoService = ServiceManager.getService(BATTERY_STATS_SERVICE_NAME);
        if (batteryInfoService == null) return;

@@ -461,7 +474,7 @@ public class BatteryService extends Binder {
        }
    }

    private final void logOutlier(long duration) {
    private void logOutlierLocked(long duration) {
        ContentResolver cr = mContext.getContentResolver();
        String dischargeThresholdString = Settings.Global.getString(cr,
                Settings.Global.BATTERY_DISCHARGE_THRESHOLD);
@@ -475,11 +488,11 @@ public class BatteryService extends Binder {
                if (duration <= durationThreshold &&
                        mDischargeStartLevel - mBatteryLevel >= dischargeThreshold) {
                    // If the discharge cycle is bad enough we want to know about it.
                    logBatteryStats();
                    logBatteryStatsLocked();
                }
                if (LOCAL_LOGV) Slog.v(TAG, "duration threshold: " + durationThreshold +
                if (DEBUG) Slog.v(TAG, "duration threshold: " + durationThreshold +
                        " discharge threshold: " + dischargeThreshold);
                if (LOCAL_LOGV) Slog.v(TAG, "duration: " + duration + " discharge: " +
                if (DEBUG) Slog.v(TAG, "duration: " + duration + " discharge: " +
                        (mDischargeStartLevel - mBatteryLevel));
            } catch (NumberFormatException e) {
                Slog.e(TAG, "Invalid DischargeThresholds GService string: " +
@@ -489,14 +502,15 @@ public class BatteryService extends Binder {
        }
    }

    private final int getIcon(int level) {
    private int getIconLocked(int level) {
        if (mBatteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) {
            return com.android.internal.R.drawable.stat_sys_battery_charge;
        } else if (mBatteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING) {
            return com.android.internal.R.drawable.stat_sys_battery;
        } else if (mBatteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING
                || mBatteryStatus == BatteryManager.BATTERY_STATUS_FULL) {
            if (isPowered() && mBatteryLevel >= 100) {
            if (isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)
                    && mBatteryLevel >= 100) {
                return com.android.internal.R.drawable.stat_sys_battery_charge;
            } else {
                return com.android.internal.R.drawable.stat_sys_battery;
@@ -517,8 +531,8 @@ public class BatteryService extends Binder {
            return;
        }

        synchronized (mLock) {
            if (args == null || args.length == 0 || "-a".equals(args[0])) {
            synchronized (this) {
                pw.println("Current Battery Service state:");
                pw.println("  AC powered: " + mAcOnline);
                pw.println("  USB powered: " + mUsbOnline);
@@ -531,7 +545,6 @@ public class BatteryService extends Binder {
                pw.println("  voltage:" + mBatteryVoltage);
                pw.println("  temperature: " + mBatteryTemperature);
                pw.println("  technology: " + mBatteryTechnology);
            }
            } else if (false) {
                // DO NOT SUBMIT WITH THIS TURNED ON
                if (args.length == 3 && "set".equals(args[0])) {
@@ -555,7 +568,7 @@ public class BatteryService extends Binder {
                            update = false;
                        }
                        if (update) {
                        processValues();
                            processValuesLocked();
                        }
                    } catch (NumberFormatException ex) {
                        pw.println("Bad value: " + value);
@@ -563,41 +576,58 @@ public class BatteryService extends Binder {
                }
            }
        }
    }

    private final UEventObserver mPowerSupplyObserver = new UEventObserver() {
        @Override
        public void onUEvent(UEventObserver.UEvent event) {
            synchronized (mLock) {
                updateLocked();
            }
        }
    };

    class Led {
        private LightsService mLightsService;
        private LightsService.Light mBatteryLight;
    private final UEventObserver mInvalidChargerObserver = new UEventObserver() {
        @Override
        public void onUEvent(UEventObserver.UEvent event) {
            final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;
            synchronized (mLock) {
                if (mInvalidCharger != invalidCharger) {
                    mInvalidCharger = invalidCharger;
                    updateLocked();
                }
            }
        }
    };

        private int mBatteryLowARGB;
        private int mBatteryMediumARGB;
        private int mBatteryFullARGB;
        private int mBatteryLedOn;
        private int mBatteryLedOff;
    private final class Led {
        private final LightsService.Light mBatteryLight;

        private boolean mBatteryCharging;
        private boolean mBatteryLow;
        private boolean mBatteryFull;
        private final int mBatteryLowARGB;
        private final int mBatteryMediumARGB;
        private final int mBatteryFullARGB;
        private final int mBatteryLedOn;
        private final int mBatteryLedOff;

        Led(Context context, LightsService lights) {
            mLightsService = lights;
        public Led(Context context, LightsService lights) {
            mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY);

            mBatteryLowARGB = mContext.getResources().getInteger(
            mBatteryLowARGB = context.getResources().getInteger(
                    com.android.internal.R.integer.config_notificationsBatteryLowARGB);
            mBatteryMediumARGB = mContext.getResources().getInteger(
            mBatteryMediumARGB = context.getResources().getInteger(
                    com.android.internal.R.integer.config_notificationsBatteryMediumARGB);
            mBatteryFullARGB = mContext.getResources().getInteger(
            mBatteryFullARGB = context.getResources().getInteger(
                    com.android.internal.R.integer.config_notificationsBatteryFullARGB);
            mBatteryLedOn = mContext.getResources().getInteger(
            mBatteryLedOn = context.getResources().getInteger(
                    com.android.internal.R.integer.config_notificationsBatteryLedOn);
            mBatteryLedOff = mContext.getResources().getInteger(
            mBatteryLedOff = context.getResources().getInteger(
                    com.android.internal.R.integer.config_notificationsBatteryLedOff);
        }

        /**
         * Synchronize on BatteryService.
         */
        void updateLightsLocked() {
        public void updateLightsLocked() {
            final int level = mBatteryLevel;
            final int status = mBatteryStatus;
            if (level < mLowBatteryWarningLevel) {
+2 −1
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.Debug;
import android.os.Handler;
import android.os.Message;
@@ -329,7 +330,7 @@ public class Watchdog extends Thread {
     * text of why it is not a good time.
     */
    String shouldWeBeBrutalLocked(long curTime) {
        if (mBattery == null || !mBattery.isPowered()) {
        if (mBattery == null || !mBattery.isPowered(BatteryManager.BATTERY_PLUGGED_ANY)) {
            return "battery";
        }

+6 −1
Original line number Diff line number Diff line
@@ -1014,7 +1014,12 @@ public final class PowerManagerService extends IPowerManager.Stub
    private void updateIsPoweredLocked(int dirty) {
        if ((dirty & DIRTY_BATTERY_STATE) != 0) {
            boolean wasPowered = mIsPowered;
            mIsPowered = mBatteryService.isPowered();
            mIsPowered = mBatteryService.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);

            if (DEBUG) {
                Slog.d(TAG, "updateIsPoweredLocked: wasPowered=" + wasPowered
                        + ", mIsPowered=" + mIsPowered);
            }

            if (wasPowered != mIsPowered) {
                mDirty |= DIRTY_IS_POWERED;