近期在看一个热点启动的问题。发现网上基本上都算android 9 的wifi热点启动流程。自己去看android 13 的源码的时候发现与之前相比已经有一些小改动了。
在此总结一下wifie热点的启动流程。
我们直接从setting的热点开启按钮 点击事件开始看起。
按钮的初始化在
packages/apps/Settings/src/com/android/settings/wifi/tether/WifiTetherSettings.java
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (mUnavailable) {
return;
}
// Assume we are in a SettingsActivity. This is only safe because we currently use
// SettingsActivity as base for all preference fragments.
final SettingsActivity activity = (SettingsActivity) getActivity();
final SettingsMainSwitchBar switchBar = activity.getSwitchBar();
switchBar.setTitle(getContext().getString(R.string.use_wifi_hotsopt_main_switch_title));
mSwitchBarController = new WifiTetherSwitchBarController(activity, switchBar);
getSettingsLifecycle().addObserver(mSwitchBarController);
switchBar.show();
}
可以看到按钮的控制类在 WifiTetherSwitchBarController 里面。
packages/apps/Settings/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java
@Override
public void onStart() {
mDataSaverBackend.addListener(this);
mSwitchBar.addOnSwitchChangeListener(this);
mContext.registerReceiver(mReceiver, WIFI_INTENT_FILTER,
Context.RECEIVER_EXPORTED_UNAUDITED);
}
@Override
public void onSwitchChanged(Switch switchView, boolean isChecked) {
// Filter out unnecessary callbacks when switch is disabled.
if (!switchView.isEnabled()) return;
if (isChecked) {
startTether();
} else {
stopTether();
}
}
void startTether() {
if (isWifiApActivated()) return;
mSwitchBar.setEnabled(false);
mConnectivityManager.startTethering(TETHERING_WIFI, true /* showProvisioningUi */,
mOnStartTetheringCallback, new Handler(Looper.getMainLooper()));
}
final ConnectivityManager.OnStartTetheringCallback mOnStartTetheringCallback =
new ConnectivityManager.OnStartTetheringCallback() {
@Override
public void onTetheringFailed() {
super.onTetheringFailed();
Log.e(TAG, "Failed to start Wi-Fi Tethering.");
handleWifiApStateChanged(mWifiManager.getWifiApState());
}
};
这个控制类为按钮设置的点击事件,点击事件里面就开始了开启wifi热点的流程 startTether()
startTether() 执行了 mConnectivityManager.startTethering() 然后就进入到了 ConnectivityManager 类里面。
public void startTethering(int type, boolean showProvisioningUi,
final OnStartTetheringCallback callback, Handler handler) {
Objects.requireNonNull(callback, "OnStartTetheringCallback cannot be null.");
final Executor executor = new Executor() {
@Override
public void execute(Runnable command) {
if (handler == null) {
command.run();
} else {
handler.post(command);
}
}
};
final StartTetheringCallback tetheringCallback = new StartTetheringCallback() {
@Override
public void onTetheringStarted() {
callback.onTetheringStarted();
}
@Override
public void onTetheringFailed(final int error) {
callback.onTetheringFailed();
}
};
final TetheringRequest request = new TetheringRequest.Builder(type)
.setShouldShowEntitlementUi(showProvisioningUi).build();
getTetheringManager().startTethering(request, executor, tetheringCallback);
}
getTetheringManager().startTethering() 又进入到了packages/modules/Connectivity/Tethering/common/TetheringLib/src/android/net/TetheringManager.java中。
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() {
@Override
public 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,
getAttributionTag(), listener));
}
private void getConnector(ConnectorConsumer consumer) {
final ITetheringConnector connector;
synchronized (mConnectorWaitQueue) {
connector = mConnector;
if (connector == null) {
mConnectorWaitQueue.add(consumer);
return;
}
}
try {
consumer.onConnectorAvailable(connector);
} catch (RemoteException e) {
throw new IllegalStateException(e);
}
}
private interface ConnectorConsumer {
void onConnectorAvailable(ITetheringConnector connector) throws RemoteException;
}
由于ConnectorConsumer 是一个接口,所以consumer.onConnectorAvailable(connector)就相当于
调用了 connector.startTethering() 。ITetheringConnector connector 是一个aidl 接口,跨进程进去到了
packages/modules/Connectivity/Tethering/src/com/android/networkstack/tethering/TetheringService.java
@Override
public void startTethering(TetheringRequestParcel request, String callerPkg,
String callingAttributionTag, IIntResultListener listener) {
if (checkAndNotifyCommonError(callerPkg,
callingAttributionTag,
request.exemptFromEntitlementCheck /* onlyAllowPrivileged */,
listener)) {
return;
}
mTethering.startTethering(request, listener);
}
这个里又调用到了mTethering.startTethering(request, listener);
frameworks/base/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
里面去了
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);
});
}
这里执行了enableTetheringInternal(request.tetheringType, true /* enabled */, listener);
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;
}
开启热点当然是执行 result = setWifiTethering(enable);
setWifiTethering()里面又执行了mgr.startTetheredHotspot(null /* use existing softap config */))
这个里面传了一个空的参数,这个参数是有意义的,代表在开启热点时,会根据配置文件的内容决定开启热点的参数。这个配置文件在手机的 data/misc/apexdata/com.android.wifi/ 下。里面有两个配置文件,一个是wifi的,一个是热点的。
WifiConfigStore.xml WifiConfigStoreSoftAp.xml
执行这个方法后又进入到了packages/modules/Wifi/framework/java/android/net/wifi/WifiManager.java类。
public boolean startTetheredHotspot(@Nullable SoftApConfiguration softApConfig) {
try {
return mService.startTetheredHotspot(softApConfig, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
mService.startTetheredHotspot()又是一个aidl 接口。进入到了
packages/modules/Wifi/service/java/com/android/server/wifi/WifiServiceImpl.java 里面
public boolean startTetheredHotspot(@Nullable SoftApConfiguration softApConfig,
@NonNull String packageName) {
// NETWORK_STACK is a signature only permission.
enforceNetworkStackPermission();
int callingUid = Binder.getCallingUid();
mWifiPermissionsUtil.checkPackage(callingUid, packageName);
// If user restriction is set, cannot start softap
if (mWifiTetheringDisallowed) {
mLog.err("startTetheredHotspot with user restriction: not permitted").flush();
return false;
}
mLog.info("startTetheredHotspot uid=%").c(callingUid).flush();
// TODO: b/233363886, handle timeout in general way.
if (!checkSetEnablingIfAllowed()) {
return false;
}
WorkSource requestorWs = new WorkSource(callingUid, packageName);
if (!mWifiThreadRunner.call(
() -> mActiveModeWarden.canRequestMoreSoftApManagers(requestorWs), false)) {
// Take down LOHS if it is up.
mLohsSoftApTracker.stopAll();
}
if (!startSoftApInternal(new SoftApModeConfiguration(
WifiManager.IFACE_IP_MODE_TETHERED, softApConfig,
mTetheredSoftApTracker.getSoftApCapability()), requestorWs)) {
mTetheredSoftApTracker.setFailedWhileEnabling();
return false;
}
mLastCallerInfoManager.put(WifiManager.API_TETHERED_HOTSPOT, Process.myTid(),
callingUid, Binder.getCallingPid(), packageName, true);
return true;
}
执行了 startSoftApInternal()
private boolean startSoftApInternal(SoftApModeConfiguration apConfig, WorkSource requestorWs) {
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, mContext))) {
Log.e(TAG, "Invalid SoftApConfiguration");
return false;
}
mActiveModeWarden.startSoftAp(apConfig, requestorWs);
return true;
}
然后执行 mActiveModeWarden.startSoftAp(apConfig, requestorWs); 进入
packages/modules/Wifi/service/java/com/android/server/wifi/ActiveModeWarden.java
public void startSoftAp(SoftApModeConfiguration softApConfig, WorkSource requestorWs) {
stopStaIfRequired(softApConfig, requestorWs);
mWifiController.sendMessage(WifiController.CMD_SET_AP, 1, 0,
Pair.create(softApConfig, requestorWs));
}
这里发送了一条消息进入到了ActiveModeWarden 的 内部类 EnabledState 里面。
case CMD_SET_AP:
// note: CMD_SET_AP is handled/dropped in ECM mode - will not start here
if (msg.arg1 == 1) {
Pair<SoftApModeConfiguration, WorkSource> softApConfigAndWs =
(Pair) msg.obj;
startSoftApModeManager(
softApConfigAndWs.first, softApConfigAndWs.second);
} else {
stopSoftApModeManagers(msg.arg2);
}
break;
这里执行了
startSoftApModeManager( softApConfigAndWs.first, softApConfigAndWs.second);
private void startSoftApModeManager(
@NonNull SoftApModeConfiguration softApConfig, @NonNull WorkSource requestorWs) {
Log.d(TAG, "Starting SoftApModeManager config = " + softApConfig.getSoftApConfiguration());
Preconditions.checkState(softApConfig.getTargetMode() == IFACE_IP_MODE_LOCAL_ONLY
|| softApConfig.getTargetMode() == IFACE_IP_MODE_TETHERED);
WifiServiceImpl.SoftApCallbackInternal callback =
softApConfig.getTargetMode() == IFACE_IP_MODE_LOCAL_ONLY
? mLohsCallback : mSoftApCallback;
SoftApManager manager = mWifiInjector.makeSoftApManager(
new SoftApListener(), callback, softApConfig, requestorWs,
getRoleForSoftApIpMode(softApConfig.getTargetMode()), mVerboseLoggingEnabled);
mSoftApManagers.add(manager);
}
这里的callback 传入的 mLohsCallback 。
后面又执行了mWifiInjector.makeSoftApManager()方法。进入到
packages/modules/Wifi/service/java/com/android/server/wifi/WifiInjector.java
public SoftApManager makeSoftApManager(
@NonNull ActiveModeManager.Listener<SoftApManager> listener,
@NonNull WifiServiceImpl.SoftApCallbackInternal callback,
@NonNull SoftApModeConfiguration config,
@NonNull WorkSource requestorWs,
@NonNull ActiveModeManager.SoftApRole role,
boolean verboseLoggingEnabled) {
return new SoftApManager(mContext, mWifiHandlerThread.getLooper(),
mFrameworkFacade, mWifiNative, mCoexManager, makeBatteryManager(),
mCountryCode.getCountryCode(), listener, callback, mWifiApConfigStore,
config, mWifiMetrics, mSarManager, mWifiDiagnostics,
new SoftApNotifier(mContext, mFrameworkFacade, mWifiNotificationManager),
mCmiMonitor, mActiveModeWarden, mClock.getElapsedSinceBootMillis(),
requestorWs, role, verboseLoggingEnabled);
}
这里直接new了一个SoftApManger。进入到了构造方法里面。
// null is a valid input and means we use the user-configured tethering settings.
if (mCurrentSoftApConfiguration == null) {
mCurrentSoftApConfiguration = mWifiApConfigStore.getApConfiguration();
// may still be null if we fail to load the default config
}
mStateMachine = new SoftApStateMachine(looper); // 在这里真正的创建了热点的状态机
mStateMachine.sendMessage(SoftApStateMachine.CMD_START, requestorWs);
由于之前的config 一直是空的,所以 mWifiApConfigStore.getApConfiguration() 这里面就从配置文件里,获取了热点的配置信息。
最后send一个message。由 SoftApManager 的内部类 IdleState processMessage() 捕获处理.
int result = startSoftAp();
里面有一段,调用了startSoftAp()方法。
private int startSoftAp() {
if (SdkLevel.isAtLeastS()) {
Log.d(getTag(), "startSoftAp: channels " + mCurrentSoftApConfiguration.getChannels()
+ " iface " + mApInterfaceName + " country " + mCountryCode);
} else {
Log.d(getTag(), "startSoftAp: band " + mCurrentSoftApConfiguration.getBand());
}
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(mCurrentSoftApConfiguration);
result = ApConfigUtil.updateApChannelConfig(
mWifiNative, mCoexManager, mContext.getResources(), mCountryCode,
localConfigBuilder, mCurrentSoftApConfiguration, mCurrentSoftApCapability);
if (result != SUCCESS) {
Log.e(getTag(), "Failed to update AP band and channel");
return result;
}
if (mCurrentSoftApConfiguration.isHiddenSsid()) {
Log.d(getTag(), "SoftAP is a hidden network");
}
if (!ApConfigUtil.checkSupportAllConfiguration(
mCurrentSoftApConfiguration, mCurrentSoftApCapability)) {
Log.d(getTag(), "Unsupported Configuration detect! config = "
+ mCurrentSoftApConfiguration);
return ERROR_UNSUPPORTED_CONFIGURATION;
}
if (!mWifiNative.startSoftAp(mApInterfaceName,
localConfigBuilder.build(),
mOriginalModeConfiguration.getTargetMode() == WifiManager.IFACE_IP_MODE_TETHERED,
mSoftApHalCallback)) {
Log.e(getTag(), "Soft AP start failed");
return ERROR_GENERIC;
}
mWifiDiagnostics.startLogging(mApInterfaceName);
mStartTimestamp = FORMATTER.format(new Date(System.currentTimeMillis()));
Log.d(getTag(), "Soft AP is started ");
return SUCCESS;
}
主要是调用mWifiNative.startSoftAp()方法
public boolean startSoftAp(
@NonNull String ifaceName, SoftApConfiguration config, boolean isMetered,
SoftApHalCallback callback) {
if (mHostapdHal.isApInfoCallbackSupported()) {
if (!mHostapdHal.registerApCallback(ifaceName, callback)) {
Log.e(TAG, "Failed to register ap hal event callback");
return false;
}
} else {
SoftApHalCallbackFromWificond softApHalCallbackFromWificond =
new SoftApHalCallbackFromWificond(ifaceName, callback);
if (!mWifiCondManager.registerApCallback(ifaceName,
Runnable::run, softApHalCallbackFromWificond)) {
Log.e(TAG, "Failed to register ap hal event callback from wificond");
return false;
}
}
if (!addAccessPoint(ifaceName, config, isMetered, callback)) {
Log.e(TAG, "Failed to add acccess point");
mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToHostapd();
return false;
}
return true;
}
后面会进入到
frameworks\opt\net\wifi\service\java\com\android\server\wifi\HostapdHal.java
@Override
public boolean addAccessPoint(@NonNull String ifaceName, @NonNull SoftApConfiguration config,
boolean isMetered, Runnable onFailureListener) {
synchronized (mLock) {
final String methodStr = "addAccessPoint";
Log.d(TAG, methodStr + ": " + ifaceName);
if (!checkHostapdAndLogFailure(methodStr)) {
return false;
}
try {
IfaceParams ifaceParams = prepareIfaceParams(ifaceName, config);
NetworkParams nwParams = prepareNetworkParams(isMetered, config);
if (ifaceParams == null || nwParams == null) {
Log.e(TAG, "addAccessPoint parameters could not be prepared.");
return false;
}
mIHostapd.addAccessPoint(ifaceParams, nwParams);
mSoftApFailureListeners.put(ifaceName, onFailureListener);
return true;
} catch (IllegalArgumentException e) {
Log.e(TAG, "Unrecognized apBand: " + config.getBand());
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
然后会调用到Hal层的方法。