欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

(七十一)Android O WiFi热点 开启流程梳理

程序员文章站 2024-03-16 14:00:34
...

前言:之前主要梳理了WiFi开启扫描连接的流程,现在梳理下WiFi 热点 的开启流程。

时序图mdj样式:https://download.csdn.net/download/sinat_20059415/10542186

 

1. wifi热点简介

 

wifi热点是将手机接收的GPRS、3G或4G信号转化为wifi信号发出去的技术,手机必须有无线AP功能,才能当做热点。有些系统自带建热点这个功能比如 IOS(比如 iPhone 4s)。

如果你把你的iPhone当做热点,那么像TOUCH,PAD这样有WIFI功能的,都可以搜索到你手机建立的WIFI网络,连接上以后,TOUCH等使用WIFI产生的流量上网都是消耗的乐WIFI里的手机卡的GPRS或3G流量,所以iphone里最好放一张包大流量的上网卡。还有,把手机当做热点很费电,最好用的时候插上充电器。

 

2. WiFi热点开启流程梳理

(七十一)Android O WiFi热点 开启流程梳理

2.1 Settings

Android O的Settings引入了PreferenceController这个包装类来实现对Preference的精细化控制,让代码结构更加地鲜明,很好地体现了单一职责原则,以前的Preference都是一大坨代码冗余在一起,看的都头疼。

TetherSettings->WifiTetherPreferenceController->WifiTetherSwitchBarController

TetherSettings是WiFi热点对应的界面,WifiTetherSwitchBarController是用来负责热点开关的相关逻辑处理的。

/packages/apps/Settings/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java

public class WifiTetherSwitchBarController implements SwitchWidgetController.OnSwitchChangeListener,
        LifecycleObserver, OnStart, OnStop {
...
   @Override
    public boolean onSwitchToggled(boolean isChecked) {
        if (isChecked) {
            startTether();
        } else {
            stopTether();
        }
        return true;
    }

    void stopTether() {
        mSwitchBar.setEnabled(false);
        mConnectivityManager.stopTethering(TETHERING_WIFI);
    }

    void startTether() {
        mSwitchBar.setEnabled(false);
        mConnectivityManager.startTethering(TETHERING_WIFI, true /* showProvisioningUi */,
                NoOpOnStartTetheringCallback.newInstance(), new Handler(Looper.getMainLooper()));
    }

可以看到WiFi热点代开是通过ConnectivityManager的startTethering方法。

1) callback(抽象类直接new出来的,内部实现还没有。。。)

class NoOpOnStartTetheringCallback {

    public static ConnectivityManager.OnStartTetheringCallback newInstance() {
        return new ConnectivityManager.OnStartTetheringCallback() {
        };
    }   
}
    /**
     * Callback for use with {@link #startTethering} to find out whether tethering succeeded.
     * @hide
     */
    @SystemApi
    public static abstract class OnStartTetheringCallback {
        /**
         * Called when tethering has been successfully started.
         */
        public void onTetheringStarted() {}

        /**
         * Called when starting tethering failed.
         */
        public void onTetheringFailed() {}
    }

 

 

2)状态监听:

 

 

    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (WifiManager.WIFI_AP_STATE_CHANGED_ACTION.equals(action)) {
                final int state = intent.getIntExtra(
                        WifiManager.EXTRA_WIFI_AP_STATE, WifiManager.WIFI_AP_STATE_FAILED);
                handleWifiApStateChanged(state);
            } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
                enableWifiSwitch();
            }
        }
    };  

    private void handleWifiApStateChanged(int state) {
        switch (state) {
            case WifiManager.WIFI_AP_STATE_ENABLING:
                mSwitchBar.setEnabled(false);
                break;
            case WifiManager.WIFI_AP_STATE_ENABLED:
                if (!mSwitchBar.isChecked()) {
                    mSwitchBar.setChecked(true);
                }
                enableWifiSwitch();
                break;
            case WifiManager.WIFI_AP_STATE_DISABLING:
                if (mSwitchBar.isChecked()) {
                    mSwitchBar.setChecked(false);
                }
                mSwitchBar.setEnabled(false);
                break;
            case WifiManager.WIFI_AP_STATE_DISABLED:
                mSwitchBar.setChecked(false);
                enableWifiSwitch();
                break;
            default:
                mSwitchBar.setChecked(false);
                enableWifiSwitch();
                break;
        }
    }   

    private void enableWifiSwitch() {
        boolean isAirplaneMode = Settings.Global.getInt(mContext.getContentResolver(),
                Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
        if (!isAirplaneMode) {
            mSwitchBar.setEnabled(!mDataSaverBackend.isDataSaverEnabled());
        } else {
            mSwitchBar.setEnabled(false);
        }
    }

 

 

 

2.2 ConnectivityManager

 

    /**
     * Runs tether provisioning for the given type if needed and then starts tethering if
     * the check succeeds. If no carrier provisioning is required for tethering, tethering is
     * enabled immediately. If provisioning fails, tethering will not be enabled. It also
     * schedules tether provisioning re-checks if appropriate.
     *
     * @param type The type of tethering to start. Must be one of
     *         {@link ConnectivityManager.TETHERING_WIFI},
     *         {@link ConnectivityManager.TETHERING_USB}, or
     *         {@link ConnectivityManager.TETHERING_BLUETOOTH}.
     * @param showProvisioningUi a boolean indicating to show the provisioning app UI if there
     *         is one. This should be true the first time this function is called and also any time
     *         the user can see this UI. It gives users information from their carrier about the
     *         check failing and how they can sign up for tethering if possible.
     * @param callback an {@link OnStartTetheringCallback} which will be called to notify the caller
     *         of the result of trying to tether.
     * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
     * @hide
     */
    @SystemApi
    @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
    public void startTethering(int type, boolean showProvisioningUi,
            final OnStartTetheringCallback callback, Handler handler) {
        Preconditions.checkNotNull(callback, "OnStartTetheringCallback cannot be null.");

        ResultReceiver wrappedCallback = new ResultReceiver(handler) {
            @Override
            protected void onReceiveResult(int resultCode, Bundle resultData) {
                if (resultCode == TETHER_ERROR_NO_ERROR) {
                    callback.onTetheringStarted();
                } else {
                    callback.onTetheringFailed();
                }
            }
        };

        try {
            String pkgName = mContext.getOpPackageName();
            Log.i(TAG, "startTethering caller:" + pkgName);
            mService.startTethering(type, wrappedCallback, showProvisioningUi, pkgName);
        } catch (RemoteException e) {
            Log.e(TAG, "Exception trying to start tethering.", e);
            wrappedCallback.send(TETHER_ERROR_SERVICE_UNAVAIL, null);
        }
    }

至于mService:

        mConnectivityManager =
                (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);

SystemServiceRegistry:

        registerService(Context.CONNECTIVITY_SERVICE, ConnectivityManager.class,
                new StaticApplicationContextServiceFetcher<ConnectivityManager>() {
            @Override
            public ConnectivityManager createService(Context context) throws ServiceNotFoundException {
                IBinder b = ServiceManager.getServiceOrThrow(Context.CONNECTIVITY_SERVICE);
                IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
                return new ConnectivityManager(context, service);
            }});
    /**
     * {@hide}
     */
    public ConnectivityManager(Context context, IConnectivityManager service) {
        mContext = Preconditions.checkNotNull(context, "missing context");
        mService = Preconditions.checkNotNull(service, "missing IConnectivityManager");
        sInstance = this;
    }

SystemServer:

                traceBeginAndSlog("StartConnectivityService");
                try {
                    connectivity = new ConnectivityService(
                            context, networkManagement, networkStats, networkPolicy);
                    ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity);
                    networkStats.bindConnectivityManager(connectivity);
                    networkPolicy.bindConnectivityManager(connectivity);
                } catch (Throwable e) {
                    reportWtf("starting Connectivity Service", e);
                }
                traceEnd();

 

所以mService是ConnectivityService。

 

 

2.3 ConnectivityService

    @Override
    public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi,
            String callerPkg) {
        ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
        if (!isTetheringSupported()) {
            receiver.send(ConnectivityManager.TETHER_ERROR_UNSUPPORTED, null);
            return;
        }
        mTethering.startTethering(type, receiver, showProvisioningUi);
    }

 

2.4 Tethering

 

    public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi) {
        if (!isTetherProvisioningRequired()) {
            enableTetheringInternal(type, true, receiver);
            return;
        }

        if (showProvisioningUi) {
            runUiTetherProvisioningAndEnable(type, receiver);
        } else {
            runSilentTetherProvisioningAndEnable(type, receiver);
        }
    }

暂时先认为打开tethering需要provision吧,继续往下看。

   /**
     * Check if the device requires a provisioning check in order to enable tethering.
     *
     * @return a boolean - {@code true} indicating tether provisioning is required by the carrier.
     */
    @VisibleForTesting
    protected boolean isTetherProvisioningRequired() {
        final TetheringConfiguration cfg = mConfig;
        if (mSystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false)
                || cfg.provisioningApp.length == 0) {
            return false;
        }
        if (carrierConfigAffirmsEntitlementCheckNotRequired()) {
            return false;
        }
        return (cfg.provisioningApp.length == 2);
    }

shouProvisioningUi由设置传入,是true

    private void runUiTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
        ResultReceiver proxyReceiver = getProxyReceiver(type, receiver);
        sendUiTetherProvisionIntent(type, proxyReceiver);
    }
    /**
     * Creates a proxy {@link ResultReceiver} which enables tethering if the provisioning result
     * is successful before firing back up to the wrapped receiver.
     *
     * @param type The type of tethering being enabled.
     * @param receiver A ResultReceiver which will be called back with an int resultCode.
     * @return The proxy receiver.
     */
    private ResultReceiver getProxyReceiver(final int type, final ResultReceiver receiver) {
        ResultReceiver rr = new ResultReceiver(null) {
            @Override
            protected void onReceiveResult(int resultCode, Bundle resultData) {
                // If provisioning is successful, enable tethering, otherwise just send the error.
                if (resultCode == TETHER_ERROR_NO_ERROR) {
                    enableTetheringInternal(type, true, receiver);
                } else {
                    sendTetherResult(receiver, resultCode);
                }
            }
        };

        // The following is necessary to avoid unmarshalling issues when sending the receiver
        // across processes.
        Parcel parcel = Parcel.obtain();
        rr.writeToParcel(parcel,0);
        parcel.setDataPosition(0);
        ResultReceiver receiverForSending = ResultReceiver.CREATOR.createFromParcel(parcel);
        parcel.recycle();
        return receiverForSending;
    }
    private void sendUiTetherProvisionIntent(int type, ResultReceiver receiver) {
        Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING);
        intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
        intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        final long ident = Binder.clearCallingIdentity();
        try {
            mContext.startActivityAsUser(intent, UserHandle.CURRENT);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

Settings有如下类会接收并处理消息

        <activity android:name="TetherProvisioningActivity"
            android:exported="true"
            android:permission="android.permission.TETHER_PRIVILEGED"
            android:excludeFromRecents="true"
            android:theme="@style/Theme.ProvisioningActivity">
            <intent-filter android:priority="1">
                <action android:name="android.settings.TETHER_PROVISIONING_UI" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
/**
 * Activity which acts as a proxy to the tether provisioning app for sanity checks and permission
 * restrictions. Specifically, the provisioning apps require
 * {@link android.permission.CONNECTIVITY_INTERNAL}, while this activity can be started by a caller
 * with {@link android.permission.TETHER_PRIVILEGED}.
 */
public class TetherProvisioningActivity extends Activity {
    private static final int PROVISION_REQUEST = 0;
    private static final String TAG = "TetherProvisioningAct";
    private static final String EXTRA_TETHER_TYPE = "TETHER_TYPE";
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
    private ResultReceiver mResultReceiver;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mResultReceiver = (ResultReceiver)getIntent().getParcelableExtra(
                ConnectivityManager.EXTRA_PROVISION_CALLBACK);

        int tetherType = getIntent().getIntExtra(ConnectivityManager.EXTRA_ADD_TETHER_TYPE,
                ConnectivityManager.TETHERING_INVALID);
        String[] provisionApp = getResources().getStringArray(
                com.android.internal.R.array.config_mobile_hotspot_provision_app);

        Intent intent = new Intent(Intent.ACTION_MAIN);
        intent.setClassName(provisionApp[0], provisionApp[1]);
        intent.putExtra(EXTRA_TETHER_TYPE, tetherType);
        if (DEBUG) {
            Log.d(TAG, "Starting provisioning app: " + provisionApp[0] + "." + provisionApp[1]);
        }

        if (getPackageManager().queryIntentActivities(intent,
                PackageManager.MATCH_DEFAULT_ONLY).isEmpty()) {
            Log.e(TAG, "Provisioning app is configured, but not available.");
            mResultReceiver.send(ConnectivityManager.TETHER_ERROR_PROVISION_FAILED, null);
            finish();
            return;
        }

        startActivityForResultAsUser(intent, PROVISION_REQUEST, UserHandle.CURRENT);
    }   

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);
        if (requestCode == PROVISION_REQUEST) {
            if (DEBUG) Log.d(TAG, "Got result from app: " + resultCode);
            int result = resultCode == Activity.RESULT_OK ?
                    ConnectivityManager.TETHER_ERROR_NO_ERROR :
                    ConnectivityManager.TETHER_ERROR_PROVISION_FAILED;
            mResultReceiver.send(result, null);
            finish();
        }
    }   
}

具体provision app需要vendor具体配置,看framework/base/core/res/res是空的

./values/config.xml:407:    <string-array translatable="false" name="config_mobile_hotspot_provision_app">
./values/config.xml-408-    <!--
./values/config.xml-409-        <item>com.example.provisioning</item>
./values/config.xml-410-        <item>com.example.provisioning.Activity</item>
./values/config.xml-411-    -->
./values/config.xml-412-    </string-array>

完成后回调onActivityResult,发送ConnectivityManager.TETHER_ERROR_NO_ERROR,继续调用enableTetheringInternal方法。

        ResultReceiver rr = new ResultReceiver(null) {
            @Override
            protected void onReceiveResult(int resultCode, Bundle resultData) {
                // If provisioning is successful, enable tethering, otherwise just send the error.
                if (resultCode == TETHER_ERROR_NO_ERROR) {
                    enableTetheringInternal(type, true, receiver);
                } else {
                    sendTetherResult(receiver, resultCode);
                }
            }
        };

 

   /**
     * Enables or disables tethering for the given type. This should only be called once
     * provisioning has succeeded or is not necessary. It will also schedule provisioning rechecks
     * for the specified interface.
     */
    private void enableTetheringInternal(int type, boolean enable, ResultReceiver receiver) {
        boolean isProvisioningRequired = enable && isTetherProvisioningRequired();
        int result;
        switch (type) {
            case TETHERING_WIFI:
                result = setWifiTethering(enable);
                if (isProvisioningRequired && result == TETHER_ERROR_NO_ERROR) {
                    scheduleProvisioningRechecks(type);
                }
                sendTetherResult(receiver, result);
                break;
            case TETHERING_USB:
                result = setUsbTethering(enable);
                if (isProvisioningRequired && result == TETHER_ERROR_NO_ERROR) {
                    scheduleProvisioningRechecks(type);
                }
                sendTetherResult(receiver, result);
                break;
            case TETHERING_BLUETOOTH:
                setBluetoothTethering(enable, receiver);
                break;
            default:
                Log.w(TAG, "Invalid tether type.");
                sendTetherResult(receiver, TETHER_ERROR_UNKNOWN_IFACE);
        }
    }

可以看到tethering不特指WiFi热点,总体包含蓝牙WiFi热点和Usb。

我们还是走不需要provision的流程把。。。

    private int setWifiTethering(final boolean enable) {
        int rval = TETHER_ERROR_MASTER_ERROR;
        final long ident = Binder.clearCallingIdentity();
        try {
            synchronized (mPublicSync) {
                mWifiTetherRequested = enable;
                final WifiManager mgr = getWifiManager();
                if ((enable && mgr.startSoftAp(null /* use existing wifi config */)) ||
                    (!enable && mgr.stopSoftAp())) {
                    rval = TETHER_ERROR_NO_ERROR;
                }
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
        return rval;
    }

这边会走到WifiManager里去,wifi config为空表示使用已有的WiFi config。后面的senTetherResult就是回调之前的receiver通知执行结果。

 

2.5 WifiManager

    /**
     * Start SoftAp mode with the specified configuration.
     * Note that starting in access point mode disables station
     * mode operation
     * @param wifiConfig SSID, security and channel details as
     *        part of WifiConfiguration
     * @return {@code true} if the operation succeeds, {@code false} otherwise
     *
     * @hide
     */
    public boolean startSoftAp(@Nullable WifiConfiguration wifiConfig) {
        try {
            return mService.startSoftAp(wifiConfig);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

 

2.6 WifiServiceImpl

 

    /**
     * see {@link android.net.wifi.WifiManager#startSoftAp(WifiConfiguration)}
     * @param wifiConfig SSID, security and channel details as part of WifiConfiguration
     * @return {@code true} if softap start was triggered
     * @throws SecurityException if the caller does not have permission to start softap
     */
    @Override
    public boolean startSoftAp(WifiConfiguration wifiConfig) {
        // NETWORK_STACK is a signature only permission.
        enforceNetworkStackPermission();

        mLog.info("startSoftAp uid=%").c(Binder.getCallingUid()).flush();

        synchronized (mLocalOnlyHotspotRequests) {
            // If a tethering request comes in while we have LOHS running (or requested), call stop
            // for softap mode and restart softap with the tethering config.
            if (!mLocalOnlyHotspotRequests.isEmpty()) {
                stopSoftApInternal();
            }

            return startSoftApInternal(wifiConfig, WifiManager.IFACE_IP_MODE_TETHERED);
        }
    }

 

    /**
     * Internal method to start softap mode. Callers of this method should have already checked
     * proper permissions beyond the NetworkStack permission.
     */
    private boolean startSoftApInternal(WifiConfiguration wifiConfig, int mode) {
        mLog.trace("startSoftApInternal uid=% mode=%")
                .c(Binder.getCallingUid()).c(mode).flush();

        // null wifiConfig is a meaningful input for CMD_SET_AP
        if (wifiConfig == null || isValid(wifiConfig)) {
            SoftApModeConfiguration softApConfig = new SoftApModeConfiguration(mode, wifiConfig);
            mWifiController.sendMessage(CMD_SET_AP, 1, 0, softApConfig);
            return true;
        }
        Slog.e(TAG, "Invalid WifiConfiguration");
        return false;
    }

这边对空的wificonfig包装成了SoftApModeConfiguration接由WifiController处理。

    /**
     * Enqueue a message to this state machine.
     *
     * Message is ignored if state machine has quit.
     */
    public void sendMessage(int what, int arg1, int arg2, Object obj) {
        // mSmHandler can be null if the state machine has quit.
        SmHandler smh = mSmHandler;
        if (smh == null) return;

        smh.sendMessage(obtainMessage(what, arg1, arg2, obj));
    }

 

 

 

2.7 WifiController

(七十一)Android O WiFi热点 开启流程梳理

ApStaDisabledState会对该消息进行对应的处理

                case CMD_SET_AP:
                    if (msg.arg1 == 1) {
                        if (msg.arg2 == 0) { // previous wifi state has not been saved yet
                            mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_DISABLED);
                        }
                        mWifiStateMachine.setHostApRunning((SoftApModeConfiguration) msg.obj,
                                true);
                        transitionTo(mApEnabledState);
                    }
                    break;

 

2.8 WifiStateMachine

(七十一)Android O WiFi热点 开启流程梳理

 

    /**
     * TODO: doc
     */
    public void setHostApRunning(SoftApModeConfiguration wifiConfig, boolean enable) {
        if (enable) {
            sendMessage(CMD_START_AP, wifiConfig);
        } else {
            sendMessage(CMD_STOP_AP);
        }
    }

 

看了一圈只有InitialState对该消息有正确响应

                case CMD_START_AP:
                    transitionTo(mSoftApState);
                    break;

InitialState exit()方法为空,看下SoftApState的enter方法

    class SoftApState extends State {
        private SoftApManager mSoftApManager;
        private String mIfaceName;
        private int mMode;

        private class SoftApListener implements SoftApManager.Listener {
            @Override
            public void onStateChanged(int state, int reason) {
                if (state == WIFI_AP_STATE_DISABLED) {
                    sendMessage(CMD_AP_STOPPED);
                } else if (state == WIFI_AP_STATE_FAILED) {
                    sendMessage(CMD_START_AP_FAILURE);
                }

                setWifiApState(state, reason, mIfaceName, mMode);
            }
        }

        @Override
        public void enter() {
            final Message message = getCurrentMessage();
            if (message.what != CMD_START_AP) {
                throw new RuntimeException("Illegal transition to SoftApState: " + message);
            }
            SoftApModeConfiguration config = (SoftApModeConfiguration) message.obj;
            mMode = config.getTargetMode();

            IApInterface apInterface = null;
            Pair<Integer, IApInterface> statusAndInterface = mWifiNative.setupForSoftApMode();
            if (statusAndInterface.first == WifiNative.SETUP_SUCCESS) {
                apInterface = statusAndInterface.second;
            } else {
                incrementMetricsForSetupFailure(statusAndInterface.first);
            }
            if (apInterface == null) {
                setWifiApState(WIFI_AP_STATE_FAILED,
                        WifiManager.SAP_START_FAILURE_GENERAL, null, mMode);
                /**
                 * Transition to InitialState to reset the
                 * driver/HAL back to the initial state.
                 */
                transitionTo(mInitialState);
                return;
            }

            try {
                mIfaceName = apInterface.getInterfaceName();
            } catch (RemoteException e) {
                // Failed to get the interface name. The name will not be available for
                // the enabled broadcast, but since we had an error getting the name, we most likely
                // won't be able to fully start softap mode.
            }

            checkAndSetConnectivityInstance();
            mSoftApManager = mWifiInjector.makeSoftApManager(mNwService,
                                                             new SoftApListener(),
                                                             apInterface,
                                                             config.getWifiConfiguration());
            mSoftApManager.start();
            mWifiStateTracker.updateState(WifiStateTracker.SOFT_AP);
        }Pair<Integer, IApInterface> statusAndInterface = mWifiNative.setupForSoftApMode();
            if (statusAndInterface.first == WifiNative.SETUP_SUCCESS) {
                apInterface = statusAndInterface.second;
            } else {
                incrementMetricsForSetupFailure(statusAndInterface.first);
            }
            if (apInterface == null) {
                setWifiApState(WIFI_AP_STATE_FAILED,
                        WifiManager.SAP_START_FAILURE_GENERAL, null, mMode);
                /**
                 * Transition to InitialState to reset the
                 * driver/HAL back to the initial state.
                 */
                transitionTo(mInitialState);
                return;
            }

            try {
                mIfaceName = apInterface.getInterfaceName();
            } catch (RemoteException e) {
                // Failed to get the interface name. The name will not be available for
                // the enabled broadcast, but since we had an error getting the name, we most likely
                // won't be able to fully start softap mode.
            }

            checkAndSetConnectivityInstance();
            mSoftApManager = mWifiInjector.makeSoftApManager(mNwService,
                                                             new SoftApListener(),
                                                             apInterface,
                                                             config.getWifiConfiguration());
            mSoftApManager.start();
            mWifiStateTracker.updateState(WifiStateTracker.SOFT_AP);
        }

这边的调用流程和WiFi的启动流程有点类似。先梳理到这,后面应该是硬菜。。。

(七十一)Android O WiFi热点 开启流程梳理

 

 

3. 总结

(七十一)Android O WiFi热点 开启流程梳理

相关标签: Wifi热点