summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Quang Luong <qal@google.com> 2022-01-25 17:05:50 -0800
committer Quang Luong <qal@google.com> 2022-02-01 14:04:57 -0800
commit2bcd93e1840264aa495dca2832977f8fa92d9822 (patch)
treea1c7a04591e429394a682da1fcb65bbba56c67dd
parentc2726268ebf3df534fd76328cefe1155c3f44624 (diff)
Add timeout to P2P invitation received dialog
Implement a timeout for P2P invitation received dialog by creating a Intent action to cancel a specific dialogId, and posting a delayed runnable to cancel the dialog after the timeout. Bug: 203017876 Test: set the timeout overlay to 5000, verify adb shell cmd wifi launch-dialog-p2p-invitation-received <ssid> is cancelled after 5000 ms, atest WifiDialogManagerTest Change-Id: I9871bf4f4435769b06b1d4a03843a13fa94b4eb2
-rw-r--r--WifiDialog/src/com/android/wifi/dialog/WifiDialogActivity.java29
-rw-r--r--framework/java/android/net/wifi/WifiManager.java16
-rw-r--r--service/ServiceWifiResources/res/values/config.xml3
-rw-r--r--service/ServiceWifiResources/res/values/overlayable.xml1
-rw-r--r--service/java/com/android/server/wifi/WifiDialogManager.java204
-rw-r--r--service/java/com/android/server/wifi/WifiInjector.java2
-rw-r--r--service/tests/wifitests/src/com/android/server/wifi/WifiDialogManagerTest.java235
7 files changed, 381 insertions, 109 deletions
diff --git a/WifiDialog/src/com/android/wifi/dialog/WifiDialogActivity.java b/WifiDialog/src/com/android/wifi/dialog/WifiDialogActivity.java
index d02ae0a979..788f8eb8f2 100644
--- a/WifiDialog/src/com/android/wifi/dialog/WifiDialogActivity.java
+++ b/WifiDialog/src/com/android/wifi/dialog/WifiDialogActivity.java
@@ -60,7 +60,7 @@ public class WifiDialogActivity extends Activity {
private boolean mIsVerboseLoggingEnabled;
private int mGravity = Gravity.NO_GRAVITY;
- private @NonNull SparseArray<Intent> mIntentsPerId = new SparseArray<>();
+ private @NonNull SparseArray<Intent> mLaunchIntentsPerId = new SparseArray<>();
private @NonNull SparseArray<Dialog> mActiveDialogsPerId = new SparseArray<>();
// Broadcast receiver for listening to ACTION_CLOSE_SYSTEM_DIALOGS
@@ -154,7 +154,7 @@ public class WifiDialogActivity extends Activity {
}
continue;
}
- mIntentsPerId.put(dialogId, intent);
+ mLaunchIntentsPerId.put(dialogId, intent);
}
}
@@ -167,9 +167,9 @@ public class WifiDialogActivity extends Activity {
registerReceiver(
mCloseSystemDialogsReceiver, new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
ArraySet<Integer> invalidDialogIds = new ArraySet<>();
- for (int i = 0; i < mIntentsPerId.size(); i++) {
- int dialogId = mIntentsPerId.keyAt(i);
- if (!createAndShowDialogForIntent(dialogId, mIntentsPerId.get(dialogId))) {
+ for (int i = 0; i < mLaunchIntentsPerId.size(); i++) {
+ int dialogId = mLaunchIntentsPerId.keyAt(i);
+ if (!createAndShowDialogForIntent(dialogId, mLaunchIntentsPerId.get(dialogId))) {
invalidDialogIds.add(dialogId);
}
}
@@ -192,7 +192,12 @@ public class WifiDialogActivity extends Activity {
}
return;
}
- mIntentsPerId.put(dialogId, intent);
+ String action = intent.getAction();
+ if (WifiManager.ACTION_CANCEL_DIALOG.equals(action)) {
+ removeIntentAndPossiblyFinish(dialogId);
+ return;
+ }
+ mLaunchIntentsPerId.put(dialogId, intent);
if (!createAndShowDialogForIntent(dialogId, intent)) {
removeIntentAndPossiblyFinish(dialogId);
}
@@ -214,8 +219,8 @@ public class WifiDialogActivity extends Activity {
@Override
protected void onSaveInstanceState(Bundle outState) {
ArrayList<Intent> intentList = new ArrayList<>();
- for (int i = 0; i < mIntentsPerId.size(); i++) {
- intentList.add(mIntentsPerId.valueAt(i));
+ for (int i = 0; i < mLaunchIntentsPerId.size(); i++) {
+ intentList.add(mLaunchIntentsPerId.valueAt(i));
}
outState.putParcelableArrayList(KEY_DIALOG_INTENTS, intentList);
super.onSaveInstanceState(outState);
@@ -226,7 +231,7 @@ public class WifiDialogActivity extends Activity {
* there are no dialogs left to show.
*/
private void removeIntentAndPossiblyFinish(int dialogId) {
- mIntentsPerId.remove(dialogId);
+ mLaunchIntentsPerId.remove(dialogId);
Dialog dialog = mActiveDialogsPerId.get(dialogId);
mActiveDialogsPerId.remove(dialogId);
if (dialog != null && dialog.isShowing()) {
@@ -235,7 +240,7 @@ public class WifiDialogActivity extends Activity {
if (mIsVerboseLoggingEnabled) {
Log.v(TAG, "Dialog id " + dialogId + " removed.");
}
- if (mIntentsPerId.size() == 0) {
+ if (mLaunchIntentsPerId.size() == 0) {
if (mIsVerboseLoggingEnabled) {
Log.v(TAG, "No dialogs left to show, finishing.");
}
@@ -257,6 +262,10 @@ public class WifiDialogActivity extends Activity {
* Returns {@code true} if the dialog was successfully created, {@code false} otherwise.
*/
private @Nullable boolean createAndShowDialogForIntent(int dialogId, @NonNull Intent intent) {
+ String action = intent.getAction();
+ if (!WifiManager.ACTION_LAUNCH_DIALOG.equals(action)) {
+ return false;
+ }
Dialog dialog = null;
int dialogType = intent.getIntExtra(
WifiManager.EXTRA_DIALOG_TYPE, WifiManager.DIALOG_TYPE_UNKNOWN);
diff --git a/framework/java/android/net/wifi/WifiManager.java b/framework/java/android/net/wifi/WifiManager.java
index 65a6d2c7eb..5594efacc3 100644
--- a/framework/java/android/net/wifi/WifiManager.java
+++ b/framework/java/android/net/wifi/WifiManager.java
@@ -9074,6 +9074,22 @@ public class WifiManager {
}
/**
+ * Intent action to launch a dialog from the WifiDialog app.
+ * Must include EXTRA_DIALOG_ID, EXTRA_DIALOG_TYPE, and appropriate extras for the dialog type.
+ * @hide
+ */
+ public static final String ACTION_LAUNCH_DIALOG =
+ "android.net.wifi.action.LAUNCH_DIALOG";
+
+ /**
+ * Intent action to cancel an existing dialog from the WifiDialog app.
+ * Must include EXTRA_DIALOG_ID.
+ * @hide
+ */
+ public static final String ACTION_CANCEL_DIALOG =
+ "android.net.wifi.action.CANCEL_DIALOG";
+
+ /**
* Unknown DialogType.
* @hide
*/
diff --git a/service/ServiceWifiResources/res/values/config.xml b/service/ServiceWifiResources/res/values/config.xml
index c5a897c3b5..669a9d978c 100644
--- a/service/ServiceWifiResources/res/values/config.xml
+++ b/service/ServiceWifiResources/res/values/config.xml
@@ -779,4 +779,7 @@
<!-- Indicate the gravity of Wifi dialogs. Must be an integer corresponding to a
android.view.Gravity.GravityFlags constant. Default = Gravity.NO_GRAVITY (0) -->
<integer translatable="false" name="config_wifiDialogGravity">0</integer>
+ <!-- Indicate the time in milliseconds to wait before auto-cancelling a P2P invitation received
+ dialog that the user has not responded to. A value of 0 indicates no timeout. -->
+ <integer translatable="false" name="config_p2pInvitationReceivedDialogTimeoutMs">0</integer>
</resources>
diff --git a/service/ServiceWifiResources/res/values/overlayable.xml b/service/ServiceWifiResources/res/values/overlayable.xml
index 342bc88ccc..67d97e33a2 100644
--- a/service/ServiceWifiResources/res/values/overlayable.xml
+++ b/service/ServiceWifiResources/res/values/overlayable.xml
@@ -218,6 +218,7 @@
<item type="integer" name="config_wifiConfigurationRestoreNetworksBatchNum" />
<item type="bool" name="config_wifiSoftapOweTransitionSupported" />
<item type="integer" name="config_wifiDialogGravity" />
+ <item type="integer" name="config_p2pInvitationReceivedDialogTimeoutMs"/>
<!-- Params from config.xml that can be overlayed -->
<!-- Params from strings.xml that can be overlayed -->
diff --git a/service/java/com/android/server/wifi/WifiDialogManager.java b/service/java/com/android/server/wifi/WifiDialogManager.java
index a00d47f0b5..d8b97f6a52 100644
--- a/service/java/com/android/server/wifi/WifiDialogManager.java
+++ b/service/java/com/android/server/wifi/WifiDialogManager.java
@@ -17,7 +17,6 @@
package com.android.server.wifi;
import android.content.Intent;
-import android.content.pm.PackageManager;
import android.net.wifi.WifiContext;
import android.net.wifi.WifiManager;
import android.os.UserHandle;
@@ -25,34 +24,39 @@ import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
+import androidx.annotation.AnyThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.wifi.resources.R;
import java.util.Set;
/**
* Class to manage launching dialogs via WifiDialog and returning the user reply.
+ * This class is thread-safe via the use of a single WifiThreadRunner.
*/
public class WifiDialogManager {
private static final String TAG = "WifiDialogManager";
- public static final String WIFI_DIALOG_ACTIVITY_CLASSNAME =
+ @VisibleForTesting static final String WIFI_DIALOG_ACTIVITY_CLASSNAME =
"com.android.wifi.dialog.WifiDialogActivity";
private boolean mVerboseLoggingEnabled;
private int mNextDialogId = 0;
- private Set<Integer> mCurrentDialogIds = new ArraySet<>();
- private @NonNull SparseArray<P2pInvitationReceivedDialogCallback>
- mP2pInvitationReceivedDialogCallbacks = new SparseArray<>();
- private @NonNull SparseArray<WifiThreadRunner>
- mP2pInvitationReceivedDialogThreadRunners = new SparseArray<>();
+ private final Set<Integer> mCurrentDialogIds = new ArraySet<>();
+ private final @NonNull SparseArray<P2pInvitationReceivedDialogHandle>
+ mP2pInvitationReceivedDialogsHandles = new SparseArray<>();
- @NonNull WifiContext mContext;
- @NonNull PackageManager mPackageManager;
+ private final @NonNull WifiContext mContext;
+ private final @NonNull WifiThreadRunner mWifiThreadRunner;
- public WifiDialogManager(WifiContext context) {
+ public WifiDialogManager(
+ @NonNull WifiContext context,
+ @NonNull WifiThreadRunner wifiThreadRunner) {
mContext = context;
- mPackageManager = context.getPackageManager();
+ mWifiThreadRunner = wifiThreadRunner;
}
/**
@@ -70,6 +74,25 @@ public class WifiDialogManager {
}
/**
+ * Sends an Intent to cancel a launched dialog with the given ID.
+ */
+ private void sendCancelIntentForDialogId(int dialogId) {
+ if (!mCurrentDialogIds.contains(dialogId)) {
+ Log.e(TAG, "Tried to cancel dialog but could not find id " + dialogId);
+ return;
+ }
+ String wifiDialogApkPkgName = mContext.getWifiDialogApkPkgName();
+ if (wifiDialogApkPkgName == null) {
+ Log.e(TAG, "Tried to cancel dialog but could not find a WifiDialog apk package name!");
+ return;
+ }
+ Intent intent = new Intent(WifiManager.ACTION_CANCEL_DIALOG);
+ intent.putExtra(WifiManager.EXTRA_DIALOG_ID, dialogId);
+ intent.setClassName(mContext.getWifiDialogApkPkgName(), WIFI_DIALOG_ACTIVITY_CLASSNAME);
+ mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ }
+
+ /**
* Callback for receiving P2P Invitation Received dialog responses.
*/
public interface P2pInvitationReceivedDialogCallback {
@@ -86,15 +109,74 @@ public class WifiDialogManager {
}
/**
+ * Handles launching and callback response for a single P2P Invitation Received dialog
+ */
+ private class P2pInvitationReceivedDialogHandle {
+ private int mDialogId;
+ private Intent mIntent;
+ private @NonNull P2pInvitationReceivedDialogCallback mCallback;
+ private @NonNull WifiThreadRunner mCallbackThreadRunner;
+ private @Nullable Runnable mTimeoutRunnable;
+
+ P2pInvitationReceivedDialogHandle(
+ final int dialogId,
+ final @NonNull String deviceName,
+ final boolean isPinRequested,
+ @Nullable String displayPin,
+ @NonNull P2pInvitationReceivedDialogCallback callback,
+ @NonNull WifiThreadRunner callbackThreadRunner) {
+ mDialogId = dialogId;
+ Intent intent = new Intent(WifiManager.ACTION_LAUNCH_DIALOG);
+ intent.putExtra(WifiManager.EXTRA_DIALOG_TYPE,
+ WifiManager.DIALOG_TYPE_P2P_INVITATION_RECEIVED);
+ intent.putExtra(WifiManager.EXTRA_DIALOG_ID, dialogId);
+ intent.putExtra(WifiManager.EXTRA_P2P_DEVICE_NAME, deviceName);
+ intent.putExtra(WifiManager.EXTRA_P2P_PIN_REQUESTED, isPinRequested);
+ intent.putExtra(WifiManager.EXTRA_P2P_DISPLAY_PIN, displayPin);
+ intent.setClassName(mContext.getWifiDialogApkPkgName(), WIFI_DIALOG_ACTIVITY_CLASSNAME);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mIntent = intent;
+ mCallback = callback;
+ mCallbackThreadRunner = callbackThreadRunner;
+ }
+
+ void launchDialog() {
+ mContext.startActivityAsUser(mIntent, UserHandle.CURRENT);
+ int timeoutMs = mContext.getResources()
+ .getInteger(R.integer.config_p2pInvitationReceivedDialogTimeoutMs);
+ if (timeoutMs > 0) {
+ mTimeoutRunnable = () -> sendCancelIntentForDialogId(mDialogId);
+ mWifiThreadRunner.postDelayed(mTimeoutRunnable, timeoutMs);
+ }
+ }
+
+ void onAccepted(@Nullable String optionalPin) {
+ mCallbackThreadRunner.post(() -> mCallback.onAccepted(optionalPin));
+ if (mTimeoutRunnable != null) {
+ mWifiThreadRunner.removeCallbacks(mTimeoutRunnable);
+ mTimeoutRunnable = null;
+ }
+ }
+
+ void onDeclined() {
+ mCallbackThreadRunner.post(() -> mCallback.onDeclined());
+ if (mTimeoutRunnable != null) {
+ mWifiThreadRunner.removeCallbacks(mTimeoutRunnable);
+ mTimeoutRunnable = null;
+ }
+ }
+ }
+
+ /**
* Launches a P2P Invitation Received dialog.
* @param deviceName Name of the device sending the invitation.
* @param isPinRequested True if a PIN was requested and a PIN input UI should be shown.
* @param displayPin Display PIN, or {@code null} if no PIN should be displayed
* @param callback Callback to receive the dialog response.
* @param threadRunner WifiThreadRunner to run the callback on.
- * @return id of the launched dialog, or {@code -1} if the dialog could not be created.
*/
- public int launchP2pInvitationReceivedDialog(
+ @AnyThread
+ public void launchP2pInvitationReceivedDialog(
String deviceName,
boolean isPinRequested,
@Nullable String displayPin,
@@ -102,32 +184,38 @@ public class WifiDialogManager {
@NonNull WifiThreadRunner threadRunner) {
if (callback == null) {
Log.e(TAG, "Cannot launch a P2P Invitation Received dialog with null callback!");
- return -1;
+ return;
}
- if (callback == null) {
- Log.e(TAG, "Cannot launch a P2P Invitation Received dialog with null handler!");
- return -1;
+ if (threadRunner == null) {
+ Log.e(TAG, "Cannot launch a P2P Invitation Received dialog with null thread runner!");
+ return;
}
+ mWifiThreadRunner.post(() ->
+ launchP2pInvitationReceivedDialogInternal(
+ deviceName,
+ isPinRequested,
+ displayPin,
+ callback,
+ threadRunner));
+ }
+
+ private void launchP2pInvitationReceivedDialogInternal(
+ String deviceName,
+ boolean isPinRequested,
+ @Nullable String displayPin,
+ @NonNull P2pInvitationReceivedDialogCallback callback,
+ @NonNull WifiThreadRunner threadRunner) {
int dialogId = getNextDialogId();
mCurrentDialogIds.add(dialogId);
- Intent intent = new Intent();
- String wifiDialogApkPkgName = mContext.getWifiDialogApkPkgName();
- if (wifiDialogApkPkgName == null) {
- Log.e(TAG, "Tried to launch P2P Invitation Received dialog but could not find a"
- + " WifiDialog apk package name!");
- return -1;
- }
- intent.setClassName(wifiDialogApkPkgName, WIFI_DIALOG_ACTIVITY_CLASSNAME);
- intent.putExtra(WifiManager.EXTRA_DIALOG_TYPE,
- WifiManager.DIALOG_TYPE_P2P_INVITATION_RECEIVED);
- intent.putExtra(WifiManager.EXTRA_DIALOG_ID, dialogId);
- intent.putExtra(WifiManager.EXTRA_P2P_DEVICE_NAME, deviceName);
- intent.putExtra(WifiManager.EXTRA_P2P_PIN_REQUESTED, isPinRequested);
- intent.putExtra(WifiManager.EXTRA_P2P_DISPLAY_PIN, displayPin);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mP2pInvitationReceivedDialogCallbacks.put(dialogId, callback);
- mP2pInvitationReceivedDialogThreadRunners.put(dialogId, threadRunner);
- mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ P2pInvitationReceivedDialogHandle dialogHandle = new P2pInvitationReceivedDialogHandle(
+ dialogId,
+ deviceName,
+ isPinRequested,
+ displayPin,
+ callback,
+ threadRunner);
+ mP2pInvitationReceivedDialogsHandles.put(dialogId, dialogHandle);
+ dialogHandle.launchDialog();
if (mVerboseLoggingEnabled) {
Log.v(TAG, "Launching P2P Invitation Received dialog."
+ " id=" + dialogId
@@ -136,7 +224,6 @@ public class WifiDialogManager {
+ " displayPin=" + displayPin
+ " callback=" + callback);
}
- return dialogId;
}
/**
@@ -146,8 +233,21 @@ public class WifiDialogManager {
* @param optionalPin PIN of the reply, or {@code null} if none was supplied.
* @hide
*/
+ @AnyThread
public void replyToP2pInvitationReceivedDialog(
- final int dialogId, final boolean accepted, final @Nullable String optionalPin) {
+ int dialogId,
+ boolean accepted,
+ @Nullable String optionalPin) {
+ mWifiThreadRunner.post(() -> replyToP2pInvitationReceivedDialogInternal(
+ dialogId,
+ accepted,
+ optionalPin));
+ }
+
+ private void replyToP2pInvitationReceivedDialogInternal(
+ int dialogId,
+ boolean accepted,
+ @Nullable String optionalPin) {
if (mVerboseLoggingEnabled) {
Log.i(TAG, "Response received for P2P Invitation Received dialog."
+ " id=" + dialogId
@@ -155,32 +255,20 @@ public class WifiDialogManager {
+ " pin=" + optionalPin);
}
mCurrentDialogIds.remove(dialogId);
- final P2pInvitationReceivedDialogCallback callback =
- mP2pInvitationReceivedDialogCallbacks.get(dialogId);
- mP2pInvitationReceivedDialogCallbacks.remove(dialogId);
- if (callback == null) {
+ P2pInvitationReceivedDialogHandle dialogHandle =
+ mP2pInvitationReceivedDialogsHandles.get(dialogId);
+ mP2pInvitationReceivedDialogsHandles.remove(dialogId);
+ if (dialogHandle == null) {
if (mVerboseLoggingEnabled) {
- Log.w(TAG, "No matching callback for P2P Invitation Received dialog"
+ Log.w(TAG, "No matching dialog handle for P2P Invitation Received dialog"
+ " id=" + dialogId);
}
return;
}
- final WifiThreadRunner threadRunner =
- mP2pInvitationReceivedDialogThreadRunners.get(dialogId);
- mP2pInvitationReceivedDialogThreadRunners.remove(dialogId);
- if (threadRunner == null) {
- if (mVerboseLoggingEnabled) {
- Log.w(TAG, "No matching handler for P2P Invitation Received dialog"
- + " id=" + dialogId);
- }
- return;
+ if (accepted) {
+ dialogHandle.onAccepted(optionalPin);
+ } else {
+ dialogHandle.onDeclined();
}
- threadRunner.post(() -> {
- if (accepted) {
- callback.onAccepted(optionalPin);
- } else {
- callback.onDeclined();
- }
- });
}
}
diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java
index 21be3be9e0..c208a1f3b8 100644
--- a/service/java/com/android/server/wifi/WifiInjector.java
+++ b/service/java/com/android/server/wifi/WifiInjector.java
@@ -531,7 +531,7 @@ public class WifiInjector {
mSimRequiredNotifier = new SimRequiredNotifier(mContext, mFrameworkFacade,
mWifiNotificationManager);
mLastCallerInfoManager = new LastCallerInfoManager();
- mWifiDialogManager = new WifiDialogManager(mContext);
+ mWifiDialogManager = new WifiDialogManager(mContext, mWifiThreadRunner);
}
/**
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiDialogManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiDialogManagerTest.java
index 3401de1171..75ae632ed4 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiDialogManagerTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiDialogManagerTest.java
@@ -16,19 +16,34 @@
package com.android.server.wifi;
+import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.res.Resources;
import android.net.wifi.WifiContext;
+import android.net.wifi.WifiManager;
+import android.os.UserHandle;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
+import com.android.wifi.resources.R;
+
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -37,18 +52,93 @@ import org.mockito.MockitoAnnotations;
*/
@SmallTest
public class WifiDialogManagerTest extends WifiBaseTest {
+ private static final int TIMEOUT_MILLIS = 30_000;
+ private static final String WIFI_DIALOG_APK_PKG_NAME = "WifiDialogApkPkgName";
@Mock WifiContext mWifiContext;
- @Mock WifiThreadRunner mCallbackThreadRunner;
+ @Mock Resources mResources;
+ @Mock WifiThreadRunner mWifiDialogManagerThreadRunner;
+ WifiDialogManager mWifiDialogManager;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- when(mWifiContext.getWifiDialogApkPkgName()).thenReturn("WifiDialogApkPkgName");
- when(mCallbackThreadRunner.post(any(Runnable.class))).then(i -> {
- ((Runnable) i.getArguments()[0]).run();
- return true;
- });
+ when(mWifiContext.getWifiDialogApkPkgName()).thenReturn(WIFI_DIALOG_APK_PKG_NAME);
+ when(mWifiContext.getResources()).thenReturn(mResources);
+ mWifiDialogManager = new WifiDialogManager(mWifiContext, mWifiDialogManagerThreadRunner);
+ }
+
+ /**
+ * Helper method to call launchP2pInvitationReceivedDialog synchronously.
+ * @return the launched dialog ID.
+ */
+ private int launchP2pInvitationReceivedDialogSynchronous(
+ String deviceName,
+ boolean isPinRequested,
+ @Nullable String displayPin,
+ @NonNull WifiDialogManager.P2pInvitationReceivedDialogCallback callback,
+ @NonNull WifiThreadRunner callbackThreadRunner,
+ int timeoutMs) {
+ when(mResources.getInteger(R.integer.config_p2pInvitationReceivedDialogTimeoutMs))
+ .thenReturn(timeoutMs);
+ mWifiDialogManager.launchP2pInvitationReceivedDialog(
+ deviceName, isPinRequested, displayPin, callback, callbackThreadRunner);
+
+ // Synchronously run the posted launchP2pInvitationReceivedDialogInternal runnable.
+ ArgumentCaptor<Runnable> runnableArgumentCaptor = ArgumentCaptor.forClass(Runnable.class);
+ verify(mWifiDialogManagerThreadRunner, atLeastOnce())
+ .post(runnableArgumentCaptor.capture());
+ runnableArgumentCaptor.getValue().run();
+
+ // Verify the launch Intent
+ ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mWifiContext, atLeastOnce())
+ .startActivityAsUser(intentArgumentCaptor.capture(), eq(UserHandle.CURRENT));
+ Intent launchIntent = intentArgumentCaptor.getValue();
+ assertThat(launchIntent.getAction()).isEqualTo(WifiManager.ACTION_LAUNCH_DIALOG);
+ ComponentName component = launchIntent.getComponent();
+ assertThat(component.getPackageName()).isEqualTo(WIFI_DIALOG_APK_PKG_NAME);
+ assertThat(component.getClassName())
+ .isEqualTo(WifiDialogManager.WIFI_DIALOG_ACTIVITY_CLASSNAME);
+ assertThat(launchIntent.hasExtra(WifiManager.EXTRA_DIALOG_ID)).isTrue();
+ int dialogId = launchIntent.getIntExtra(WifiManager.EXTRA_DIALOG_ID, -1);
+ assertThat(dialogId).isNotEqualTo(-1);
+ assertThat(launchIntent.hasExtra(WifiManager.EXTRA_DIALOG_TYPE)).isTrue();
+ assertThat(launchIntent.getIntExtra(WifiManager.EXTRA_DIALOG_TYPE,
+ WifiManager.DIALOG_TYPE_UNKNOWN))
+ .isEqualTo(WifiManager.DIALOG_TYPE_P2P_INVITATION_RECEIVED);
+ assertThat(launchIntent.hasExtra(WifiManager.EXTRA_P2P_DEVICE_NAME)).isTrue();
+ assertThat(launchIntent.getStringExtra(WifiManager.EXTRA_P2P_DEVICE_NAME))
+ .isEqualTo(deviceName);
+ assertThat(launchIntent.hasExtra(WifiManager.EXTRA_P2P_PIN_REQUESTED)).isTrue();
+ assertThat(launchIntent.getBooleanExtra(WifiManager.EXTRA_P2P_PIN_REQUESTED, false))
+ .isEqualTo(isPinRequested);
+ assertThat(launchIntent.hasExtra(WifiManager.EXTRA_P2P_DISPLAY_PIN)).isTrue();
+ assertThat(launchIntent.getStringExtra(WifiManager.EXTRA_P2P_DISPLAY_PIN))
+ .isEqualTo(displayPin);
+ return dialogId;
+ }
+
+ /**
+ * Helper method to call replyToP2pInvitationReceivedDialog synchronously.
+ */
+ public void replyToP2pInvitationReceivedDialogSynchronous(
+ int dialogId,
+ boolean accepted,
+ @Nullable String optionalPin) {
+ mWifiDialogManager.replyToP2pInvitationReceivedDialog(dialogId, accepted, optionalPin);
+
+ // Synchronously run the posted replyToP2pInvitationReceivedDialogInternal runnable.
+ ArgumentCaptor<Runnable> runnableArgumentCaptor = ArgumentCaptor.forClass(Runnable.class);
+ verify(mWifiDialogManagerThreadRunner, atLeastOnce())
+ .post(runnableArgumentCaptor.capture());
+ runnableArgumentCaptor.getValue().run();
+ }
+
+ private void dispatchMockWifiThreadRunner(WifiThreadRunner wifiThreadRunner) {
+ ArgumentCaptor<Runnable> runnableArgumentCaptor = ArgumentCaptor.forClass(Runnable.class);
+ verify(wifiThreadRunner, atLeastOnce()).post(runnableArgumentCaptor.capture());
+ runnableArgumentCaptor.getValue().run();
}
/**
@@ -57,61 +147,69 @@ public class WifiDialogManagerTest extends WifiBaseTest {
*/
@Test
public void testP2pInvitationReceivedDialog_launchAndResponse_notifiesCallback() {
- WifiDialogManager wifiDialogManager = new WifiDialogManager(mWifiContext);
WifiDialogManager.P2pInvitationReceivedDialogCallback callback =
mock(WifiDialogManager.P2pInvitationReceivedDialogCallback.class);
+ WifiThreadRunner callbackThreadRunner = mock(WifiThreadRunner.class);
int dialogId;
// Accept without PIN
- dialogId = wifiDialogManager.launchP2pInvitationReceivedDialog(
- "deviceName", false, null, callback, mCallbackThreadRunner);
- wifiDialogManager.replyToP2pInvitationReceivedDialog(dialogId, true, null);
+ dialogId = launchP2pInvitationReceivedDialogSynchronous(
+ "deviceName", false, null, callback, callbackThreadRunner, 0);
+ replyToP2pInvitationReceivedDialogSynchronous(dialogId, true, null);
+ dispatchMockWifiThreadRunner(callbackThreadRunner);
verify(callback, times(1)).onAccepted(null);
// Callback should be removed from callback list, so a second notification should be ignored
- wifiDialogManager.replyToP2pInvitationReceivedDialog(dialogId, true, "012345");
+ replyToP2pInvitationReceivedDialogSynchronous(dialogId, true, "012345");
verify(callback, times(0)).onAccepted("012345");
// Accept with PIN
- dialogId = wifiDialogManager.launchP2pInvitationReceivedDialog(
- "deviceName", true, null, callback, mCallbackThreadRunner);
- wifiDialogManager.replyToP2pInvitationReceivedDialog(dialogId, true, "012345");
+ dialogId = launchP2pInvitationReceivedDialogSynchronous(
+ "deviceName", true, null, callback, callbackThreadRunner, 0);
+ replyToP2pInvitationReceivedDialogSynchronous(dialogId, true, "012345");
+ dispatchMockWifiThreadRunner(callbackThreadRunner);
verify(callback, times(1)).onAccepted("012345");
// Accept with PIN but PIN was not requested
- dialogId = wifiDialogManager.launchP2pInvitationReceivedDialog(
- "deviceName", false, null, callback, mCallbackThreadRunner);
- wifiDialogManager.replyToP2pInvitationReceivedDialog(dialogId, true, "012345");
+ dialogId = launchP2pInvitationReceivedDialogSynchronous(
+ "deviceName", false, null, callback, callbackThreadRunner, 0);
+ replyToP2pInvitationReceivedDialogSynchronous(dialogId, true, "012345");
+ dispatchMockWifiThreadRunner(callbackThreadRunner);
verify(callback, times(2)).onAccepted("012345");
// Accept without PIN but PIN was requested
- dialogId = wifiDialogManager.launchP2pInvitationReceivedDialog(
- "deviceName", true, null, callback, mCallbackThreadRunner);
- wifiDialogManager.replyToP2pInvitationReceivedDialog(dialogId, true, null);
+ dialogId = launchP2pInvitationReceivedDialogSynchronous(
+ "deviceName", true, null, callback, callbackThreadRunner, 0);
+ replyToP2pInvitationReceivedDialogSynchronous(dialogId, true, null);
+ dispatchMockWifiThreadRunner(callbackThreadRunner);
verify(callback, times(2)).onAccepted(null);
// Decline without PIN
- dialogId = wifiDialogManager.launchP2pInvitationReceivedDialog(
- "deviceName", false, null, callback, mCallbackThreadRunner);
- wifiDialogManager.replyToP2pInvitationReceivedDialog(dialogId, false, null);
+ dialogId = launchP2pInvitationReceivedDialogSynchronous(
+ "deviceName", false, null, callback, callbackThreadRunner, 0);
+ replyToP2pInvitationReceivedDialogSynchronous(dialogId, false, null);
+ dispatchMockWifiThreadRunner(callbackThreadRunner);
verify(callback, times(1)).onDeclined();
// Decline with PIN
- dialogId = wifiDialogManager.launchP2pInvitationReceivedDialog(
- "deviceName", true, null, callback, mCallbackThreadRunner);
- wifiDialogManager.replyToP2pInvitationReceivedDialog(dialogId, false, "012345");
+ dialogId = launchP2pInvitationReceivedDialogSynchronous(
+ "deviceName", true, null, callback, callbackThreadRunner, 0);
+ replyToP2pInvitationReceivedDialogSynchronous(dialogId, false, "012345");
+ dispatchMockWifiThreadRunner(callbackThreadRunner);
verify(callback, times(2)).onDeclined();
// Decline with PIN but PIN was not requested
- dialogId = wifiDialogManager.launchP2pInvitationReceivedDialog(
- "deviceName", false, null, callback, mCallbackThreadRunner);
- wifiDialogManager.replyToP2pInvitationReceivedDialog(dialogId, false, "012345");
+ dialogId = launchP2pInvitationReceivedDialogSynchronous(
+ "deviceName", false, null, callback, callbackThreadRunner, 0);
+ replyToP2pInvitationReceivedDialogSynchronous(dialogId, false, "012345");
+ dispatchMockWifiThreadRunner(callbackThreadRunner);
verify(callback, times(3)).onDeclined();
// Decline without PIN but PIN was requested
- dialogId = wifiDialogManager.launchP2pInvitationReceivedDialog(
- "deviceName", true, null, callback, mCallbackThreadRunner);
- wifiDialogManager.replyToP2pInvitationReceivedDialog(dialogId, false, null);
+ dialogId = launchP2pInvitationReceivedDialogSynchronous(
+ "deviceName", true, null, callback, callbackThreadRunner, 0);
+ replyToP2pInvitationReceivedDialogSynchronous(dialogId, false, null);
+ dispatchMockWifiThreadRunner(callbackThreadRunner);
verify(callback, times(4)).onDeclined();
}
@@ -120,28 +218,85 @@ public class WifiDialogManagerTest extends WifiBaseTest {
*/
@Test
public void testP2pInvitationReceivedDialog_multipleDialogs_responseMatchedToCorrectCallback() {
- WifiDialogManager wifiDialogManager = new WifiDialogManager(mWifiContext);
-
// Launch Dialog1
WifiDialogManager.P2pInvitationReceivedDialogCallback callback1 = mock(
WifiDialogManager.P2pInvitationReceivedDialogCallback.class);
- int dialogId1 = wifiDialogManager.launchP2pInvitationReceivedDialog(
- "deviceName", false, null, callback1, mCallbackThreadRunner);
+ WifiThreadRunner callbackThreadRunner = mock(WifiThreadRunner.class);
+ int dialogId1 = launchP2pInvitationReceivedDialogSynchronous(
+ "deviceName", false, null, callback1, callbackThreadRunner, 0);
// Launch Dialog2
WifiDialogManager.P2pInvitationReceivedDialogCallback callback2 = mock(
WifiDialogManager.P2pInvitationReceivedDialogCallback.class);
- int dialogId2 = wifiDialogManager.launchP2pInvitationReceivedDialog(
- "deviceName", false, null, callback2, mCallbackThreadRunner);
+ int dialogId2 = launchP2pInvitationReceivedDialogSynchronous(
+ "deviceName", false, null, callback2, callbackThreadRunner, 0);
// callback1 notified
- wifiDialogManager.replyToP2pInvitationReceivedDialog(dialogId1, true, null);
+ replyToP2pInvitationReceivedDialogSynchronous(dialogId1, true, null);
+ dispatchMockWifiThreadRunner(callbackThreadRunner);
verify(callback1, times(1)).onAccepted(null);
verify(callback2, times(0)).onAccepted(null);
// callback2 notified
- wifiDialogManager.replyToP2pInvitationReceivedDialog(dialogId2, true, null);
+ replyToP2pInvitationReceivedDialogSynchronous(dialogId2, true, null);
+ dispatchMockWifiThreadRunner(callbackThreadRunner);
verify(callback1, times(1)).onAccepted(null);
verify(callback2, times(1)).onAccepted(null);
}
+
+ /**
+ * Verifies that a P2P Invitation Received dialog is cancelled after the specified timeout
+ */
+ @Test
+ public void testP2pInvitationReceivedDialog_timeout_cancelsDialog() {
+ // Launch Dialog without timeout.
+ WifiDialogManager.P2pInvitationReceivedDialogCallback callback = mock(
+ WifiDialogManager.P2pInvitationReceivedDialogCallback.class);
+ WifiThreadRunner callbackThreadRunner = mock(WifiThreadRunner.class);
+ launchP2pInvitationReceivedDialogSynchronous(
+ "deviceName", false, null, callback, callbackThreadRunner, 0);
+
+ // Verify cancel runnable wasn't posted.
+ verify(mWifiDialogManagerThreadRunner, never()).postDelayed(any(Runnable.class), anyInt());
+
+ // Launch Dialog with timeout
+ callback = mock(WifiDialogManager.P2pInvitationReceivedDialogCallback.class);
+ int dialogId = launchP2pInvitationReceivedDialogSynchronous(
+ "deviceName", false, null, callback, callbackThreadRunner, TIMEOUT_MILLIS);
+
+ // Verify the timeout runnable was posted and run it.
+ ArgumentCaptor<Runnable> runnableArgumentCaptor = ArgumentCaptor.forClass(Runnable.class);
+ verify(mWifiDialogManagerThreadRunner, times(1))
+ .postDelayed(runnableArgumentCaptor.capture(), eq((long) TIMEOUT_MILLIS));
+ runnableArgumentCaptor.getValue().run();
+
+ // Verify that a cancel Intent was sent.
+ ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mWifiContext, atLeastOnce())
+ .startActivityAsUser(intentArgumentCaptor.capture(), eq(UserHandle.CURRENT));
+ Intent cancelIntent = intentArgumentCaptor.getValue();
+ assertThat(cancelIntent.getAction()).isEqualTo(WifiManager.ACTION_CANCEL_DIALOG);
+ ComponentName component = cancelIntent.getComponent();
+ assertThat(component.getPackageName()).isEqualTo(WIFI_DIALOG_APK_PKG_NAME);
+ assertThat(component.getClassName())
+ .isEqualTo(WifiDialogManager.WIFI_DIALOG_ACTIVITY_CLASSNAME);
+ assertThat(cancelIntent.hasExtra(WifiManager.EXTRA_DIALOG_ID)).isTrue();
+ assertThat(cancelIntent.getIntExtra(WifiManager.EXTRA_DIALOG_ID, -1)).isEqualTo(dialogId);
+
+ // Launch Dialog without timeout
+ callback = mock(WifiDialogManager.P2pInvitationReceivedDialogCallback.class);
+ dialogId = launchP2pInvitationReceivedDialogSynchronous(
+ "deviceName", false, null, callback, callbackThreadRunner, TIMEOUT_MILLIS);
+
+ // Reply before the timeout is over
+ replyToP2pInvitationReceivedDialogSynchronous(dialogId, true, null);
+ dispatchMockWifiThreadRunner(callbackThreadRunner);
+
+ // Verify callback was replied to, and the cancel runnable was posted but then removed.
+ verify(callback).onAccepted(null);
+ verify(callback, never()).onDeclined();
+ verify(mWifiDialogManagerThreadRunner, times(2))
+ .postDelayed(runnableArgumentCaptor.capture(), eq((long) TIMEOUT_MILLIS));
+ verify(mWifiDialogManagerThreadRunner).removeCallbacks(runnableArgumentCaptor.getValue());
+ }
}