Android Wifi实战——P2P状态变化广播(二)

        WiFi P2P 也就是前面介绍的 WLAN 直连,这里我们就来介绍一个开发中的真是案例——P2P重连失败问题。通过日志初步分析发现,WiFi P2P 的可用状态不断变化,导致功能不可用,这里就以该现象为例分析一个 WiFi P2P 可用状态广播的发送逻辑。

一、P2P状态广播

1、WifiP2pManager

源码位置:/frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pManager.java

@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String WIFI_P2P_STATE_CHANGED_ACTION =
        "android.net.wifi.p2p.STATE_CHANGED";

        这里仅仅是一个广播的定义,接下来需要找到该广播的发送位置。 

2、WifiP2pServiceImpl

源码位置:/frameworks/opt/net/wifi/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java

sendP2pStateChangedBroadcast

private void sendP2pStateChangedBroadcast(boolean enabled) {
	final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
	intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
	if (enabled) {
		intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE,
				WifiP2pManager.WIFI_P2P_STATE_ENABLED);
	} else {
		intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE,
				WifiP2pManager.WIFI_P2P_STATE_DISABLED);
	}
	mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}

        这里就是 APP 接收广播的发送位置,根据 P2P 的可用状态发送对应的广播,继续寻找发送广播的调用。

checkAndSendP2pStateChangedBroadcast 

private void checkAndSendP2pStateChangedBroadcast() {
	Log.d(TAG, "Wifi enabled=" + mIsWifiEnabled + ", P2P Interface availability="
			+ mIsInterfaceAvailable);
	sendP2pStateChangedBroadcast(mIsWifiEnabled && mIsInterfaceAvailable);
}

        通过方法名可以大致判断,该函数用于检查 P2P 的状态并发送广播。继续向上查找该函数的调用位置。

P2pStateMachine

P2pStateMachine(String name, Looper looper, boolean p2pSupported) {
	super(name, looper);
	……
	if (p2pSupported) {
		// 注册wifi开/关广播
		mContext.registerReceiver(new BroadcastReceiver() {
			@Override
			public void onReceive(Context context, Intent intent) {
				int wifistate = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
						WifiManager.WIFI_STATE_UNKNOWN);
				if (wifistate == WifiManager.WIFI_STATE_ENABLED) {
					mIsWifiEnabled = true;
					checkAndReEnableP2p();
				} else {
					mIsWifiEnabled = false;
					// 禁用P2P
					sendMessage(DISABLE_P2P);
				}
				checkAndSendP2pStateChangedBroadcast();
			}
		}, new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION));

		// 注册接口可用性
		mWifiNative.registerInterfaceAvailableListener((boolean isAvailable) -> {
			mIsInterfaceAvailable = isAvailable;
			if (isAvailable) {
				checkAndReEnableP2p();
			}
			checkAndSendP2pStateChangedBroadcast();
		}, getHandler());
	}
}

        可以看到,检查并发送广播是在 P2pStateMachine 状态机中执行的,这里监听了 WiFi 的状态。并根据 WiFi 状态去调整 P2P 的可用状态。同时监听 P2P 的可用状态发送对应的广播通知,该监听很关键,后面还会提到。

二、P2P状态监听

        上面已经确定了 P2P 可用状态广播的发送流程,下一步根据 Log 信息进一步确定调用链路。 

D WifiP2pService: Wifi enabled=true, P2P Interface availability=true, Numberof clients=1
D WifiP2pService: Wifi enabled=true, P2P Interface availability=true
D WifiP2pService: Wifi enabled=true, P2P Interface availability=false

        第一行为 checkAndReEnableP2p() 方法中的打印,第二行为 checkAndSendP2pStateChangedBroadcast() 中的打印,而第三行又是一条 P2P 不可用的的广播通知,因此这里需要确定第三条信息的原因。

1、WifiP2pServiceImpl

checkAndReEnableP2p

// 检查并重新启用P2P
// P2P接口创建条件:
// a) 开启Wifi
// b) P2P接口可用
// c) 至少有一个客户端应用程序调用了initialize()
private void checkAndReEnableP2p() {
	Log.d(TAG, "Wifi enabled=" + mIsWifiEnabled + ", P2P Interface availability="
			+ mIsInterfaceAvailable + ", Number of clients=" + mDeathDataByBinder.size());
	if (mIsWifiEnabled && mIsInterfaceAvailable && !mDeathDataByBinder.isEmpty()) {
		sendMessage(ENABLE_P2P);
	}
}

        通过上面 Log 可知,WiFi 已经开启、P2P接口可用,并且 Numberof clients = 1
,因此这里会发送一个异步消息 sendMessage(ENABLE_P2P) 。

sendMessage

class P2pDisabledState extends State {
	……
	@Override
	public boolean processMessage(Message message) {
		if (DBG) logd(getName() + message.toString());
		switch (message.what) {
			case ENABLE_P2P:
				if (!mIsWifiEnabled) {
					Log.e(TAG, "Ignore P2P enable since wifi is disabled");
					break;
				}
                // 传入 Lambda 表达式
				mInterfaceName = mWifiNative.setupInterface((String ifaceName) -> {
					sendMessage(DISABLE_P2P);
				}, getHandler());
				if (mInterfaceName == null) {
					Log.e(TAG, "Failed to setup interface for P2P");
					break;
				}
				……
				break;
		}
		return HANDLED;
	}
}

        Log 中发现了 Failed to setup interface for P2P 内容,说明 mInterfaceName 为空,而调用 setupInterface() 接口时传入了 Lambda 表达式,会在接口设置完成时调用该表达式,即执行 sendMessage(DISABLE_P2P)。

class P2pEnabledState extends State {
    ……
    @Override
	public boolean processMessage(Message message) {
		if (DBG) logd(getName() + message.toString());
		switch (message.what) {
			……
			case DISABLE_P2P:
				if (mPeers.clear()) {
					sendPeersChangedBroadcast();
				}
				if (mGroups.clear()) sendP2pPersistentGroupsChangedBroadcast();
				mWifiMonitor.stopMonitoring(mInterfaceName);
				mWifiNative.teardownInterface();
				transitionTo(mP2pDisablingState);
				break;
            ……
        }
    }
}

2、WifiP2pNative

源码位置:/frameworks/opt/net/wifi/service/java/com/android/server/wifi/p2p/WifiP2pNative.java

setupInterface

public String setupInterface(
		@NonNull HalDeviceManager.InterfaceDestroyedListener destroyedListener,
		Handler handler) {
	Log.d(TAG, "Setup P2P interface");
	if (mIWifiP2pIface == null) {
		mInterfaceDestroyedListener = new InterfaceDestroyedListenerInternal(destroyedListener);
		mIWifiP2pIface = mHalDeviceManager.createP2pIface(mInterfaceDestroyedListener, handler);
		if (mIWifiP2pIface == null) {
			Log.e(TAG, "Failed to create P2p iface in HalDeviceManager");
			return null;
		}
		if (!waitForSupplicantConnection()) {
			Log.e(TAG, "Failed to connect to supplicant");
			teardownInterface();
			return null;
		}
		String ifaceName = HalDeviceManager.getName(mIWifiP2pIface);
		if (TextUtils.isEmpty(ifaceName)) {
			Log.e(TAG, "Failed to get p2p iface name");
			teardownInterface();
			return null;
		}
		if (!mSupplicantP2pIfaceHal.setupIface(ifaceName)) {
			Log.e(TAG, "Failed to setup P2p iface in supplicant");
			teardownInterface();
			return null;
		}
		Log.i(TAG, "P2P interface setup completed");
	}
	return HalDeviceManager.getName(mIWifiP2pIface);
}

        这里看到了关键了 Failed to setup P2p iface in supplicant,可以确定 setupIface() 返回了 false,并执行了 teardownInterface() 函数。

teardownInterface

public void teardownInterface() {
	Log.d(TAG, "Teardown P2P interface");
	if (mIWifiP2pIface != null) {
		String ifaceName = HalDeviceManager.getName(mIWifiP2pIface);
		mHalDeviceManager.removeIface(mIWifiP2pIface);
		mInterfaceDestroyedListener.teardownAndInvalidate(ifaceName);
		Log.i(TAG, "P2P interface teardown completed");
	}
}

        该函数用于移除 P2P 接口 ,HAL 层完成移除后会执行上面的回调,即执行 sendMessage(DISABLE_P2P),因此出现了上面的 Log 输出顺序。

        通过以上分析,其实可以得出结论,获取硬件 P2P 接口失败导致 P2P 功能不可用,因此需要底层调查获取接口。

三、P2P Hal层配置

        这里在简单看一下 P2P 硬件接口的 Hal 层调用。

1、SupplicantP2pIfaceHal

源码位置:/frameworks/opt/net/wifi/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceHal.java

setupIface

public boolean setupIface(@NonNull String ifaceName) {
	synchronized (mLock) {
		if (mISupplicantP2pIface != null) return false;
		ISupplicantIface ifaceHwBinder;
		if (isV1_1()) {
			ifaceHwBinder = addIfaceV1_1(ifaceName);
		} else {
			ifaceHwBinder = getIfaceV1_0(ifaceName);
		}
		if (ifaceHwBinder == null) {
			Log.e(TAG, "initSupplicantP2pIface got null iface");
			return false;
		}
		……
	}
}

        这里通过 initSupplicantP2pIface got null iface 日志确认了 ifaceHwBinder 获取失败。接着通过下面日志确认使用了 V1.1 的接口:

D SupplicantP2pIfaceHal: entering addInterface({.type=P2P, .name=p2p0})

addInterface

private ISupplicantIface addIfaceV1_1(@NonNull String ifaceName) {
	synchronized (mLock) {
		……
		try {
			android.hardware.wifi.supplicant.V1_1.ISupplicant supplicant_v1_1 =
					getSupplicantMockableV1_1();
			……
			supplicant_v1_1.addInterface(ifaceInfo,
					(SupplicantStatus status, ISupplicantIface iface) -> {
						if (status.code != SupplicantStatusCode.SUCCESS
								&& status.code != SupplicantStatusCode.FAILURE_IFACE_EXISTS) {
							Log.e(TAG, "Failed to get ISupplicantIface " + status.code);
							return;
						}
						supplicantIface.setResult(status, iface);
					});
		}
        ……
		return supplicantIface.getResult();
	}
}

        这里又通过下面的关键日志确定了 supplicant_v1_1.addInterface 调用失败,并返回了 code 值为 1。

E SupplicantP2pIfaceHal: Failed to get ISSupplicantIface 1

2、supplicant.cpp

源码位置:/external/wpa_supplicant_8/wpa_supplicant/hidl/1.1/supplicant.cpp

addInterface

Return Supplicant::addInterface(
    const IfaceInfo& iface_info, addInterface_cb _hidl_cb)
{
	return validateAndCall(
	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
	    &Supplicant::addInterfaceInternal, _hidl_cb, iface_info);
}

        这里调用了 validateAndCall() 函数。

3、hidl_return_util.h

源码位置:/external/wpa_supplicant_8/wpa_supplicant/hidl/1.1/hidl_return_util.h

validateAndCall

template 
Return validateAndCall(
    ObjT* obj, SupplicantStatusCode status_code_if_invalid, WorkFuncT&& work,
    const std::function& hidl_cb, Args&&... args)
{
	if (obj->isValid()) {
		hidl_cb((obj->*work)(std::forward(args)...));
	} else {
		hidl_cb({status_code_if_invalid, ""});
	}
	return Void();
}

        该函数是一个有多个不同参数的方法,这里仅展示其中的一个,更多内容可以直接查看源码。

你可能感兴趣的:(android,WiFi开发)