学习
实践
活动
专区
工具
TVP
写文章
专栏首页程序猿的那点事Android9.0Wifi热点开启流程梳理

Android9.0Wifi热点开启流程梳理

Android9.0中对热点做了较大改动,将热点很大程度从Wifi中剥离出来了。 下面我们看一下热点是怎么开启的。

首先是在WifiTetherSettings中,调用startTether()函数,可以看到startTether函数中主要是调用了WifiTetherSwitchBarController.java中的startTether()函数。 源码路径: http://androidxref.com/9.0.0_r3/xref/packages/apps/Settings/src/com/android/settings/wifi/tether/WifiTetherSettings.java#174

    private void startTether() {
          mRestartWifiApAfterConfigChange = false;
          mSwitchBarController.startTether();
    }

然后我们看WifiTetherSwitchBarController里的startTether函数,WifiTetherSwitchBarController是用来控制开关栏切换的,可以看到他调用了ConnectivityManager的startTethering函数。 源码路径: http://androidxref.com/9.0.0_r3/xref/packages/apps/Settings/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java#107

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

然后在这里调用了ConnectivityService的startTethering函数。 源码路径: http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/net/ConnectivityManager.java#2261

    public void startTethering(int type, boolean showProvisioningUi,
            final OnStartTetheringCallback callback, Handler handler) {
        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);
        }
    }

在ConnectivityService中,会跳到Tethering.java的startTethering函数。 源码路径: http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/ConnectivityService.java#3232

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

在Tethering.java中,函数的调用顺序如下:startTethering—>enableTetheringInternal—>setWifiTethering。在setWifiTethering中,调用了WifiManager的startSoftAp函数。 源码路径: http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/connectivity/Tethering.java#368

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

在这里调用了WifiServiceImpl.java的startSoftAp函数。 源码路径: http://androidxref.com/9.0.0_r3/xref/frameworks/base/wifi/java/android/net/wifi/WifiManager.java#1909

    public boolean startSoftAp(@Nullable WifiConfiguration wifiConfig) {
        try {
            return mService.startSoftAp(wifiConfig);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

在这里调用了startSoftApInternal函数,然后向WifiController的状态机发送了CMD_SET_AP消息。 源码路径: http://androidxref.com/9.0.0_r3/xref/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiServiceImpl.java#1004

    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);
        }
    }
    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 || WifiApConfigStore.validateApWifiConfiguration(wifiConfig)) {
            SoftApModeConfiguration softApConfig = new SoftApModeConfiguration(mode, wifiConfig);
            mWifiController.sendMessage(CMD_SET_AP, 1, 0, softApConfig);
            return true;
        }
        Slog.e(TAG, "Invalid WifiConfiguration");
        return false;
    }

在这里,处理结果是调用WifiStateMachinePrime的enterSoftAPMode方法。 源码路径: http://androidxref.com/9.0.0_r3/xref/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiController.java#593

    class DefaultState extends State {
                case CMD_SET_AP:
                    // note: CMD_SET_AP is handled/dropped in ECM mode - will not start here

                    // first make sure we aren't in airplane mode
                    if (mSettingsStore.isAirplaneModeOn()) {
                        log("drop softap requests when in airplane mode");
                        break;
                    }
                    if (msg.arg1 == 1) {
                        SoftApModeConfiguration config = (SoftApModeConfiguration) msg.obj;
                       mWifiStateMachinePrime.enterSoftAPMode((SoftApModeConfiguration) msg.obj);
                    } else {
                        mWifiStateMachinePrime.stopSoftAPMode();
                    }
                    break;

在WifiStateMachinePrime中,创建了SoftApManager对象,并且调用了start方法。 源码路径: http://androidxref.com/9.0.0_r3/xref/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiStateMachinePrime.java#549

    public void enterSoftAPMode(@NonNull SoftApModeConfiguration wifiConfig) {
        mHandler.post(() -> {
            startSoftAp(wifiConfig);
        });
    }
    private void startSoftAp(SoftApModeConfiguration softapConfig) {
        Log.d(TAG, "Starting SoftApModeManager");

        WifiConfiguration config = softapConfig.getWifiConfiguration();
        if (config != null && config.SSID != null) {
            Log.d(TAG, "Passing config to SoftApManager! " + config);
        } else {
            config = null;
        }

        SoftApCallbackImpl callback = new SoftApCallbackImpl();
        ActiveModeManager manager = mWifiInjector.makeSoftApManager(callback, softapConfig);
        callback.setActiveModeManager(manager);
        manager.start();
        mActiveModeManagers.add(manager);
        updateBatteryStatsWifiState(true);
    }

在这里,StateMachine发送了消息CMD_START,然后由IdleState 处理,这里主要的处理结果就是调用startSoftAp方法。在startSoftAp里,调用了WifiNative.java的startSoftAp方法。 源码路径: http://androidxref.com/9.0.0_r3/xref/frameworks/opt/net/wifi/service/java/com/android/server/wifi/SoftApManager.java#142

    public void start() {
        mStateMachine.sendMessage(SoftApStateMachine.CMD_START, mApConfig);
    }
        private class IdleState extends State {
            public 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);
                            break;
                        }
                        updateApState(WifiManager.WIFI_AP_STATE_ENABLING,
                                WifiManager.WIFI_AP_STATE_DISABLED, 0);
                        int result = startSoftAp((WifiConfiguration) message.obj);
                        if (result != SUCCESS) {
                            int failureReason = WifiManager.SAP_START_FAILURE_GENERAL;
                            if (result == ERROR_NO_CHANNEL) {
                                failureReason = WifiManager.SAP_START_FAILURE_NO_CHANNEL;
                            }
                            updateApState(WifiManager.WIFI_AP_STATE_FAILED,
                                          WifiManager.WIFI_AP_STATE_ENABLING,
                                          failureReason);
                            stopSoftAp();
                            mWifiMetrics.incrementSoftApStartResult(false, failureReason);
                            break;
                        }
                        transitionTo(mStartedState);
                        break;
    private int startSoftAp(WifiConfiguration config) {
        if (config == null || config.SSID == null) {
            Log.e(TAG, "Unable to start soft AP without valid configuration");
            return ERROR_GENERIC;
        }

        // Make a copy of configuration for updating AP band and channel.
        WifiConfiguration localConfig = new WifiConfiguration(config);

        int result = ApConfigUtil.updateApChannelConfig(
                mWifiNative, mCountryCode,
                mWifiApConfigStore.getAllowed2GChannel(), localConfig);

        if (result != SUCCESS) {
            Log.e(TAG, "Failed to update AP band and channel");
            return result;
        }

        // Setup country code if it is provided.
        if (mCountryCode != null) {
            // Country code is mandatory for 5GHz band, return an error if failed to set
            // country code when AP is configured for 5GHz band.
            if (!mWifiNative.setCountryCodeHal(
                    mApInterfaceName, mCountryCode.toUpperCase(Locale.ROOT))
                    && config.apBand == WifiConfiguration.AP_BAND_5GHZ) {
                Log.e(TAG, "Failed to set country code, required for setting up "
                        + "soft ap in 5GHz");
                return ERROR_GENERIC;
            }
        }
        if (localConfig.hiddenSSID) {
            Log.d(TAG, "SoftAP is a hidden network");
        }
        if (!mWifiNative.startSoftAp(mApInterfaceName, localConfig, mSoftApListener)) {
            Log.e(TAG, "Soft AP start failed");
            return ERROR_GENERIC;
        }
        Log.d(TAG, "Soft AP is started");

        return SUCCESS;
    }

在这里,主要是调用WificondControl.java的startHostapd方法。 源码路径: http://androidxref.com/9.0.0_r3/xref/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNative.java#1250

    public boolean startSoftAp(
            @NonNull String ifaceName, WifiConfiguration config, SoftApListener listener) {
        if (!mWificondControl.startHostapd(ifaceName, listener)) {
            Log.e(TAG, "Failed to start hostapd");
            mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToHostapd();
            return false;
        }
        if (!waitForHostapdConnection()) {
            Log.e(TAG, "Failed to establish connection to hostapd");
            mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToHostapd();
            return false;
        }
        if (!mHostapdHal.registerDeathHandler(new HostapdDeathHandlerInternal())) {
            Log.e(TAG, "Failed to register hostapd death handler");
            mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToHostapd();
            return false;
        }
        if (!mHostapdHal.addAccessPoint(ifaceName, config)) {
            Log.e(TAG, "Failed to add acccess point");
            mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToHostapd();
            return false;
        }
        return true;
    }

在这里可以看到创建了IApInterface 对象,然后调用了他的startHostapd方法。而IApInterface 是由ap_interface_impl.cpp实现的。 源码路径: http://androidxref.com/9.0.0_r3/xref/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WificondControl.java#787

    public boolean startHostapd(@NonNull String ifaceName,
                               SoftApListener listener) {
        IApInterface iface = getApInterface(ifaceName);
        if (iface == null) {
            Log.e(TAG, "No valid ap interface handler");
            return false;
        }
        try {
            IApInterfaceEventCallback  callback = new ApInterfaceEventCallback(listener);
            mApInterfaceListeners.put(ifaceName, callback);
            boolean success = iface.startHostapd(callback);
            if (!success) {
                Log.e(TAG, "Failed to start hostapd.");
                return false;
            }
        } catch (RemoteException e) {
            Log.e(TAG, "Exception in starting soft AP: " + e);
            return false;
        }
        return true;
    }

在这里又调用了hostapd_manager的StartHostapd函数。 源码路径: http://androidxref.com/9.0.0_r3/xref/system/connectivity/wificond/ap_interface_impl.cpp#86

bool ApInterfaceImpl::StartHostapd() {
  return hostapd_manager_->StartHostapd();
}

然后在hostapd_manager.cpp里调用了StartHostapd方法。 源码路径: http://androidxref.com/9.0.0_r3/xref/frameworks/opt/net/wifi/libwifi_system/hostapd_manager.cpp#26

bool HostapdManager::StartHostapd() {
  if (property_set("ctl.start", kHostapdServiceName) != 0) {
    LOG(ERROR) << "Failed to start SoftAP";
    return false;
      LOG(DEBUG) << "SoftAP started successfully";
  return true;
}

再往后就是底层C的代码了,不是特别看得懂。然后返回success的话,热点就成功打开了,状态也变为started。

本文参与 腾讯云自媒体分享计划 ,欢迎热爱写作的你一起参与!
本文分享自作者个人站点/博客:https://blog.csdn.net/qq_43804080复制
如有侵权,请联系 cloudcommunity@tencent.com 删除。
登录 后参与评论
0 条评论

相关文章

  • Android 10.0热点为Enhanced Open模式时不允许WiFI和热点同时开启代码流程梳理

    WLAN Enhanced Open :WiFi增强开放,这个功能就是当手机开启热点时,Securty的一个选项,与WPA2/WPA3同级,Enhanced...

    用户7557625
  • vivo商城促销系统架构设计与实践-概览篇

    随着商城业务渠道不断扩展,促销玩法不断增多,原商城v2.0架构已经无法满足不断增加的活动玩法,需要进行促销系统的独立建设,与商城解耦,提供纯粹的商城营销活动玩法...

    2020labs小助手
  • Sentinel热点参数如何限流【原理源码】

    热点参数限流通过对请求的第几个参数以及参数值的流量进行统计,超过阈值触发流控的一种方式,例如:售卖的热销产品的抢购场景。

    瓜农老梁
  • IPv6升级测试指南(Android/iOS/Mac)

    (注意⚠️,网上说的那种直接修改默认运营商的APN的方法,可能造成问题,而且很多手机也不允许修改默认的APN)

    啦啦啦321
  • 磨刀不误砍柴工-流程梳理

    昨天开始写一个数据逻辑,整体来说就是对于数据生命周期的管理,如何做到系统化管理,而且对于数据库的侵入性最小。 通常来说,我们会投入较多的时间来逻辑开发部分,...

    jeanron100
  • 电商大促,性能测试都在做什么?

    自从09年阿里开启了双十一活动,近几年各大电商平台的促销活动如火如荼。电商大促期间剧增的流量,对电商平台相关的软件系统也带来了更严峻的挑战。

    老_张
  • Redis面试题汇总

      redis中的五种常用类型分别是string,Hash,List,Set,ZSet。

    用户4919348
  • 云点播智能降码,成本与观看体验兼得的降本增效法宝

    时至今日,视频流媒体已经成为主要的信息传播媒介之一,但对于在业务中提供音视频内容服务的企业而言,依然存在诸多挑战。用户对观看体验的需求一路走高,需要更高的清晰度...

    腾讯云音视频
  • 造火箭-流程设计之绘图篇

    今天主要介绍的不是怎么写文档,只是想分享一下怎么更好的用 图 表达,结构化的表达很重要,我把图 的表达看做代码中的 设计模式 来类比,文字中的 设计模式(Des...

    西南_张家辉
  • 历时三个月,微博推荐引擎架构蜕变之路

    可靠性保障是一个复杂的系统工程,特别对于可靠性已经出现问题的线上服务,在业务迭代、成本约束、人力投入等方面的约束下 ,提升其可用性就不再是单纯的技术问题了。

    深度学习与Python
  • JVM笔记-后端编译与优化

    前面分析了 JVM 的前端编译器 Javac,本文分析后端编译器:即时编译器(JIT 编译器)和提前编译器(AOT 编译器)。

    WriteOnRead
  • 营销知识丨企业新媒体营销框架概览

    跟上述概念不谋而合,近几年各种新媒体平台如雨后春笋扑面而来,追赶热潮的人不乏有信誓旦旦,觉得坚持就能在传播和变现的路上越走越远,但事实略显骨感,能够脱颖而出的总...

    齿轮易创说互联网
  • 1分钟售出5万张票!电影节抢票技术揭秘

    对于电影爱好者来说,每次的电影节、影展活动,都是抢票大战的开启,出票速度几乎可 以用“秒空”来形容,例如上海国际电影节线上开售的记录是 60 秒售出 5 万张。

    AI科技大本营
  • Redis中缓存雪崩、缓存穿透等问题的解决方案

    缓存雪崩是指缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。

    码农编程进阶笔记
  • Redis中缓存雪崩、缓存穿透等问题的解决方案「建议收藏」

    缓存雪崩是指缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。

    全栈程序员站长
  • 干货 | 1分钟售票8万张!门票抢票背后的技术思考

    HongLiang,携程高级技术专家,专注系统性能、稳定性、承载能力和交易质量,在技术架构演进、高并发等领域有丰富的实践经验。

    携程技术
  • 企业知识管理的步骤有哪些?

    知识管理是新世纪企业发展的一项重要内容,以知识管理为核心的管理模式已经受到了各个企业的重视,也开始成为企业管理工作中的热点问题。

    小炮
  • PD 调度策略最佳实践

    众所周知,PD 是整个 TiDB 集群的核心,负责全局元信息的存储以及 TiKV 集群负载均衡调度,本文将详细介绍 PD 调度系统的原理,并通过几个典型场景的分...

    PingCAP
  • 如何设计一个秒杀系统?

    于是,秒杀系统一般会引入MQ、Redis、MySQL、Nginx等中间件,需要对每个中间件进行高性能、高并发、高可用的分析。

    用户7353950

扫码关注腾讯云开发者

领取腾讯云代金券