Android的平台上,耗电量的问题一直被人所诟病。从Lollipop开始,Google也一直非常重视对于省电模式的改造。本篇文章将会基于最新的Android Pie的代码,来系统分析现在Android的省电模式流程,并且对一些可以继续优化的点来给出一些建议。本篇文章将会从SystemUI开始讲起。
这个图片相信使用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);
}
BatterySaverUtils
是frameworks/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);
}
}
这边有几个需要注意的地方:
-
enforceCallingOrSelfPermission
是Android framework的一个安全记住,主要是检查当前调用进程的UID是否具有操作某一项系统的权限。比如省电模式,因为是SystemUI里面进行操作,所以SystemUI
的AndroidManifest.xml
里面,一定会声明DEVICE_POWER
的权限,否则将会被做为异常抛出。
- 当SystemUI通过调用PowerManager且检查完权限之后,
Binder.clearCallingIdentity
将会清除SystemUI Process的UID
,PID
的信息,将其换成PownerManager所在进程UID
,PID
的内容。因为这块涉及到了binder通信,所以我们将会在后续Binder的内容中对其进行阐述。 - 在try{}finally{}中,调用了
Binder.restoreCallingIdentity()
。
这个作用是恢复远程调用端的uid和pid信息,正好是clearCallingIdentity
的反过程。 - 接下来,就是核心调用了,
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 + ")");
}
}
-
wasEnabled
首先会去判断是否之前已经是enable的状态,如果是的话,那么就return。 -
isPowered
如果为true的话的也是会直接返回。 -
mLastChangedIntReason
,mLastChangedStrReason
的值会被保存为之前传进来的值,也就是mLastChangedIntReason=2
,mLastChangedStrReason="Manual ON"
. -
manual
和enable
都是true
的状态,所以会upateSnoozingLocked
.而这个函数,其实只是设置mBatterySaverSnoozing
的值为ture
。而这个值的使用,我们后续还会遇到。 -
putGlobalSetting(Global.LOW_POWER_MODE, enable ? 1 : 0);
,将LOW_POWER_MODE
的值在数据库中置为1. -
putGlobalSetting(Global.LOW_POWER_MODE_STICKY, enable ? 1 : 0);
,将LOW_POWER_MODE_STICKY
的值在数据库中置为1. -
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;
}
}
- 因为
MSG_STATE_CHANGED
是之前填充并发送的消息,所以会到MSG_STATE_CHANGED
的case中进行处理。 -
handleBatterySaverStateChanged(msg.arg1 == ARG_SEND_BROADCAST, msg.arg2);
的函数中,第一个参数由于之前传递的也是ARG_SEND_BROADCAST
, 所以为true;第二个参数是之前填充的reason
,所以为2
.handleBatterySaverStateChanged
的函数非常的复杂,涉及到了jni
,native
,devices
,broadcast
等信息,所以接下来是真正的重头戏了。
/**
* 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);
}
}
}
这个函数非常的大,进行的操作详细分解如下:
-
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
。
-
listeners = mListeners.toArray(new LowPowerModeListener[mListeners.size()]);
这里的mListeners
其实是之前注册的时候,所有添加LowPowerModeListener
的service。包含了VibratorService
,NetworkPolicyManagerService
等。这些内容都会进行一次保存,方便后面的消息分发。 - 此处
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......
- 我们继续回到刚才的主函数中进行分析。
分析完了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
,但是只是用于保存当前的状态,所以我们不深究。
-
fileValues
默认为空,我们也不处理分析。 - 接下来的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
。
- 接下来就是向各个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_CHANGED
,ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL
,
我们接下来对着三个广播进行一对一的分析。
-
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和界面显示上面的一些操作。
-
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();
}
}
- 最后是之前保存的一个数组回调函数:
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);
}
}
- 更新临时白名单、白名单、除了idle app之外的白名单都将允许网络访问
- 如果是进程优先级是前台服务以上的允许网络访问
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;
}
-
isUidValidForBlacklistRules
当uid
是media
或者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;
}
- 如果是前台进程,就算是受限模式下也会允许访问网络
- 其它进程,非白名单将设置成拒绝访问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);
}
}
}