Wifi Module - Source Code Analysis to Get IP Address (Android P)

Keywords: Mobile network Java Android Attribute

Preface

In the previous article, we analyzed the process of connecting AP. When the wifi connection is completed at the bottom level, wifi Monitor will be notified of the event. wifi Monitor will send a message NETWORK_CONNECTION_EVENT when it hears the event.

                                       

Two Code Specific Flow

 1 frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiMonitor.java

When the bottom layer completes the wifi connection, it notifies WifiMonitor of the event and sends the message NETWORK_CONNECTION_EVENT after WifiMonitor hears the event.

/**
* Broadcast the network connection event to all the handlers registered for this event.
*
* @param iface Name of iface on which this occurred.
* @param networkId ID of the network in wpa_supplicant.
* @param bssid BSSID of the access point.
*/
public void broadcastNetworkConnectionEvent(String iface, int networkId, String bssid) {
    sendMessage(iface, NETWORK_CONNECTION_EVENT, networkId, 0, bssid);
}

 

2 frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiStateMachine.java

WifiStateMachine is now in Disconnected State, which fails to process the message and is thrown to its parent ConnectModeState for processing.

case WifiMonitor.NETWORK_CONNECTION_EVENT:
    if (mVerboseLoggingEnabled) log("Network connection established");
    mLastNetworkId = message.arg1;
    mWifiConfigManager.clearRecentFailureReason(mLastNetworkId);
    mLastBssid = (String) message.obj;
    reasonCode = message.arg2;
    // TODO: This check should not be needed after WifiStateMachinePrime refactor.
    // Currently, the last connected network configuration is left in
    // wpa_supplicant, this may result in wpa_supplicant initiating connection
    // to it after a config store reload. Hence the old network Id lookups may not
    // work, so disconnect the network and let network selector reselect a new
    // network.
    config = getCurrentWifiConfiguration();
    if (config != null) {
        mWifiInfo.setBSSID(mLastBssid);
        mWifiInfo.setNetworkId(mLastNetworkId);
        mWifiInfo.setMacAddress(mWifiNative.getMacAddress(mInterfaceName));

        ScanDetailCache scanDetailCache =
                mWifiConfigManager.getScanDetailCacheForNetwork(config.networkId);
        if (scanDetailCache != null && mLastBssid != null) {
            ScanResult scanResult = scanDetailCache.getScanResult(mLastBssid);
            if (scanResult != null) {
                mWifiInfo.setFrequency(scanResult.frequency);
            }
        }
        mWifiConnectivityManager.trackBssid(mLastBssid, true, reasonCode);
        // We need to get the updated pseudonym from supplicant for EAP-SIM/AKA/AKA'
        if (config.enterpriseConfig != null
                && TelephonyUtil.isSimEapMethod(
                        config.enterpriseConfig.getEapMethod())) {
            String anonymousIdentity =
                    mWifiNative.getEapAnonymousIdentity(mInterfaceName);
            if (anonymousIdentity != null) {
                config.enterpriseConfig.setAnonymousIdentity(anonymousIdentity);
            } else {
                Log.d(TAG, "Failed to get updated anonymous identity"
                        + " from supplicant, reset it in WifiConfiguration.");
                config.enterpriseConfig.setAnonymousIdentity(null);
            }
            mWifiConfigManager.addOrUpdateNetwork(config, Process.WIFI_UID);
        }
        sendNetworkStateChangeBroadcast(mLastBssid);
        transitionTo(mObtainingIpState);
    } else {
        logw("Connected to unknown networkId " + mLastNetworkId
                + ", disconnecting...");
        sendMessage(CMD_DISCONNECT);
    }
    break;

You can see some operations that assign the relevant attribute values of ap to mWifiInfo.

                         mWifiInfo.setBSSID(mLastBssid)
                         mWifiInfo.setNetworkId(mLastNetworkId)
                         mWifiInfo.setMacAddress(mWifiNative.getMacAddress(mInterfaceName))

Update Wifi status information NetworkInfo, mWifiConfigManager. addOrUpdate Network (config, process. WIFI_UID)

And call sendNetwork State Change Broadcast (mLastBssid).

private void sendNetworkStateChangeBroadcast(String bssid) {
    Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    NetworkInfo networkInfo = new NetworkInfo(mNetworkInfo);
    networkInfo.setExtraInfo(null);
    intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo);
    //TODO(b/69974497) This should be non-sticky, but settings needs fixing first.
    mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}

Call mContext.sendStickyBroadcast AsUser to send broadcast WifiManager.NETWORK_STATE_CHANGED_ACTION.

Then go to Obtaining IpState, transitionTo (mObtaining IpState), which gets ip, and first enter its parent state, L2 Connected 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);

    final NetworkCapabilities nc;
    if (mWifiInfo != null && !mWifiInfo.getSSID().equals(WifiSsid.NONE)) {
        nc = new NetworkCapabilities(mNetworkCapabilitiesFilter);
        nc.setSSID(mWifiInfo.getSSID());
    } else {
        nc = mNetworkCapabilitiesFilter;
    }
    mNetworkAgent = new WifiNetworkAgent(getHandler().getLooper(), mContext,
            "WifiNetworkAgent", mNetworkInfo, nc, mLinkProperties, 60, mNetworkMisc);

    // We must clear the config BSSID, as the wifi chipset may decide to roam
    // from this point on and having the BSSID specified in the network block would
    // cause the roam to faile and the device to disconnect
    clearTargetBssid("L2ConnectedState");
    mCountryCode.setReadyForChange(false);
    mWifiMetrics.setWifiState(WifiMetricsProto.WifiLog.WIFI_ASSOCIATED);
}

Set the mNetwork Info state to CONNECTING, which calls setNetwork Detailed State (Detailed State. CONNECTING); create a WifiNetwork Agent object, which is skipped this time for the registration process of WifiNetwork Agent.

case WifiMonitor.NETWORK_CONNECTION_EVENT:
    mWifiInfo.setBSSID((String) message.obj);
    mLastNetworkId = message.arg1;
    mWifiInfo.setNetworkId(mLastNetworkId);
    mWifiInfo.setMacAddress(mWifiNative.getMacAddress(mInterfaceName));
    if(!mLastBssid.equals(message.obj)) {
        mLastBssid = (String) message.obj;
        sendNetworkStateChangeBroadcast(mLastBssid);
    }
    break;

Look at sendNetwork State Change Broadcast (mLastBssid).

private void sendNetworkStateChangeBroadcast(String bssid) {
    Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    NetworkInfo networkInfo = new NetworkInfo(mNetworkInfo);
    networkInfo.setExtraInfo(null);
    intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo);
    //TODO(b/69974497) This should be non-sticky, but settings needs fixing first.
    mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}

Call mContext.sendSticky Broadcast AsUser (intent, UserHandle.ALL) to send broadcast WifiManager.NETWORK_STATE_CHANGED_ACTION.

 

4 frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java

Broadcast WifiManager.NETWORK_STATE_CHANGED_ACTION was received.

/**
*  Receiver for handling broadcasts.
*
*  This receiver is registered on the WorkHandler.
*/
@VisibleForTesting
final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
            updateWifiState(
                    intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
                            WifiManager.WIFI_STATE_UNKNOWN));
        } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) {
            mStaleScanResults = false;

            fetchScansAndConfigsAndUpdateAccessPoints();
        } else if (WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action)
                || WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) {
            fetchScansAndConfigsAndUpdateAccessPoints();
        } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
            // TODO(sghuman): Refactor these methods so they cannot result in duplicate
            // onAccessPointsChanged updates being called from this intent.
            NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
            updateNetworkInfo(info);
            fetchScansAndConfigsAndUpdateAccessPoints();
        } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
            NetworkInfo info =
                    mConnectivityManager.getNetworkInfo(mWifiManager.getCurrentNetwork());
            updateNetworkInfo(info);
        }
    }
};

WifiTracker receives the broadcast and starts refreshing fetchScansAndConfigs AndUpdate Access Points ().

 

5 packages/apps/Settings/src/com/android/settings/wifi/WifiEnabler.java

private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
            handleWifiStateChanged(mWifiManager.getWifiState());
        } else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) {
            if (!mConnected.get()) {
                handleStateChanged(WifiInfo.getDetailedStateOf((SupplicantState)
                        intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE)));
            }
        } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
            NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
                    WifiManager.EXTRA_NETWORK_INFO);
            mConnected.set(info.isConnected());
            handleStateChanged(info.getDetailedState());
        }
    }
};

The application layer Settings module receives the handleWifiStateChanged(mWifiManager.getWifiState()) for processing the broadcast status.

Then the WifiState Machine enters the Obtaining IpState state state, where it completes the IP configuration, which is divided into static IP and dynamic ip.

    @Override
    public void enter() {
        final WifiConfiguration currentConfig = getCurrentWifiConfiguration();
        final boolean isUsingStaticIp =
                (currentConfig.getIpAssignment() == IpConfiguration.IpAssignment.STATIC);
        if (mVerboseLoggingEnabled) {
            final String key = currentConfig.configKey();
            log("enter ObtainingIpState netId=" + Integer.toString(mLastNetworkId)
                    + " " + key + " "
                    + " roam=" + mIsAutoRoaming
                    + " static=" + isUsingStaticIp);
        }

        // Send event to CM & network change broadcast
        setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);

        // We must clear the config BSSID, as the wifi chipset may decide to roam
        // from this point on and having the BSSID specified in the network block would
        // cause the roam to fail and the device to disconnect.
        clearTargetBssid("ObtainingIpAddress");

        // Stop IpClient in case we're switching from DHCP to static
        // configuration or vice versa.
        //
        // TODO: Only ever enter this state the first time we connect to a
        // network, never on switching between static configuration and
        // DHCP. When we transition from static configuration to DHCP in
        // particular, we must tell ConnectivityService that we're
        // disconnected, because DHCP might take a long time during which
        // connectivity APIs such as getActiveNetworkInfo should not return
        // CONNECTED.
        stopIpClient();

        mIpClient.setHttpProxy(currentConfig.getHttpProxy());
        if (!TextUtils.isEmpty(mTcpBufferSizes)) {
            mIpClient.setTcpBufferSizes(mTcpBufferSizes);
        }
        final IpClient.ProvisioningConfiguration prov;
        if (!isUsingStaticIp) {
            prov = IpClient.buildProvisioningConfiguration()
                        .withPreDhcpAction()
                        .withApfCapabilities(mWifiNative.getApfCapabilities(mInterfaceName))
                        .withNetwork(getCurrentNetwork())
                        .withDisplayName(currentConfig.SSID)
                        .withRandomMacAddress()
                        .build();
        } else {
            StaticIpConfiguration staticIpConfig = currentConfig.getStaticIpConfiguration();
            prov = IpClient.buildProvisioningConfiguration()
                        .withStaticConfiguration(staticIpConfig)
                        .withApfCapabilities(mWifiNative.getApfCapabilities(mInterfaceName))
                        .withNetwork(getCurrentNetwork())
                        .withDisplayName(currentConfig.SSID)
                        .build();
        }
        mIpClient.startProvisioning(prov);
        // Get Link layer stats so as we get fresh tx packet counters
        getWifiLinkLayerStats();
    }
       

Callback the IpClient.Callback.onProvisioningSuccess interface.

class IpClientCallback extends IpClient.Callback {
    @Override
    public void onPreDhcpAction() {
        sendMessage(DhcpClient.CMD_PRE_DHCP_ACTION);
    }

    @Override
    public void onPostDhcpAction() {
        sendMessage(DhcpClient.CMD_POST_DHCP_ACTION);
    }

    @Override
    public void onNewDhcpResults(DhcpResults dhcpResults) {
        if (dhcpResults != null) {
            sendMessage(CMD_IPV4_PROVISIONING_SUCCESS, dhcpResults);
        } else {
            sendMessage(CMD_IPV4_PROVISIONING_FAILURE);
            mWifiInjector.getWifiLastResortWatchdog().noteConnectionFailureAndTriggerIfNeeded(
                    getTargetSsid(), mTargetRoamBSSID,
                    WifiLastResortWatchdog.FAILURE_CODE_DHCP);
        }
    }

    @Override
    public void onProvisioningSuccess(LinkProperties newLp) {
        mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_IP_CONFIGURATION_SUCCESSFUL);
        sendMessage(CMD_UPDATE_LINKPROPERTIES, newLp);
        sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);
    }

    ...
}

Send the message CMD_IP_CONFIGURATION_SUCCESSFUL in the onProvisioningSuccess function.

The message CMD_IP_CONFIGURATION_SUCCESSFUL is processed in L2 ConnectedState.

case CMD_IP_CONFIGURATION_SUCCESSFUL:
    handleSuccessfulIpConfiguration();
    reportConnectionAttemptEnd(
            WifiMetrics.ConnectionEvent.FAILURE_NONE,
            WifiMetricsProto.ConnectionEvent.HLF_NONE);
    if (getCurrentWifiConfiguration() == null) {
        // The current config may have been removed while we were connecting,
        // trigger a disconnect to clear up state.
        mWifiNative.disconnect(mInterfaceName);
        transitionTo(mDisconnectingState);
    } else {
        sendConnectedState();
        transitionTo(mConnectedState);
    }
    break;

Look at sendConnected State.

private void sendConnectedState() {
    // If this network was explicitly selected by the user, evaluate whether to call
    // explicitlySelected() so the system can treat it appropriately.
    WifiConfiguration config = getCurrentWifiConfiguration();
    if (shouldEvaluateWhetherToSendExplicitlySelected(config)) {
        boolean prompt =
                mWifiPermissionsUtil.checkNetworkSettingsPermission(config.lastConnectUid);
        if (mVerboseLoggingEnabled) {
            log("Network selected by UID " + config.lastConnectUid + " prompt=" + prompt);
        }
        if (prompt) {
            // Selected by the user via Settings or QuickSettings. If this network has Internet
            // access, switch to it. Otherwise, switch to it only if the user confirms that they
            // really want to switch, or has already confirmed and selected "Don't ask again".
            if (mVerboseLoggingEnabled) {
                log("explictlySelected acceptUnvalidated=" + config.noInternetAccessExpected);
            }
            if (mNetworkAgent != null) {
                mNetworkAgent.explicitlySelected(config.noInternetAccessExpected);
            }
        }
    }

    setNetworkDetailedState(DetailedState.CONNECTED);
    sendNetworkStateChangeBroadcast(mLastBssid);
}

Set Network Detailed State (Detailed State. CONNECTED) with Network Info status as CONNECTED status.

And call sendNetwork State Change Broadcast (mLastBssid)

private void sendNetworkStateChangeBroadcast(String bssid) {
    Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    NetworkInfo networkInfo = new NetworkInfo(mNetworkInfo);
    networkInfo.setExtraInfo(null);
    intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo);
    //TODO(b/69974497) This should be non-sticky, but settings needs fixing first.
    mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}

 

6 frameworks/base/frameworks/base/services/net/java/android/net/ip/IpClient.java

The following is the interface for the previous callback.

/**
* Callbacks for handling IpClient events.
*
* These methods are called by IpClient on its own thread. Implementations
* of this class MUST NOT carry out long-running computations or hold locks
* for which there might be contention with other code calling public
* methods of the same IpClient instance.
*/
public static class Callback {
    // In order to receive onPreDhcpAction(), call #withPreDhcpAction()
    // when constructing a ProvisioningConfiguration.
    //
    // Implementations of onPreDhcpAction() must call
    // IpClient#completedPreDhcpAction() to indicate that DHCP is clear
    // to proceed.
    public void onPreDhcpAction() {}
    public void onPostDhcpAction() {}

    // This is purely advisory and not an indication of provisioning
    // success or failure.  This is only here for callers that want to
    // expose DHCPv4 results to other APIs (e.g., WifiInfo#setInetAddress).
    // DHCPv4 or static IPv4 configuration failure or success can be
    // determined by whether or not the passed-in DhcpResults object is
    // null or not.
    public void onNewDhcpResults(DhcpResults dhcpResults) {}

    public void onProvisioningSuccess(LinkProperties newLp) {}
    public void onProvisioningFailure(LinkProperties newLp) {}

    // Invoked on LinkProperties changes.
    public void onLinkPropertiesChange(LinkProperties newLp) {}

    // Called when the internal IpReachabilityMonitor (if enabled) has
    // detected the loss of a critical number of required neighbors.
    public void onReachabilityLost(String logMsg) {}

    // Called when the IpClient state machine terminates.
    public void onQuit() {}

    // Install an APF program to filter incoming packets.
    public void installPacketFilter(byte[] filter) {}

    // Asynchronously read back the APF program & data buffer from the wifi driver.
    // Due to Wifi HAL limitations, the current implementation only supports dumping the entire
    // buffer. In response to this request, the driver returns the data buffer asynchronously
    // by sending an IpClient#EVENT_READ_PACKET_FILTER_COMPLETE message.
    public void startReadPacketFilter() {}

    // If multicast filtering cannot be accomplished with APF, this function will be called to
    // actuate multicast filtering using another means.
    public void setFallbackMulticastFilter(boolean enabled) {}

    // Enabled/disable Neighbor Discover offload functionality. This is
    // called, for example, whenever 464xlat is being started or stopped.
    public void setNeighborDiscoveryOffload(boolean enable) {}
}

 

Posted by davo666 on Tue, 29 Jan 2019 08:36:14 -0800