Android 9.0 电源管理之省电模式

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

《Android 9.0 电源管理之省电模式》 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的权限,否则将会被做为异常抛出。
<uses-permission android:name="android.permission.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. mLastChangedIntReasonmLastChangedStrReason的值会被保存为之前传进来的值,也就是 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,所以为2. handleBatterySaverStateChanged的函数非常的复杂,涉及到了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<String, String> 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<PowerHint>(hintId), data);
}

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

static void sendPowerHint(PowerHint hintId, uint32_t data) {
    sp<IPowerV1_1> powerHalV1_1 = getPowerHalV1_1();
    Return<void> ret;
    if (powerHalV1_1 != nullptr) {
        ret = powerHalV1_1->powerHintAsync(hintId, data);
        processPowerHalReturn(ret, "powerHintAsync");
    } else {
        sp<IPowerV1_0> 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<void> Power::powerHintAsync(PowerHint hint, int32_t data) {
    // just call the normal power hint in this oneway function
    return powerHint(hint, data);
}

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

Return<void> 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<power_hint_t>(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_CHANGINGACTION_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.java
case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED:
SoundTriggerHelper.java
if (!PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(intent.getAction())) {
GnssLocationProvider.java
PowerManager.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<UserInfo> 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);
                        }
                    }
                }
    原文作者:猿氏物语
    原文地址: https://www.jianshu.com/p/095bd974c925
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞