WiFi P2P 也就是前面介绍的 WLAN 直连,这里我们就来介绍一个开发中的真是案例——P2P重连失败问题。通过日志初步分析发现,WiFi P2P 的可用状态不断变化,导致功能不可用,这里就以该现象为例分析一个 WiFi P2P 可用状态广播的发送逻辑。
源码位置:/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";
这里仅仅是一个广播的定义,接下来需要找到该广播的发送位置。
源码位置:/frameworks/opt/net/wifi/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java
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 的可用状态发送对应的广播,继续寻找发送广播的调用。
private void checkAndSendP2pStateChangedBroadcast() {
Log.d(TAG, "Wifi enabled=" + mIsWifiEnabled + ", P2P Interface availability="
+ mIsInterfaceAvailable);
sendP2pStateChangedBroadcast(mIsWifiEnabled && mIsInterfaceAvailable);
}
通过方法名可以大致判断,该函数用于检查 P2P 的状态并发送广播。继续向上查找该函数的调用位置。
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 可用状态广播的发送流程,下一步根据 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 不可用的的广播通知,因此这里需要确定第三条信息的原因。
// 检查并重新启用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) 。
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;
……
}
}
}
源码位置:/frameworks/opt/net/wifi/service/java/com/android/server/wifi/p2p/WifiP2pNative.java
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() 函数。
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 层调用。
源码位置:/frameworks/opt/net/wifi/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceHal.java
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})
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
源码位置:/external/wpa_supplicant_8/wpa_supplicant/hidl/1.1/supplicant.cpp
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() 函数。
源码位置:/external/wpa_supplicant_8/wpa_supplicant/hidl/1.1/hidl_return_util.h
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();
}
该函数是一个有多个不同参数的方法,这里仅展示其中的一个,更多内容可以直接查看源码。