美文网首页SystemUI
SystemUI之PowerUI启动流程

SystemUI之PowerUI启动流程

作者: Monster_de47 | 来源:发表于2018-08-14 19:19 被阅读314次

    PowerUI是SystemUI控制电量提醒的模块,包括低电提醒和高低温关机提醒。

    初始化

    PowerUI继承SystemUI,故在SystemUI启动时,PowerUI也会随之调用start(),其代码如下:

    public void start() {
            // 获取PowerManager
            mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
            // 获取HardwarePorpertiesManger 以便获取当前手机温度
            mHardwarePropertiesManager = (HardwarePropertiesManager)
                    mContext.getSystemService(Context.HARDWARE_PROPERTIES_SERVICE);
            mScreenOffTime = mPowerManager.isScreenOn() ? -1 : SystemClock.elapsedRealtime();
            // 获取WarningsUI实例
            mWarnings = Dependency.get(WarningsUI.class);
            mLastConfiguration.setTo(mContext.getResources().getConfiguration());
            // 注册低电系统阈值变化观察者
            ContentObserver obs = new ContentObserver(mHandler) {
                @Override
                public void onChange(boolean selfChange) {
                    updateBatteryWarningLevels();
                }
            };
            final ContentResolver resolver = mContext.getContentResolver();
            resolver.registerContentObserver(Settings.Global.getUriFor(
                    Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
                    false, obs, UserHandle.USER_ALL);
            // 初始化低电警告阈值
            updateBatteryWarningLevels();
            // 初始化电量相关信息广播接收者
            mReceiver.init();
    
            // Check to see if we need to let the user know that the phone previously shut down due
            // to the temperature being too high.
            showThermalShutdownDialog();
            // 初始化温度警告阈值
            initTemperatureWarning();
        }
    

    从上面不难看出,PowerUI两个功能:
    一、监听电量变化,发出低电警告;
    二、监听电池温度变化,发出温度警告。

    低电警告

    在start()时,会进行updateBatteryWarningLevels(),其代码如下:

    void updateBatteryWarningLevels() {
            int critLevel = mContext.getResources().getInteger(
                    com.android.internal.R.integer.config_criticalBatteryWarningLevel);
    
            final ContentResolver resolver = mContext.getContentResolver();
            int defWarnLevel = mContext.getResources().getInteger(
                    com.android.internal.R.integer.config_lowBatteryWarningLevel);
            int warnLevel =  Settings.Global.getInt(resolver,
                        Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, defWarnLevel);
           
            if (warnLevel == 0) {
                warnLevel = defWarnLevel;
            }
            if (warnLevel < critLevel) {
                warnLevel = critLevel;
            }
    
            mLowBatteryReminderLevels[0] = warnLevel;
            mLowBatteryReminderLevels[1] = critLevel;
            mLowBatteryAlertCloseLevel = mLowBatteryReminderLevels[0]
                    + mContext.getResources().getInteger(
                            com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
        }
    

    这里有两个值需要关注:warnLevel和critLevel,warnLevel默认值是15,可以随系统数据库字段LOW_POWER_MODE_TRIGGER_LEVEL对应值而改变,其主要作用为进入低电警告的阈值。critLevel默认值是5,其主要作用是打开低电保护的阈值。这两个值都被写入数组mLowBatteryReminderLevels[],用来计算是否需要显示或者更新低电提醒。
    那什么时候触发低电警告呢?可以看到,在初始化的时候,PowerUI会去注册一个广播接收者,代码如下:

    private final class Receiver extends BroadcastReceiver {
    
            public void init() {
                // Register for Intent broadcasts for...
                IntentFilter filter = new IntentFilter();
                filter.addAction(Intent.ACTION_BATTERY_CHANGED);
                filter.addAction(Intent.ACTION_SCREEN_OFF);
                filter.addAction(Intent.ACTION_SCREEN_ON);
                filter.addAction(Intent.ACTION_USER_SWITCHED);
                mContext.registerReceiver(this, filter, null, mHandler);
            }
    
            @Override
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
                    final int oldBatteryLevel = mBatteryLevel;
                    mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100);
                    final int oldBatteryStatus = mBatteryStatus;
                    mBatteryStatus = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
                            BatteryManager.BATTERY_STATUS_UNKNOWN);
                    final int oldPlugType = mPlugType;
                    mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);
                    final int oldInvalidCharger = mInvalidCharger;
                    mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);
    
                    final boolean plugged = mPlugType != 0;
                    final boolean oldPlugged = oldPlugType != 0;
    
                    int oldBucket = findBatteryLevelBucket(oldBatteryLevel);
                    int bucket = findBatteryLevelBucket(mBatteryLevel);
    
                    if (DEBUG) {
                        Slog.d(TAG, "buckets   ....." + mLowBatteryAlertCloseLevel
                                + " .. " + mLowBatteryReminderLevels[0]
                                + " .. " + mLowBatteryReminderLevels[1]);
                        Slog.d(TAG, "level          " + oldBatteryLevel + " --> " + mBatteryLevel);
                        Slog.d(TAG, "status         " + oldBatteryStatus + " --> " + mBatteryStatus);
                        Slog.d(TAG, "plugType       " + oldPlugType + " --> " + mPlugType);
                        Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);
                        Slog.d(TAG, "bucket         " + oldBucket + " --> " + bucket);
                        Slog.d(TAG, "plugged        " + oldPlugged + " --> " + plugged);
                    }
    
                    mWarnings.update(mBatteryLevel, bucket, mScreenOffTime);
                    if (oldInvalidCharger == 0 && mInvalidCharger != 0) {
                        Slog.d(TAG, "showing invalid charger warning");
                        mWarnings.showInvalidChargerWarning();
                        return;
                    } else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {
                        mWarnings.dismissInvalidChargerWarning();
                    } else if (mWarnings.isInvalidChargerWarningShowing()) {
                        // if invalid charger is showing, don't show low battery
                        return;
                    }
    
                    boolean isPowerSaver = mPowerManager.isPowerSaveMode();
                    if (!plugged
                            && !isPowerSaver
                            && (bucket < oldBucket || oldPlugged)
                            && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
                            && bucket < 0) {
    
                        // only play SFX when the dialog comes up or the bucket changes
                        final boolean playSound = bucket != oldBucket || oldPlugged;
                        mWarnings.showLowBatteryWarning(playSound);
                    } else if (isPowerSaver || plugged || (bucket > oldBucket && bucket > 0)) {
                        mWarnings.dismissLowBatteryWarning();
                    } else {
                        mWarnings.updateLowBatteryWarning();
                    }
                } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
                    mScreenOffTime = SystemClock.elapsedRealtime();
                } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
                    mScreenOffTime = -1;
                } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
                    mWarnings.userSwitched();
                } else {
                    Slog.w(TAG, "unknown intent: " + intent);
                }
            }
        };
    

    可以看到当收到ACTION_BATTERY_CHANGED的广播时,将进行一系列的flag判断,如是否开启电量保护、是否usb插入以及对当前电量的判断,这里电量判断通过一个bucket值来进行判断是否属于健康电量,其判断逻辑如下:

    /**
         * Buckets the battery level.
         *
         * The code in this function is a little weird because I couldn't comprehend
         * the bucket going up when the battery level was going down. --joeo
         *
         * 1 means that the battery is "ok"
         * 0 means that the battery is between "ok" and what we should warn about.
         * less than 0 means that the battery is low
         */
        private int findBatteryLevelBucket(int level) {
            if (level >= mLowBatteryAlertCloseLevel) {
                return 1;
            }
            if (level > mLowBatteryReminderLevels[0]) {
                return 0;
            }
            final int N = mLowBatteryReminderLevels.length;
            for (int i=N-1; i>=0; i--) {
                if (level <= mLowBatteryReminderLevels[i]) {
                    return -1-i;
                }
            }
            throw new RuntimeException("not possible!");
        }
    

    这里用到了之前介绍的mLowBatteryReminderLevels[]判断,即warnLevel和critLevel两个值进行判断,当return值小于0则认为当前电量不健康。从而触发低电警告。

    高温警告

    高温警告触发于电池温度过高时,会提示用户电池温度过高,将设备关机。初始化逻辑如下:

    private void initTemperatureWarning() {
            ContentResolver resolver = mContext.getContentResolver();
            Resources resources = mContext.getResources();
            if (Settings.Global.getInt(resolver, Settings.Global.SHOW_TEMPERATURE_WARNING,
                    resources.getInteger(R.integer.config_showTemperatureWarning)) == 0) {
                return;
            }
    
            mThresholdTemp = Settings.Global.getFloat(resolver, Settings.Global.WARNING_TEMPERATURE,
                    resources.getInteger(R.integer.config_warningTemperature));
    
            if (mThresholdTemp < 0f) {
                // Get the throttling temperature. No need to check if we're not throttling.
                float[] throttlingTemps = mHardwarePropertiesManager.getDeviceTemperatures(
                        HardwarePropertiesManager.DEVICE_TEMPERATURE_SKIN,
                        HardwarePropertiesManager.TEMPERATURE_SHUTDOWN);
                if (throttlingTemps == null
                        || throttlingTemps.length == 0
                        || throttlingTemps[0] == HardwarePropertiesManager.UNDEFINED_TEMPERATURE) {
                    return;
                }
                mThresholdTemp = throttlingTemps[0] -
                        resources.getInteger(R.integer.config_warningTemperatureTolerance);
            }
    
            setNextLogTime();
    
            // This initialization method may be called on a configuration change. Only one set of
            // ongoing callbacks should be occurring, so remove any now. updateTemperatureWarning will
            // schedule an ongoing callback.
            mHandler.removeCallbacks(mUpdateTempCallback);
    
            // We have passed all of the checks, start checking the temp
            updateTemperatureWarning();
        }
    

    通过这个获取高温警告阈值,供其在updateTemperatureWarning()中进行判断,其代码如下:

     // We create a method reference here so that we are guaranteed that we can remove a callback
     // by using the same instance (method references are not guaranteed to be the same object
     // each time they are created).
     private final Runnable mUpdateTempCallback = this::updateTemperatureWarning;
     protected void updateTemperatureWarning() {
            float[] temps = mHardwarePropertiesManager.getDeviceTemperatures(
                    HardwarePropertiesManager.DEVICE_TEMPERATURE_SKIN,
                    HardwarePropertiesManager.TEMPERATURE_CURRENT);
            if (temps.length != 0) {
                float temp = temps[0];
                mRecentTemps[mNumTemps++] = temp;
    
                StatusBar statusBar = getComponent(StatusBar.class);
                if (statusBar != null && !statusBar.isDeviceInVrMode()
                        && temp >= mThresholdTemp) {
                    logAtTemperatureThreshold(temp);
                    mWarnings.showHighTemperatureWarning();
                } else {
                    mWarnings.dismissHighTemperatureWarning();
                }
            }
    
            logTemperatureStats();
    
            mHandler.postDelayed(mUpdateTempCallback, TEMPERATURE_INTERVAL);
        }
    

    并且将该方法加入Looper中,这里的TEMPERATURE_INTERVAL=30s,即表示每30s进行一次设备电池温度的获取,并判断是否需要发出高温警告。

    小结

    PowerUI是SystemUI中监听系统电池模块状态,并以UI形式展现出来的一种框架。如果在定制修改中需要监听系统的某些模块的状态,同样可以采取类似框架进行客制化处理。

    相关文章

      网友评论

        本文标题:SystemUI之PowerUI启动流程

        本文链接:https://www.haomeiwen.com/subject/tsumbftx.html