Android 13热点打开流程

Android 13热点打开流程

版本Android-13.0.0_r3
在Settings中打开热点 点击事件开始
按钮初始化

/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();
}

获取activity and switchBar,设置switchBar的Title。new WifiTetherSwitchBarController来进行控制控件。最后show显示。

来看WifiTetherSwitchBarController方法

/packages/apps/Settings/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java
    //构造函数
WifiTetherSwitchBarController(Context context, SettingsMainSwitchBar switchBar) {
    mContext = context;
    mSwitchBar = switchBar;
    mSwitch = mSwitchBar.getSwitch();
    mDataSaverBackend = new DataSaverBackend(context);
    mConnectivityManager =
            (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
    mSwitchBar.setChecked(mWifiManager.getWifiApState() == WIFI_AP_STATE_ENABLED);
    updateWifiSwitch();
}

@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());
            }
        };

在这个WifiTetherSwitchBarController方法中为按钮设置了点击事件,执行的方法是startTether(),开始了开启热点的相关流程。
startTether()中if (isWifiApActivated()) return;当前状态是不是WIFI_AP_STATE_ENABLED and WIFI_AP_STATE_ENABLING,true直接return。
mSwitchBar.setEnabled(false);禁用控件
下来调用ConnectivityManager中的startTethering()

/packages/modules/Connectivity/framework/src/android/net/ConnectivityManager.java
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(request, executor, tetheringCallback);

/packages/modules/Connectivity/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
//如果需要,启动绑定并运行给定类型的绑定配置。如果配置失败,stopTethering将被自动调用。
@RequiresPermission(anyOf = {
        android.Manifest.permission.TETHER_PRIVILEGED,
        android.Manifest.permission.WRITE_SETTINGS
})
//Executor:指定调用TetheringRequest回调函数的线程。
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();	//Success
                } else {
                    callback.onTetheringFailed(resultCode);	//Failed
                }
            });
        }
    };
    getConnector(c -> c.startTethering(request.getParcel(), callerPkg,
            getAttributionTag(), listener));
}
//ConnectorConsumer是一个接口,所以consumer.onConnectorAvailable(connector)就相当于调用了
//connector.startTethering()。ITetheringConnector connector  是一个aidl 接口,跨进程到
//TetheringService.java
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);
    }
}
//aidl接口---》TetheringService.java
private interface ConnectorConsumer {
    void onConnectorAvailable(ITetheringConnector connector) throws RemoteException;
}
/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); mTethering—》Tethering

/packages/modules/Connectivity/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);
    });
}

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 {
        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;
}

开启热点执行TETHERING_WIFI类型,在setWifiTethering方法中getWifiManager,然后再调用WifiManager 中的startTetheredHotspot传参数是个null。
这个参数代表着在开启热点时,会根据配置文件的内容决定开启热点的参数。这个配置文件在手机的data/misc/apexdata/com.android.wifi/ 下面。一个是热点,一个是WiFi
WifiConfigStore.xml WifiConfigStoreSoftAp.xml

/packages/modules/Wifi/framework/java/android/net/wifi/WifiManager.java
@SystemApi
@RequiresPermission(anyOf = {
    android.Manifest.permission.NETWORK_STACK,
    NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
})
public boolean startTetheredHotspot(@Nullable SoftApConfiguration softApConfig) {
    try {
        return mService.startTetheredHotspot(softApConfig, mContext.getOpPackageName());
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

这里就是打开热点的一个API,SoftApConfiguration 是nullmService—》WifiServiceImpl.startTetheredHotspot()

/packages/modules/Wifi/service/java/com/android/server/wifi/WifiServiceImpl.java
@Override
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;
}

Check一些权限然后执行到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;
}

进入到ActiveModeWarden中的startSoftAp

/packages/modules/Wifi/service/java/com/android/server/wifi/ActiveModeWarden.java
/** Starts SoftAp. */
public void startSoftAp(SoftApModeConfiguration softApConfig, WorkSource requestorWs) {
    mWifiController.sendMessage(WifiController.CMD_SET_AP, 1, 0,
    Pair.create(softApConfig, requestorWs));
}

在startSoftAp方法中发送一条消息(CMD_SET_AP),在ActiveModeWarden的内部类EnabledState中处理

@Override
public boolean processMessageFiltered(Message msg) {
    ...
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;
    ...
}



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。然后通过WifiInjector new了一个softApManager

/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);
}

========================================================================
此构造函数有点长,直接在里面进行注释

/packages/modules/Wifi/service/java/com/android/server/wifi/SoftApManager.java
public SoftApManager(
    @NonNull WifiContext context,
    @NonNull Looper looper,
    @NonNull FrameworkFacade framework,
    @NonNull WifiNative wifiNative,
    @NonNull CoexManager coexManager,
    @NonNull BatteryManager batteryManager,
    String countryCode,
    @NonNull Listener<SoftApManager> listener,
    @NonNull WifiServiceImpl.SoftApCallbackInternal callback,
    @NonNull WifiApConfigStore wifiApConfigStore,
    @NonNull SoftApModeConfiguration apConfig,
    @NonNull WifiMetrics wifiMetrics,
    @NonNull SarManager sarManager,
    @NonNull WifiDiagnostics wifiDiagnostics,
    @NonNull SoftApNotifier softApNotifier,
    @NonNull ClientModeImplMonitor cmiMonitor,
    @NonNull ActiveModeWarden activeModeWarden,
    long id,
    @NonNull WorkSource requestorWs,
    @NonNull SoftApRole role,
    boolean verboseLoggingEnabled) {
    mContext = context;
    mFrameworkFacade = framework;
    mSoftApNotifier = softApNotifier;
    mWifiNative = wifiNative;
    mCoexManager = coexManager;
    mBatteryManager = batteryManager;
    if (SdkLevel.isAtLeastS()) {
        mCoexListener = new CoexListener() {
            @Override
            public void onCoexUnsafeChannelsChanged() {
                if (mCurrentSoftApConfiguration == null) {
                    return;
                }
                mStateMachine.sendMessage(
                        SoftApStateMachine.CMD_SAFE_CHANNEL_FREQUENCY_CHANGED);
            }
        };
    } else {
        mCoexListener = null;
    }
    mCountryCode = countryCode;
    mModeListener = listener;
    mSoftApCallback = callback;
    mWifiApConfigStore = wifiApConfigStore;
    mCurrentSoftApConfiguration = apConfig.getSoftApConfiguration();
    mCurrentSoftApCapability = apConfig.getCapability();
//传递的是null,这里就会从WifiApConfigStore中AP获取相关的配置
    // 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
    }
    // Store mode configuration before update the configuration.
    mOriginalModeConfiguration = new SoftApModeConfiguration(apConfig.getTargetMode(),
            mCurrentSoftApConfiguration, mCurrentSoftApCapability);
    if (mCurrentSoftApConfiguration != null) {
        mIsUnsetBssid = mCurrentSoftApConfiguration.getBssid() == null;
        if (mCurrentSoftApCapability.areFeaturesSupported(
                SoftApCapability.SOFTAP_FEATURE_MAC_ADDRESS_CUSTOMIZATION)) {
            mCurrentSoftApConfiguration = mWifiApConfigStore.randomizeBssidIfUnset(
                    mContext, mCurrentSoftApConfiguration);
        }
    }
    mWifiMetrics = wifiMetrics;
    mSarManager = sarManager;
    mWifiDiagnostics = wifiDiagnostics;
//在这里创建了状态机stateMachine,它有两个状态:mIdleState\mStartedState
    mStateMachine = new SoftApStateMachine(looper);
    configureInternalConfiguration();
    mDefaultShutdownTimeoutMillis = mContext.getResources().getInteger(
            R.integer.config_wifiFrameworkSoftApShutDownTimeoutMilliseconds);
    mDefaultShutdownIdleInstanceInBridgedModeTimeoutMillis = mContext.getResources().getInteger(
            R.integer
            .config_wifiFrameworkSoftApShutDownIdleInstanceInBridgedModeTimeoutMillisecond);

    mIsDisableShutDownBridgedModeIdleInstanceTimerWhenCharging = mContext.getResources()
            .getBoolean(R.bool
            .config_wifiFrameworkSoftApDisableBridgedModeShutdownIdleInstanceWhenCharging);
    mCmiMonitor = cmiMonitor;
    mActiveModeWarden = activeModeWarden;
    mCmiMonitor.registerListener(new ClientModeImplListener() {
        @Override
        public void onL2Connected(@NonNull ConcreteClientModeManager clientModeManager) {
            SoftApManager.this.onL2Connected(clientModeManager);
        }
    });
    updateSafeChannelFrequencyList();
    mId = id;
    mRole = role;
    enableVerboseLogging(verboseLoggingEnabled);
//这里发送一条消息CMD_START让状态机stateMachine中的mIdleState来处理
    mStateMachine.sendMessage(SoftApStateMachine.CMD_START, requestorWs);
}
======================================================================================
case CMD_START:
    ...
    int result = startSoftAp();
    transitionTo(mStartedState);
    break;

========================================================================
这里主要是调用startSoftAp()方法,然后让stateMachine状态机从mIdleState切换到mStartedState。
我们来看看startSoftAp()

/**
 * Start a soft AP instance as configured.
 *
 * @return integer result code
 */
private int startSoftAp() {
    Log.d(getTag(), "startSoftAp: band " + mCurrentSoftApConfiguration.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(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;
}

从配置中获取Band and channel,进行一些配置上的判断。主要是调用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 (!mHostapdHal.addAccessPoint(ifaceName, config, isMetered, callback::onFailure)) {
        Log.e(TAG, "Failed to add acccess point");
        mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToHostapd();
        return false;
    }

    return true;
}
/packages/modules/Wifi/service/java/com/android/server/wifi/HostapdHal.java
public boolean addAccessPoint(@NonNull String ifaceName, @NonNull SoftApConfiguration config,
                              boolean isMetered, @NonNull Runnable onFailureListener) {
    synchronized (mLock) {
        String methodStr = "addAccessPoint";
        if (mIHostapd == null) {
            return handleNullIHostapd(methodStr);
        }
        return mIHostapd.addAccessPoint(ifaceName, config, isMetered, onFailureListener);
    }
}

添加并启动一个AccessPoint,调用HAL层的方法;IHostapdHal HAL interface object

  • 10
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值