Android应用后台网络管控机制

应用后台网络管控机制

概述

   在维护手管应用时,常常遇到与应用后台网络控制相关的问题,在解决这些问题的过程当中,学习了下应用后台网络控制的流程以及一些日志的分析方法,如今把它总结一下,方便本身以及他人的学习。java

网络管控流程

   对于后台网络管控主要的参与者以下:android

com.meizu.safe:NetworkManager
frameworks/base/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
frameworks/base/services/core/java/com/android/server/NetworkManagementService.javaweb

注:这里其实还有个底层的网络守护进程的参与,可是咱们这里不作太多描述,只描述框架层以上的逻辑。shell

   在介绍总的流程时,先简单的介绍下NetworkManager,顾名思义其是手管中用以控制网络管理的组件,而在应用后台管理时,主要有三种模式,以下。数据库

后台联网

   从上图能够看出,三种模式中,智能模式实际上是在应用规则前作了一层逻辑判断,但最终也是调用禁止或容许的接口,就不在赘述。微信

   下面大体介绍下后台网络管控的流程。网络

public void updateBackgroundAppInfo(String pkgName, final int controlType, final int uid) {
    int uid_key = -1;
    //获取转换后的过滤uid值
    if (-1 != uid) {
        uid_key = uid;
    } else {
        //to do parse fensheng app uid
        Log.d("myTemp66", "app install updateBackgroundAppInfo uid = -1");
        uid_key = transPkgToUid(mContext, pkgName);
    }
    //更新数据库
    boolean updateResult = trafficDataBase.updateAppControllType(uid, pkgName, controlType);
    Log.d("myTemp66", "updateBackgroundAppInfo --> updateResult:" + updateResult
            + " pkgName:" + pkgName + " controlType:" + controlType + " uid:" + uid_key);
    if (TrafficConst.STATUS_VALUE_INTELLIGENCE == controlType && updateResult) {
        //当为智能类型时,才用获取智能模式下的后台联网设置状态
        AppAlphameInfo alphameInfo = trafficAlphameManager.doQueryAlpmeApp(pkgName, TrafficConst.STATUS_BACKGROUND);
        Log.d("myTemp66", "intelligence status: " + alphameInfo.toString());
        appBackGourndNetControl(uid_key, alphameInfo.isWifiState(), alphameInfo.isMobileState());
    } else if (TrafficConst.STATUS_VALUE_REJECT == controlType && updateResult) {
        appBackGourndNetControl(uid_key, false, false);
        Mtj.onEvent(mContext, Mtj.CLICK_TRAFFIC_BGD_APP_BAN, "后台联网应用禁止点击次数");
    } else if (TrafficConst.STATUS_VALUE_ALLOW == controlType && updateResult) {
        appBackGourndNetControl(uid_key, true, true);
        Mtj.onEvent(mContext, Mtj.CLICK_TRAFFIC_BGD_APP_ALLOW, "后台联网应用容许点击次数");
    }
}

   首先,在用户修改模式后,NetworkManager组件会调用updateBackgroundAppInfo()方法,而在这个方法中会调用updateAppControllType()方法去更新数据库,而后再调用appBackGourndNetControl()去设置后台网络控制。app

/** * 设置底层后台流量数据控制接口(通信组接口) * @param uid 应用的UID * @param isWifiControl WIFI控制:true为打开,false为关闭 * @param isMobileControl Mobile控制(与wifi相似) */
public void appBackGourndNetControl(int uid, boolean isWifiControl, boolean isMobileControl) {
    Log.i(TAG, "appBackGourndNetControl: uid:" + uid + " wifi:" + isWifiControl + " mobile:" + isMobileControl);
    if (isWifiControl) {
        //打开wifi
        if (!networkControllManager.isAppWifiBackgroundUsageOpened(uid)) {
            Log.d(TAG, "appBackGourndNetControl: change wifibackground open:" + uid);
            networkControllManager.openAppWifiBackgroundUsage(uid);
        }

    } else {
        //关闭wifi
        if (networkControllManager.isAppWifiBackgroundUsageOpened(uid)) {
            Log.d(TAG, "appBackGourndNetControl: change wifibackground close:" + uid);
            networkControllManager.closeAppWifiBackgroundUsage(uid);
        }
    }

    if (isMobileControl) {
        //打开后台移动网络
        if (!networkControllManager.isAppMobileBackgroundUsageOpened(uid)) {
            Log.d(TAG, "appBackGourndNetControl: change mobilebackground open:" + uid);
            networkControllManager.openAppMobileBackgroundUsage(uid);
        }
    } else {
        //关闭后台移动网络
        if (networkControllManager.isAppMobileBackgroundUsageOpened(uid)) {
            Log.d(TAG, "appBackGourndNetControl: change mobilebackground close:" + uid);
            networkControllManager.closeAppMobileBackgroundUsage(uid);
        }
    }
}

public void closeAppWifiBackgroundUsage(String pkName) {
    int uid = PackageManagerUtil.getUid(pkName);
    Log.i(TAG, "close uid=" + uid + " background wifi net");
    addUidPolicy(new Object[]{uid, getPolicyRejectAppBackgroundNetWifi()});
}

/** * 关闭网络. * @param args */
private void addUidPolicy(Object... args) {
    Method addUidPolicyMethod = getAddUidPolicyMethod();
    try {
        addUidPolicyMethod.invoke(mNetWorkPolicyManager, args);
    } catch (IllegalAccessException e) {
        Log.e(TrafficConst.TRAFFIC_EXCEPTION, TAG + ":addUidPolicy --> " + e.toString());
    } catch (IllegalArgumentException e) {
        Log.e(TrafficConst.TRAFFIC_EXCEPTION, TAG + ":addUidPolicy --> " + e.toString());
    } catch (Exception e) {
        Log.e(TrafficConst.TRAFFIC_EXCEPTION, TAG + ":addUidPolicy --> " + e.toString());
    }
}

   在appBackGourndNetControl()方法中会经过NetworkControllManager类去调用关闭后台联网的接口,最终经过反射调用到框架层NetworkPolicyManagerService(NPMS)类中的addUidPolicy()方法。框架

@Override
public void addUidPolicy(int uid, int policy) {
    mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);

    if (!UserHandle.isApp(uid)) {
        throw new IllegalArgumentException("cannot apply policy to UID " + uid);
    }

    synchronized (mRulesLock) {
        final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
        policy |= oldPolicy;
        if (oldPolicy != policy) {
            setUidPolicyUncheckedLocked(uid, oldPolicy, policy, true);
        }
    }
}

private void setUidPolicyUncheckedLocked(int uid, int policy, boolean persist) {


    mLastUidPolicy = getUidPolicy(uid);


    mUidPolicy.put(uid, policy);



    Slog.d(TAG, "mLastUidPolicy " + mLastUidPolicy + " " + policy);
    updateRulesForUidWifiAndMobileLocked(uid);


    // uid policy changed, recompute rules and persist policy.
    updateRulesForDataUsageRestrictionsLocked(uid);
    if (persist) {
        writePolicyLocked();
    }
}

   在addUidPolicy()方法中,最主要的是调用了setUidPolicyUncheckedLocked()方法,在这方法中,flyme系统添加了updateRulesForUidWifiAndMobileLocked()方法,去设置对应UID进程的网络的规则变化。而对于应用先后台状态改变网络管理,最主要的是updateRulesForDataUsageRestrictionsLocked()方法的管控,下面咱们来看下该方法:ide

private void updateRulesForDataUsageRestrictionsLocked(int uid, boolean uidDeleted) {

    ...
    //获取进程的策略
    final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE);
    //获取进程旧规则
    final int oldUidRules = mUidRules.get(uid, RULE_NONE);
    //判断应用进程的先后台状态
    final boolean isForeground = isUidForegroundOnRestrictBackgroundLocked(uid);

    //表示是否在黑名单
    final boolean isBlacklisted = (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
    //表示是否在白名单
    final boolean isWhitelisted = mRestrictBackgroundWhitelistUids.get(uid);
    //旧规则
    final int oldRule = oldUidRules & MASK_METERED_NETWORKS;
    //新规则
    int newRule = RULE_NONE;

    // First step: define the new rule based on user restrictions and foreground state.
    //第一步,根据用户设置与先后台状态定义新网络规则
    if (isForeground) {
        if (isBlacklisted || (mRestrictBackground && !isWhitelisted)) {
            //暂时容许规则
            newRule = RULE_TEMPORARY_ALLOW_METERED;
        } else if (isWhitelisted) {
            //容许规则
            newRule = RULE_ALLOW_METERED;
        }
    } else {
        if (isBlacklisted) {
            //拒绝规则
            newRule = RULE_REJECT_METERED;
        } else if (mRestrictBackground && isWhitelisted) {
            newRule = RULE_ALLOW_METERED;
        }
    }
    //获取新的进程规则
    int newUidRules = newRule | (oldUidRules & MASK_ALL_NETWORKS);

    if (LOGV) {
        Slog.i(TAG, "updateRuleForRestrictBackgroundLocked(" + uid + ")"
                + ": isForeground=" +isForeground
                + ", isBlacklisted=" + isBlacklisted
                + ", isWhitelisted=" + isWhitelisted
                + ", oldRule=" + uidRulesToString(oldRule)
                + ", newRule=" + uidRulesToString(newRule)
                + ", newUidRules=" + uidRulesToString(newUidRules)
                + ", oldUidRules=" + uidRulesToString(oldUidRules));
    }

    if (!isForeground && (uidPolicy & POLICY_REJECT_APP_BACKGROUND_NET_MOBILE) != 0) {
        newUidRules |= RULE_REJECT_BACKGROUND_MOBILE;
    }
    if (!isForeground && (uidPolicy & POLICY_REJECT_APP_BACKGROUND_NET_WIFI) != 0) {
        newUidRules |= RULE_REJECT_BACKGROUND_WIFI;
    }
    boolean rule3gBackgroundChanged = false;
    boolean ruleWifiBackgroundChanged = false;

    //第二步,根据状态的改变应用后台网控规则
    // Second step: apply bw changes based on change of state.

    //获取网络规则变化
    rule3gBackgroundChanged = ((oldUidRules & RULE_REJECT_BACKGROUND_MOBILE)
            ^ (newUidRules & RULE_REJECT_BACKGROUND_MOBILE)) != 0;
    ruleWifiBackgroundChanged = ((oldUidRules & RULE_REJECT_BACKGROUND_WIFI)
            ^ (newUidRules & RULE_REJECT_BACKGROUND_WIFI)) != 0;
    Slog.i(TAG, "rule3gBackgroundChanged " + uid + " " + rule3gBackgroundChanged);
    Slog.i(TAG, "ruleWifiBackgroundChanged " + uid + " " + ruleWifiBackgroundChanged);
    if (rule3gBackgroundChanged) {
        //获取移动数网络状态
        final boolean allow = (newUidRules & RULE_REJECT_BACKGROUND_MOBILE) == 0;
        Slog.i(TAG, "rule3gBackgroundChanged " + allow);
        try {
            //将规则设置到网络守护进程
            mNetworkManagerFlyme.setFirewallUidChainRule(uid, TYPE_MOBILE, allow);
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
    if (ruleWifiBackgroundChanged) {
        //获取无线网络状态
        final boolean allow = (newUidRules & RULE_REJECT_BACKGROUND_WIFI) == 0;
        Slog.i(TAG, "ruleWifiBackgroundChanged " + allow);
        try {
            //将规则设置到网络守护进程
            mNetworkManagerFlyme.setFirewallUidChainRule(uid, TYPE_WIFI, allow);
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
    // @}

    if (newUidRules == RULE_NONE) {
        mUidRules.delete(uid);
    } else {
        mUidRules.put(uid, newUidRules);
    }

    ...
    //分发回调接口
    // Dispatch changed rule to existing listeners.
    mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRules).sendToTarget();

}

   根据以上的代码,能够看到,后台网络控制主要分为两步:

  1. 第一步,根据用户设置与先后台状态定义新网络规则;
  2. 第二步,根据状态的改变应用后台网控规则;
  3. 第三步,回调状态改变消息。

   在第一步里,有个判断进程先后台状态的方法isUidForegroundOnRestrictBackgroundLocked(),其最终调用的是isProcStateAllowedWhileOnRestrictBackgroundLocked()方法会根据进程的运状态去判断进程先后台的状态,当应用的状态小于等于所设定的值,系统即不认为应用处于后台状态,反之则是。

private boolean isUidForegroundOnRestrictBackgroundLocked(int uid) {
    final int procState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
    return isProcStateAllowedWhileOnRestrictBackgroundLocked(procState);
}

static boolean isProcStateAllowedWhileOnRestrictBackgroundLocked(int procState) {

    // return procState <= ActivityManager.PROCESS_STATE_TOP;
    return procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;

}

注:这里可能会是大多数应用如今没法被限制后台联网的缘由,这是因为以前Google的GMS还未接入咱们系统时,咱们的设定的临界指是ActivityManager.PROCESS_STATE_TOP = 2,而接入Google的GMS后,因为要过CTS,用例要求咱们须要将临界值修改成ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE = 4;

   这里须要提下:应用进入后台时,AMS经过Binder通知NPMS, 而后NPMS检测应用是否在后台(查看 AMS 传递过来的processState),在禁止后台联网状况下,应用若在后台,则禁止访问网络;不然就容许。这块的代码逻辑以下

final private IUidObserver mUidObserver = new IUidObserver.Stub() {
    @Override public void onUidStateChanged(int uid, int procState) throws RemoteException {

        Slog.i(TAG, "onUidStateChanged uid=" + uid + " " + procState);

        synchronized (mRulesLock) {
            updateUidStateLocked(uid, procState);
        }
        updateNetworkStats(uid, isUidStateForegroundLocked(procState));
    }

    @Override public void onUidGone(int uid) throws RemoteException {
        synchronized (mRulesLock) {
            removeUidStateLocked(uid);
        }
        updateNetworkStats(uid, false);
    }

    @Override public void onUidActive(int uid) throws RemoteException {
    }

    @Override public void onUidIdle(int uid) throws RemoteException {
    }
};

private void updateUidStateLocked(int uid, int uidState) {
    final int oldUidState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
    if (oldUidState != uidState) {
        // state changed, push updated rules
        mUidState.put(uid, uidState);
        updateRestrictBackgroundRulesOnUidStatusChangedLocked(uid, oldUidState, uidState);
        if (isProcStateAllowedWhileIdleOrPowerSaveMode(oldUidState)
                != isProcStateAllowedWhileIdleOrPowerSaveMode(uidState) ) {
            if (isUidIdle(uid)) {
                updateRuleForAppIdleLocked(uid);
            }
            if (mDeviceIdleMode) {
                updateRuleForDeviceIdleLocked(uid);
            }
            if (mRestrictPower) {
                updateRuleForRestrictPowerLocked(uid);
            }
            updateRulesForPowerRestrictionsLocked(uid);
        }
    }
}

   第二步中,获取到的新规则会经过Binder通讯,调用网络管理服务NetworkManagementService(如下简称NMS)中的setFirewallUidChainRule方法,以下:

public void setFirewallUidChainRule(int uid, int networkType, boolean allow) {
    //enforceSystemUid();
    final String MOBILE = "mobile";
    final String WIFI = "wifi";

    final String rule = allow ? "allow" : "deny";
    final String chain = (networkType == 1) ? WIFI : MOBILE;

    try {
        //向网络守护进程发送执行事件
        mFlymeDaemonConnector.execute("firewall", "set_uid_fw_rule", uid, chain, rule);
    } catch (NativeDaemonConnectorException e) {
        throw e.rethrowAsParcelableException();
    }
}

   紧接着NMS会经过守护进程的链接者NativeDaemonConnector(NDC)使用Socket通讯向守护进程发送对应的事件,从而让底层去根据事件来作出对应的反馈。

注:这里的通讯流程因为代码比较多,就不附上了,有兴趣的能够搜索源码中NativeDaemonConnector这个类去查看。

   第三步,设置完规则后,NPMS会回调进程网络规则改变的接口,通知整个Android该应用的规则改变,从而完成后台网络控制。

   简单的绘制了下该过程的流程,以下。
后台网络控制流程

日志分析

   在分析这类网络问题的时候,学习了下日志分析方法,把它总结出来,以便学习查阅。

准备前提

  1. 使用logreport抓取Log(包括NetWork Log);
  2. 使用如下指令抓取应用的先后台状态oom_adj;
while [ 1 ];do date +%Y-%m-%d-%H:%M:%S-%N;adb shell dumpsys activity oom| sed -En -e '/Proc .* trm:.*com.*\.youku/,/state: /p';printf "\n";sleep 1;done >oom_adj.txt

注:这里的.youku须要换成指定应用的包名信息

  1. 使用如下指令抓取网络访问流量状况。
adb shell "while [ 1 ];do cat /proc/net/xt_qtaguid/stats |grep \"wlan0.*10081\";sleep 1;done"

注:10081指的是应用进程的uid,根据不一样的应用进行修改

分析

Log

   对于Log,最主要的是mainlog、netpolicy以及辅助的eventlog(用以查看应用先后台状态)。

   分析mainlog时,首先从上层手管应用开始。

01-22 16:55:52.671 9321 9536 D myTemp66: 更新应用后台网络控制类型 记录已存在,更新 pkName=com.tencent.qqmusic, uid=10092
01-22 16:55:52.676 9321 9536 D myTemp66: updateBackgroundAppInfo --> updateResult:true pkgName:com.tencent.qqmusic controlType:0 uid:10092

01-22 16:55:52.676 9321 9536 I TrafficControlImpl: appBackGourndNetControl: uid:10092 wifi:false mobile:false
01-22 16:55:52.676 9321 9536 D TrafficControlImpl: appBackGourndNetControl: change wifibackground close:10092
01-22 16:55:52.676 9321 9536 I trafficNetworkControll: close uid=10092 background wifi net
01-22 16:55:52.715 9321 9536 D TrafficControlImpl: appBackGourndNetControl: change mobilebackground close:10092
01-22 16:55:52.715 9321 9536 D trafficNetworkControll: close uid=10092 background modern net

   其中myTemp66指的是在修改模式后,手管去更新数据的打印日志,会根据更新的状况反馈不一样的信息日志;TrafficControlImpl则是设置底层后台流量数据控制接口的日志;trafficNetworkControll则是成功调用底层接口的标志,从这些log上咱们能够了解上层应用逻辑是否存在问题。

   其次是分析框架层日志。

//NPMS检测到优酷进入后台,
01-22 16:55:53.926702 3172 3172 D WifiService: onReceive, action:android.intent.action.SCREEN_OFF
01-22 16:55:53.932184 3172 3188 D NetworkPolicy: rule3gBackgroundChanged 10081 true
01-22 16:55:53.932238 3172 3188 D NetworkPolicy: ruleWifiBackgroundChanged 10081 true
01-22 16:55:53.973140 3172 3188 D NetworkPolicy: ruleWifiBackgroundChanged false // 禁止10081访问网络,并设定iptable 
01-10 16:24:53.985761 321 3172 I iptables: /system/bin/iptables -I wifi -m owner --uid-owner 10081 -j REJECT --reject-with icmp-net-prohibited

// 可是很快又检测到 优酷到了前台,
01-22 16:55:54.067005 3172 3172 D WifiStateMachine: onReceive, action:android.intent.action.SCREEN_ON
01-22 16:55:54.082619 3172 3188 D NetworkPolicy: rule3gBackgroundChanged 10081 true
01-22 16:55:54.082675 3172 3188 D NetworkPolicy: ruleWifiBackgroundChanged 10081 true
01-22 16:55:54.117942 3172 3188 D NetworkPolicy: ruleWifiBackgroundChanged true // 设定 优酷 容许访问网络
01-22 16:55:54.133298 321 3172 I iptables: /system/bin/iptables -D wifi -m owner --uid-owner 10081 -j REJECT --reject-with icmp-net-prohibited


01-22 16:56:01.537 3172 3188 I NetworkPolicy: onUidStateChanged uid=10081 2//前台进程UID状态
01-22 16:56:19.745 3172 3188 I NetworkPolicy: onUidStateChanged uid=10081 3//后台进程UID状态

   NetworkPolicy: ruleWifiBackgroundChanged/rule3gBackgroundChanged false经过这条日志,能够判断进程在后台时的wifi或移动数据的状态,true表示能够联网,false表示禁止联网。iptables: /system/bin/iptables -I wifi -m owner --uid-owner 10081 -j REJECT --reject-with icmp-net-prohibited将联网信息设定到iptable中。经过以上日志就能够分析出,出问题应用进程在后台联网的状态。

   而后再根据dump中的netpolicy日志中的UID=10081 policy=[]REJECT_APP_BACKGROUND_NET_MOBILE
REJECT_APP_BACKGROUND_NET_WIFI
进一步确认后台联网规则。

   从以上日志里,基本上能够提炼出一个应用进程从上层到框架层的后台联网信息。

注:上面的日志最后两条,onUidStateChanged,表示的就是AMS向NPMS发送进程状态改变的消息,最后一列的值表示进程在的状态值具体参考如下数据。

/** @hide Process is a persistent system process. */
public static final int PROCESS_STATE_PERSISTENT = 0;

/** @hide Process is a persistent system process and is doing UI. */
public static final int PROCESS_STATE_PERSISTENT_UI = 1;

/** @hide Process is hosting the current top activities. Note that this covers * all activities that are visible to the user. */
public static final int PROCESS_STATE_TOP = 2;

/** @hide Process is hosting a foreground service due to a system binding. */
public static final int PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 3;

/** @hide Process is hosting a foreground service. */
public static final int PROCESS_STATE_FOREGROUND_SERVICE = 4;

/** @hide Same as {@link #PROCESS_STATE_TOP} but while device is sleeping. */
public static final int PROCESS_STATE_TOP_SLEEPING = 5;

/** @hide Process is important to the user, and something they are aware of. */
public static final int PROCESS_STATE_IMPORTANT_FOREGROUND = 6;

/** @hide Process is important to the user, but not something they are aware of. */
public static final int PROCESS_STATE_IMPORTANT_BACKGROUND = 7;

/** @hide Process is in the background running a backup/restore operation. */
public static final int PROCESS_STATE_BACKUP = 8;

/** @hide Process is in the background, but it can't restore its state so we want * to try to avoid killing it. */
public static final int PROCESS_STATE_HEAVY_WEIGHT = 9;

/** @hide Process is in the background running a service. Unlike oom_adj, this level * is used for both the normal running in background state and the executing * operations state. */
public static final int PROCESS_STATE_SERVICE = 10;

/** @hide Process is in the background running a receiver. Note that from the * perspective of oom_adj receivers run at a higher foreground level, but for our * prioritization here that is not necessary and putting them below services means * many fewer changes in some process states as they receive broadcasts. */
public static final int PROCESS_STATE_RECEIVER = 11;

/** @hide Process is in the background but hosts the home activity. */
public static final int PROCESS_STATE_HOME = 12;

/** @hide Process is in the background but hosts the last shown activity. */
public static final int PROCESS_STATE_LAST_ACTIVITY = 13;

/** @hide Process is being cached for later use and contains activities. */
public static final int PROCESS_STATE_CACHED_ACTIVITY = 14;

/** @hide Process is being cached for later use and is a client of another cached * process that contains activities. */
public static final int PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 15;

/** @hide Process is being cached for later use and is empty. */
public static final int PROCESS_STATE_CACHED_EMPTY = 16;

oom_adj分析

   oom_adj是dumpsys activity p 中表示应用进程状态的信息,如下是截取的QQ应用以及微信在N机器的上的先后台信息。

QQ前台状态 2018-02-06-15:01:10-625398404 Proc # 0: fore T/A/T trm: 0 16235:com.tencent.mobileqq/u0a100 (top-activity) oom: max=1001 curRaw=0 setRaw=0 cur=0 set=0 state: cur=T set=T lastPss=166MB lastSwapPss=11MB lastCachedPss=0.00 Proc # 6: vis F/ /T trm: 0 18259:com.tencent.mobileqq:TMAssistantDownloadSDKService/u0a100 (service) com.tencent.mobileqq/com.tencent.tmdownloader.TMAssistantDownloadService<=Proc{16235:com.tencent.mobileqq/u0a100} oom: max=1001 curRaw=100 setRaw=100 cur=100 set=100 state: cur=T set=T lastPss=21MB lastSwapPss=603KB lastCachedPss=0.00 Proc # 1: vis F/ /SB trm: 0 16285:com.tencent.mobileqq:MSF/u0a100 (service) com.tencent.mobileqq/.msf.service.MsfService<=Proc{21231:com.tencent.mobileqq:qzone/u0a100} oom: max=1001 curRaw=100 setRaw=100 cur=100 set=100 state: cur=SB set=SB lastPss=28MB lastSwapPss=4.3MB lastCachedPss=0.00 Proc # 5: svc B/ /S trm: 0 21231:com.tencent.mobileqq:qzone/u0a100 (started-services) oom: max=1001 curRaw=500 setRaw=500 cur=500 set=500 state: cur=S set=S lastPss=0.00 lastSwapPss=0.00 lastCachedPss=0.00 QQ后台状态 2018-02-06-15:01:11-698162429 Proc # 6: prcp F/ /SB trm: 0 18259:com.tencent.mobileqq:TMAssistantDownloadSDKService/u0a100 (service) com.tencent.mobileqq/com.tencent.tmdownloader.TMAssistantDownloadService<=Proc{16235:com.tencent.mobileqq/u0a100} oom: max=1001 curRaw=200 setRaw=200 cur=200 set=200 state: cur=SB set=SB lastPss=21MB lastSwapPss=603KB lastCachedPss=0.00 Proc # 2: prcp F/ /SB trm: 0 16285:com.tencent.mobileqq:MSF/u0a100 (service) com.tencent.mobileqq/.msf.service.MsfService<=Proc{16235:com.tencent.mobileqq/u0a100} oom: max=1001 curRaw=200 setRaw=200 cur=200 set=200 state: cur=SB set=SB lastPss=28MB lastSwapPss=4.3MB lastCachedPss=0.00 Proc # 1: prcp F/S/SB trm: 0 16235:com.tencent.mobileqq/u0a100 (fg-service) oom: max=1001 curRaw=200 setRaw=200 cur=200 set=200 state: cur=SB set=SB lastPss=166MB lastSwapPss=11MB lastCachedPss=0.00 Proc # 5: svc B/ /S trm: 0 21231:com.tencent.mobileqq:qzone/u0a100 (started-services) oom: max=1001 curRaw=500 setRaw=500 cur=500 set=500 state: cur=S set=S lastPss=0.00 lastSwapPss=0.00 lastCachedPss=0.00 微信前台状态 2018-02-06-15:55:22-743317581 Proc # 0: fore T/A/T trm: 0 23765:com.tencent.mm/u0a99 (top-activity) oom: max=1001 curRaw=0 setRaw=0 cur=0 set=0 state: cur=T set=T lastPss=138MB lastSwapPss=8.8MB lastCachedPss=76MB Proc # 3: vis F/ /SB trm: 0 14557:com.tencent.mm:push/u0a99 (service) com.tencent.mm/.booter.CoreService<=Proc{23765:com.tencent.mm/u0a99} oom: max=1001 curRaw=100 setRaw=100 cur=100 set=100 state: cur=SB set=SB lastPss=15MB lastSwapPss=3.4MB lastCachedPss=0.00 Proc #36: svcb B/ /S trm: 0 24482:com.tencent.mm:sandbox/u0a99 (started-services) oom: max=1001 curRaw=800 setRaw=800 cur=800 set=800 state: cur=S set=S lastPss=24MB lastSwapPss=308KB lastCachedPss=0.00 Proc # 5: cch B/ /CE trm: 0 25785:com.tencent.mm:appbrand0/u0a99 (cch-empty) oom: max=1001 curRaw=900 setRaw=900 cur=900 set=900 state: cur=CE set=CE lastPss=14MB lastSwapPss=240KB lastCachedPss=14MB Proc #19: cch+2 B/ /CE trm: 0 25918:com.tencent.mm:tools/u0a99 (cch-empty) oom: max=1001 curRaw=902 setRaw=902 cur=902 set=902 state: cur=CE set=CE lastPss=17MB lastSwapPss=255KB lastCachedPss=17MB 微信后台状态 2018-02-06-15:55:23-830229592 Proc # 3: svc B/ /S trm: 0 14557:com.tencent.mm:push/u0a99 (started-services) oom: max=1001 curRaw=500 setRaw=500 cur=500 set=500 state: cur=S set=S lastPss=15MB lastSwapPss=3.4MB lastCachedPss=0.00 Proc # 1: prev B/ /S trm: 0 23765:com.tencent.mm/u0a99 (cch-started-ui-services) oom: max=1001 curRaw=700 setRaw=700 cur=700 set=700 state: cur=S set=S lastPss=138MB lastSwapPss=8.8MB lastCachedPss=76MB Proc #36: svcb B/ /S trm: 0 24482:com.tencent.mm:sandbox/u0a99 (started-services) oom: max=1001 curRaw=800 setRaw=800 cur=800 set=800 state: cur=S set=S lastPss=24MB lastSwapPss=308KB lastCachedPss=0.00 Proc # 5: cch B/ /CE trm: 0 25785:com.tencent.mm:appbrand0/u0a99 (cch-empty) oom: max=1001 curRaw=900 setRaw=900 cur=900 set=900 state: cur=CE set=CE lastPss=14MB lastSwapPss=240KB lastCachedPss=14MB Proc #19: cch+2 B/ /CE trm: 0 25918:com.tencent.mm:tools/u0a99 (cch-empty) oom: max=1001 curRaw=902 setRaw=902 cur=902 set=902 state: cur=CE set=CE lastPss=17MB lastSwapPss=255KB lastCachedPss=17MB

   从log中能够清楚的看到状态信息的变化,从这边的结果看,QQ是没法正常禁止后台联网,微信能够(微信可能第一个消息收的到,后面发的消息收不到)。而产生这个问题的缘由是这样的,谷歌CTS要求咱们的网控机制判断应用先后台的状态的临界点是ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE = 4,qq退到后台的状态是ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 3,不知足咱们禁网的条件;微信退到后台的状态是ActivityManager.PROCESS_STATE_SERVICE = 10,知足禁网的条件,因此才会致使这样的问题。

   所以之后遇到这类的问题,能够先从这步入手。

流量log
   抓这个log主要是为了查看应用在后台联网时,是否有流量的使用,直接上图吧。
流量使用

结语

   基本上一个应用的后台联网问题,能够经过以上分析方法去分析,而后根据结果去找对应的人设去处理,上述分析,若有问题或者疑问能够相互交流。