Android R WiFi热点流程浅析
Android R WiFi热点流程浅析
Android上的WiFi SoftAp功能是用户常用的功能之一,它能让我们分享手机的网络给其他设备使用。
那Android系统是如何实现SoftAp的呢,这里在FWK层面做一个简要的流程分析,供自己记录和大家参考。
以Android R版本为例,我们知道Android大部分的系统FWK服务都在SystemServer中启动,SoftAp的Service也不例外:
/*** Use with {@link #getSystemService(String)} to retrieve a {@link android.net.TetheringManager}* for managing tethering functions.* @hide* @see android.net.TetheringManager*/@SystemApipublic static final String TETHERING_SERVICE = "tethering";private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector";/*** Starts a miscellaneous grab bag of stuff that has yet to be refactored and organized.*/private void startOtherServices(@NonNull TimingsTraceAndSlog t) {t.traceBegin("startOtherServices");......t.traceBegin("StartTethering");try {// TODO: hide implementation details, b/146312721.ConnectivityModuleConnector.getInstance().startModuleService(TETHERING_CONNECTOR_CLASS,PERMISSION_MAINLINE_NETWORK_STACK, service -> {ServiceManager.addService(Context.TETHERING_SERVICE, service,false /* allowIsolated */,DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);});} catch (Throwable e) {reportWtf("starting Tethering", e);}t.traceEnd();......}
这里借助了ConnectivityModuleConnector工具类的startModuleService()函数去启动、绑定服务:
/*** Start a module running in the network stack or system_server process. Should be called only* once for each module per device startup.** <p>This method will start a networking module either in the network stack* process, or inside the system server on devices that do not support the corresponding* mainline network . The corresponding networking module service's binder* object will then be delivered asynchronously via the provided {@link ModuleServiceCallback}.** @param serviceIntentBaseAction Base action to use for constructing the intent needed to* bind to the corresponding module.* @param servicePermissionName Permission to be held by the corresponding module.*/public void startModuleService(@NonNull String serviceIntentBaseAction,@NonNull String servicePermissionName,@NonNull ModuleServiceCallback callback) {log("Starting networking module " + serviceIntentBaseAction);final PackageManager pm = mContext.getPackageManager();// Try to bind in-process if the device was shipped with an in-process versionIntent intent = mDeps.getModuleServiceIntent(pm, serviceIntentBaseAction,servicePermissionName, true /* inSystemProcess */);// Otherwise use the updatable module versionif (intent == null) {intent = mDeps.getModuleServiceIntent(pm, serviceIntentBaseAction,servicePermissionName, false /* inSystemProcess */);log("Starting networking module in network_stack process");} else {log("Starting networking module in system_server process");}if (intent == null) {maybeCrashWithTerribleFailure("Could not resolve the networking module", null);return;}final String packageName = intent.getComponent().getPackageName();// Start the network stack. The service will be added to the service manager by the// corresponding client in ModuleServiceCallback.onModuleServiceConnected().if (!mContext.bindServiceAsUser(intent, new ModuleServiceConnection(packageName, callback),Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) {maybeCrashWithTerribleFailure("Could not bind to networking module in-process, or in app with "+ intent, packageName);return;}log("Networking module service start requested");}private class ModuleServiceConnection implements ServiceConnection {@NonNullprivate final String mPackageName;@NonNullprivate final ModuleServiceCallback mModuleServiceCallback;private ModuleServiceConnection(@NonNull String packageName,@NonNull ModuleServiceCallback moduleCallback) {mPackageName = packageName;mModuleServiceCallback = moduleCallback;}@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {logi("Networking module service connected");mModuleServiceCallback.onModuleServiceConnected(service);}@Overridepublic void onServiceDisconnected(ComponentName name) {// onServiceDisconnected is not being called on device shutdown, so this method being// called always indicates a bad state for the system server.// This code path is only run by the system server: only the system server binds// to the NetworkStack as a service. Other processes get the NetworkStack from// the ServiceManager.maybeCrashWithTerribleFailure("Lost network stack", mPackageName);}}
结合代码可知,就是通过bindService()启动一个服务,并通过一个lambda表达式在服务connect成功之后进行服务注册的过程,它绑定的服务的filter是:android.net.ITetheringConnector。
在源码中,该服务定义在frameworks\base\packages\Tethering\AndroidManifest.xml中:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.android.networkstack.tethering"android:sharedUserId="android.uid.networkstack"><uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" /><!-- Permissions must be defined here, and not in the base manifest, as the tetheringrunning in the system server process does not need any permission, and havingprivileged permissions added would cause crashes on startup unless they are alsoadded to the privileged permissions whitelist for that package. --><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><uses-permission android:name="android.permission.BLUETOOTH" /><uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" /><uses-permission android:name="android.permission.BROADCAST_STICKY" /><uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /><uses-permission android:name="android.permission.MANAGE_USB" /><uses-permission android:name="android.permission.MODIFY_PHONE_STATE" /><uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /><uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" /><uses-permission android:name="android.permission.READ_PHONE_STATE"/><uses-permission android:name="android.permission.TETHER_PRIVILEGED" /><uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" /><uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" /><uses-permission android:name="android.permission.WRITE_SETTINGS" /><protected-broadcast android:name="com.android.server.connectivity.tethering.DISABLE_TETHERING" /><applicationandroid:process="com.android.networkstack.process"android:extractNativeLibs="false"android:persistent="true"><service android:name="com.android.networkstack.tethering.TetheringService"android:permission="android.permission.MAINLINE_NETWORK_STACK"android:exported="true"><intent-filter><action android:name="android.net.ITetheringConnector"/></intent-filter></service></application>
</manifest>
它对应的Service是TetheringService,Tethering.apk封装了网络共享相关的主要服务实现,它的使用类似于Bluetooth.apk,被bind service拉起随后运行;继续看TetheringService:
/** Copyright (C) 2019 The Android Open Source Project** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package com.android.networkstack.tethering;/*** Android service used to manage tethering.** <p>The service returns a binder for the system server to communicate with the tethering.*/
public class TetheringService extends Service {private static final String TAG = TetheringService.class.getSimpleName();private TetheringConnector mConnector;@Overridepublic void onCreate() {final TetheringDependencies deps = makeTetheringDependencies();// The Tethering object needs a fully functional context to start, so this can't be done// in the constructor.mConnector = new TetheringConnector(makeTethering(deps), TetheringService.this);}/*** Make a reference to Tethering object.*/@VisibleForTestingpublic Tethering makeTethering(TetheringDependencies deps) {System.loadLibrary("tetherutilsjni");return new Tethering(deps);}@NonNull@Overridepublic IBinder onBind(Intent intent) {return mConnector;}private static class TetheringConnector extends ITetheringConnector.Stub {private final TetheringService mService;private final Tethering mTethering;TetheringConnector(Tethering tether, TetheringService service) {mTethering = tether;mService = service;}}
}/** @hide */
oneway interface ITetheringConnector {void tether(String iface, String callerPkg, IIntResultListener receiver);void untether(String iface, String callerPkg, IIntResultListener receiver);void setUsbTethering(boolean enable, String callerPkg, IIntResultListener receiver);void startTethering(in TetheringRequestParcel request, String callerPkg,IIntResultListener receiver);void stopTethering(int type, String callerPkg, IIntResultListener receiver);void requestLatestTetheringEntitlementResult(int type, in ResultReceiver receiver,boolean showEntitlementUi, String callerPkg);void registerTetheringEventCallback(ITetheringEventCallback callback, String callerPkg);void unregisterTetheringEventCallback(ITetheringEventCallback callback, String callerPkg);void isTetheringSupported(String callerPkg, IIntResultListener receiver);void stopAllTethering(String callerPkg, IIntResultListener receiver);
}
TetheringService主要实现了AIDL ITetheringConnector定义的业务接口,根据function名称可以很清晰的知道,它主要针对的是网络共享。服务启动成功,会以tethering的名字注册进系统中,其他需要使用的组件就可以很容易的向Android查询、获取它。
Tethering组件正常启动之后,就是APP使用它了;我们以CarSetting为例:
/*** Fragment to host tethering-related preferences.*/
@SearchIndexable
public class WifiTetherFragment extends SettingsFragment {private TetheringManager mTetheringManager;private void startTethering() {mTetheringManager.startTethering(ConnectivityManager.TETHERING_WIFI,ConcurrentUtils.DIRECT_EXECUTOR,new TetheringManager.StartTetheringCallback() {@Overridepublic void onTetheringFailed(final int result) {mTetherSwitch.setChecked(false);mTetherSwitch.setEnabled(true);}});}private void stopTethering() {mTetheringManager.stopTethering(ConnectivityManager.TETHERING_WIFI);}private void restartTethering() {stopTethering();mRestartBooked = true;}}
WifiTetherFragment是实现开关热点的Fragment,可知它会调用TetherManager::startTethering()去开启WiFi共享,TetheringManager内部通过ITetheringConnector实现具体功能,类似于WifiManager:
/*** Starts tethering and runs tether provisioning for the given type if needed. If provisioning* fails, stopTethering will be called automatically.** <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will* fail if a tethering entitlement check is required.** @param request a {@link TetheringRequest} which can specify the preferred configuration.* @param executor {@link Executor} to specify the thread upon which the callback of* TetheringRequest will be invoked.* @param callback A callback that will be called to indicate the success status of the* tethering start request.*/@RequiresPermission(anyOf = {android.Manifest.permission.TETHER_PRIVILEGED,android.Manifest.permission.WRITE_SETTINGS})public void startTethering(@NonNull final TetheringRequest request,@NonNull final Executor executor, @NonNull final StartTetheringCallback callback) {final String callerPkg = mContext.getOpPackageName();Log.i(TAG, "startTethering caller:" + callerPkg);final IIntResultListener listener = new IIntResultListener.Stub() {@Overridepublic void onResult(final int resultCode) {executor.execute(() -> {if (resultCode == TETHER_ERROR_NO_ERROR) {callback.onTetheringStarted();} else {callback.onTetheringFailed(resultCode);}});}};getConnector(c -> c.startTethering(request.getParcel(), callerPkg, listener));}/*** Starts tethering and runs tether provisioning for the given type if needed. If provisioning* fails, stopTethering will be called automatically.** <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will* fail if a tethering entitlement check is required.** @param type The tethering type, on of the {@code TetheringManager#TETHERING_*} constants.* @param executor {@link Executor} to specify the thread upon which the callback of* TetheringRequest will be invoked.* @hide*/@RequiresPermission(anyOf = {android.Manifest.permission.TETHER_PRIVILEGED,android.Manifest.permission.WRITE_SETTINGS})@SystemApi(client = MODULE_LIBRARIES)public void startTethering(int type, @NonNull final Executor executor,@NonNull final StartTetheringCallback callback) {startTethering(new TetheringRequest.Builder(type).build(), executor, callback);}
从前述可知,传入的Tethering type是ConnectivityManager.TETHERING_WIFI,直接看TetheringService:
private final Tethering mTethering;@Overridepublic void startTethering(TetheringRequestParcel request, String callerPkg,IIntResultListener listener) {if (checkAndNotifyCommonError(callerPkg,request.exemptFromEntitlementCheck /* onlyAllowPrivileged */,listener)) {return;}m
Tethering是一个包含各种网络共享业务逻辑的实现类,比如BT网络共享、USB网络共享,以及我们这里关注的WiFi网络共享:
/**** This class holds much of the business logic to allow Android devices* to act as IP gateways via USB, BT, and WiFi interfaces.*/
public class Tethering {public Tethering(TetheringDependencies deps) {......startStateMachineUpdaters();}/*** Start to register callbacks.* Call this function when tethering is ready to handle callback events.*/private void startStateMachineUpdaters() {try {mNetd.registerUnsolicitedEventListener(mNetdCallback);} catch (RemoteException e) {mLog.e("Unable to register netd UnsolicitedEventListener");}mCarrierConfigChange.startListening();mContext.getSystemService(TelephonyManager.class).listen(mActiveDataSubIdListener,PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);IntentFilter filter = new IntentFilter();filter.addAction(UsbManager.ACTION_USB_STATE);filter.addAction(CONNECTIVITY_ACTION);filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);filter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);filter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED);filter.addAction(ACTION_RESTRICT_BACKGROUND_CHANGED);mContext.registerReceiver(mStateReceiver, filter, null, mHandler);final IntentFilter noUpstreamFilter = new IntentFilter();noUpstreamFilter.addAction(TetheringNotificationUpdater.ACTION_DISABLE_TETHERING);mContext.registerReceiver(mStateReceiver, noUpstreamFilter, PERMISSION_MAINLINE_NETWORK_STACK, mHandler);final WifiManager wifiManager = getWifiManager();if (wifiManager != null) {wifiManager.registerSoftApCallback(mExecutor, new TetheringSoftApCallback());}startTrackDefaultNetwork();}private class StateReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context content, Intent intent) {final String action = intent.getAction();if (action == null) return;if (action.equals(UsbManager.ACTION_USB_STATE)) {handleUsbAction(intent);} else if (action.equals(CONNECTIVITY_ACTION)) {handleConnectivityAction(intent);} else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {handleWifiApAction(intent);} else if (action.equals(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)) {handleWifiP2pAction(intent);} else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {mLog.log("OBSERVED configuration changed");updateConfiguration();} else if (action.equals(UserManager.ACTION_USER_RESTRICTIONS_CHANGED)) {mLog.log("OBSERVED user restrictions changed");handleUserRestrictionAction();} else if (action.equals(ACTION_RESTRICT_BACKGROUND_CHANGED)) {mLog.log("OBSERVED data saver changed");handleDataSaverChanged();} else if (action.equals(TetheringNotificationUpdater.ACTION_DISABLE_TETHERING)) {untetherAll();}}void startTethering(final TetheringRequestParcel request, final IIntResultListener listener) {mHandler.post(() -> {final TetheringRequestParcel unfinishedRequest = mActiveTetheringRequests.get(request.tetheringType);// If tethering is already enabled with a different request,// disable before re-enabling.if (unfinishedRequest != null&& !TetheringUtils.isTetheringRequestEquals(unfinishedRequest, request)) {enableTetheringInternal(request.tetheringType, false /* disabled */, null);mEntitlementMgr.stopProvisioningIfNeeded(request.tetheringType);}mActiveTetheringRequests.put(request.tetheringType, request);if (request.exemptFromEntitlementCheck) {mEntitlementMgr.setExemptedDownstreamType(request.tetheringType);} else {mEntitlementMgr.startProvisioningIfNeeded(request.tetheringType,request.showProvisioningUi);}enableTetheringInternal(request.tetheringType, true /* enabled */, listener);});}/*** Enables or disables tethering for the given type. If provisioning is required, it will* schedule provisioning rechecks for the specified interface.*/private void enableTetheringInternal(int type, boolean enable,final IIntResultListener listener) {int result = TETHER_ERROR_NO_ERROR;switch (type) {case TETHERING_WIFI:result = setWifiTethering(enable);break;case TETHERING_USB:result = setUsbTethering(enable);break;case TETHERING_BLUETOOTH:setBluetoothTethering(enable, listener);break;case TETHERING_NCM:result = setNcmTethering(enable);break;case TETHERING_ETHERNET:result = setEthernetTethering(enable);break;default:Log.w(TAG, "Invalid tether type.");result = TETHER_ERROR_UNKNOWN_TYPE;}// The result of Bluetooth tethering will be sent by #setBluetoothTethering.if (type != TETHERING_BLUETOOTH) {sendTetherResult(listener, result, type);}}private int setWifiTethering(final boolean enable) {final long ident = Binder.clearCallingIdentity();try {synchronized (mPublicSync) {final WifiManager mgr = getWifiManager();if (mgr == null) {mLog.e("setWifiTethering: failed to get WifiManager!");return TETHER_ERROR_SERVICE_UNAVAIL;}if ((enable && mgr.startTetheredHotspot(null /* use existing softap config */))|| (!enable && mgr.stopSoftAp())) {mWifiTetherRequested = enable;return TETHER_ERROR_NO_ERROR;}}} finally {Binder.restoreCallingIdentity(ident);}return TETHER_ERROR_INTERNAL_ERROR;}}
从构造函数可知,它监听了WifiManager.WIFI_AP_STATE_CHANGED_ACTION AP状态改变的广播,这让它有了在驱动创建成功SoftAp iface后,为它配置IP的能力,这个后面再介绍。根据startTethering()的调用逻辑,发现它调用了 WifiManager.startTetheredHotspot(),WifiManager我们应该很熟悉:
/*** This class provides the primary API for managing all aspects of Wi-Fi* connectivity.* <p>* On releases before {@link android.os.Build.VERSION_CODES#N}, this object* should only be obtained from an {@linkplain Context#getApplicationContext()* application context}, and not from any other derived context to avoid memory* leaks within the calling process.* <p>* It deals with several categories of items:* </p>* <ul>* <li>The list of configured networks. The list can be viewed and updated, and* attributes of individual entries can be modified.</li>* <li>The currently active Wi-Fi network, if any. Connectivity can be* established or torn down, and dynamic information about the state of the* network can be queried.</li>* <li>Results of access point scans, containing enough information to make* decisions about what access point to connect to.</li>* <li>It defines the names of various Intent actions that are broadcast upon* any sort of change in Wi-Fi state.* </ul>* <p>* This is the API to use when performing Wi-Fi specific operations. To perform* operations that pertain to network connectivity at an abstract level, use* {@link android.net.ConnectivityManager}.* </p>*/
@SystemService(Context.WIFI_SERVICE)
public class WifiManager {/*** Start Soft AP (hotspot) mode for tethering purposes with the specified configuration.* Note that starting Soft AP mode may disable station mode operation if the device does not* support concurrency.* @param softApConfig A valid SoftApConfiguration specifying the configuration of the SAP,* or null to use the persisted Soft AP configuration that was previously* set using {@link #setSoftApConfiguration(softApConfiguration)}.* @return {@code true} if the operation succeeded, {@code false} otherwise** @hide*/@SystemApi@RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_STACK,NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK})public boolean startTetheredHotspot(@Nullable SoftApConfiguration softApConfig) {try {return mService.startTetheredHotspot(softApConfig);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}}/*** WifiService handles remote WiFi operation requests by implementing* the IWifiManager interface.*/
public class WifiServiceImpl extends BaseWifiService {/*** see {@link android.net.wifi.WifiManager#startTetheredHotspot(SoftApConfiguration)}* @param softApConfig SSID, security and channel details as part of SoftApConfiguration* @return {@code true} if softap start was triggered* @throws SecurityException if the caller does not have permission to start softap*/@Overridepublic boolean startTetheredHotspot(@Nullable SoftApConfiguration softApConfig) {// NETWORK_STACK is a signature only permission.enforceNetworkStackPermission();mLog.info("startTetheredHotspot uid=%").c(Binder.getCallingUid()).flush();if (!mTetheredSoftApTracker.setEnablingIfAllowed()) {mLog.err("Tethering is already active.").flush();return false;}if (!mWifiThreadRunner.call(() -> mActiveModeWarden.canRequestMoreSoftApManagers(), false)) {// Take down LOHS if it is up.mLohsSoftApTracker.stopAll();}if (!startSoftApInternal(new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, softApConfig,mTetheredSoftApTracker.getSoftApCapability()))) {mTetheredSoftApTracker.setFailedWhileEnabling();return false;}return true;}/*** Internal method to start softap mode. Callers of this method should have already checked* proper permissions beyond the NetworkStack permission.*/private boolean startSoftApInternal(SoftApModeConfiguration apConfig) {int uid = Binder.getCallingUid();boolean privileged = isSettingsOrSuw(Binder.getCallingPid(), uid);mLog.trace("startSoftApInternal uid=% mode=%").c(uid).c(apConfig.getTargetMode()).flush();// null wifiConfig is a meaningful input for CMD_SET_AP; it means to use the persistent// AP config.SoftApConfiguration softApConfig = apConfig.getSoftApConfiguration();if (softApConfig != null&& (!WifiApConfigStore.validateApWifiConfiguration(softApConfig, privileged)|| !validateSoftApBand(softApConfig.getBand()))) {Log.e(TAG, "Invalid SoftApConfiguration");return false;}mActiveModeWarden.startSoftAp(apConfig);return true;}}
主要的,首先创建了一个目标共享MODE为WifiManager.IFACE_IP_MODE_TETHERED的SoftApModeConfiguration对象,它描述了我们对开启的热点的配置信息,随后调用ActiveModeWarden.startSoftAp():
/*** This class provides the implementation for different WiFi operating modes.*/
public class ActiveModeWarden {/*** WifiController is the class used to manage wifi state for various operating* modes (normal, airplane, wifi hotspot, etc.).*/private class WifiController extends StateMachine {WifiController() {super(TAG, mLooper);DefaultState defaultState = new DefaultState();addState(defaultState); {addState(mDisabledState, defaultState);addState(mEnabledState, defaultState);}setLogRecSize(100);setLogOnlyTransitions(false);}@Overridepublic void start() {if (shouldEnableSta()) {startClientModeManager();setInitialState(mEnabledState);} else {setInitialState(mDisabledState);}super.start();}class DefaultState extends State {}class EnabledState extends BaseState {private boolean mIsDisablingDueToAirplaneMode;@Overridepublic void enter() {log("EnabledState.enter()");super.enter();if (!hasAnyModeManager()) {Log.e(TAG, "Entered EnabledState, but no active mode managers");}mIsDisablingDueToAirplaneMode = false;}@Overridepublic void exit() {log("EnabledState.exit()");if (hasAnyModeManager()) {Log.e(TAG, "Existing EnabledState, but has active mode managers");}super.exit();}@Overridepublic boolean processMessageFiltered(Message msg) {switch (msg.what) {case CMD_WIFI_TOGGLED:case CMD_SCAN_ALWAYS_MODE_CHANGED:if (shouldEnableSta()) {if (hasAnyClientModeManager()) {switchAllClientModeManagers();} else {startClientModeManager();}} else {stopAllClientModeManagers();}break;case CMD_SET_AP:// note: CMD_SET_AP is handled/dropped in ECM mode - will not start hereif (msg.arg1 == 1) {startSoftApModeManager((SoftApModeConfiguration) msg.obj);} else {stopSoftApModeManagers(msg.arg2);}break;case CMD_AIRPLANE_TOGGLED:// airplane mode toggled on is handled in the default stateif (mSettingsStore.isAirplaneModeOn()) {mIsDisablingDueToAirplaneMode = true;return NOT_HANDLED;} else {if (mIsDisablingDueToAirplaneMode) {// Previous airplane mode toggle on is being processed, defer the// message toggle off until previous processing is completed.// Once previous airplane mode toggle is complete, we should// transition to DisabledState. There, we will process the deferred// airplane mode toggle message to disable airplane mode.deferMessage(msg);} else {// when airplane mode is toggled off, but wifi is on, we can keep it// onlog("airplane mode toggled - and airplane mode is off. return "+ "handled");}return HANDLED;}case CMD_AP_STOPPED:case CMD_AP_START_FAILURE:if (!hasAnyModeManager()) {if (shouldEnableSta()) {log("SoftAp disabled, start client mode");startClientModeManager();} else {log("SoftAp mode disabled, return to DisabledState");transitionTo(mDisabledState);}} else {log("AP disabled, remain in EnabledState.");}break;case CMD_STA_START_FAILURE:case CMD_STA_STOPPED:// Client mode stopped. Head to Disabled to wait for next command if there// no active mode managers.if (!hasAnyModeManager()) {log("STA disabled, return to DisabledState.");transitionTo(mDisabledState);} else {log("STA disabled, remain in EnabledState.");}break;case CMD_RECOVERY_RESTART_WIFI:final String bugTitle;final String bugDetail;if (msg.arg1 < SelfRecovery.REASON_STRINGS.length && msg.arg1 >= 0) {bugDetail = SelfRecovery.REASON_STRINGS[msg.arg1];bugTitle = "Wi-Fi BugReport: " + bugDetail;} else {bugDetail = "";bugTitle = "Wi-Fi BugReport";}if (msg.arg1 != SelfRecovery.REASON_LAST_RESORT_WATCHDOG) {mHandler.post(() -> mClientModeImpl.takeBugReport(bugTitle, bugDetail));}log("Recovery triggered, disable wifi");deferMessage(obtainMessage(CMD_DEFERRED_RECOVERY_RESTART_WIFI));shutdownWifi();// onStopped will move the state machine to "DisabledState".break;default:return NOT_HANDLED;}return HANDLED;}}class DisabledState extends BaseState {@Overridepublic void enter() {log("DisabledState.enter()");super.enter();if (hasAnyModeManager()) {Log.e(TAG, "Entered DisabledState, but has active mode managers");}}@Overridepublic void exit() {log("DisabledState.exit()");super.exit();}@Overridepublic boolean processMessageFiltered(Message msg) {switch (msg.what) {case CMD_WIFI_TOGGLED:case CMD_SCAN_ALWAYS_MODE_CHANGED:if (shouldEnableSta()) {startClientModeManager();transitionTo(mEnabledState);}break;case CMD_SET_AP:// note: CMD_SET_AP is handled/dropped in ECM mode - will not start hereif (msg.arg1 == 1) {startSoftApModeManager((SoftApModeConfiguration) msg.obj);transitionTo(mEnabledState);}break;case CMD_RECOVERY_RESTART_WIFI:log("Recovery triggered, already in disabled state");// intentional fallthroughcase CMD_DEFERRED_RECOVERY_RESTART_WIFI:// wait mRecoveryDelayMillis for letting driver clean reset.sendMessageDelayed(CMD_RECOVERY_RESTART_WIFI_CONTINUE,readWifiRecoveryDelay());break;case CMD_RECOVERY_RESTART_WIFI_CONTINUE:if (shouldEnableSta()) {startClientModeManager();transitionTo(mEnabledState);}break;default:return NOT_HANDLED;}return HANDLED;}}}/** Starts SoftAp. */public void startSoftAp(SoftApModeConfiguration softApConfig) {mWifiController.sendMessage(WifiController.CMD_SET_AP, 1, 0, softApConfig);}}
经常接触Wifi或者BT Framework框架的打工人应该很熟悉StateMachine的流程了,这里不再介绍相关的流程,只关注开启热点的处理流程,startSoftAp()的操作只是发送了WifiController.CMD_SET_AP命令,让状态机处理开启的流程,假设这是我们第一次开热点,也是只开热点,DisabledState状态处理该cmd:
case CMD_SET_AP:
// note: CMD_SET_AP is handled/dropped in ECM mode - will not start here
if (msg.arg1 == 1) {startSoftApModeManager((SoftApModeConfiguration) msg.obj);transitionTo(mEnabledState);
}/*** Method to enable soft ap for wifi hotspot.** The supplied SoftApModeConfiguration includes the target softap WifiConfiguration (or null if* the persisted config is to be used) and the target operating mode (ex,* {@link WifiManager#IFACE_IP_MODE_TETHERED} {@link WifiManager#IFACE_IP_MODE_LOCAL_ONLY}).** @param softApConfig SoftApModeConfiguration for the hostapd softap*/private void startSoftApModeManager(@NonNull SoftApModeConfiguration softApConfig) {Log.d(TAG, "Starting SoftApModeManager config = "+ softApConfig.getSoftApConfiguration());Preconditions.checkState(softApConfig.getTargetMode() == IFACE_IP_MODE_LOCAL_ONLY|| softApConfig.getTargetMode() == IFACE_IP_MODE_TETHERED);WifiManager.SoftApCallback callback =softApConfig.getTargetMode() == IFACE_IP_MODE_LOCAL_ONLY? mLohsCallback : mSoftApCallback;SoftApListener listener = new SoftApListener();ActiveModeManager manager =mWifiInjector.makeSoftApManager(listener, callback, softApConfig);listener.setActiveModeManager(manager);manager.start();manager.setRole(getRoleForSoftApIpMode(softApConfig.getTargetMode()));mActiveModeManagers.add(manager);}private @ActiveModeManager.Role int getRoleForSoftApIpMode(int ipMode) {return ipMode == IFACE_IP_MODE_TETHERED? ActiveModeManager.ROLE_SOFTAP_TETHERED : ActiveModeManager.ROLE_SOFTAP_LOCAL_ONLY;}
创建SoftApManager并调用其start(),因为之前我们创建的MODE是IFACE_IP_MODE_TETHERED所以给SoftApManager set的role这里是ROLE_SOFTAP_TETHERED;随后进入EnabledState。我们看SoftApManager,它是一个管理WiFi AP mode管理类:
/*** Manage WiFi in AP mode.* The internal state machine runs under the ClientModeImpl handler thread context.*/
public class SoftApManager implements ActiveModeManager {/*** Listener for soft AP events.*/private final SoftApListener mSoftApListener = new SoftApListener() {@Overridepublic void onFailure() {mStateMachine.sendMessage(SoftApStateMachine.CMD_FAILURE);}@Overridepublic void onConnectedClientsChanged(NativeWifiClient client, boolean isConnected) {if (client != null) {mStateMachine.sendMessage(SoftApStateMachine.CMD_ASSOCIATED_STATIONS_CHANGED,isConnected ? 1 : 0, 0, client);} else {Log.e(TAG, "onConnectedClientsChanged: Invalid type returned");}}@Overridepublic void onSoftApChannelSwitched(int frequency,@WifiAnnotations.Bandwidth int bandwidth) {mStateMachine.sendMessage(SoftApStateMachine.CMD_SOFT_AP_CHANNEL_SWITCHED, frequency, bandwidth);}};public SoftApManager(@NonNull WifiContext context,@NonNull Looper looper,@NonNull FrameworkFacade framework,@NonNull WifiNative wifiNative,String countryCode,@NonNull Listener listener,@NonNull WifiManager.SoftApCallback callback,@NonNull WifiApConfigStore wifiApConfigStore,@NonNull SoftApModeConfiguration apConfig,@NonNull WifiMetrics wifiMetrics,@NonNull SarManager sarManager,@NonNull BaseWifiDiagnostics wifiDiagnostics) {mStateMachine = new SoftApStateMachine(looper);}/*** Start soft AP, as configured in the constructor.*/@Overridepublic void start() {mStateMachine.sendMessage(SoftApStateMachine.CMD_START);}private class SoftApStateMachine extends StateMachine {private final State mIdleState = new IdleState();private final State mStartedState = new StartedState();private final InterfaceCallback mWifiNativeInterfaceCallback = new InterfaceCallback() {@Overridepublic void onDestroyed(String ifaceName) {if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) {sendMessage(CMD_INTERFACE_DESTROYED);}}@Overridepublic void onUp(String ifaceName) {if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) {sendMessage(CMD_INTERFACE_STATUS_CHANGED, 1);}}@Overridepublic void onDown(String ifaceName) {if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) {sendMessage(CMD_INTERFACE_STATUS_CHANGED, 0);}}};SoftApStateMachine(Looper looper) {super(TAG, looper);addState(mIdleState);addState(mStartedState);setInitialState(mIdleState);start();}private class IdleState extends State {}private class StartedState extends State {}}}
SoftApManager内部包含一个小的状态机SoftApStateMachine,它只有两个状态,Idle状态是初始状态,它处理发来的SoftApStateMachine.CMD_START:
private class IdleState extends State {@Overridepublic void enter() {}@Overridepublic boolean processMessage(Message message) {switch (message.what) {case CMD_START:mApInterfaceName = mWifiNative.setupInterfaceForSoftApMode(mWifiNativeInterfaceCallback);if (TextUtils.isEmpty(mApInterfaceName)) {Log.e(TAG, "setup failure when creating ap interface.");updateApState(WifiManager.WIFI_AP_STATE_FAILED,WifiManager.WIFI_AP_STATE_DISABLED,WifiManager.SAP_START_FAILURE_GENERAL);mWifiMetrics.incrementSoftApStartResult(false, WifiManager.SAP_START_FAILURE_GENERAL);mModeListener.onStartFailure();break;}mSoftApNotifier.dismissSoftApShutDownTimeoutExpiredNotification();updateApState(WifiManager.WIFI_AP_STATE_ENABLING,WifiManager.WIFI_AP_STATE_DISABLED, 0);int result = startSoftAp();if (result != SUCCESS) {int failureReason = WifiManager.SAP_START_FAILURE_GENERAL;if (result == ERROR_NO_CHANNEL) {failureReason = WifiManager.SAP_START_FAILURE_NO_CHANNEL;} else if (result == ERROR_UNSUPPORTED_CONFIGURATION) {failureReason = WifiManager.SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION;}updateApState(WifiManager.WIFI_AP_STATE_FAILED,WifiManager.WIFI_AP_STATE_ENABLING,failureReason);stopSoftAp();mWifiMetrics.incrementSoftApStartResult(false, failureReason);mModeListener.onStartFailure();break;}transitionTo(mStartedState);break;}
}/*** Start a soft AP instance as configured.** @return integer result code*/private int startSoftAp() {SoftApConfiguration config = mApConfig.getSoftApConfiguration();if (config == null || config.getSsid() == null) {Log.e(TAG, "Unable to start soft AP without valid configuration");return ERROR_GENERIC;}Log.d(TAG, "band " + config.getBand() + " iface "+ mApInterfaceName + " country " + mCountryCode);int result = setMacAddress();if (result != SUCCESS) {return result;}result = setCountryCode();if (result != SUCCESS) {return result;}// Make a copy of configuration for updating AP band and channel.SoftApConfiguration.Builder localConfigBuilder = new SoftApConfiguration.Builder(config);boolean acsEnabled = mCurrentSoftApCapability.areFeaturesSupported(SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD);result = ApConfigUtil.updateApChannelConfig(mWifiNative, mContext.getResources(), mCountryCode, localConfigBuilder, config,acsEnabled);if (result != SUCCESS) {Log.e(TAG, "Failed to update AP band and channel");return result;}if (config.isHiddenSsid()) {Log.d(TAG, "SoftAP is a hidden network");}if (!ApConfigUtil.checkSupportAllConfiguration(config, mCurrentSoftApCapability)) {Log.d(TAG, "Unsupported Configuration detect! config = " + config);return ERROR_UNSUPPORTED_CONFIGURATION;}if (!mWifiNative.startSoftAp(mApInterfaceName,localConfigBuilder.build(), mSoftApListener)) {Log.e(TAG, "Soft AP start failed");return ERROR_GENERIC;}mWifiDiagnostics.startLogging(mApInterfaceName);mStartTimestamp = FORMATTER.format(new Date(System.currentTimeMillis()));Log.d(TAG, "Soft AP is started ");return SUCCESS;}
主要的:
1、WifiNative.setupInterfaceForSoftApMode():创建一个供AP MODE使用的interface,它的状态由mWifiNativeInterfaceCallback回调管理.
2、SoftApManager.startSoftAp():按配置信息,在HAL/Driver层创建一个管理Soft ap的instance,它的状态由SoftApListener回调管理
如果都调用成功,进入StartedState:
private class StartedState extends State {private WakeupMessage mSoftApTimeoutMessage;private void scheduleTimeoutMessage() {if (!mTimeoutEnabled || mConnectedClients.size() != 0) {cancelTimeoutMessage();return;}long timeout = mApConfig.getSoftApConfiguration().getShutdownTimeoutMillis();if (timeout == 0) {timeout = mDefaultShutDownTimeoutMills;}mSoftApTimeoutMessage.schedule(SystemClock.elapsedRealtime()+ timeout);Log.d(TAG, "Timeout message scheduled, delay = "+ timeout);}@Overridepublic void enter() {mIfaceIsUp = false;mIfaceIsDestroyed = false;onUpChanged(mWifiNative.isInterfaceUp(mApInterfaceName));Handler handler = mStateMachine.getHandler();mSoftApTimeoutMessage = new WakeupMessage(mContext, handler,SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG,SoftApStateMachine.CMD_NO_ASSOCIATED_STATIONS_TIMEOUT);mSarManager.setSapWifiState(WifiManager.WIFI_AP_STATE_ENABLED);Log.d(TAG, "Resetting connected clients on start");mConnectedClients.clear();mEverReportMetricsForMaxClient = false;scheduleTimeoutMessage();}
}
进入StartedState::enter()时,比较重要的是此时会设置一个默认600000ms(10min)的timeout超时机制,如果此时间段一直没有设备连接该AP,就会自动关闭AP。
整个开启AP的过程都会有对外通知当前AP状态改变的广播发送:
/*** Update AP state.** @param newState new AP state* @param currentState current AP state* @param reason Failure reason if the new AP state is in failure state*/private void updateApState(int newState, int currentState, int reason) {mSoftApCallback.onStateChanged(newState, reason);//send the AP state change broadcastfinal Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, newState);intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, currentState);if (newState == WifiManager.WIFI_AP_STATE_FAILED) {//only set reason number when softAP start failedintent.putExtra(WifiManager.EXTRA_WIFI_AP_FAILURE_REASON, reason);}intent.putExtra(WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME, mApInterfaceName);intent.putExtra(WifiManager.EXTRA_WIFI_AP_MODE, mApConfig.getTargetMode());mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);}
假设我们正常开启了热点,此时也会发送AP开启成功的广播。回到前面介绍Tethering的内容,它内部监听了该广播:
private class StateReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context content, Intent intent) {final String action = intent.getAction();if (action == null) return;if (action.equals(UsbManager.ACTION_USB_STATE)) {handleUsbAction(intent);} else if (action.equals(CONNECTIVITY_ACTION)) {handleConnectivityAction(intent);} else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {handleWifiApAction(intent);} else if (action.equals(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)) {handleWifiP2pAction(intent);} else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {mLog.log("OBSERVED configuration changed");updateConfiguration();} else if (action.equals(UserManager.ACTION_USER_RESTRICTIONS_CHANGED)) {mLog.log("OBSERVED user restrictions changed");handleUserRestrictionAction();} else if (action.equals(ACTION_RESTRICT_BACKGROUND_CHANGED)) {mLog.log("OBSERVED data saver changed");handleDataSaverChanged();} else if (action.equals(TetheringNotificationUpdater.ACTION_DISABLE_TETHERING)) {untetherAll();}}private void handleWifiApAction(Intent intent) {final int curState = intent.getIntExtra(EXTRA_WIFI_AP_STATE, WIFI_AP_STATE_DISABLED);final String ifname = intent.getStringExtra(EXTRA_WIFI_AP_INTERFACE_NAME);final int ipmode = intent.getIntExtra(EXTRA_WIFI_AP_MODE, IFACE_IP_MODE_UNSPECIFIED);synchronized (Tethering.this.mPublicSync) {switch (curState) {case WifiManager.WIFI_AP_STATE_ENABLING:// We can see this state on the way to both enabled and failure states.break;case WifiManager.WIFI_AP_STATE_ENABLED:enableWifiIpServingLocked(ifname, ipmode);break;case WifiManager.WIFI_AP_STATE_DISABLING:// We can see this state on the way to disabled.break;case WifiManager.WIFI_AP_STATE_DISABLED:case WifiManager.WIFI_AP_STATE_FAILED:default:disableWifiIpServingLocked(ifname, curState);break;}}}
}private void enableWifiIpServingLocked(String ifname, int wifiIpMode) {// Map wifiIpMode values to IpServer.Callback serving states, inferring// from mWifiTetherRequested as a final "best guess".final int ipServingMode;switch (wifiIpMode) {case IFACE_IP_MODE_TETHERED:ipServingMode = IpServer.STATE_TETHERED;break;case IFACE_IP_MODE_LOCAL_ONLY:ipServingMode = IpServer.STATE_LOCAL_ONLY;break;default:mLog.e("Cannot enable IP serving in unknown WiFi mode: " + wifiIpMode);return;}if (!TextUtils.isEmpty(ifname)) {maybeTrackNewInterfaceLocked(ifname);changeInterfaceState(ifname, ipServingMode);} else {mLog.e(String.format("Cannot enable IP serving in mode %s on missing interface name",ipServingMode));}}
根据前面的说明,此处会调用Tethering.enableWifiIpServingLocked(),主要的处理有改变ipServerMode的值为IpServer.STATE_TETHERED,以及如下两个函数:
private void maybeTrackNewInterfaceLocked(final String iface) {// If we don't care about this type of interface, ignore.final int interfaceType = ifaceNameToType(iface);if (interfaceType == TETHERING_INVALID) {mLog.log(iface + " is not a tetherable iface, ignoring");return;}maybeTrackNewInterfaceLocked(iface, interfaceType);}private void maybeTrackNewInterfaceLocked(final String iface, int interfaceType) {// If we have already started a TISM for this interface, skip.if (mTetherStates.containsKey(iface)) {mLog.log("active iface (" + iface + ") reported as added, ignoring");return;}mLog.log("adding TetheringInterfaceStateMachine for: " + iface);final TetherState tetherState = new TetherState(new IpServer(iface, mLooper, interfaceType, mLog, mNetd, mBpfCoordinator,makeControlCallback(), mConfig.enableLegacyDhcpServer,mConfig.isBpfOffloadEnabled(), mPrivateAddressCoordinator,mDeps.getIpServerDependencies()));mTetherStates.put(iface, tetherState);tetherState.ipServer.start();}private void changeInterfaceState(String ifname, int requestedState) {final int result;switch (requestedState) {case IpServer.STATE_UNAVAILABLE:case IpServer.STATE_AVAILABLE:result = untether(ifname);break;case IpServer.STATE_TETHERED:case IpServer.STATE_LOCAL_ONLY:result = tether(ifname, requestedState);break;default:Log.wtf(TAG, "Unknown interface state: " + requestedState);return;}if (result != TETHER_ERROR_NO_ERROR) {Log.e(TAG, "unable start or stop tethering on iface " + ifname);return;}}
这里两个个函数的内容分别介绍,首先maybeTrackNewInterfaceLocked():
private void maybeTrackNewInterfaceLocked(final String iface, int interfaceType) {// If we have already started a TISM for this interface, skip.if (mTetherStates.containsKey(iface)) {mLog.log("active iface (" + iface + ") reported as added, ignoring");return;}mLog.log("adding TetheringInterfaceStateMachine for: " + iface);final TetherState tetherState = new TetherState(new IpServer(iface, mLooper, interfaceType, mLog, mNetd, mBpfCoordinator,makeControlCallback(), mConfig.enableLegacyDhcpServer,mConfig.isBpfOffloadEnabled(), mPrivateAddressCoordinator,mDeps.getIpServerDependencies()));mTetherStates.put(iface, tetherState);tetherState.ipServer.start();}
1、它为每个iface创建了TetherState对象,并保存一个Map里
2、同时创建了IpServer类型实例,并调用了IpServer.start(),它是给AP配置的IP地址的来源
/*** Provides the interface to IP-layer serving functionality for a given network* interface, e.g. for tethering or "local-only hotspot" mode.** @hide*/
public class IpServer extends StateMachine {/** IpServer callback. */public static class Callback {/*** Notify that |who| has changed its tethering state.** @param who the calling instance of IpServer* @param state one of STATE_** @param lastError one of TetheringManager.TETHER_ERROR_**/public void updateInterfaceState(IpServer who, int state, int lastError) { }/*** Notify that |who| has new LinkProperties.** @param who the calling instance of IpServer* @param newLp the new LinkProperties to report*/public void updateLinkProperties(IpServer who, LinkProperties newLp) { }/*** Notify that the DHCP leases changed in one of the IpServers.*/public void dhcpLeasesChanged() { }/*** Request Tethering change.** @param tetheringType the downstream type of this IpServer.* @param enabled enable or disable tethering.*/public void requestEnableTethering(int tetheringType, boolean enabled) { }}private final State mInitialState;private final State mLocalHotspotState;private final State mTetheredState;private final State mUnavailableState;private final State mWaitingForRestartState;private LinkAddress mIpv4Address;// TODO: Add a dependency object to pass the data members or variables from the tethering// object. It helps to reduce the arguments of the constructor.public IpServer(String ifaceName, Looper looper, int interfaceType, SharedLog log,INetd netd, @NonNull BpfCoordinator coordinator, Callback callback,boolean usingLegacyDhcp, boolean usingBpfOffload,PrivateAddressCoordinator addressCoordinator, Dependencies deps) {mInitialState = new InitialState();mLocalHotspotState = new LocalHotspotState();mTetheredState = new TetheredState();mUnavailableState = new UnavailableState();mWaitingForRestartState = new WaitingForRestartState();addState(mInitialState);addState(mLocalHotspotState);addState(mTetheredState);addState(mWaitingForRestartState, mTetheredState);addState(mUnavailableState);setInitialState(mInitialState);}class InitialState extends State {}class BaseServingState extends State {}class LocalHotspotState extends BaseServingState{}class TetheredState extends BaseServingState {}/*** This state is terminal for the per interface state machine. At this* point, the master state machine should have removed this interface* specific state machine from its list of possible recipients of* tethering requests. The state machine itself will hang around until* the garbage collector finds it.*/class UnavailableState extends State {@Overridepublic void enter() {mIpNeighborMonitor.stop();mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;sendInterfaceState(STATE_UNAVAILABLE);}}class WaitingForRestartState extends State {@Overridepublic boolean processMessage(Message message) {logMessage(this, message.what);switch (message.what) {case CMD_TETHER_UNREQUESTED:transitionTo(mInitialState);mLog.i("Untethered (unrequested) and restarting " + mIfaceName);mCallback.requestEnableTethering(mInterfaceType, true /* enabled */);break;case CMD_INTERFACE_DOWN:transitionTo(mUnavailableState);mLog.i("Untethered (interface down) and restarting" + mIfaceName);mCallback.requestEnableTethering(mInterfaceType, true /* enabled */);break;default:return false;}return true;}}
}
IpServer也是一个小状态机,它主要是计算IP地址并在对应的iface进行地址配置,这样我们ifconfig查看网卡信息时,才能看到配置的信息。IpServer.start()启动之后,处理暂时告一段落.
接着看Tethering.changeInterfaceState():
private void changeInterfaceState(String ifname, int requestedState) {final int result;switch (requestedState) {case IpServer.STATE_UNAVAILABLE:case IpServer.STATE_AVAILABLE:result = untether(ifname);break;case IpServer.STATE_TETHERED:case IpServer.STATE_LOCAL_ONLY:result = tether(ifname, requestedState);break;default:Log.wtf(TAG, "Unknown interface state: " + requestedState);return;}if (result != TETHER_ERROR_NO_ERROR) {Log.e(TAG, "unable start or stop tethering on iface " + ifname);return;}}
根据前面的Mode,我们进入Tethering.tether():
private int tether(String iface, int requestedState) {if (DBG) Log.d(TAG, "Tethering " + iface);synchronized (mPublicSync) {TetherState tetherState = mTetherStates.get(iface);if (tetherState == null) {Log.e(TAG, "Tried to Tether an unknown iface: " + iface + ", ignoring");return TETHER_ERROR_UNKNOWN_IFACE;}// Ignore the error status of the interface. If the interface is available,// the errors are referring to past tethering attempts anyway.if (tetherState.lastState != IpServer.STATE_AVAILABLE) {Log.e(TAG, "Tried to Tether an unavailable iface: " + iface + ", ignoring");return TETHER_ERROR_UNAVAIL_IFACE;}// NOTE: If a CMD_TETHER_REQUESTED message is already in the TISM's queue but not yet// processed, this will be a no-op and it will not return an error.//// This code cannot race with untether() because they both synchronize on mPublicSync.// TODO: reexamine the threading and messaging model to totally remove mPublicSync.final int type = tetherState.ipServer.interfaceType();final TetheringRequestParcel request = mActiveTetheringRequests.get(type, null);if (request != null) {mActiveTetheringRequests.delete(type);}tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_REQUESTED, requestedState, 0,request);return TETHER_ERROR_NO_ERROR;}}
主要向IpServer这个StateMachine发送CMD_TETHER_REQUESTED cmd,它的初始状态是InitialState,它处理此消息:
class InitialState extends State {@Overridepublic void enter() {sendInterfaceState(STATE_AVAILABLE);}@Overridepublic boolean processMessage(Message message) {logMessage(this, message.what);switch (message.what) {case CMD_TETHER_REQUESTED:mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;switch (message.arg1) {case STATE_LOCAL_ONLY:maybeConfigureStaticIp((TetheringRequestParcel) message.obj);transitionTo(mLocalHotspotState);break;case STATE_TETHERED:maybeConfigureStaticIp((TetheringRequestParcel) message.obj);transitionTo(mTetheredState);break;default:mLog.e("Invalid tethering interface serving state specified.");}break;case CMD_INTERFACE_DOWN:transitionTo(mUnavailableState);break;case CMD_IPV6_TETHER_UPDATE:updateUpstreamIPv6LinkProperties((LinkProperties) message.obj, message.arg1);break;default:return NOT_HANDLED;}return HANDLED;}}private void maybeConfigureStaticIp(final TetheringRequestParcel request) {// Ignore static address configuration if they are invalid or null. In theory, static// addresses should not be invalid here because TetheringManager do not allow caller to// specify invalid static address configuration.if (request == null || request.localIPv4Address == null|| request.staticClientAddress == null || !checkStaticAddressConfiguration(request.localIPv4Address, request.staticClientAddress)) {return;}mStaticIpv4ServerAddr = request.localIPv4Address;mStaticIpv4ClientAddr = request.staticClientAddress;}// Handling errors in BaseServingState.enter() by transitioning is// problematic because transitioning during a multi-state jump yields// a Log.wtf(). Ultimately, there should be only one ServingState,// and forwarding and NAT rules should be handled by a coordinating// functional element outside of IpServer.class TetheredState extends BaseServingState {@Overridepublic void enter() {super.enter();if (mLastError != TetheringManager.TETHER_ERROR_NO_ERROR) {transitionTo(mInitialState);}if (DBG) Log.d(TAG, "Tethered " + mIfaceName);sendInterfaceState(STATE_TETHERED);}@Overridepublic void exit() {cleanupUpstream();super.exit();}private void cleanupUpstream() {if (mUpstreamIfaceSet == null) return;for (String ifname : mUpstreamIfaceSet.ifnames) cleanupUpstreamInterface(ifname);mUpstreamIfaceSet = null;clearIpv6ForwardingRules();}private void cleanupUpstreamInterface(String upstreamIface) {// Note that we don't care about errors here.// Sometimes interfaces are gone before we get// to remove their rules, which generates errors.// Just do the best we can.try {mNetd.ipfwdRemoveInterfaceForward(mIfaceName, upstreamIface);} catch (RemoteException | ServiceSpecificException e) {mLog.e("Exception in ipfwdRemoveInterfaceForward: " + e.toString());}try {mNetd.tetherRemoveForward(mIfaceName, upstreamIface);} catch (RemoteException | ServiceSpecificException e) {mLog.e("Exception in disableNat: " + e.toString());}}@Overridepublic boolean processMessage(Message message) {if (super.processMessage(message)) return true;logMessage(this, message.what);switch (message.what) {case CMD_TETHER_REQUESTED:mLog.e("CMD_TETHER_REQUESTED while already tethering.");break;case CMD_TETHER_CONNECTION_CHANGED:final InterfaceSet newUpstreamIfaceSet = (InterfaceSet) message.obj;if (noChangeInUpstreamIfaceSet(newUpstreamIfaceSet)) {if (VDBG) Log.d(TAG, "Connection changed noop - dropping");break;}if (newUpstreamIfaceSet == null) {cleanupUpstream();break;}for (String removed : upstreamInterfacesRemoved(newUpstreamIfaceSet)) {cleanupUpstreamInterface(removed);}final Set<String> added = upstreamInterfacesAdd(newUpstreamIfaceSet);// This makes the call to cleanupUpstream() in the error// path for any interface neatly cleanup all the interfaces.mUpstreamIfaceSet = newUpstreamIfaceSet;for (String ifname : added) {try {mNetd.tetherAddForward(mIfaceName, ifname);mNetd.ipfwdAddInterfaceForward(mIfaceName, ifname);} catch (RemoteException | ServiceSpecificException e) {mLog.e("Exception enabling NAT: " + e.toString());cleanupUpstream();mLastError = TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR;transitionTo(mInitialState);return true;}}break;case CMD_NEIGHBOR_EVENT:handleNeighborEvent((NeighborEvent) message.obj);break;default:return false;}return true;}private boolean noChangeInUpstreamIfaceSet(InterfaceSet newIfaces) {if (mUpstreamIfaceSet == null && newIfaces == null) return true;if (mUpstreamIfaceSet != null && newIfaces != null) {return mUpstreamIfaceSet.equals(newIfaces);}return false;}private Set<String> upstreamInterfacesRemoved(InterfaceSet newIfaces) {if (mUpstreamIfaceSet == null) return new HashSet<>();final HashSet<String> removed = new HashSet<>(mUpstreamIfaceSet.ifnames);removed.removeAll(newIfaces.ifnames);return removed;}private Set<String> upstreamInterfacesAdd(InterfaceSet newIfaces) {final HashSet<String> added = new HashSet<>(newIfaces.ifnames);if (mUpstreamIfaceSet != null) added.removeAll(mUpstreamIfaceSet.ifnames);return added;}}class BaseServingState extends State {@Overridepublic void enter() {if (!startIPv4()) {mLastError = TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;return;}try {NetdUtils.tetherInterface(mNetd, mIfaceName, asIpPrefix(mIpv4Address));} catch (RemoteException | ServiceSpecificException | IllegalStateException e) {mLog.e("Error Tethering", e);mLastError = TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;return;}if (!startIPv6()) {mLog.e("Failed to startIPv6");// TODO: Make this a fatal error once Bluetooth IPv6 is sorted.return;}}@Overridepublic void exit() {// Note that at this point, we're leaving the tethered state. We can fail any// of these operations, but it doesn't really change that we have to try them// all in sequence.stopIPv6();try {NetdUtils.untetherInterface(mNetd, mIfaceName);} catch (RemoteException | ServiceSpecificException e) {mLastError = TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;mLog.e("Failed to untether interface: " + e);}stopIPv4();resetLinkProperties();}
maybeConfigureStaticIp()主要是根据传入的config信息初始化一下变量,以便后续配置为设定的static ip;这里假设没有指定IP,直接进入mTetheredState,根据StateMachine的切换逻辑,首先执行父状态的enter(),其中:
/** Internals. */private boolean startIPv4() {return configureIPv4(true);}private boolean configureIPv4(boolean enabled) {if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")");if (enabled) {mIpv4Address = requestIpv4Address();}if (mIpv4Address == null) {mLog.e("No available ipv4 address");return false;}if (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) {// BT configures the interface elsewhere: only start DHCP.// TODO: make all tethering types behave the same way, and delete the bluetooth// code that calls into NetworkManagementService directly.return configureDhcp(enabled, mIpv4Address, null /* clientAddress */);}final IpPrefix ipv4Prefix = asIpPrefix(mIpv4Address);final Boolean setIfaceUp;if (mInterfaceType == TetheringManager.TETHERING_WIFI|| mInterfaceType == TetheringManager.TETHERING_WIFI_P2P|| mInterfaceType == TetheringManager.TETHERING_ETHERNET|| mInterfaceType == TetheringManager.TETHERING_WIGIG) {// The WiFi and Ethernet stack has ownership of the interface up/down state.// It is unclear whether the Bluetooth or USB stacks will manage their own// state.setIfaceUp = null;} else {setIfaceUp = enabled;}if (!mInterfaceCtrl.setInterfaceConfiguration(mIpv4Address, setIfaceUp)) {mLog.e("Error configuring interface");if (!enabled) stopDhcp();return false;}if (enabled) {mLinkProperties.addLinkAddress(mIpv4Address);mLinkProperties.addRoute(getDirectConnectedRoute(mIpv4Address));} else {mLinkProperties.removeLinkAddress(mIpv4Address);mLinkProperties.removeRoute(getDirectConnectedRoute(mIpv4Address));}return configureDhcp(enabled, mIpv4Address, mStaticIpv4ClientAddr);}private LinkAddress requestIpv4Address() {if (mStaticIpv4ServerAddr != null) return mStaticIpv4ServerAddr;if (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) {return new LinkAddress(BLUETOOTH_IFACE_ADDR);}return mPrivateAddressCoordinator.requestDownstreamAddress(this);}private boolean configureDhcp(boolean enable, final LinkAddress serverAddr,final LinkAddress clientAddr) {if (enable) {return startDhcp(serverAddr, clientAddr);} else {stopDhcp();return true;}}private boolean startDhcp(final LinkAddress serverLinkAddr, final LinkAddress clientLinkAddr) {if (mUsingLegacyDhcp) {return true;}final Inet4Address addr = (Inet4Address) serverLinkAddr.getAddress();final Inet4Address clientAddr = clientLinkAddr == null ? null :(Inet4Address) clientLinkAddr.getAddress();final DhcpServingParamsParcel params = makeServingParams(addr /* defaultRouter */,addr /* dnsServer */, serverLinkAddr, clientAddr);mDhcpServerStartIndex++;mDeps.makeDhcpServer(mIfaceName, params, new DhcpServerCallbacksImpl(mDhcpServerStartIndex));return true;}
这里我们没指定static Ip,就会通过requestIpv4Address()函数获取一个随机的IP地址(基本是xxx.xxx.xxx.xxx/netmask),获取到地址后,紧接着就是去设置和通过DHCP为interface配置IP了。
之后就是通过Netd配置iface,因为这类网络共享需要一些路由配置,这部分要由Netd的内部负责处理:
NetdUtils.tetherInterface(mNetd, mIfaceName, asIpPrefix(mIpv4Address));
随后进入TetheredState。
这样SoftAp的开启的流程就粗略的过了一遍,至于大家工作中有跟其他细致流程相关的,可以根据这个流程进行自己的开发与分析了。
相关文章:
Android R WiFi热点流程浅析
Android R WiFi热点流程浅析 Android上的WiFi SoftAp功能是用户常用的功能之一,它能让我们分享手机的网络给其他设备使用。 那Android系统是如何实现SoftAp的呢,这里在FWK层面做一个简要的流程分析,供自己记录和大家参考。 以Android R版本为…...
【C++进阶】二、多态详解(总)
目录 一、多态的概念 二、多态的定义及实现 2.1 多态的构成条件 2.2 虚函数 2.3 虚函数的重写 2.4 虚函数重写的两个例外 2.4.1 协变 2.4.2 析构函数的重写 2.5 C11 override 和 final 2.5.1 final 2.5.2 override 2.6 重载、覆盖(重写)、隐藏(重定义)的对比 三、…...
node-sass@4.14.1 包含风险, 如何升级依赖至 dart-sass
文章目录需求我上网都查到了哪些信息在 github 看到了 node-sass 依赖的最新版本的列表:关于方案2的失败不同版本的 nodejs 和 node-sass依赖的**适配关系**从何得知替代方案——dart-sass如何安装 dart sass?需求 在做一个基于Node、React的前端项目&a…...
DataWhale 大数据处理技术组队学习task2
三、Hadoop分布式文件系统 1. 产生背景 数据量越来越大,一台独立的计算机已经无法存储所有的数据---->将大规模的数据存储到成百上千的计算机中------为了解决数据管理以及维护极其繁琐与低效------>分布式文件系统 分布式文件系统是管理网络中跨多台计算机…...
一文读懂select、poll、epoll的用法
select,poll,epoll都是IO多路复用的机制。I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,…...
《C陷阱与缺陷》----词法“陷阱”
导言: 由于一个程序错误可以从不同层面采用不同方式进行考察,而根据程序错误与考察程序的方式之间的相关性,可以将程序错误进行划分为各种陷阱与缺陷: ①.词法“陷阱” ②.语法“陷阱” ③.语义“陷阱” ④.连接问题 ⑤.库函数问…...
千锋教育+计算机四级网络-计算机网络学习-04
UDP概述 UDP协议 面向无连接的用户数据报协议,在传输数据前不需要先建立连接;目地主机的运输层收到UDP报文后,不需要给出任何确认 UDP特点 相比TCP速度稍快些简单的请求/应答应用程序可以使用UDP对于海量数据传输不应该使用UDP广播和多播应用…...
蓝桥杯算法训练合集十四 1.P08052.P07053.同余方程4.P08015.ascii应用
目录 1.P0805 2.P0705 3.同余方程 4.P0801 5.ascii应用 1.P0805 问题描述 当两个比较大的整数相乘时,可能会出现数据溢出的情形。为避免溢出,可以采用字符串的方法来实现两个大数之间的乘法。具体来说,首先以字符串的形式输入两个整数&…...
判断字符串中的字符的类型isdecimal();isalpha();isdigit();isalnum()
【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 判断字符串中的字符的类型 isdecimal();isalpha();isdigit();isalnum() [太阳]选择题 对于代码中isdecimal()和isalnum()输出的结果是? s "ABc123&…...
VSCode远程调试Linux代码,python解释器配置
安装插件并配置 安装后找到插件图标,点击 点击SSH上的 号 在弹出框中输入命令:ssh usernameip -p port username: 远程服务器的用户名 ip: 远程ip port:端口号,没有可以不用 输入完毕后点击enter 选择ssh配置文件保存…...
03:入门篇 - CTK Plugin Framework 基本原理
作者: 一去、二三里 个人微信号: iwaleon 微信公众号: 高效程序员 CTK Plugin Framework 技术是面向 C++ 的动态模型系统。该系统允许插件之间的松散耦合,并且提供了设计良好的方式来进行功能和数据的交互。此外,它没有预先对插件施加限制,这样就可以很容易地将插件的相关…...
面试攻略,Java 基础面试 100 问(九)
数组有没有 length()方法?String 有没有 length()方法? 数组没有 length()方法,有 length 的属性。String 有 length()方法。JavaScript 中,获得字符串的长度是通过 length 属性得到的,这一点容易和 Java混淆。 在 Java 中&…...
JavaScript 代码不嵌套主义
文章目录前言一、何为嵌套代码二、避免嵌套1.提炼抽取2.反转排列总结前言 看过不少过度嵌套的代码, 我真正意识到问题的严重性是刚入职那会, 我在一个老项目里看到了40个连续的else if, 套了6层的if, for和forEach, 因为我们并没有做什么限制代码嵌套的提前约定. 呃, 那之后认…...
使用默认参数的4大要点
概述 默认参数是C中新增的特性。在C中,可以为函数的参数指定默认值。调用函数时,如果没有指定实参,则自动使用默认参数。默认参数的基本语法这里就不作介绍了,下面重点介绍使用默认参数的一些知识要点。 基本规则 1、当函数中某个…...
Linux文件系统中的硬链接及常见面试题
如果能对inode的概念有所了解,对理解本文会有所帮助。如果对inode的概念不太清楚也没有关系,我们会捎带介绍一下。在文件系统的实现层面,我们可以认为包含两个组件:一个是包含数据块的池子,池子中的数据块是等大小的&a…...
opencv-StereoBM算法
原理解释目前立体匹配算法是计算机视觉中的一个难点和热点,算法很多,但是一般的步骤是:A、匹配代价计算匹配代价计算是整个立体匹配算法的基础,实际是对不同视差下进行灰度相似性测量。常见的方法有灰度差的平方SD(squ…...
图像分类竞赛进阶技能:OpenAI-CLIP使用范例
OpenAI-CLIP 官方介绍 尽管深度学习已经彻底改变了计算机视觉,但目前的方法存在几个主要问题:典型的视觉数据集是劳动密集型的,创建成本高,同时只教授一组狭窄的视觉概念;标准视觉模型擅长于一项任务且仅擅长于一项任务,并且需要大…...
Metasploit框架基础(一)
文章目录前言一、基础认知二、批量POC/EXP的构想三、poc检测框架的简单实现四、xray五、Meatsploit框架参考前言 Metasploit 一款渗透测试框架漏洞利用的集合与构建和定制满足你的需求的基础漏洞利用和验证的工具 这几个说法都是百度或者官方文档中出现的手法,说…...
pytorch零基础实现语义分割项目(二)——标签转换与数据加载
数据转换与加载项目列表前言标签转换RGB标签到类别标签映射RGB标签转换成类别标签数据数据加载随机裁剪数据加载项目列表 语义分割项目(一)——数据概况及预处理 语义分割项目(二)——标签转换与数据加载 语义分割项目&#x…...
python(8.5)--列表习题
目录 一、求输出结果题 二、计算列表元素个数 三、查找是否存在某元素 四、删除某元素 五、如何在列表中插入元素 六、如何从列表中删除重复的元素 七、 如何将列表中的元素按照从小到大的顺序排序 八、从列表中删除重复的元素 九、大到小的顺序排序 一、求输出结…...
rt-thread pwm 多通道
一通道pwm参考 https://blog.csdn.net/yangshengwei230612/article/details/128738351?spm1001.2014.3001.5501 以下主要是多通道与一通道的区别 芯片 stm32f407rgt6 1、配置PWM设备驱动相关宏定义 添加PWM宏定义 #define BSP_USING_PWM8 #define BSP_USING_PWM8_CH1 #d…...
C语言练习 | 初学者经典练习汇总
目录 1、下面代码输出多少,为什么? 2、你要好好学习么? 3、一直写代码, 4、两个数求最大值 5、输入1-5输出工作日,输入6-7输出休息日,其他输入错误 6、写一个输入密码的代码 7、怎么样当输入数字时候…...
华为OD机试 - 自动曝光(Python) | 机试题算法思路 【2023】
最近更新的博客 华为OD机试 - 卡片组成的最大数字(Python) | 机试题算法思路 华为OD机试 - 网上商城优惠活动(一)(Python) | 机试题算法思路 华为OD机试 - 统计匹配的二元组个数(Python) | 机试题算法思路 华为OD机试 - 找到它(Python) | 机试题算法思路 华为OD机试…...
「6」线性代数(期末复习)
🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀 目录 第五章 相似矩阵及二次型 &2)方阵的特征值与特征向量 &3ÿ…...
1.1 硬件与micropython固件烧录及自编译固件
1.ESP32硬件和固件 淘宝搜ESP32模块,20-50元都有,自带usb口,即插即用. 固件下载地址:MicroPython - Python for microcontrollers 2.烧录方法 为简化入门难度,建议此处先使用带GUI的开发工具THonny,记得不是给你理发的tony老师. 烧录的入口是: 后期通过脚本一次型生成和烧…...
【MySQL进阶】视图 存储过程 触发器
😊😊作者简介😊😊 : 大家好,我是南瓜籽,一个在校大二学生,我将会持续分享Java相关知识。 🎉🎉个人主页🎉🎉 : 南瓜籽的主页…...
[Linux篇] Linux常见命令和权限
文章目录使用XShell登录Linux1.Linux常用基本命令:1.1 ls(列出当前的目录下都有哪些文件和目录)1.2 cd (change directory 切换目录)1.3 pwd(查看当前目录的绝对路径)1.4 touch(创建文件)1.5 ca…...
29岁从事功能测试被辞,面试2个月都找不到工作吗?
最近一个28岁老同学联系我,因为被公司辞退,找我倾诉,于是写下此文。 他是14年二本毕业,在我的印象里人特别懒,不爱学习,专业不好,毕业前因为都没找到合适工作,直接去创业了…...
【C#个人错题笔记1】
观前提醒 记录一些我不会或者少见的内容,不一定适合所有人 字符串拼接 int a3,b8; Console.WriteLine(ab);//11 Console.WriteLine("ab");//ab Console.WriteLine(a""b);//38 Console.WriteLine("ab"ab);//ab38 Console.WriteLine…...
基于lambda的mongodb查询插件
需求背景需要一个像mybatis plus 一样的基于lambda, 且面向对象的查询mongo数据的插件。在网上找了很久,没有发现有类似功能的插件。于是自己手写了一个,借助mongoTemplate屏蔽了底层查询语句的实现细节。在此基础上,实现了查询的统一封装。技…...
安徽省合肥市建设局网站/阿里指数查询官网入口
声明 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4392611.html 【系列】 Android Studio 入门级教程(一) Android Studio 入门级教程(二):新建一个属…...
wordpress post_class/如何让自己的网站快速被百度收录
(虚拟专用网络\/pn): 1.作用:帮助外网的用户访问内网 2.两种实现方式: 1.内网中转服务器:在内网建一个\/pn服务器端,对外网公开,用户登录\/pn客户端,所有的请求都将被\/pn客户端拦…...
好多网站没排名了/个人网页制作
linux目录的大小4k怎么来的?发布时间:2010-08-05 13:54:37来源:红联作者:xfox如下命令:lufenglufeng-desktop:~$ ls -lh /总用量 93Kdrwxr-xr-x 2 root root 4.0K 2010-08-03 13:18 bindrwxr-xr-x 4 root root 1.0K 2010-08-03 13:28 bootdrwxr-xr-x 2 r…...
网站怎样做平面设计图/网站大全软件下载
编辑 /etc/gdm/custom.conf 文件,具体如下所示: 在daemon选项中修改AutomaticLoginEnabletrue,AutomaticLoginroot 如果daemon下为空则添加: AutomaticLoginEnabletrue AutomaticLoginroot 如: [daemon] Auto…...
公司网络推广公司/百度seo文章
//头文件的东西,请根据自己的安装目录设置#include #include #include #include #include #include #include #include #include using namespace std;void close_app(GtkWidget *widget, gpointer data) {gtk_main_quit();}//使用gtk编译参数加上 pkg-config --cfl…...
360网站制作潍坊/网络营销案例分析题
2019独角兽企业重金招聘Python工程师标准>>> 想当初我是新手,对plist的操作也是一知半解,想发个贴,让大家可以方便一点,解除疑惑,先说明很多人不知道操作plist的一个主要原因是因为很多人把plist建在了工程…...