Android NetworkPolicyManagerService

Android 网络策略管理

工作中遇到一个问题,App在后台运行获取当前网络链接状态,网络是BLOCKED状态。跟踪下这个状态的产生。
问题出现在Android7,我看的源码是Android 12.

获取当前网络状态

   ConnectivityManager mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();

NetworkInfo里面关于连接状态的分类为:
public enum DetailedState {
/** Ready to start data connection setup. /
IDLE,
/
* Searching for an available access point. /
SCANNING,
/
* Currently setting up data connection. /
CONNECTING,
/
* Network link established, performing authentication. /
AUTHENTICATING,
/
* Awaiting response from DHCP server in order to assign IP address information. /
OBTAINING_IPADDR,
/
* IP traffic should be available. /
CONNECTED,
/
* IP traffic is suspended /
SUSPENDED,
/
* Currently tearing down data connection. /
DISCONNECTING,
/
* IP traffic not available. /
DISCONNECTED,
/
* Attempt to connect failed. /
FAILED,
/
* Access to this network is blocked. /
BLOCKED,
/
* Link has poor connectivity. /
VERIFYING_POOR_LINK,
/
* Checking if network is a captive portal */
CAPTIVE_PORTAL_CHECK
}

看下这个状态的生成:

ConnectivityService.java
 public NetworkInfo getActiveNetworkInfo() {
        enforceAccessPermission();
        final int uid = mDeps.getCallingUid();
        final NetworkAgentInfo nai = getNetworkAgentInfoForUid(uid);
        if (nai == null) return null;
        final NetworkInfo networkInfo = getFilteredNetworkInfo(nai, uid, false);
        maybeLogBlockedNetworkInfo(networkInfo, uid);
        return networkInfo;
    }

final NetworkInfo networkInfo = getFilteredNetworkInfo(nai, uid, false); 这个里面就会判断是否BLOCKED。

 @NonNull
    private NetworkInfo filterNetworkInfo(@NonNull NetworkInfo networkInfo, int type,
            @NonNull NetworkCapabilities nc, int uid, boolean ignoreBlocked) {
        final NetworkInfo filtered = new NetworkInfo(networkInfo);
        // Many legacy types (e.g,. TYPE_MOBILE_HIPRI) are not actually a property of the network
        // but only exists if an app asks about them or requests them. Ensure the requesting app
        // gets the type it asks for.
        filtered.setType(type);
        if (isNetworkWithCapabilitiesBlocked(nc, uid, ignoreBlocked)) {
            filtered.setDetailedState(DetailedState.BLOCKED, null /* reason */,
                    null /* extraInfo */);
        }
        filterForLegacyLockdown(filtered);
        return filtered;
    }

isNetworkWithCapabilitiesBlocked就是最终判断入口。

  private boolean isNetworkWithCapabilitiesBlocked(@Nullable final NetworkCapabilities nc,
            final int uid, final boolean ignoreBlocked) {
        // Networks aren't blocked when ignoring blocked status
        if (ignoreBlocked) {
            return false;
        }
        if (isUidBlockedByVpn(uid, mVpnBlockedUidRanges)) return true;
        final long ident = Binder.clearCallingIdentity();
        try {
            final boolean metered = nc == null ? true : nc.isMetered();
            return mPolicyManager.isUidNetworkingBlocked(uid, metered);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

ignoreBlocked是false。所以会继续往下。
具体的实现就是在NetworkPolicyManagerService。

NetworkPolicyManagerService


 static boolean isUidNetworkingBlockedInternal(int uid, int uidRules, boolean isNetworkMetered,
            boolean isBackgroundRestricted, @Nullable NetworkPolicyLogger logger) {
        final int reason;
        // Networks are never blocked for system components
        if (isSystem(uid)) {
            reason = NTWK_ALLOWED_SYSTEM;
        } else if (hasRule(uidRules, RULE_REJECT_RESTRICTED_MODE)) {
            reason = NTWK_BLOCKED_RESTRICTED_MODE;
        } else if (hasRule(uidRules, RULE_REJECT_ALL)) {
            reason = NTWK_BLOCKED_POWER;
        } else if (!isNetworkMetered) {
            reason = NTWK_ALLOWED_NON_METERED;
        } else if (hasRule(uidRules, RULE_REJECT_METERED)) {
            reason = NTWK_BLOCKED_DENYLIST;
        } else if (hasRule(uidRules, RULE_ALLOW_METERED)) {
            reason = NTWK_ALLOWED_ALLOWLIST;
        } else if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) {
            reason = NTWK_ALLOWED_TMP_ALLOWLIST;
        } else if (isBackgroundRestricted) {
            reason = NTWK_BLOCKED_BG_RESTRICT;
        } else {
            reason = NTWK_ALLOWED_DEFAULT;
        }

        final boolean blocked;
        switch(reason) {
            case NTWK_ALLOWED_DEFAULT:
            case NTWK_ALLOWED_NON_METERED:
            case NTWK_ALLOWED_TMP_ALLOWLIST:
            case NTWK_ALLOWED_ALLOWLIST:
            case NTWK_ALLOWED_SYSTEM:
                blocked = false;
                break;
            case NTWK_BLOCKED_RESTRICTED_MODE:
            case NTWK_BLOCKED_POWER:
            case NTWK_BLOCKED_DENYLIST:
            case NTWK_BLOCKED_BG_RESTRICT:
                blocked = true;
                break;
            default:
                throw new IllegalArgumentException();
        }
        if (logger != null) {
            logger.networkBlocked(uid, reason);
        }

        return blocked;
    }

这里是取出一个保存的状态,一直比较状态。
根据顺序形成优先级判断。这里会说明是否blocked和原因是什么,但是在设置给networkInfo的时候reason直接给的null,所以上层app只知道blocked,但是不知道原因。
根据上面的代码可以知道:1system app是永远不会blocked。

private static boolean isSystem(int uid) {
        return uid < Process.FIRST_APPLICATION_UID;//  public static final int FIRST_APPLICATION_UID = 10000;
    }

然后我的情况是Wi-Fi,不是计费网络,所以
if (!isNetworkMetered) {
reason = NTWK_ALLOWED_NON_METERED;
}

这段代码以后判断不用看了。那么就是之前的几个判断:
NTWK_BLOCKED_RESTRICTED_MODE,NTWK_BLOCKED_POWER,对于规则的判断就是RULE_REJECT_RESTRICTED_MODE,RULE_REJECT_ALL。

那么问题来了,这里的rule是怎么来的?

 synchronized (mUidRulesFirstLock) {
            uidRules = mUidRules.get(uid, RULE_NONE);
            isBackgroundRestricted = mRestrictBackground;
        }

那么mUidRules又是怎么生成,怎么变化的?

服务初始化

SystemServer里面会调用到下面这个,在之前的版本是在本类的systemReady里面初始化。

    private void initService(CountDownLatch initCompleteSignal) {
    ......
 }

这部分比较长。不直接贴,我下面分开贴。

1.PowerManager

   updatePowerSaveWhitelistUL();//白名单
 mPowerManagerInternal.registerLowPowerModeObserver(
                            new PowerManagerInternal.LowPowerModeListener() {
                                @Override
                                public int getServiceType() {
                                    return ServiceType.NETWORK_FIREWALL;
                                }

                                @Override
                                public void onLowPowerModeChanged(PowerSaveState result) {
                                    final boolean enabled = result.batterySaverEnabled;
                                    if (LOGD) {
                                        Slog.d(TAG, "onLowPowerModeChanged(" + enabled + ")");
                                    }
                                    synchronized (mUidRulesFirstLock) {
                                        if (mRestrictPower != enabled) {
                                            mRestrictPower = enabled;
                                            updateRulesForRestrictPowerUL();
                                        }
                                    }
                                }
                            });
                            mPowerManagerInternal.registerLowPowerModeObserver(
                            new PowerManagerInternal.LowPowerModeListener() {
                                @Override
                                public int getServiceType() {
                                    return ServiceType.DATA_SAVER;
                                }

                                @Override
                                public void onLowPowerModeChanged(PowerSaveState result) {
                                    synchronized (mUidRulesFirstLock) {
                                        updateRestrictBackgroundByLowPowerModeUL(result);
                                    }
                                }
                            });

2.ams uid

 mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
            try {
                final int changes = ActivityManager.UID_OBSERVER_PROCSTATE
                        | ActivityManager.UID_OBSERVER_GONE
                        | ActivityManager.UID_OBSERVER_CAPABILITY;
                mActivityManager.registerUidObserver(mUidObserver, changes,
                        NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE, "android");
                mNetworkManager.registerObserver(mAlertObserver);
            } catch (RemoteException e) {
                // ignored; both services live in system_server
            }

3.一堆广播接收器

// listen for changes to power save allowlist
            final IntentFilter whitelistFilter = new IntentFilter(
                    PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED);
            mContext.registerReceiver(mPowerSaveWhitelistReceiver, whitelistFilter, null, mHandler);

            // watch for network interfaces to be claimed
            final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION);
            mContext.registerReceiver(mConnReceiver, connFilter, NETWORK_STACK, mHandler);

            // listen for package changes to update policy
            final IntentFilter packageFilter = new IntentFilter();
            packageFilter.addAction(ACTION_PACKAGE_ADDED);
            packageFilter.addDataScheme("package");
            mContext.registerReceiver(mPackageReceiver, packageFilter, null, mHandler);

            // listen for UID changes to update policy
            mContext.registerReceiver(
                    mUidRemovedReceiver, new IntentFilter(ACTION_UID_REMOVED), null, mHandler);

            // listen for user changes to update policy
            final IntentFilter userFilter = new IntentFilter();
            userFilter.addAction(ACTION_USER_ADDED);
            userFilter.addAction(ACTION_USER_REMOVED);
            mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);

            // listen for stats update events
            final IntentFilter statsFilter = new IntentFilter(ACTION_NETWORK_STATS_UPDATED);
            mContext.registerReceiver(
                    mStatsReceiver, statsFilter, READ_NETWORK_USAGE_HISTORY, mHandler);

            // listen for restrict background changes from notifications
            final IntentFilter allowFilter = new IntentFilter(ACTION_ALLOW_BACKGROUND);
            mContext.registerReceiver(mAllowReceiver, allowFilter, MANAGE_NETWORK_POLICY, mHandler);

            // Listen for snooze from notifications
            mContext.registerReceiver(mSnoozeReceiver,
                    new IntentFilter(ACTION_SNOOZE_WARNING), MANAGE_NETWORK_POLICY, mHandler);
            mContext.registerReceiver(mSnoozeReceiver,
                    new IntentFilter(ACTION_SNOOZE_RAPID), MANAGE_NETWORK_POLICY, mHandler);

            // listen for configured wifi networks to be loaded
            final IntentFilter wifiFilter =
                    new IntentFilter(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
            mContext.registerReceiver(mWifiReceiver, wifiFilter, null, mHandler);

            // listen for carrier config changes to update data cycle information
            final IntentFilter carrierConfigFilter = new IntentFilter(
                    ACTION_CARRIER_CONFIG_CHANGED);
            mContext.registerReceiver(mCarrierConfigReceiver, carrierConfigFilter, null, mHandler);

这部分直接看源码注视就好了。
4.ConnManager

  // listen for meteredness changes
            mConnManager.registerNetworkCallback(
                    new NetworkRequest.Builder().build(), mNetworkCallback);

5.AppStandby

mAppStandby.addListener(new NetPolicyAppIdleStateChangeListener());
   synchronized (mUidRulesFirstLock) {
      updateRulesForAppIdleParoleUL();//把防火墙里面的数据同步下
}
上面的代码是12的,7的这部分是
 mUsageStats.addAppIdleStateChangeListener(new AppIdleStateChangeListener());
          

6.SubscriptionManager

// Listen for subscriber changes
            mContext.getSystemService(SubscriptionManager.class).addOnSubscriptionsChangedListener(
                    new HandlerExecutor(mHandler),
                    new OnSubscriptionsChangedListener() {
                        @Override
                        public void onSubscriptionsChanged() {
                            updateNetworksInternal();
                        }
                    });

综上,就是监听一系列网络变化,用户变化,app变化和app空闲的判断,低电量和省电等。这里可以参考android官方:低电耗模式和应用待机模式。

AppStandby

这部分怎么来的,本期不关注,直接看回调。NetPolicyAppIdleStateChangeListener。

   abstract static class AppIdleStateChangeListener {

        /** Callback to inform listeners that the idle state has changed to a new bucket. */
        public abstract void onAppIdleStateChanged(String packageName, @UserIdInt int userId,
                boolean idle, int bucket, int reason);

        /**
         * Callback to inform listeners that the parole state has changed. This means apps are
         * allowed to do work even if they're idle or in a low bucket.
         */
        public void onParoleStateChanged(boolean isParoleOn) {
            // No-op by default
        }

        /**
         * Optional callback to inform the listener that the app has transitioned into
         * an active state due to user interaction.
         */
        public void onUserInteractionStarted(String packageName, @UserIdInt int userId) {
            // No-op by default
        }
    }

onAppIdleStateChanged具体实现就在下面:

  public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket,
                int reason) {
            try {
                final int uid = mContext.getPackageManager().getPackageUidAsUser(packageName,
                        PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
                synchronized (mUidRulesFirstLock) {
                    mLogger.appIdleStateChanged(uid, idle);
                    updateRuleForAppIdleUL(uid);
                    updateRulesForPowerRestrictionsUL(uid);
                }
            } catch (NameNotFoundException nnfe) {
            }
        }

最后执行到: updateRulesForPowerRestrictionsULInner(uid, oldUidRules, isUidIdle),这里就更新rule。

      final boolean restrictMode = isUidIdle || mRestrictPower || mDeviceIdleMode;
        final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid);

        final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid, mDeviceIdleMode);
   if (isForeground) {
            if (restrictMode) {
                newUidRules |= RULE_ALLOW_ALL;
            }
        } else if (restrictMode) {
            newUidRules |= isWhitelisted ? RULE_ALLOW_ALL : RULE_REJECT_ALL;
        }

restrictMode 根据我的情况,这个肯定是true。
isUidForegroundOnRestrictPowerUL 这里,是不是比前台服务优先级更高:

public static final int FOREGROUND_THRESHOLD_STATE =
            ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
 procState <= FOREGROUND_THRESHOLD_STATE

基本断定是false,因为我没有启动前台。
isWhitelistedFromPowerSaveUL

 private boolean isWhitelistedFromPowerSaveUL(int uid, boolean deviceIdleMode) {
        final int appId = UserHandle.getAppId(uid);
        boolean isWhitelisted = mPowerSaveTempWhitelistAppIds.get(appId)
                || mPowerSaveWhitelistAppIds.get(appId);
        if (!deviceIdleMode) {
            isWhitelisted = isWhitelisted || isWhitelistedFromPowerSaveExceptIdleUL(uid);
        }
        return isWhitelisted;
    }

是否在白名单中,false。

所以newUidRules=RULE_REJECT_ALL。

OK,状态确认。那么回到最开始的判断,状态是:NTWK_BLOCKED_POWER,blocked。结束。
然后为啥这个状态会影响网络访问:

updateRuleForAppIdleUL(uid);
 if (!mPowerSaveTempWhitelistAppIds.get(appId) && isUidIdle(uid)
                    && !isUidForegroundOnRestrictPowerUL(uid)) {
                setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DENY);
                if (LOGD) Log.d(TAG, "updateRuleForAppIdleUL DENY " + uid);
 }

网络在这里被关了。

以上,完毕。如有纰漏,请大家斧正。谢谢

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值