LocalOnlyHotspot学习总结(二)

mac2024-08-04  52

前几天学习了LocalOnlyHotspot相关内容,做了总结LocalOnlyHotspot学习总结,还有一些问题没有搞明白,最后搞清楚了,在这里记录一下。

问题

1、LocalOnlyHotspot开启以后,应用退出前台几秒热点就会自动关闭。 2、连接LocalOnlyHotspot不能访问外网。

针对这俩个问题,在下面会分开讨论。

一、LocalOnlyHotspot开启以后,应用退出后台几秒热点就会自动关闭。

先看一下原来的代码,我们通过startLocalOnlyHotspot打开热点,需要LocalOnlyHotspotCallback参数,而在LocalOnlyHotspotCallback的onStart函数中又有一个LocalOnlyHotspotReservation参数。在关闭热点时,需要调用LocalOnlyHotspotReservation的close方法来关闭。 而当你第二次打开热点时,又会创建一个LocalOnlyHotspotReservation对象,这时第一次开启热点时创建的LocalOnlyHotspotReservation对象就会被回收,LocalOnlyHotspotReservation对象被回收时会调用它的close函数,从而关闭热点。这也就是为什么应用退出前台后热点会关闭。

if (isChecked) { Log.d(TAG, "startLocalOnlyHotspot: "); mWifiManager.startLocalOnlyHotspot(new WifiManager.LocalOnlyHotspotCallback() { @Override public void onStarted(LocalOnlyHotspotReservation reservation) { super.onStarted(reservation); mLocalOnlyHotspotReservation = reservation; ssid = reservation.getWifiConfiguration().SSID; pwd = reservation.getWifiConfiguration().preSharedKey; Log.e(TAG, "ssid and pwd is" + ssid + "and" + pwd); HandlerThread testHandlerThread = new HandlerThread(reservation); } @Override public void onStopped() { super.onStopped(); Log.e("SetLocalOnlyHotSpotController", "stopped"); } @Override public void onFailed(int reason) { super.onFailed(reason); } }, new Handler()); } else { Log.d(TAG, "stopLocalOnlyHotspot: "); if (mLocalOnlyHotspotReservation != null) { mLocalOnlyHotspotReservation.close(); mLocalOnlyHotspotReservation = null; } }

可以看到LocalOnlyHotspotReservation被回收时会调用自己的close函数。

protected void finalize() throws Throwable { try { if (mCloseGuard != null) { mCloseGuard.warnIfOpen(); } close(); } finally { super.finalize(); } }

解决策略: 写一个线程,在开启热点的时候把创建的LocalOnlyHotspotReservation的值传给线程的成员变量,然后把这个线程设为守护线程。这样只要进程不被杀死,之前创建的LocalOnlyHotspotReservation 对象就不会被回收,就可以解决热点自动关闭这个问题了。

mWifiManager.startLocalOnlyHotspot(new WifiManager.LocalOnlyHotspotCallback() { @Override public void onStarted(LocalOnlyHotspotReservation reservation) { super.onStarted(reservation); mLocalOnlyHotspotReservation = reservation; ssid = reservation.getWifiConfiguration().SSID; pwd = reservation.getWifiConfiguration().preSharedKey; Log.e(TAG, "ssid and pwd is" + ssid + "and" + pwd); HandlerThread testHandlerThread = new HandlerThread(reservation); testHandlerThread.setDaemon(true); testHandlerThread.start(); }

线程代码:

private class HandlerThread extends Thread { private LocalOnlyHotspotReservation mHandlerThreadHotspotReservation; public HandlerThread(LocalOnlyHotspotReservation localOnlyHotspotReservation) { mHandlerThreadHotspotReservation = localOnlyHotspotReservation; } @Override public void run() { super.run(); while (true) { try { sleep(500); Log.d("HandlerThread" , "HandlerThread is running, thread id: " + Thread.currentThread().getId()); } catch (InterruptedException e) { e.printStackTrace(); } } } }

二、连接LocalOnlyHotspot不能访问外网。

代码链接:http://androidxref.com/9.0.0_r3/xref/frameworks/opt/net/wifi/service/java/com/android/server/wifi/SoftApManager.java 热点开启我们就不多说了,直接到SoftApManager中去看。SoftApManager也是一个状态机。我们直接看StartedState 状态,因为这个是热点打开以后的配置。onUpChanged函数会调用updateApState函数,改变softAp的状态。

private class StartedState extends State { private void onUpChanged(boolean isUp) { if (isUp == mIfaceIsUp) { return; // no change } mIfaceIsUp = isUp; if (isUp) { Log.d(TAG, "SoftAp is ready for use"); updateApState(WifiManager.WIFI_AP_STATE_ENABLED, WifiManager.WIFI_AP_STATE_ENABLING, 0); mWifiMetrics.incrementSoftApStartResult(true, 0); if (mCallback != null) { mCallback.onNumClientsChanged(mNumAssociatedStations); } } else { // the interface was up, but goes down sendMessage(CMD_INTERFACE_DOWN); } mWifiMetrics.addSoftApUpChangedEvent(isUp, mMode); }

updateApState函数主要是发送广播。我们看WIFI_AP_STATE_CHANGED_ACTION这个关闭被谁接收到了。

private void updateApState(int newState, int currentState, int reason) { mCallback.onStateChanged(newState, reason); //send the AP state change broadcast final 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 failed intent.putExtra(WifiManager.EXTRA_WIFI_AP_FAILURE_REASON, reason); } intent.putExtra(WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME, mApInterfaceName); intent.putExtra(WifiManager.EXTRA_WIFI_AP_MODE, mMode); mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); }

代码链接:http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/connectivity/Tethering.java 在Tethering.java中找到了WIFI_AP_STATE_CHANGED_ACTION这个广播的相关处理。curState这个参数在此时应该是WIFI_AP_STATE_ENABLED,因为热点成功打开了。相应的处理我们看enableWifiIpServingLocked函数。

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_DISABLED: case WifiManager.WIFI_AP_STATE_DISABLING: case WifiManager.WIFI_AP_STATE_FAILED: default: disableWifiIpServingLocked(ifname, curState); break; } } } }

在这里通过changeInterfaceState函数把ipServingMode参数传了出去,ipServingMode参数是来区分是普通的热点还是localonly热点。

private void enableWifiIpServingLocked(String ifname, int wifiIpMode) { // Map wifiIpMode values to IControlsTethering serving states, inferring // from mWifiTetherRequested as a final "best guess". final int ipServingMode; switch (wifiIpMode) { case IFACE_IP_MODE_TETHERED: ipServingMode = IControlsTethering.STATE_TETHERED; break; case IFACE_IP_MODE_LOCAL_ONLY: ipServingMode = IControlsTethering.STATE_LOCAL_ONLY; break; default: mLog.e("Cannot enable IP serving in unknown WiFi mode: " + wifiIpMode); return; } if (!TextUtils.isEmpty(ifname)) { maybeTrackNewInterfaceLocked(ifname, TETHERING_WIFI); changeInterfaceState(ifname, ipServingMode); } else { mLog.e(String.format( "Cannot enable IP serving in mode %s on missing interface name", ipServingMode)); } }

可以看到,在changeInterfaceState这里,普通热点和本地热点(localonlyhotspot)没有区分,统一处理。

private void changeInterfaceState(String ifname, int requestedState) { final int result; switch (requestedState) { case IControlsTethering.STATE_UNAVAILABLE: case IControlsTethering.STATE_AVAILABLE: result = untether(ifname); break; case IControlsTethering.STATE_TETHERED: case IControlsTethering.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; } }

在这里,tetherState发送了CMD_TETHER_REQUESTED消息给TetherInterfaceStateMachine状态机,requestedState是CMD_TETHER_REQUESTED消息里的内容,用来区分是普通热点还是本地热点。

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 != IControlsTethering.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. // // TODO: reexamine the threading and messaging model. tetherState.stateMachine.sendMessage( TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, requestedState); return TETHER_ERROR_NO_ERROR; } }

代码链接:http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java

我们看TetherInterfaceStateMachine状态机是怎么处理的。此时状态机还是InitialState 状态,这时候CMD_TETHER_REQUESTED消息里的requestedState参数就起了作用。根据requestedState来判断热点的类型,然后跳转到不同的状态机。然后我们看LocalHotspotState和TetheredState俩种状态机的区别。

class InitialState extends State { @Override public void enter() { sendInterfaceState(IControlsTethering.STATE_AVAILABLE); } @Override public boolean processMessage(Message message) { logMessage(this, message.what); switch (message.what) { case CMD_TETHER_REQUESTED: mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR; switch (message.arg1) { case IControlsTethering.STATE_LOCAL_ONLY: transitionTo(mLocalHotspotState); break; case IControlsTethering.STATE_TETHERED: 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); break; default: return NOT_HANDLED; } return HANDLED; } }

我们看俩个状态机对CMD_TETHER_CONNECTION_CHANGED消息的处理。 可以看到LocalHotspotState 不对这个消息做任何处理,而TetheredState 则做了处理,那么这里就是区别所在。我们直接去看TetheredState 的处理内容。

class LocalHotspotState extends BaseServingState { ········· @Override public 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 in local-only hotspot mode."); break; case CMD_TETHER_CONNECTION_CHANGED: // Ignored in local hotspot state. break; default: return false; } return true; } }

我们看TetheredState里的处理。调用了enableNat和startInterfaceForwarding方法,再看mNMService是哪个类,mNMService是NetworkManagementService对象。那我们到NetworkManagementService里看这俩个函数的实现。

class TetheredState extends BaseServingState { ·············· @Override public boolean processMessage(Message message) { 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 { mNMService.enableNat(mIfaceName, ifname); mNMService.startInterfaceForwarding(mIfaceName, ifname); } catch (Exception e) { mLog.e("Exception enabling NAT: " + e); cleanupUpstream(); mLastError = ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR; transitionTo(mInitialState); return true; } } break; default: return false; } return true; } ··········· }

代码链接: http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/NetworkManagementService.java enableNat() 的作用是启用两个接口之间的网络地址转换,

public void enableNat(String internalInterface, String externalInterface) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { modifyNat("enable", internalInterface, externalInterface); } catch (SocketException e) { throw new IllegalStateException(e); } }

startInterfaceForwarding的作用是启用从端到端的单向数据包转发。

public void startInterfaceForwarding(String fromIface, String toIface) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); modifyInterfaceForward(true, fromIface, toIface); }

也就是说,enableNat 和 startInterfaceForwarding是打开Nat,启用转发,也就是服务端把客户端的请求转发到网络,客户端才能够连接到网络。而LocalHotspotState没有做这一步,当然访问不了外网喽。

关注公众号,获取更多开发必备知识

最新回复(0)