在第一篇Android 8.1 Doze模式分析(一)我们知道,进入idle模式后调用NetworkPolicyManagerService.java的setDeviceIdleMode()对网络进行限制,现在我们就开始分析网络限制的流程
@Override
public void setDeviceIdleMode(boolean enabled) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "setDeviceIdleMode");
try {
synchronized (mUidRulesFirstLock) {
if (mDeviceIdleMode == enabled) {
return;
}
mDeviceIdleMode = enabled;
if (mSystemReady) {
// Device idle change means we need to rebuild rules for all
// known apps, so do a global refresh.
updateRulesForRestrictPowerUL();
}
}
if (enabled) {
EventLogTags.writeDeviceIdleOnPhase("net");
} else {
EventLogTags.writeDeviceIdleOffPhase("net");
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
}
}
NetworkPolicyManagerService.java-->updateRulesForRestrictPowerUL()
private void updateRulesForRestrictPowerUL() {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForRestrictPowerUL");
try {
updateRulesForDeviceIdleUL();
updateRulesForPowerSaveUL();
updateRulesForAllAppsUL(TYPE_RESTRICT_POWER);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
}
}
首先看下updateRulesForDeviceIdleUL()方法
void updateRulesForDeviceIdleUL() {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForDeviceIdleUL");
try {
updateRulesForWhitelistedPowerSaveUL(mDeviceIdleMode, FIREWALL_CHAIN_DOZABLE,
mUidFirewallDozableRules);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
}
}
NetworkPolicyManagerService.java-->updateRulesForWhitelistedPowerSaveUL()
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--) {
//如果应用在白名单里,这是rule为FIREWALL_RULE_ALLOW
if (isProcStateAllowedWhileIdleOrPowerSaveMode(mUidState.valueAt(i))
// for bug#776330, wechat cannot receive msg in low-power mode
|| mAbsDeviceIdleController.isInPresetWhiteAppList(mUidState.keyAt(i))) {
uidRules.put(mUidState.keyAt(i), FIREWALL_RULE_ALLOW);
}
}
setUidFirewallRulesUL(chain, uidRules, CHAIN_TOGGLE_ENABLE);
} else {
setUidFirewallRulesUL(chain, null, CHAIN_TOGGLE_DISABLE);
}
}
首先分析下三个参数,因为已经进入到了zone模式,所以mDeviceIdleMode未true,FIREWALL_CHAIN_DOZABLE是FIREWALL的CHAIN模式的四种之一
public static final int FIREWALL_CHAIN_NONE = 0;
public static final int FIREWALL_CHAIN_DOZABLE = 1;
public static final int FIREWALL_CHAIN_STANDBY = 2;
public static final int FIREWALL_CHAIN_POWERSAVE = 3;
我们在看下最后一个参数mUidFirewallDozableRules,是一个SparseIntArray 对象
@GuardedBy("mUidRulesFirstLock")
final SparseIntArray mUidFirewallDozableRules = new SparseIntArray();
在setUidFirewallRule中,就应用的uid和rule 存入进去
/**
* 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);
}
}
参数分析完后,我们看下setUidFirewallRulesUL()
private void setUidFirewallRulesUL(int chain, @Nullable SparseIntArray uidRules,
@ChainToggleType int toggle) {
if (uidRules != null) {
setUidFirewallRulesUL(chain, uidRules);
}
if (toggle != CHAIN_TOGGLE_NONE) {
enableFirewallChainUL(chain, toggle == CHAIN_TOGGLE_ENABLE);
}
}
setUidFirewallRulesUL()
private void setUidFirewallRulesUL(int chain, SparseIntArray uidRules) {
try {
int size = uidRules.size();
int[] uids = new int[size];
int[] rules = new int[size];
for(int index = size - 1; index >= 0; --index) {
uids[index] = uidRules.keyAt(index);
rules[index] = uidRules.valueAt(index);
}
mNetworkManager.setFirewallUidRules(chain, uids, rules);
} catch (IllegalStateException e) {
Log.wtf(TAG, "problem setting firewall uid rules", e);
} catch (RemoteException e) {
// ignored; service lives in system_server
}
}
NetworkManagementService.java-->setFirewallUidRules()
@Override
public void setFirewallUidRules(int chain, int[] uids, int[] rules) {
enforceSystemUid();
synchronized (mQuotaLock) {
synchronized (mRulesLock) {
SparseIntArray uidFirewallRules = getUidFirewallRulesLR(chain);
SparseIntArray newRules = new SparseIntArray();
// apply new set of rules
for (int index = uids.length - 1; index >= 0; --index) {
int uid = uids[index];
int rule = rules[index];
updateFirewallUidRuleLocked(chain, uid, rule);
newRules.put(uid, rule);
}
// collect the rules to remove.
SparseIntArray rulesToRemove = new SparseIntArray();
for (int index = uidFirewallRules.size() - 1; index >= 0; --index) {
int uid = uidFirewallRules.keyAt(index);
if (newRules.indexOfKey(uid) < 0) {
rulesToRemove.put(uid, FIREWALL_RULE_DEFAULT);
}
}
// remove dead rules
for (int index = rulesToRemove.size() - 1; index >= 0; --index) {
int uid = rulesToRemove.keyAt(index);
updateFirewallUidRuleLocked(chain, uid, FIREWALL_RULE_DEFAULT);
}
}
try {
switch (chain) {
case FIREWALL_CHAIN_DOZABLE:
mNetdService.firewallReplaceUidChain("fw_dozable", true, uids);
break;
case FIREWALL_CHAIN_STANDBY:
mNetdService.firewallReplaceUidChain("fw_standby", false, uids);
break;
case FIREWALL_CHAIN_POWERSAVE:
mNetdService.firewallReplaceUidChain("fw_powersave", true, uids);
break;
case FIREWALL_CHAIN_NONE:
default:
Slog.d(TAG, "setFirewallUidRules() called on invalid chain: " + chain);
}
} catch (RemoteException e) {
Slog.w(TAG, "Error flushing firewall chain " + chain, e);
}
}
}