Android的平台上,耗电量的问题一直被人所诟病。从Lollipop开始,Google也一直非常重视对于省电模式的改造。本篇文章将会基于最新的Android Pie的代码,来系统分析现在Android的省电模式流程,并且对一些可以继续优化的点来给出一些建议。本篇文章将会从SystemUI开始讲起。


QuickSettings

 

这个图片相信使用android手机的同学都会有所印象,是属于SystemUI的QuickSettings。

    @Override    protected void handleClick() {        mBatteryController.setPowerSaveMode(!mPowerSave);    }

BatterySaverTile的handleCllick响应了对于省电模式的点击。
mBatteryController是BatteryController类的一个实例化的对象,所以setPowerSaveMode是BatteryControllerImpl中进行了实现。

    @Override    public void setPowerSaveMode(boolean powerSave) {        BatterySaverUtils.setPowerSaveMode(mContext, powerSave, /*needFirstTimeWarning*/ true);    }

BatterySaverUtilsframeworks/base/packages/SettingsLib中的一个类,这个类的方法都是static类型,方便其他的类进行方法的调用。
当我们点击了省电模式的按钮,启动省电模式的话,这里的参数powerSave将会被置为true,并且needFirstTimeWarning也一定会为true。

    /**     * Enable / disable battery saver by user request.     * - If it's the first time and needFirstTimeWarning, show the first time dialog.     * - If it's 4th time through 8th time, show the schedule suggestion notification.     *     * @param enable true to disable battery saver.     *     * @return true if the request succeeded.     */    public static synchronized boolean setPowerSaveMode(Context context,            boolean enable, boolean needFirstTimeWarning) {        if (DEBUG) {            Log.d(TAG, "Battery saver turning " + (enable ? "ON" : "OFF"));        }        final ContentResolver cr = context.getContentResolver();        if (enable && needFirstTimeWarning && maybeShowBatterySaverConfirmation(context)) {            return false;        }        if (enable && !needFirstTimeWarning) {            setBatterySaverConfirmationAcknowledged(context);        }        if (context.getSystemService(PowerManager.class).setPowerSaveMode(enable)) {            if (enable) {                final int count =                        Secure.getInt(cr, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, 0) + 1;                Secure.putInt(cr, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, count);                final Parameters parameters = new Parameters(context);                if ((count >= parameters.startNth)                        && (count <= parameters.endNth)                        && Global.getInt(cr, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0) == 0                        && Secure.getInt(cr,                        Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, 0) == 0) {                    showAutoBatterySaverSuggestion(context);                }            }            return true;        }        return false;    }

这个类里面,实现的方法主要为context.getSystemService(PowerManager.class).setPowerSaveMode(enable)
会通过getSystemService去拿到PowerManager的对象,然后去调用setPowerSaveMode的函数进行具体的设置。

PowerManager是android的核心service之一,其代码位于frameworks/base/core/java/android/os/PowerManager.java

    /**     * Set the current power save mode.     *     * @return True if the set was allowed.     *     * @see #isPowerSaveMode()     *     * @hide     */    public boolean setPowerSaveMode(boolean mode) {        try {            return mService.setPowerSaveMode(mode);        } catch (RemoteException e) {            throw e.rethrowFromSystemServer();        }    }

PowerManager是framework暴露对外的接口,真正的实现是在PowerManagerService

        @Override // Binder call        public boolean setPowerSaveMode(boolean enabled) {            mContext.enforceCallingOrSelfPermission(                    android.Manifest.permission.DEVICE_POWER, null);            final long ident = Binder.clearCallingIdentity();            try {                 return setLowPowerModeInternal(enabled);            } finally {                Binder.restoreCallingIdentity(ident);            }        }

这边有几个需要注意的地方:

  1. enforceCallingOrSelfPermission是Android framework的一个安全记住,主要是检查当前调用进程的UID是否具有操作某一项系统的权限。比如省电模式,因为是SystemUI里面进行操作,所以SystemUIAndroidManifest.xml里面,一定会声明DEVICE_POWER的权限,否则将会被做为异常抛出。
  1. 当SystemUI通过调用PowerManager且检查完权限之后,Binder.clearCallingIdentity将会清除SystemUI Process的UIDPID的信息,将其换成PownerManager所在进程UID,PID的内容。因为这块涉及到了binder通信,所以我们将会在后续Binder的内容中对其进行阐述。
  2. 在try{}finally{}中,调用了Binder.restoreCallingIdentity()
    这个作用是恢复远程调用端的uid和pid信息,正好是clearCallingIdentity的反过程。
  3. 接下来,就是核心调用了,setLowPowerModeInternal(enable)这里的参数,在点击省电模式的情况, enable = true
    private boolean setLowPowerModeInternal(boolean enabled) {        synchronized (mLock) {            if (DEBUG) {                Slog.d(TAG, "setLowPowerModeInternal " + enabled + " mIsPowered=" + mIsPowered);            }            if (mIsPowered) {                return false;            }            mBatterySaverStateMachine.setBatterySaverEnabledManually(enabled);            return true;        }    }

setLowPowerModeInternal的操作其实是很简单的,使用BatterySaverStateMachine的对象mBatterySaverStateMachine,去调用了setBatterySaverEnabledManually

    /**     * {@link com.android.server.power.PowerManagerService} calls it when     * {@link android.os.PowerManager#setPowerSaveMode} is called.     *     * Note this could? be called before {@link #onBootCompleted} too.     */    public void setBatterySaverEnabledManually(boolean enabled) {        if (DEBUG) {             Slog.d(TAG, "setBatterySaverEnabledManually: enabled=" + enabled);        }        synchronized (mLock) {            enableBatterySaverLocked(/*enable=*/ enabled, /*manual=*/ true,                    (enabled ? BatterySaverController.REASON_MANUAL_ON                            : BatterySaverController.REASON_MANUAL_OFF),                    (enabled ? "Manual ON" : "Manual OFF"));        }    }

这边因为enabled之前传进来的为true,所以可以将其翻译为

enableBatterySaverLocked(true, true, BatterySaverController.REASON_MANUAL_ON, "Manual ON");

public static final int REASON_MANUAL_ON = 2;

接下来的enableBatterySaverLocked函数,将会将内容更新到global setting中。

    /**     * Actually enable / disable battery saver. Write the new state to the global settings     * and propagate it to {@link #mBatterySaverController}.     */    private void enableBatterySaverLocked(boolean enable, boolean manual, int intReason,            String strReason) {        if (DEBUG) {            Slog.d(TAG, "enableBatterySaver: enable=" + enable + " manual=" + manual                    + " reason=" + strReason + "(" + intReason + ")");        }        final boolean wasEnabled = mBatterySaverController.isEnabled();        if (wasEnabled == enable) {            if (DEBUG) {                Slog.d(TAG, "Already " + (enable ? "enabled" : "disabled"));            }            return;        }        if (enable && mIsPowered) {            if (DEBUG) Slog.d(TAG, "Can't enable: isPowered");            return;         }            mLastChangedIntReason = intReason;        mLastChangedStrReason = strReason;        if (manual) {            if (enable) {                updateSnoozingLocked(false, "Manual snooze OFF");            } else {                // When battery saver is disabled manually (while battery saver is enabled)                // when the battery level is low, we "snooze" BS -- i.e. disable auto battery saver.                // We resume auto-BS once the battery level is not low, or the device is plugged in.                if (isBatterySaverEnabled() && mIsBatteryLevelLow) {                    updateSnoozingLocked(true, "Manual snooze");                }            }        }        mSettingBatterySaverEnabled = enable;        putGlobalSetting(Global.LOW_POWER_MODE, enable ? 1 : 0);        if (manual) {            mSettingBatterySaverEnabledSticky = enable;            putGlobalSetting(Global.LOW_POWER_MODE_STICKY, enable ? 1 : 0);        }        mBatterySaverController.enableBatterySaver(enable, intReason);        if (DEBUG) {            Slog.d(TAG, "Battery saver: Enabled=" + enable                    + " manual=" + manual                    + " reason=" + strReason + "(" + intReason + ")");        }    }
  1. wasEnabled首先会去判断是否之前已经是enable的状态,如果是的话,那么就return。
  2. isPowered如果为true的话的也是会直接返回。
  3. mLastChangedIntReason ,mLastChangedStrReason的值会被保存为之前传进来的值,也就是 mLastChangedIntReason=2mLastChangedStrReason="Manual ON".
  4. manualenable都是true的状态,所以会upateSnoozingLocked.而这个函数,其实只是设置mBatterySaverSnoozing的值为ture。而这个值的使用,我们后续还会遇到。
  5. putGlobalSetting(Global.LOW_POWER_MODE, enable ? 1 : 0);,将LOW_POWER_MODE的值在数据库中置为1.
  6. putGlobalSetting(Global.LOW_POWER_MODE_STICKY, enable ? 1 : 0);,将LOW_POWER_MODE_STICKY的值在数据库中置为1.
  7. mBatterySaverController.enableBatterySaver(enable, intReason); 在保存完相应的数据库之后,将会调用这个函数进行真正的操作。
    /**     * Called by {@link PowerManagerService} to update the battery saver stete.     */    public void enableBatterySaver(boolean enable, int reason) {        synchronized (mLock) {            if (mEnabled == enable) {                return;            }            mEnabled = enable;            mHandler.postStateChanged(/*sendBroadcast=*/ true, reason);        }    }

在调用controller的enableBatterySaver函数中,主要是讲mEnable设置为true。并且将实际的reason给传递到Handler里面。

public void postStateChanged(boolean sendBroadcast, int reason) {        obtainMessage(MSG_STATE_CHANGED, sendBroadcast ?                ARG_SEND_BROADCAST : ARG_DONT_SEND_BROADCAST, reason).sendToTarget(); }

这个函数的作用就是填充消息并且sendToTarget。

    @Override    public void dispatchMessage(Message msg) {        switch (msg.what) {            case MSG_STATE_CHANGED:                handleBatterySaverStateChanged(                        msg.arg1 == ARG_SEND_BROADCAST,                        msg.arg2);                break;            case MSG_SYSTEM_READY:                for (Plugin p : mPlugins) {                    p.onSystemReady(BatterySaverController.this);                }                break;        }    }
  1. 因为MSG_STATE_CHANGED是之前填充并发送的消息,所以会到MSG_STATE_CHANGED的case中进行处理。
  2. handleBatterySaverStateChanged(msg.arg1 == ARG_SEND_BROADCAST, msg.arg2); 的函数中,第一个参数由于之前传递的也是ARG_SEND_BROADCAST, 所以为true;第二个参数是之前填充的reason,所以为2handleBatterySaverStateChanged的函数非常的复杂,涉及到了jninativedevicesbroadcast等信息,所以接下来是真正的重头戏了。
    /**     * Dispatch power save events to the listeners.     *     * This method is always called on the handler thread.     *     * This method is called only in the following cases:     * - When battery saver becomes activated.     * - When battery saver becomes deactivated.     * - When battery saver is on the interactive state changes.     * - When battery saver is on the battery saver policy changes.     */    void handleBatterySaverStateChanged(boolean sendBroadcast, int reason) {        final LowPowerModeListener[] listeners;        final boolean enabled;        final boolean isInteractive = getPowerManager().isInteractive();        final ArrayMap fileValues;        synchronized (mLock) {            EventLogTags.writeBatterySaverMode(                    mPreviouslyEnabled ? 1 : 0, // Previously off or on.                    mEnabled ? 1 : 0, // Now off or on.                    isInteractive ?  1 : 0, // Device interactive state.                    mEnabled ? mBatterySaverPolicy.toEventLogString() : "",                    reason);            mPreviouslyEnabled = mEnabled;            listeners = mListeners.toArray(new LowPowerModeListener[mListeners.size()]);            enabled = mEnabled;            mIsInteractive = isInteractive;            if (enabled) {                fileValues = mBatterySaverPolicy.getFileValues(isInteractive);            } else {                fileValues = null;            }        }        final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);        if (pmi != null) {            pmi.powerHint(PowerHint.LOW_POWER, enabled ? 1 : 0);        }        updateBatterySavingStats();        if (ArrayUtils.isEmpty(fileValues)) {            mFileUpdater.restoreDefault();        } else {            mFileUpdater.writeFiles(fileValues);        }        for (Plugin p : mPlugins) {            p.onBatterySaverChanged(this);        }        if (sendBroadcast) {            if (DEBUG) {                Slog.i(TAG, "Sending broadcasts for mode: " + enabled);            }            // Send the broadcasts and notify the listeners. We only do this when the battery saver            // mode changes, but not when only the screen state changes.            Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING)                    .putExtra(PowerManager.EXTRA_POWER_SAVE_MODE, enabled)                    .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);            intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);            // Send internal version that requires signature permission.            intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL);            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);            mContext.sendBroadcastAsUser(intent, UserHandle.ALL,                    Manifest.permission.DEVICE_POWER);            for (LowPowerModeListener listener : listeners) {                final PowerSaveState result =                        mBatterySaverPolicy.getBatterySaverPolicy(                                listener.getServiceType(), enabled);                listener.onLowPowerModeChanged(result);            }        }    }

这个函数非常的大,进行的操作详细分解如下:

  1. final boolean isInteractive = getPowerManager().isInteractive();
    此处,从PowerManager中获取是否为Interactive的状态。
    isInteractive很简单:
    /**     * Returns true if the wakefulness state represents an interactive state     * as defined by {@link android.os.PowerManager#isInteractive}.     */    public static boolean isInteractive(int wakefulness) {        return wakefulness == WAKEFULNESS_AWAKE || wakefulness == WAKEFULNESS_DREAMING;    }

其实就是在判断wakefulness的值是否为WAKEFULNESS_AWAKE或者WAKEFULNESS_DREAMING,那么这个值代表什么呢?

WAKEFULNESS_ASLEEP:表示系统当前处于休眠状态,只能被wakeUp()调用唤醒。
WAKEFULNESS_AWAKE:表示系统目前处于正常运行状态。
WAKEFULNESS_DREAMING:表示系统当前正处于互动屏保的状态。
WAKEFULNESS_DOZING:表示系统正处于“doze”状态

由于这边我们分析的是省电模式,所以也就不详细的展开。
这边在正常使用点击省电模式的按钮的时候,isInteractive返回的是true

  1. listeners = mListeners.toArray(new LowPowerModeListener[mListeners.size()]);
    这里的mListeners其实是之前注册的时候,所有添加LowPowerModeListener的service。包含了VibratorServiceNetworkPolicyManagerService等。这些内容都会进行一次保存,方便后面的消息分发。
  2. 此处PowerManagerInternal的调用比较复杂,但是其实并无实际的作用。但是针对各个厂商来说,可以在此处关注,因为后续cpu的频率可以顺着这条线进行设置。
        final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);        if (pmi != null) {            pmi.powerHint(PowerHint.LOW_POWER, enabled ? 1 : 0);        }

首先是powerHint的函数

        @Override // Binder call        public void powerHint(int hintId, int data) {            if (!mSystemReady) {                // Service not ready yet, so who the heck cares about power hints, bah.                return;            }            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);            powerHintInternal(hintId, data);        }

和之前一样,这边仍然会check一下是否有DEVICE_POWER的权限。
然后调用powerHintInternal函数进行处理。

private void powerHintInternal(int hintId, int data) {        // Maybe filter the event.        switch (hintId) {            case PowerHint.LAUNCH: // 1: activate launch boost 0: deactivate.                if (data == 1 && mBatterySaverController.isLaunchBoostDisabled()) {                    return;                }                break;        }        nativeSendPowerHint(hintId, data);    }

在这个函数中,我们传进来的hintId是5,而PowerHint.LAUNCH的值是在PowerHint的类中定义的,这个值为public static final int LAUNCH = 8;.
所以会直接调用native的方法。
对应的文件和函数为:

frameworks/base/services/core/jni/com_android_server_power_PowerManagerService.cpp

static void nativeSendPowerHint(JNIEnv* /* env */, jclass /* clazz */, jint hintId, jint data) {    sendPowerHint(static_cast(hintId), data);}

因为是jni的调用,所以这边只是一个简单的封装。

static void sendPowerHint(PowerHint hintId, uint32_t data) {    sp powerHalV1_1 = getPowerHalV1_1();    Return ret;    if (powerHalV1_1 != nullptr) {        ret = powerHalV1_1->powerHintAsync(hintId, data);        processPowerHalReturn(ret, "powerHintAsync");    } else {        sp powerHalV1_0 = getPowerHalV1_0();        if (powerHalV1_0 != nullptr) {            ret = powerHalV1_0->powerHint(hintId, data);            processPowerHalReturn(ret, "powerHint");        }    }}

笔者用的手机是pixel xl,其对应的devices为marlin,且powerHal是powerHalV1_1的 版本,所以会走到

ret = powerHalV1_1->powerHintAsync(hintId, data);
processPowerHalReturn(ret, "powerHintAsync");

所以对应的函数为:
powerHintAsync device/google/marlin/power/Power.cpp
以下是具体实现:

Return Power::powerHintAsync(PowerHint hint, int32_t data) {    // just call the normal power hint in this oneway function    return powerHint(hint, data);}

又是一个抓狂的封装。。。

Return Power::powerHint(PowerHint hint, int32_t data) {    if (android::base::GetProperty("init.svc.vendor.perfd", "") != "running") {        ALOGW("perfd is not started");        return Void();    }    power_hint(static_cast(hint), data ? (&data) : NULL);    return Void();}

对于perfd,我们这边暂时不做分析。因为正常情况下,是继续在power_hint
接下来的power_hint,实现的code很大,但是其实都是无用功。
这个函数在设置不同电源状态下的cpu频率,可是并没有对传进来的hint=5进行判断,所以并无实际作用。。。但是当我们如果想设置cpu的低频状态的处理,这边无疑是一个最好的选择。

void power_hint(power_hint_t hint, void *data){    /* Check if this hint has been overridden. */    if (power_hint_override(hint, data) == HINT_HANDLED) {        ALOGE("power_hint_override");        /* The power_hint has been handled. We can skip the rest. */        return;    }    switch(hint) {        case POWER_HINT_VSYNC:        break;        case POWER_HINT_SUSTAINED_PERFORMANCE:        {            ...            break;        }        case POWER_HINT_VR_MODE:        {            ...            break;        }        case POWER_HINT_INTERACTION:        {            ...            break;        }        default:        break;}

这里走的是default break......

  1. 我们继续回到刚才的主函数中进行分析。
    分析完了pmi的调用后,就来到了updateBatterySavingStats();
    private void updateBatterySavingStats() {        final PowerManager pm = getPowerManager();        if (pm == null) {            Slog.wtf(TAG, "PowerManager not initialized");            return;        }        final boolean isInteractive = pm.isInteractive();        final int dozeMode =                pm.isDeviceIdleMode() ? DozeState.DEEP                        : pm.isLightDeviceIdleMode() ? DozeState.LIGHT                        : DozeState.NOT_DOZING;        synchronized (mLock) {            if (mIsPluggedIn) {                mBatterySavingStats.startCharging();                return;            }            mBatterySavingStats.transitionState(                    mEnabled ? BatterySaverState.ON : BatterySaverState.OFF,                    isInteractive ? InteractiveState.INTERACTIVE : InteractiveState.NON_INTERACTIVE,                    dozeMode);        }    }

这个函数的核心在于transitionState,但是只是用于保存当前的状态,所以我们不深究。

  1. fileValues默认为空,我们也不处理分析。
  2. 接下来的Plugin就有点意思了,因为是插件的方式进行操作。
        for (Plugin p : mPlugins) {            p.onBatterySaverChanged(this);        }

但是真正的实现,aosp只实现了一种:
onBatterySaverChanged

frameworks/base/services/core/java/com/android/server/power/batterysaver/BatterySaverLocationPlugin.java
这里的具体实现为:

    private void updateLocationState(BatterySaverController caller) {        final boolean kill =                (caller.getBatterySaverPolicy().getGpsMode()                        == PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF) &&                caller.isEnabled() && !caller.isInteractive();        boolean gpsMode = (caller.getBatterySaverPolicy().getGpsMode() == PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);        if (DEBUG) {            Slog.d(TAG, "Battery saver " + (kill ? "stopping" : "restoring") + " location.");        }        Settings.Global.putInt(mContext.getContentResolver(),                Global.LOCATION_GLOBAL_KILL_SWITCH, kill ? 1 : 0);    }

killer = false; 所以这边设置Global Settings数据库的时候,将LOCATION_GLOBAL_KILL_SWITCH置为0

  1. 接下来就是向各个service,package来进行发送广播要求进行相应的处理了。
            Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING)                    .putExtra(PowerManager.EXTRA_POWER_SAVE_MODE, enabled)                    .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);            intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);            // Send internal version that requires signature permission.            intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL);            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);            mContext.sendBroadcastAsUser(intent, UserHandle.ALL,                    Manifest.permission.DEVICE_POWER);            for (LowPowerModeListener listener : listeners) {                final PowerSaveState result =                        mBatterySaverPolicy.getBatterySaverPolicy(                                listener.getServiceType(), enabled);                listener.onLowPowerModeChanged(result);            }

这里主要是三个广播:ACTION_POWER_SAVE_MODE_CHANGING, ACTION_POWER_SAVE_MODE_CHANGEDACTION_POWER_SAVE_MODE_CHANGED_INTERNAL,

我们接下来对着三个广播进行一对一的分析。

  1. ACTION_POWER_SAVE_MODE_CHANGING
    该广播首先加了一个flag为FLAG_RECEIVER_REGISTERED_ONLY,表示了只有动态注册的接收才可以。
    接受的地方主要为:
BatterySaverReceiver.java   (PowerManager.ACTION_POWER_SAVE_MODE_CHANGING.equals(action)) 
BatteryControllerImpl.java  (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING)) 

一个是位于Packages/apps/Settings
一个是frameworks/base/packages/SystemUI/
对于BatterySaverReceiver来说这里更新的主要是settings里面的状态。
对于sBatteryControllerImpl来说,这里的调用为

    private void setPowerSave(boolean powerSave) {        if (powerSave == mPowerSave) return;        mPowerSave = powerSave;        // AOD power saving setting might be different from PowerManager power saving mode.        PowerSaveState state = mPowerManager.getPowerSaveState(PowerManager.ServiceType.AOD);        mAodPowerSave = state.batterySaverEnabled;        if (DEBUG) Log.d(TAG, "Power save is " + (mPowerSave ? "on" : "off"));        firePowerSaveChanged();    }

这里的会对PowerSave的状态进行保存,并且调用firePowerSaveChanged方法来进行实现。

    private void firePowerSaveChanged() {        synchronized (mChangeCallbacks) {            final int N = mChangeCallbacks.size();            for (int i = 0; i < N; i++) {                mChangeCallbacks.get(i).onPowerSaveChanged(mPowerSave);            }        }    }

这里会去遍历mChangeCallbacks,并且回调onPowerSaveChanged的方法。
实现回调的方法主要为:

BatteryMeterView.java
StatusBar.java
KeyguardStatusBarView.java
LightBarController.java
BatterySaverTile.java

这边主要是SystemUI和界面显示上面的一些操作。

  1. ACTION_POWER_SAVE_MODE_CHANGED
    该广播和之前的一样,也是增加了一个Flag: FLAG_RECEIVER_REGISTERED_ONLY
    接收方的主要操作为:
BatteryBroadcastReceiver.java    (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(intent.getAction())
BatteryControllerImpl.java(action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED))
PowerUI.java(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) 
DeviceStateMonitor.javacase PowerManager.ACTION_POWER_SAVE_MODE_CHANGED:
SoundTriggerHelper.javaif (!PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(intent.getAction())) {
GnssLocationProvider.javaPowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)

而这个六个接收广播的地方都分别做了什么事情呢?

作用
BatteryBroadcastReceiver 通知了电池电量的改变,进入power save的模式
PowerUI 如果在power save的模式下,就忽略电池低电的提醒
DeviceStateMonitor 设置modem为power save的模式
SoundTriggerHelper 关闭语音互动的功能
GnssLocationProvider 限制gps使用,灭屏后会关闭gps

DeviceStateMonitor的最终调用如下:

    /**     * Send the device state to the modem.     *     * @param type Device state type. See DeviceStateType defined in types.hal.     * @param state True if enabled/on, otherwise disabled/off     */    private void sendDeviceState(int type, boolean state) {        log("send type: " + deviceTypeToString(type) + ", state=" + state, true);        mPhone.mCi.sendDeviceState(type, state, null);    }

SoundTriggerHelper的实现是去改了mIsPowerSaveMode的值,作用如下:

    // Whether we are allowed to run any recognition at all. The conditions that let us run    // a recognition include: no active phone call or not being in a power save mode. Also,    // the native service should be enabled.    private boolean isRecognitionAllowed() {        return !mCallActive && !mServiceDisabled && !mIsPowerSaveMode;    }

GnssLocationProvider的调用,实现如下,从comments里面可以很容易的读懂。

    private void updateLowPowerMode() {        // Disable GPS if we are in device idle mode.        boolean disableGps = mPowerManager.isDeviceIdleMode();        final PowerSaveState result =                mPowerManager.getPowerSaveState(ServiceType.GPS);        switch (result.gpsMode) {            case PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF:                // If we are in battery saver mode and the screen is off, disable GPS.                disableGps |= result.batterySaverEnabled && !mPowerManager.isInteractive();                break;        }        if (disableGps != mDisableGps) {            mDisableGps = disableGps;            updateRequirements();        }    }
  1. 最后是之前保存的一个数组回调函数:
            for (LowPowerModeListener listener : listeners) {                final PowerSaveState result =                        mBatterySaverPolicy.getBatterySaverPolicy(                                listener.getServiceType(), enabled);                listener.onLowPowerModeChanged(result);            }

Listeners在我们前面的文章中也提到过,这边详细的总结一下:

作用
VibratorService.java 取消手机的震动效果
NetworkPolicyManagerService.java 更新白名单以及应用对网络访问的限制
WindowManagerService.java 取消窗口动画

NetworkPolicyManagerService的调用如下:

    // NOTE: since both fw_dozable and fw_powersave uses the same map    // (mPowerSaveTempWhitelistAppIds) for whitelisting, we can reuse their logic in this method.    private void updateRulesForWhitelistedPowerSaveUL(boolean enabled, int chain,            SparseIntArray rules) {        if (enabled) {            // Sync the whitelists before enabling the chain.  We don't care about the rules if            // we are disabling the chain.            final SparseIntArray uidRules = rules;            uidRules.clear();            final List users = mUserManager.getUsers();            for (int ui = users.size() - 1; ui >= 0; ui--) {                UserInfo user = users.get(ui);                updateRulesForWhitelistedAppIds(uidRules, mPowerSaveTempWhitelistAppIds, user.id);                updateRulesForWhitelistedAppIds(uidRules, mPowerSaveWhitelistAppIds, user.id);                if (chain == FIREWALL_CHAIN_POWERSAVE) {                    updateRulesForWhitelistedAppIds(uidRules,                            mPowerSaveWhitelistExceptIdleAppIds, user.id);                }            }            for (int i = mUidState.size() - 1; i >= 0; i--) {                if (isProcStateAllowedWhileIdleOrPowerSaveMode(mUidState.valueAt(i))) {                    uidRules.put(mUidState.keyAt(i), FIREWALL_RULE_ALLOW);                }            }            setUidFirewallRulesUL(chain, uidRules, CHAIN_TOGGLE_ENABLE);        } else {            setUidFirewallRulesUL(chain, null, CHAIN_TOGGLE_DISABLE);        }    }
  1. 更新临时白名单、白名单、除了idle app之外的白名单都将允许网络访问
  2. 如果是进程优先级是前台服务以上的允许网络访问
    private int updateRulesForPowerRestrictionsULInner(int uid, int oldUidRules, boolean paroled) {        if (!isUidValidForBlacklistRules(uid)) {            if (LOGD) Slog.d(TAG, "no need to update restrict power rules for uid " + uid);            return RULE_NONE;        }        final boolean isIdle = !paroled && isUidIdle(uid);        final boolean restrictMode = isIdle || mRestrictPower || mDeviceIdleMode;        final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid);        final boolean isWhitelisted = isWhitelistedBatterySaverUL(uid, mDeviceIdleMode);        final int oldRule = oldUidRules & MASK_ALL_NETWORKS;        int newRule = RULE_NONE;        // First step: define the new rule based on user restrictions and foreground state.        // NOTE: if statements below could be inlined, but it's easier to understand the logic        // by considering the foreground and non-foreground states.        if (isForeground) {            if (restrictMode) {                newRule = RULE_ALLOW_ALL;            }        } else if (restrictMode) {            newRule = isWhitelisted ? RULE_ALLOW_ALL : RULE_REJECT_ALL;        }        final int newUidRules = (oldUidRules & MASK_METERED_NETWORKS) | newRule;        if (LOGV) {            Log.v(TAG, "updateRulesForPowerRestrictionsUL(" + uid + ")"                    + ", isIdle: " + isIdle                    + ", mRestrictPower: " + mRestrictPower                    + ", mDeviceIdleMode: " + mDeviceIdleMode                    + ", isForeground=" + isForeground                    + ", isWhitelisted=" + isWhitelisted                    + ", oldRule=" + uidRulesToString(oldRule)                    + ", newRule=" + uidRulesToString(newRule)                    + ", newUidRules=" + uidRulesToString(newUidRules)                    + ", oldUidRules=" + uidRulesToString(oldUidRules));        }        // Second step: notify listeners if state changed.        if (newRule != oldRule) {            if (newRule == RULE_NONE || hasRule(newRule, RULE_ALLOW_ALL)) {                if (LOGV) Log.v(TAG, "Allowing non-metered access for UID " + uid);            } else if (hasRule(newRule, RULE_REJECT_ALL)) {                if (LOGV) Log.v(TAG, "Rejecting non-metered access for UID " + uid);            } else {                // All scenarios should have been covered above                Log.wtf(TAG, "Unexpected change of non-metered UID state for " + uid                        + ": foreground=" + isForeground                        + ", whitelisted=" + isWhitelisted                        + ", newRule=" + uidRulesToString(newUidRules)                        + ", oldRule=" + uidRulesToString(oldUidRules));            }            mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRules).sendToTarget();        }        return newUidRules;    }
  1. isUidValidForBlacklistRulesuidmedia或者drm类型的不需要,或者之前已经授权INTERNET网络访问的app,不许用更新
    // TODO: the MEDIA / DRM restriction might not be needed anymore, in which case both    // methods below could be merged into a isUidValidForRules() method.    private boolean isUidValidForBlacklistRules(int uid) {        // allow rules on specific system services, and any apps        if (uid == android.os.Process.MEDIA_UID || uid == android.os.Process.DRM_UID            || (UserHandle.isApp(uid) && hasInternetPermissions(uid))) {            return true;        }        return false;    }
  1. 如果是前台进程,就算是受限模式下也会允许访问网络
  2. 其它进程,非白名单将设置成拒绝访问RULE_REJECT_ALL
        if (isForeground) {            if (restrictMode) {                newRule = RULE_ALLOW_ALL;            }        } else if (restrictMode) {            newRule = isWhitelisted ? RULE_ALLOW_ALL : RULE_REJECT_ALL;        }

针对WindowManagerService来说,作用是取消窗口动画的效果。

                @Override                public void onLowPowerModeChanged(PowerSaveState result) {                    synchronized (mWindowMap) {                        final boolean enabled = result.batterySaverEnabled;                        if (mAnimationsDisabled != enabled && !mAllowAnimationsInLowPowerMode) {                            mAnimationsDisabled = enabled;                            dispatchNewAnimatorScaleLocked(null);                        }                    }                }

更多相关文章

  1. No.11 使用firewall配置的防火墙策略的生效模式
  2. 如何利用google原生包在android平台上实现语音识别
  3. Android(安卓)利用OnDraw实现自定义View
  4. Android(安卓)App签名(证书)校验过程源码分析
  5. android各阶段目标与要求
  6. android EditText处理焦点问题和键盘收起问题
  7. 【Android每周专题】横竖屏切换和Activity中View状态的保持
  8. Android的SurfaceView使用
  9. android:JNI与Android(安卓)VM之关系

随机推荐

  1. 解决 Android 在Eclipse 开发中 Class No
  2. android gravity and LinerarLayout for
  3. android vim编辑器的移植
  4. Android 使用Vitamio打造自己的万能播放
  5. Android SDK r20.x更新时,没有Android API
  6. android webview拦截超链接
  7. Android历史
  8. Android 2011年开发风向标
  9. android shape 代码实现按钮背景
  10. Pyqtdeploy Android PyQtForAndroid 编译