待机数据联网,会控制应用后台数据访问的频率,从而降低待机功耗。针对的是应用后台频繁访问网络的优化。
待机网络功耗优化,首先需要将捕捉app的状态
在framework中有这样一个binder IUidObserver
oneway interface IUidObserver {
/**
* General report of a state change of an uid.
*
* @param uid The uid for which the state change is being reported.
* @param procState The updated process state for the uid.
* @param procStateSeq The sequence no. associated with process state change of the uid,
* see UidRecord.procStateSeq for details.
*/
void onUidStateChanged(int uid, int procState, long procStateSeq);
/**
* Report that there are no longer any processes running for a uid.
*/
void onUidGone(int uid, boolean disabled);
/**
* Report that a uid is now active (no longer idle).
*/
void onUidActive(int uid);
/**
* Report that a uid is idle -- it has either been running in the background for
* a sufficient period of time, or all of its processes have gone away.
*/
void onUidIdle(int uid, boolean disabled);
/**
* Report when the cached state of a uid has changed.
* If true, a uid has become cached -- that is, it has some active processes that are
* all in the cached state. It should be doing as little as possible at this point.
* If false, that a uid is no longer cached. This will only be called after
* onUidCached() has been reported true. It will happen when either one of its actively
* running processes is no longer cached, or it no longer has any actively running processes.
*/
void onUidCachedChanged(int uid, boolean cached);
}
展讯的方案是监听onUidGone这个方法,为啥不用onUidIdle,我还没理解,我们就暂时这么理解就是当调用到onUidGone的时候,说明app已经进入adle模式,下面我拿就接着onUidGone分析
PowerController.java-->onUidGone()
@Override public void onUidGone(int uid, boolean disabled) throws RemoteException {
synchronized (mUidStateLock) {
removeUidStateLocked(uid);
}
mAppStateInfoCollector.removeUidState(uid);
}
PowerController.java-->updateUidStateLocked()
private void removeUidStateLocked(int uid) {
final int index = mUidState.indexOfKey(uid);
if (index >= 0) {
final int oldUidState = mUidState.valueAt(index);
mUidState.removeAt(index);
String appName = null;
try {
appName = AppGlobals.getPackageManager().getNameForUid(uid);
} catch (RemoteException e) {
// can't happen; package manager is process-local
}
if (DEBUG_MORE) Slog.d(TAG, "removeUidStateLocked: packageName:" + appName + ", uid:" + uid + " state : " + Util.ProcState2Str(oldUidState));
if ((null != appName) ) {
msgHandler.sendMessage(msgHandler.obtainMessage(MSG_UID_STATE_CHANGED, uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY, appName));
// Fot PowerHint
if (mBackgroundCleanHelper != null) {
mBackgroundCleanHelper.noteAppStopped(uid, appName);
}
}
}
}
发送MSG_UID_STATE_CHANGED
case MSG_UID_STATE_CHANGED:
handleProcStateChanged((String)msg.obj, msg.arg1, msg.arg2);
break;
PowerController.java-->handleProcStateChanged()
private void handleProcStateChanged(String appName, int uid, int procState) {
if (DEBUG) Slog.d(TAG, "- handleProcstateChanged() E - packageName:" + appName
+ " uid:" + uid + " procState:" + Util.ProcState2Str(procState));
if (mAppStateInfoCollector.reportAppProcStateInfo(appName, uid, procState)) {
// Note: Bug 698133 appIdle cts fail -->BEG
// Ugly: we have to check if doing cts/gts test
// is cts/gts test, then
checkCtsGtsTesting(appName);
// Note: Bug 698133 appIdle cts fail <--END
}
int userId = UserHandle.getUserId(uid);
AppState appState = mAppStateInfoCollector.getAppState(appName, userId);
if (appState == null) {
Slog.w(TAG, "null appState for packageName:" + appName
+ " uid:" + uid + " procState:" + Util.ProcState2Str(procState));
return;
}
// if app is stopped, notify WakelockConstraintHelper
if (appState.mProcState == ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
appState.updateAppState(Event.NONE);
mWakelockConstraintHelper.noteAppStopped(appState);
}
mRecogAlgorithm.reportEvent(appName, uid, RecogAlgorithm.EVENT_TYPE_PROC_STATE, procState);
if (mCharging || mScreenOn)
return;
// recode the rxBytes if needed
// should be called before doing Evaluated time stamp update
appState.updateAppTrafficStats(false);
// notify helpers to update time stamp
updateAppEvaluatedTimeStamp(appState);
// Note: Bug#695969 Audio Recoder fail --> BEG
// if new procState is <= PROCESS_STATE_FOREGROUND_SERVICE
// then update its Notification state
if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
appState.updateActiveNotificationState(mContext);
}
// Note: Bug#695969 Audio Recoder fail <-- END
msgHandler.removeMessages(MSG_CHECK);
msgHandler.sendMessage(msgHandler.obtainMessage(MSG_CHECK));
cancelAlarmLocked();
}
发送MSG_CHECK
case MSG_CHECK:
checkAllAppStateInfo();
break;
PowerController.java-->checkAllAppStateInfo()
private void checkAllAppStateInfo() {
if (DEBUG) Slog.d(TAG, "- checkAllAppStateInfo() E -");
if (DEBUG) Slog.d(TAG, "mCharging:" + mCharging + " mScreenOn:" + mScreenOn + " mMobileConnected:" + mMobileConnected);
//set next check
//msgHandler.removeMessages(MSG_CHECK);
//msgHandler.sendMessageDelayed(msgHandler.obtainMessage(MSG_CHECK), CHECK_INTERVAL);
scheduleAlarmLocked(CHECK_INTERVAL);
if (mCharging || mScreenOn)
return;
checkSystemUpTime();
boolean bChanged = false;
// Note:use the same now elapsed time for all the AppStateInfo
// otherwise, when checking app Parole, some app may have not
// opportunity to exit app standby.
long now = SystemClock.elapsedRealtime();
updatePowerSaveMode();
try {
final List<UserInfo> users = mUserManager.getUsers();
for (int ui = users.size() - 1; ui >= 0; ui--) {
UserInfo user = users.get(ui);
if (DEBUG) Slog.d(TAG, "- checkAllAppStateInfo() for user: " + user.id);
final ArrayMap<String, AppState> appStateInfoList = mAppStateInfoCollector.getAppStateInfoList(user.id);
for (int i=0;i<appStateInfoList.size();i++) {
AppState appState = appStateInfoList.valueAt(i);
//let app to be parole
mAppIdleHelper.checkAppParole(appState, now);
// check app state info
if (checkAppStateInfo(appState, now)) {
bChanged = true;
}
}
}
} catch (Exception e) {
}
// note AppidleHelper all check done
mAppIdleHelper.noteCheckAllAppStateInfoDone();
// note mWakelockConstraintHelper all check done
mWakelockConstraintHelper.noteCheckAllAppStateInfoDone();
//send notification to powerguru & appstandby
// changed for bug#891620
//if (bChanged) msgHandler.sendMessage(msgHandler.obtainMessage(MSG_NOTIFY_CHANGED));
if (bChanged) notifyChanged();
if (needCheckNetworkConnection(now)) {
if (DEBUG) Slog.d(TAG, "- checkNetworkConnection() in checkAllAppStateInfo -");
checkNetworkConnection(true);
}
// check doze state
checkDozeState();
}
PowerController.java-->notifyChanged()
private void notifyChanged() {
if (DEBUG) Slog.d(TAG, "- notifyChanged() E -");
for (int i = 0; i < mHelpers.size(); i++) {
PowerSaveHelper helper = mHelpers.get(i);
helper.applyConstrain();
}
}
AppIdleHelper 继承PowerSaveHelper
AppIdleHelper.java-->applyConstrain()
@Override
void applyConstrain() {
for (int index=mNewStandbyListForUsers.size()-1; index>=0; index--) {
ArrayMap<String, Integer> mNewStandbyList = mNewStandbyListForUsers.valueAt(index);
for (int i=0;i<mNewStandbyList.size();i++) {
try {
if (DEBUG) Slog.d(TAG, "packageName:" + mNewStandbyList.keyAt(i)
+ " userId:" + mNewStandbyList.valueAt(i) + " enter standby");
int userId = mNewStandbyList.valueAt(i);
setAppInactive(mNewStandbyList.keyAt(i), true, userId);
updateAppStandbyState(mNewStandbyList.keyAt(i), true, userId);
} catch (Exception e) {
// fall through
}
}
mNewStandbyList.clear();
}
}
AppIdleHelper.java-->setAppInactive()
private void setAppInactive(String packageName, boolean idle, int userId) {
if (!mPowerControllerAppIdleEnabled || mUsageStatsInternal == null) return;
mUsageStatsInternal.setAppInactive(packageName, idle, userId);
}
UsageStatsManagerInternal是一个抽象类,UsageStatsService的内部类LocalService继承了UsageStatsManagerInternal,UsageStatsService是一个Android私有service,用于统计应用使用情况,主要作用是收集用户使用每一个APP的频率、使用时常。
UsageStatsService.java-->LocalService-->setAppInactive()
@Override
public void setAppInactive(String packageName, boolean idle, int userId) {
if (mPowerControllerHelper != null)
mPowerControllerHelper.setAppInactive(packageName, idle, userId);
}
UsageStatsService.java-->setAppInactive()
public void setAppInactive(String packageName, boolean idle, int userId) {
try {
PackageInfo pi = AppGlobals.getPackageManager()
.getPackageInfo(packageName, 0, userId);
if (pi == null) return;
UsageStatsService.this.setAppIdleAsync(packageName, idle, userId);
setAppForceIdleFlag(packageName, idle, userId);
} catch (RemoteException re) {
} finally {
}
}
UsageStatsService.java-->setAppIdleAsync()
void setAppIdleAsync(String packageName, boolean idle, int userId) {
if (packageName == null) return;
mHandler.obtainMessage(MSG_FORCE_IDLE_STATE, userId, idle ? 1 : 0, packageName)
.sendToTarget();
}
发送MSG_FORCE_IDLE_STATE
case MSG_FORCE_IDLE_STATE:
forceIdleState((String) msg.obj, msg.arg1, msg.arg2 == 1);
break;
UsageStatsService.java-->forceIdleState()
void forceIdleState(String packageName, int userId, boolean idle) {
final int appId = getAppId(packageName);
if (appId < 0) return;
final long elapsedRealtime = SystemClock.elapsedRealtime();
final boolean previouslyIdle = isAppIdleFiltered(packageName, appId,
userId, elapsedRealtime);
synchronized (mAppIdleLock) {
mAppIdleHistory.setIdle(packageName, userId, idle, elapsedRealtime);
}
final boolean stillIdle = isAppIdleFiltered(packageName, appId,
userId, elapsedRealtime);
// Inform listeners if necessary
if (previouslyIdle != stillIdle) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
/* idle = */ stillIdle ? 1 : 0, packageName));
if (!stillIdle) {
notifyBatteryStats(packageName, userId, idle);
}
}
}
发送MSG_INFORM_LISTENERS
case MSG_INFORM_LISTENERS:
informListeners((String) msg.obj, msg.arg1, msg.arg2 == 1);
break;
UsageStatsService.java-->informListeners()
void informListeners(String packageName, int userId, boolean isIdle) {
for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
listener.onAppIdleStateChanged(packageName, userId, isIdle);
}
}
NetworkPolicyManagerService的内部类AppIdleStateChangeListener继承了UsageStatsManagerInternal.AppIdleStateChangeListener
private class AppIdleStateChangeListener
extends UsageStatsManagerInternal.AppIdleStateChangeListener {
@Override
public void onAppIdleStateChanged(String packageName, int userId, boolean idle) {
try {
final int uid = mContext.getPackageManager().getPackageUidAsUser(packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
if (LOGV) Log.v(TAG, "onAppIdleStateChanged(): uid=" + uid + ", idle=" + idle);
synchronized (mUidRulesFirstLock) {
updateRuleForAppIdleUL(uid);
updateRulesForPowerRestrictionsUL(uid);
}
} catch (NameNotFoundException nnfe) {
}
}
......
}
NetworkPolicyManagerService.java-->updateRuleForAppIdleUL()
void updateRuleForAppIdleUL(int uid) {
if (!isUidValidForBlacklistRules(uid)) return;
// NOTE: Bug #693427 low power Feature BEG -->
boolean ignoreProcState = PowerControllerHelper.getInstance(mContext).ignoreProcStateForAppIdle();
// <-- NOTE: Bug #693427 low power Feature END
if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRuleForAppIdleUL: " + uid );
}
try {
int appId = UserHandle.getAppId(uid);
if (!mPowerSaveTempWhitelistAppIds.get(appId) && isUidIdle(uid)
&& (ignoreProcState || !isUidForegroundOnRestrictPowerUL(uid))) {
setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DENY);
} else {
setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT);
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
}
}
NetworkPolicyManagerService.java-->setUidFirewallRule()
/**
* Add or remove a uid to the firewall blacklist for all network ifaces.
*/
private void setUidFirewallRule(int chain, int uid, int rule) {
if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK,
"setUidFirewallRule: " + chain + "/" + uid + "/" + rule);
}
try {
if (chain == FIREWALL_CHAIN_DOZABLE) {
mUidFirewallDozableRules.put(uid, rule);
} else if (chain == FIREWALL_CHAIN_STANDBY) {
mUidFirewallStandbyRules.put(uid, rule);
} else if (chain == FIREWALL_CHAIN_POWERSAVE) {
mUidFirewallPowerSaveRules.put(uid, rule);
}
try {
mNetworkManager.setFirewallUidRule(chain, uid, rule);
} catch (IllegalStateException e) {
Log.wtf(TAG, "problem setting firewall uid rules", e);
} catch (RemoteException e) {
// ignored; service lives in system_server
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
}
}
NetworkManagementService.java-->setFirewallUidRule()
@Override
public void setFirewallUidRule(int chain, int uid, int rule) {
enforceSystemUid();
synchronized (mQuotaLock) {
setFirewallUidRuleLocked(chain, uid, rule);
}
}
NetworkManagementService.java-->setFirewallUidRuleLocked()
private void setFirewallUidRuleLocked(int chain, int uid, int rule) {
if (updateFirewallUidRuleLocked(chain, uid, rule)) {
try {
mConnector.execute("firewall", "set_uid_rule", getFirewallChainName(chain), uid,
getFirewallRuleName(chain, rule));
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
}
}