Android 9.0 电源管理之省电模式

2019-07-14 03:04发布

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