NetworkCallback 无法收到回调

起因

某APP注册NetworkCallback无法收到回调,当使用默认的NetworkRequest可以收到回调

     connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        networkCallback = new NetworkCallbackImpl();
        NetworkRequest request = new NetworkRequest.Builder()
                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) //12
                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) //1
                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) //0
                .build();
    // 默认的NetworkRequest
    //NetworkRequest request =new NetworkRequest.Builder().build();


    if (connMgr != null) {
        connMgr.registerNetworkCallback(request, networkCallback);
    }

NetworkCallback 注册流程

// frameworks/base/core/java/android/net/ConnectivityManager.java
public void registerNetworkCallback(
        NetworkRequest request, NetworkCallback networkCallback, Handler handler) {
    CallbackHandler cbHandler = new CallbackHandler(handler);
    NetworkCapabilities nc = request.networkCapabilities; 
    sendRequestForNetwork(nc, networkCallback, 0, LISTEN, TYPE_NONE, cbHandler); 
} 
    private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, NetworkCallback callback,
            int timeoutMs, int action, int legacyType, CallbackHandler handler) {
        // 省略
        Messenger messenger = new Messenger(handler);
        Binder binder = new Binder();
        if (action == LISTEN) {
            // 进入 ConnectivityService
            request = mService.listenForNetwork(need, messenger, binder);
        } else {
            request = mService.requestNetwork(
                    need, messenger, timeoutMs, binder, legacyType);
        }
        // 省略
            
        return request;
    }

//  frameworks/base/services/core/java/com/android/server/ConnectivityService.java
@Override
public NetworkRequest listenForNetwork(NetworkCapabilities networkCapabilities,
        Messenger messenger, IBinder binder) {
    if (!hasWifiNetworkListenPermission(networkCapabilities)) {
        enforceAccessPermission();
    }

    NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
    if (!ConnectivityManager.checkChangePermission(mContext)) {
        // Apps without the CHANGE_NETWORK_STATE permission can't use background networks, so
        // make all their listens include NET_CAPABILITY_FOREGROUND. That way, they will get
        // onLost and onAvailable callbacks when networks move in and out of the background.
        // There is no need to do this for requests because an app without CHANGE_NETWORK_STATE
        // can't request networks.
        // 添加NET_CAPABILITY_FOREGROUND的FLAG
        nc.addCapability(NET_CAPABILITY_FOREGROUND);
    }
    ensureValidNetworkSpecifier(networkCapabilities);

    NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
            NetworkRequest.Type.LISTEN);
    NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder);                                                                                                                 
    if (VDBG) log("listenForNetwork for " + nri);
    // 构建出nri, 然后sendmessage
    mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
    return networkRequest;
}

构建出NetworkRequestInfo, 然后sendmessage, 事件类型EVENT_REGISTER_NETWORK_LISTENER,最终调用到handleRegisterNetworkRequest

private void handleRegisterNetworkRequest(NetworkRequestInfo nri) {
    mNetworkRequests.put(nri.request, nri);  // 保存请求
    mNetworkRequestInfoLogs.log("REGISTER " + nri);
    if (nri.request.isListen()) {
        for (NetworkAgentInfo network : mNetworkAgentInfos.values()) {
            if (nri.request.networkCapabilities.hasSignalStrength() &&
                    network.satisfiesImmutableCapabilitiesOf(nri.request)) {
                updateSignalStrengthThresholds(network, "REGISTER", nri.request);
            }
        }
    }
    rematchAllNetworksAndRequests(null, 0); //关键函数
    if (nri.request.isRequest() && getNetworkForRequest(nri.request.requestId) == null) {
        sendUpdatedScoreToFactories(nri.request, 0);
    }

    ...
} 

    private void rematchAllNetworksAndRequests(NetworkAgentInfo changed, int oldScore) {
        final long now = SystemClock.elapsedRealtime();
        if (changed != null && oldScore < changed.getCurrentScore()) {
            rematchNetworkAndRequests(changed, ReapUnvalidatedNetworks.REAP, now);
        } else {
            final NetworkAgentInfo[] nais = mNetworkAgentInfos.values().toArray(
                    new NetworkAgentInfo[mNetworkAgentInfos.size()]);
            Arrays.sort(nais);
            for (NetworkAgentInfo nai : nais) { //注册callbakc,走这个分支
                rematchNetworkAndRequests(nai,
                        // Only reap the last time through the loop.  Reaping before all rematching
                        // is complete could incorrectly teardown a network that hasn't yet been
                        // rematched.
                        (nai != nais[nais.length-1]) ? ReapUnvalidatedNetworks.DONT_REAP
                                : ReapUnvalidatedNetworks.REAP,
                        now);
            }
        }
    }


关键函数rematchAllNetworksAndRequests就是遍历NetworkAgentInfo找到匹配的NetworkCapabilities,如果有就进行回调

NetworkAgentInfo生成

回调的Network就是这里创建的 new Network(reserveNetId())

    public int registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
            LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
            int currentScore, NetworkMisc networkMisc) {
        enforceConnectivityInternalPermission();

        LinkProperties lp = new LinkProperties(linkProperties);
        lp.ensureDirectlyConnectedRoutes();
        // TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network
        // satisfies mDefaultRequest.
        final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
                new Network(reserveNetId()), new NetworkInfo(networkInfo), lp,
                new NetworkCapabilities(networkCapabilities), currentScore,
                mContext, mTrackerHandler, new NetworkMisc(networkMisc), mDefaultRequest, this);
        synchronized (this) {
            nai.networkMonitor.systemReady = mSystemReady;
        }
        addValidationLogs(nai.networkMonitor.getValidationLogs(), nai.network,
                networkInfo.getExtraInfo());
        if (DBG) log("registerNetworkAgent " + nai);
        mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai));
        return nai.network.netId;
    }

从上图我们可以看出NetworkAgentInfo的网络信息,这部分开机启动的时候生成的,没有可重启机器试试

更多可以参考 网络连接评分机制之NetworkAgent

总结

  • NetworkRequestInfo 网络请求信息,用户可以指定自己关注的信息
  • NetworkAgentInfo 机器的网络信息
  • rematchAllNetworksAndRequests 关键函数
    如果NetworkAgentInfo中有满足NetworkRequestInfo的网络配置,此时就会进行回调

问题排查

前面已经介绍过在rematchAllNetworksAndRequests函数中匹配,最终会调用到processListenRequests函数

    // 
    private void processListenRequests(NetworkAgentInfo nai, boolean capabilitiesChanged) { 
        // For consistency with previous behaviour, send onLost callbacks before onAvailable.
        for (NetworkRequestInfo nri : mNetworkRequests.values()) { 
            NetworkRequest nr = nri.request;
            if (!nr.isListen()) continue;  
            if (nai.isSatisfyingRequest(nr.requestId) && !nai.satisfies(nr)) {
                nai.removeRequest(nri.request.requestId);
                callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_LOST, 0);
            }
        }

        if (capabilitiesChanged) {     
            notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
        }

        for (NetworkRequestInfo nri : mNetworkRequests.values()) { 
            NetworkRequest nr = nri.request;
            if (!nr.isListen()) continue;
            if (nai.satisfies(nr) && !nai.isSatisfyingRequest(nr.requestId)) {
                nai.addRequest(nr);          
                notifyNetworkAvailable(nai, nri); // 正常的触发回调 
            }
        }
    }     

通过观察我们发现satisfies函数失败了,看看satisfies如何判断的

    // Does this network satisfy request? // 该网络是否满足请求?
    public boolean satisfies(NetworkRequest request) {
        return created &&
                request.networkCapabilities.satisfiedByNetworkCapabilities(networkCapabilities);
    }

    public boolean satisfiedByNetworkCapabilities(NetworkCapabilities nc) {
        return satisfiedByNetworkCapabilities(nc, false);
    }

    // satisfiedByNetworkCapabilities 条件特别多
    private boolean satisfiedByNetworkCapabilities(NetworkCapabilities nc, boolean onlyImmutable) {
        return (nc != null &&
                satisfiedByNetCapabilities(nc, onlyImmutable) &&
                satisfiedByTransportTypes(nc) &&
                (onlyImmutable || satisfiedByLinkBandwidths(nc)) &&
                satisfiedBySpecifier(nc) &&
                (onlyImmutable || satisfiedBySignalStrength(nc)));
    }

    private boolean satisfiedByNetCapabilities(NetworkCapabilities nc, boolean onlyImmutable) {
        long networkCapabilities = this.mNetworkCapabilities;
        if (onlyImmutable) { // 唯一不可变的
            networkCapabilities = networkCapabilities & ~MUTABLE_CAPABILITIES;
        }
        return ((nc.mNetworkCapabilities & networkCapabilities) == networkCapabilities);
    }

    private boolean satisfiedByTransportTypes(NetworkCapabilities nc) {                                                         
        return ((this.mTransportTypes == 0) ||
                ((this.mTransportTypes & nc.mTransportTypes) != 0));
    }

条件想当的多,大致还是判断NetworkCapabilitiesTransportTypes是否匹配,以及带宽是否足够

使用frida对这两个函数进行调试,非常明显是TransportTypes不匹配,修改代码重新测试添加NetworkCapabilities.TRANSPORT_ETHERNET 即可收到回调

    NetworkRequest request = new NetworkRequest.Builder()
            .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) //12
            .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) //1
            //.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) //3
            .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) //0
            .build();

NetworkAgent创建

那么ETHERNET这个NetworkAgent哪里创建的?

// frameworks/opt/net/ethernet/java/com/android/server/ethernet/EthernetNetworkFactory.java
    private void initNetworkCapabilities() {
        mNetworkCapabilities = new NetworkCapabilities();
        mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET);
        mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);                                                                                              
        mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
        // We have no useful data on bandwidth. Say 100M up and 100M down. :-(
        mNetworkCapabilities.setLinkUpstreamBandwidthKbps(100 * 1000);
        mNetworkCapabilities.setLinkDownstreamBandwidthKbps(100 * 1000);
    }

    void onIpLayerStarted(LinkProperties linkProperties) {
        if (mNetworkAgent != null) {
            Log.e(TAG, "Already have a NetworkAgent - aborting new request");
            stopIpManager();
            return;
        }
        mLinkProperties = linkProperties;
        mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, mHwAddr);
        
        // Create our NetworkAgent.
        mNetworkAgent = new NetworkAgent(mHandler.getLooper(), mContext,                                                                                                              
                NETWORK_TYPE, mNetworkInfo, mNetworkCapabilities, mLinkProperties,
                NETWORK_SCORE) {
            public void unwanted() {
                if (this == mNetworkAgent) {
                    stopIpManager();
                } else if (mNetworkAgent != null) {
                    Log.d(TAG, "Ignoring unwanted as we have a more modern " +
                            "instance");
                }  // Otherwise, we've already called stopIpManager.
            }
        };  
    }

下面是一些其他的NetworkAgent创建

// frameworks/base/services/core/java/com/android/server/connectivity/Vpn.java
private void agentConnect() {
        LinkProperties lp = makeLinkProperties();

        if (lp.hasIPv4DefaultRoute() || lp.hasIPv6DefaultRoute()) {
            mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
        } else {
            mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
        }

        mNetworkInfo.setDetailedState(DetailedState.CONNECTING, null, null);

        NetworkMisc networkMisc = new NetworkMisc();
        networkMisc.allowBypass = mConfig.allowBypass && !mLockdown;

        long token = Binder.clearCallingIdentity();
        try {
            mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE,
                    mNetworkInfo, mNetworkCapabilities, lp, 0, networkMisc) {
                            @Override
                            public void unwanted() {
                                // We are user controlled, not driven by NetworkRequest.
                            }
                        };
        } finally {
            Binder.restoreCallingIdentity(token);
        } 
        ....
}

// frameworks/opt/net/lowpan/service/java/com/android/server/lowpan/LowpanInterfaceTracker.java
    private void bringUpNetworkAgent() {
        if (mNetworkAgent == null) {
            mNetworkAgent =
                    new NetworkAgent(
                            mNetworkFactory.getLooper(),
                            mContext,
                            NETWORK_TYPE,
                            mNetworkInfo,                                                                                                                                   
                            mNetworkCapabilities,
                            mLinkProperties,
                            NETWORK_SCORE) {
                        public void unwanted() {
                            LowpanInterfaceTracker.this.sendMessage(CMD_UNWANTED, this);
                        };
                    };
        }
    }

// wifi
// frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiStateMachine.java
    class L2ConnectedState extends State {
        @Override
        public void enter() {
            mRssiPollToken++;
            if (mEnableRssiPolling) {
                sendMessage(CMD_RSSI_POLL, mRssiPollToken, 0);
            }
            if (mNetworkAgent != null) {
                loge("Have NetworkAgent when entering L2Connected");
                setNetworkDetailedState(DetailedState.DISCONNECTED);
            }
            setNetworkDetailedState(DetailedState.CONNECTING);
            // 创建
            mNetworkAgent = new WifiNetworkAgent(getHandler().getLooper(), mContext,
                    "WifiNetworkAgent", mNetworkInfo, mNetworkCapabilitiesFilter,                                                                                                          
                    mLinkProperties, 60, mNetworkMisc);

            clearTargetBssid("L2ConnectedState");
            mCountryCode.setReadyForChange(false);
            mWifiMetrics.setWifiState(WifiMetricsProto.WifiLog.WIFI_ASSOCIATED);
        }
        ...
}

回调细节

对应函数onCapabilitiesChanged(@NonNull Network network, @NonNull NetworkCapabilities networkCapabilities)

networkAgent.networknetworkAgent.networkCapabilities 回调过去

    private static void callCallbackForRequest(NetworkRequestInfo nri,
            NetworkAgentInfo networkAgent, int notificationType, int arg1) {
        ...
        Bundle bundle = new Bundle();  
        Message msg = Message.obtain();
        if (notificationType != ConnectivityManager.CALLBACK_UNAVAIL) {
            putParcelable(bundle, networkAgent.network);   //对应network                                                                                                                         
        }
        switch (notificationType) {
            case ConnectivityManager.CALLBACK_LOSING: {
                msg.arg1 = arg1;
                break;
            }
            case ConnectivityManager.CALLBACK_CAP_CHANGED: {
                putParcelable(bundle, new NetworkCapabilities(networkAgent.networkCapabilities)); // 对应 networkCapabilities
                break;
            }
            case ConnectivityManager.CALLBACK_IP_CHANGED: {
                putParcelable(bundle, new LinkProperties(networkAgent.linkProperties));
                break;
            }
        }
        ...
    }
posted @ 2024-03-15 10:25  梦过无声  阅读(513)  评论(0)    收藏  举报