| /* |
| * Copyright (C) 2022 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.wifi; |
| |
| import android.app.ActivityOptions; |
| import android.content.Intent; |
| import android.net.wifi.WifiContext; |
| import android.net.wifi.WifiManager; |
| import android.os.UserHandle; |
| import android.util.ArraySet; |
| import android.util.Log; |
| import android.util.SparseArray; |
| import android.view.Display; |
| |
| import androidx.annotation.AnyThread; |
| import androidx.annotation.NonNull; |
| import androidx.annotation.Nullable; |
| import androidx.annotation.VisibleForTesting; |
| |
| import com.android.modules.utils.build.SdkLevel; |
| |
| import java.util.Set; |
| |
| import javax.annotation.concurrent.ThreadSafe; |
| |
| /** |
| * Class to manage launching dialogs via WifiDialog and returning the user reply. |
| * All methods run on the main Wi-Fi thread runner except those annotated with @AnyThread, which can |
| * run on any thread. |
| */ |
| public class WifiDialogManager { |
| private static final String TAG = "WifiDialogManager"; |
| @VisibleForTesting |
| static final String WIFI_DIALOG_ACTIVITY_CLASSNAME = |
| "com.android.wifi.dialog.WifiDialogActivity"; |
| |
| private boolean mVerboseLoggingEnabled; |
| |
| private int mNextDialogId = 0; |
| private final Set<Integer> mActiveDialogIds = new ArraySet<>(); |
| private final @NonNull SparseArray<DialogHandleInternal> mActiveDialogHandles = |
| new SparseArray<>(); |
| |
| private final @NonNull WifiContext mContext; |
| private final @NonNull WifiThreadRunner mWifiThreadRunner; |
| |
| /** |
| * Constructs a WifiDialogManager |
| * |
| * @param context Main Wi-Fi context. |
| * @param wifiThreadRunner Main Wi-Fi thread runner. |
| */ |
| public WifiDialogManager( |
| @NonNull WifiContext context, |
| @NonNull WifiThreadRunner wifiThreadRunner) { |
| mContext = context; |
| mWifiThreadRunner = wifiThreadRunner; |
| } |
| |
| /** |
| * Enables verbose logging. |
| */ |
| public void enableVerboseLogging(boolean enabled) { |
| mVerboseLoggingEnabled = enabled; |
| } |
| |
| private int getNextDialogId() { |
| if (mActiveDialogIds.isEmpty() || mNextDialogId == WifiManager.INVALID_DIALOG_ID) { |
| mNextDialogId = 0; |
| } |
| return mNextDialogId++; |
| } |
| |
| private @Nullable Intent getBaseLaunchIntent(@WifiManager.DialogType int dialogType) { |
| Intent intent = new Intent(WifiManager.ACTION_LAUNCH_DIALOG) |
| .putExtra(WifiManager.EXTRA_DIALOG_TYPE, dialogType) |
| .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| String wifiDialogApkPkgName = mContext.getWifiDialogApkPkgName(); |
| if (wifiDialogApkPkgName == null) { |
| Log.w(TAG, "Could not get WifiDialog APK package name!"); |
| return null; |
| } |
| intent.setClassName(wifiDialogApkPkgName, WIFI_DIALOG_ACTIVITY_CLASSNAME); |
| return intent; |
| } |
| |
| private @Nullable Intent getDismissIntent(int dialogId) { |
| Intent intent = new Intent(WifiManager.ACTION_DISMISS_DIALOG); |
| intent.putExtra(WifiManager.EXTRA_DIALOG_ID, dialogId); |
| String wifiDialogApkPkgName = mContext.getWifiDialogApkPkgName(); |
| if (wifiDialogApkPkgName == null) { |
| Log.w(TAG, "Could not get WifiDialog APK package name!"); |
| return null; |
| } |
| intent.setClassName(wifiDialogApkPkgName, WIFI_DIALOG_ACTIVITY_CLASSNAME); |
| return intent; |
| } |
| |
| /** |
| * Handle for launching and dismissing a dialog from any thread. |
| */ |
| @ThreadSafe |
| public class DialogHandle { |
| DialogHandleInternal mInternalHandle; |
| private DialogHandle(DialogHandleInternal internalHandle) { |
| mInternalHandle = internalHandle; |
| } |
| |
| /** |
| * Launches the dialog. |
| */ |
| @AnyThread |
| public void launchDialog() { |
| mWifiThreadRunner.post(() -> mInternalHandle.launchDialog(0)); |
| } |
| |
| /** |
| * Launches the dialog with a timeout before it is auto-cancelled. |
| * @param timeoutMs timeout in milliseconds before the dialog is auto-cancelled. A value <=0 |
| * indicates no timeout. |
| */ |
| @AnyThread |
| public void launchDialog(long timeoutMs) { |
| mWifiThreadRunner.post(() -> mInternalHandle.launchDialog(timeoutMs)); |
| |
| } |
| |
| /** |
| * Dismisses the dialog. Dialogs will automatically be dismissed once the user replies, but |
| * this method may be used to dismiss unanswered dialogs that are no longer needed. |
| */ |
| @AnyThread |
| public void dismissDialog() { |
| mWifiThreadRunner.post(() -> mInternalHandle.dismissDialog()); |
| } |
| } |
| |
| /** |
| * Internal handle for launching and dismissing a dialog on the main Wi-Fi thread runner. |
| * @see {@link DialogHandle} |
| */ |
| private class DialogHandleInternal { |
| private int mDialogId = WifiManager.INVALID_DIALOG_ID; |
| private final @NonNull Intent mIntent; |
| private Runnable mTimeoutRunnable; |
| private final int mDisplayId; |
| |
| DialogHandleInternal(@NonNull Intent intent, int displayId) |
| throws IllegalArgumentException { |
| if (intent == null) { |
| throw new IllegalArgumentException("Intent cannot be null!"); |
| } |
| mDisplayId = displayId; |
| mIntent = intent; |
| } |
| |
| /** |
| * @see {@link DialogHandle#launchDialog(long)} |
| */ |
| void launchDialog(long timeoutMs) { |
| if (mDialogId != WifiManager.INVALID_DIALOG_ID) { |
| // Dialog is already active, ignore. |
| return; |
| } |
| registerDialog(); |
| mIntent.putExtra(WifiManager.EXTRA_DIALOG_ID, mDialogId); |
| boolean launched = false; |
| if (SdkLevel.isAtLeastT() && mDisplayId != Display.DEFAULT_DISPLAY) { |
| try { |
| mContext.startActivityAsUser(mIntent, |
| ActivityOptions.makeBasic().setLaunchDisplayId(mDisplayId).toBundle(), |
| UserHandle.CURRENT); |
| launched = true; |
| } catch (Exception e) { |
| Log.e(TAG, "Error startActivityAsUser - " + e); |
| } |
| } |
| if (!launched) { |
| mContext.startActivityAsUser(mIntent, UserHandle.CURRENT); |
| } |
| if (mVerboseLoggingEnabled) { |
| Log.v(TAG, "Launching dialog with id=" + mDialogId); |
| } |
| if (timeoutMs > 0) { |
| mTimeoutRunnable = () -> onTimeout(); |
| mWifiThreadRunner.postDelayed(mTimeoutRunnable, timeoutMs); |
| } |
| } |
| |
| /** |
| * Callback to run when the dialog times out. |
| */ |
| void onTimeout() { |
| dismissDialog(); |
| } |
| |
| /** |
| * @see {@link DialogHandle#dismissDialog()} |
| */ |
| void dismissDialog() { |
| if (mDialogId == WifiManager.INVALID_DIALOG_ID) { |
| // Dialog is not active, ignore. |
| return; |
| } |
| Intent dismissIntent = getDismissIntent(mDialogId); |
| if (dismissIntent == null) { |
| Log.e(TAG, "Could not create intent for dismissing dialog with id: " |
| + mDialogId); |
| return; |
| } |
| mContext.startActivityAsUser(dismissIntent, UserHandle.CURRENT); |
| if (mVerboseLoggingEnabled) { |
| Log.v(TAG, "Dismissing dialog with id=" + mDialogId); |
| } |
| unregisterDialog(); |
| } |
| |
| /** |
| * Assigns a dialog id to the dialog and registers it as an active dialog. |
| */ |
| void registerDialog() { |
| if (mDialogId != WifiManager.INVALID_DIALOG_ID) { |
| // Already registered. |
| return; |
| } |
| mDialogId = getNextDialogId(); |
| mActiveDialogIds.add(mDialogId); |
| mActiveDialogHandles.put(mDialogId, this); |
| if (mVerboseLoggingEnabled) { |
| Log.v(TAG, "Registered dialog with id=" + mDialogId); |
| } |
| } |
| |
| /** |
| * Unregisters the dialog as an active dialog and removes its dialog id. |
| * This should be called after a dialog is replied to or dismissed. |
| */ |
| void unregisterDialog() { |
| if (mDialogId == WifiManager.INVALID_DIALOG_ID) { |
| // Already unregistered. |
| return; |
| } |
| if (mTimeoutRunnable != null) { |
| mWifiThreadRunner.removeCallbacks(mTimeoutRunnable); |
| } |
| mTimeoutRunnable = null; |
| mActiveDialogIds.remove(mDialogId); |
| mActiveDialogHandles.remove(mDialogId); |
| mDialogId = WifiManager.INVALID_DIALOG_ID; |
| if (mVerboseLoggingEnabled) { |
| Log.v(TAG, "Unregistered dialog with id=" + mDialogId); |
| } |
| } |
| } |
| |
| private class SimpleDialogHandle extends DialogHandleInternal { |
| private @NonNull SimpleDialogCallback mCallback; |
| private @NonNull WifiThreadRunner mCallbackThreadRunner; |
| |
| SimpleDialogHandle( |
| final String title, |
| final String message, |
| final String messageUrl, |
| final int messageUrlStart, |
| final int messageUrlEnd, |
| final String positiveButtonText, |
| final String negativeButtonText, |
| final String neutralButtonText, |
| @NonNull SimpleDialogCallback callback, |
| @NonNull WifiThreadRunner callbackThreadRunner) throws IllegalArgumentException { |
| super(getBaseLaunchIntent(WifiManager.DIALOG_TYPE_SIMPLE) |
| .putExtra(WifiManager.EXTRA_DIALOG_TITLE, title) |
| .putExtra(WifiManager.EXTRA_DIALOG_MESSAGE, message) |
| .putExtra(WifiManager.EXTRA_DIALOG_MESSAGE_URL, messageUrl) |
| .putExtra(WifiManager.EXTRA_DIALOG_MESSAGE_URL_START, messageUrlStart) |
| .putExtra(WifiManager.EXTRA_DIALOG_MESSAGE_URL_END, messageUrlEnd) |
| .putExtra(WifiManager.EXTRA_DIALOG_POSITIVE_BUTTON_TEXT, positiveButtonText) |
| .putExtra(WifiManager.EXTRA_DIALOG_NEGATIVE_BUTTON_TEXT, negativeButtonText) |
| .putExtra(WifiManager.EXTRA_DIALOG_NEUTRAL_BUTTON_TEXT, neutralButtonText), |
| Display.DEFAULT_DISPLAY); |
| if (messageUrl != null) { |
| if (message == null) { |
| throw new IllegalArgumentException("Cannot set span for null message!"); |
| } |
| if (messageUrlStart < 0) { |
| throw new IllegalArgumentException("Span start cannot be less than 0!"); |
| } |
| if (messageUrlEnd > message.length()) { |
| throw new IllegalArgumentException("Span end index " + messageUrlEnd |
| + " cannot be greater than message length " + message.length() + "!"); |
| } |
| } |
| if (callback == null) { |
| throw new IllegalArgumentException("Callback cannot be null!"); |
| } |
| if (callbackThreadRunner == null) { |
| throw new IllegalArgumentException("Callback thread runner cannot be null!"); |
| } |
| mCallback = callback; |
| mCallbackThreadRunner = callbackThreadRunner; |
| } |
| |
| void notifyOnPositiveButtonClicked() { |
| mCallbackThreadRunner.post(() -> mCallback.onPositiveButtonClicked()); |
| unregisterDialog(); |
| } |
| |
| void notifyOnNegativeButtonClicked() { |
| mCallbackThreadRunner.post(() -> mCallback.onNegativeButtonClicked()); |
| unregisterDialog(); |
| } |
| |
| void notifyOnNeutralButtonClicked() { |
| mCallbackThreadRunner.post(() -> mCallback.onNeutralButtonClicked()); |
| unregisterDialog(); |
| } |
| |
| void notifyOnCancelled() { |
| mCallbackThreadRunner.post(() -> mCallback.onCancelled()); |
| unregisterDialog(); |
| } |
| |
| @Override |
| void onTimeout() { |
| dismissDialog(); |
| notifyOnCancelled(); |
| } |
| } |
| |
| /** |
| * Callback for receiving simple dialog responses. |
| */ |
| public interface SimpleDialogCallback { |
| /** |
| * The positive button was clicked. |
| */ |
| void onPositiveButtonClicked(); |
| |
| /** |
| * The negative button was clicked. |
| */ |
| void onNegativeButtonClicked(); |
| |
| /** |
| * The neutral button was clicked. |
| */ |
| void onNeutralButtonClicked(); |
| |
| /** |
| * The dialog was cancelled (back button or home button or timeout). |
| */ |
| void onCancelled(); |
| } |
| |
| /** |
| * Creates a simple dialog with optional title, message, and positive/negative/neutral buttons. |
| * |
| * @param title Title of the dialog. |
| * @param message Message of the dialog. |
| * @param positiveButtonText Text of the positive button or {@code null} for no button. |
| * @param negativeButtonText Text of the negative button or {@code null} for no button. |
| * @param neutralButtonText Text of the neutral button or {@code null} for no button. |
| * @param callback Callback to receive the dialog response. |
| * @param callbackThreadRunner WifiThreadRunner to run the callback on. |
| * @return DialogHandle Handle for the dialog, or {@code null} if no dialog could |
| * be created. |
| */ |
| @AnyThread |
| @Nullable |
| public DialogHandle createSimpleDialog( |
| @Nullable String title, |
| @Nullable String message, |
| @Nullable String positiveButtonText, |
| @Nullable String negativeButtonText, |
| @Nullable String neutralButtonText, |
| @NonNull SimpleDialogCallback callback, |
| @NonNull WifiThreadRunner callbackThreadRunner) { |
| try { |
| return new DialogHandle( |
| new SimpleDialogHandle( |
| title, |
| message, |
| null /* messageUrl */, |
| 0 /* messageUrlStart */, |
| 0 /* messageUrlEnd */, |
| positiveButtonText, |
| negativeButtonText, |
| neutralButtonText, |
| callback, |
| callbackThreadRunner) |
| ); |
| } catch (IllegalArgumentException e) { |
| Log.e(TAG, "Could not create DialogHandle for simple dialog: " + e); |
| return null; |
| } |
| } |
| |
| /** |
| * Creates a simple dialog with a URL embedded in the message. |
| * |
| * @param title Title of the dialog. |
| * @param message Message of the dialog. |
| * @param messageUrl URL to embed in the message. If non-null, then message must also |
| * be non-null. |
| * @param messageUrlStart Start index (inclusive) of the URL in the message. Must be |
| * non-negative. |
| * @param messageUrlEnd End index (exclusive) of the URL in the message. Must be less |
| * than the length of message. |
| * @param positiveButtonText Text of the positive button or {@code null} for no button. |
| * @param negativeButtonText Text of the negative button or {@code null} for no button. |
| * @param neutralButtonText Text of the neutral button or {@code null} for no button. |
| * @param callback Callback to receive the dialog response. |
| * @param callbackThreadRunner WifiThreadRunner to run the callback on. |
| * @return DialogHandle Handle for the dialog, or {@code null} if no dialog could |
| * be created. |
| */ |
| @AnyThread |
| @Nullable |
| public DialogHandle createSimpleDialogWithUrl( |
| @Nullable String title, |
| @Nullable String message, |
| @Nullable String messageUrl, |
| int messageUrlStart, |
| int messageUrlEnd, |
| @Nullable String positiveButtonText, |
| @Nullable String negativeButtonText, |
| @Nullable String neutralButtonText, |
| @NonNull SimpleDialogCallback callback, |
| @NonNull WifiThreadRunner callbackThreadRunner) { |
| try { |
| return new DialogHandle( |
| new SimpleDialogHandle( |
| title, |
| message, |
| messageUrl, |
| messageUrlStart, |
| messageUrlEnd, |
| positiveButtonText, |
| negativeButtonText, |
| neutralButtonText, |
| callback, |
| callbackThreadRunner) |
| ); |
| } catch (IllegalArgumentException e) { |
| Log.e(TAG, "Could not create DialogHandle for simple dialog: " + e); |
| return null; |
| } |
| } |
| |
| /** |
| * Returns the reply to a simple dialog to the callback of matching dialogId. |
| * @param dialogId id of the replying dialog. |
| * @param reply reply of the dialog. |
| */ |
| public void replyToSimpleDialog(int dialogId, @WifiManager.DialogReply int reply) { |
| if (mVerboseLoggingEnabled) { |
| Log.i(TAG, "Response received for simple dialog. id=" + dialogId + " reply=" + reply); |
| } |
| DialogHandleInternal internalHandle = mActiveDialogHandles.get(dialogId); |
| if (internalHandle == null) { |
| if (mVerboseLoggingEnabled) { |
| Log.w(TAG, "No matching dialog handle for simple dialog id=" + dialogId); |
| } |
| return; |
| } |
| if (!(internalHandle instanceof SimpleDialogHandle)) { |
| if (mVerboseLoggingEnabled) { |
| Log.w(TAG, "Dialog handle with id " + dialogId + " is not for a simple dialog."); |
| } |
| return; |
| } |
| switch (reply) { |
| case WifiManager.DIALOG_REPLY_POSITIVE: |
| ((SimpleDialogHandle) internalHandle).notifyOnPositiveButtonClicked(); |
| break; |
| case WifiManager.DIALOG_REPLY_NEGATIVE: |
| ((SimpleDialogHandle) internalHandle).notifyOnNegativeButtonClicked(); |
| break; |
| case WifiManager.DIALOG_REPLY_NEUTRAL: |
| ((SimpleDialogHandle) internalHandle).notifyOnNeutralButtonClicked(); |
| break; |
| case WifiManager.DIALOG_REPLY_CANCELLED: |
| ((SimpleDialogHandle) internalHandle).notifyOnCancelled(); |
| break; |
| default: |
| if (mVerboseLoggingEnabled) { |
| Log.w(TAG, "Received invalid reply=" + reply); |
| } |
| } |
| } |
| |
| private class P2pInvitationReceivedDialogHandle extends DialogHandleInternal { |
| private @NonNull P2pInvitationReceivedDialogCallback mCallback; |
| private @NonNull WifiThreadRunner mCallbackThreadRunner; |
| |
| P2pInvitationReceivedDialogHandle( |
| final @NonNull String deviceName, |
| final boolean isPinRequested, |
| @Nullable String displayPin, |
| int displayId, |
| @NonNull P2pInvitationReceivedDialogCallback callback, |
| @NonNull WifiThreadRunner callbackThreadRunner) throws IllegalArgumentException { |
| super(getBaseLaunchIntent(WifiManager.DIALOG_TYPE_P2P_INVITATION_RECEIVED) |
| .putExtra(WifiManager.EXTRA_P2P_DEVICE_NAME, deviceName) |
| .putExtra(WifiManager.EXTRA_P2P_PIN_REQUESTED, isPinRequested) |
| .putExtra(WifiManager.EXTRA_P2P_DISPLAY_PIN, displayPin), displayId); |
| if (deviceName == null) { |
| throw new IllegalArgumentException("Device name cannot be null!"); |
| } |
| if (callback == null) { |
| throw new IllegalArgumentException("Callback cannot be null!"); |
| } |
| if (callbackThreadRunner == null) { |
| throw new IllegalArgumentException("Callback thread runner cannot be null!"); |
| } |
| mCallback = callback; |
| mCallbackThreadRunner = callbackThreadRunner; |
| } |
| |
| void notifyOnAccepted(@Nullable String optionalPin) { |
| mCallbackThreadRunner.post(() -> mCallback.onAccepted(optionalPin)); |
| unregisterDialog(); |
| } |
| |
| void notifyOnDeclined() { |
| mCallbackThreadRunner.post(() -> mCallback.onDeclined()); |
| unregisterDialog(); |
| } |
| |
| @Override |
| void onTimeout() { |
| dismissDialog(); |
| notifyOnDeclined(); |
| } |
| } |
| |
| /** |
| * Callback for receiving P2P Invitation Received dialog responses. |
| */ |
| public interface P2pInvitationReceivedDialogCallback { |
| /** |
| * Invitation was accepted. |
| * |
| * @param optionalPin Optional PIN if a PIN was requested, or {@code null} otherwise. |
| */ |
| void onAccepted(@Nullable String optionalPin); |
| |
| /** |
| * Invitation was declined or cancelled (back button or home button or timeout). |
| */ |
| void onDeclined(); |
| } |
| |
| /** |
| * Creates 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 displayId The ID of the Display on which to place the dialog |
| * (Display.DEFAULT_DISPLAY |
| * refers to the default display) |
| * @param callback Callback to receive the dialog response. |
| * @param callbackThreadRunner WifiThreadRunner to run the callback on. |
| * @return DialogHandle Handle for the dialog, or {@code null} if no dialog could |
| * be created. |
| */ |
| @AnyThread |
| public DialogHandle createP2pInvitationReceivedDialog( |
| @NonNull String deviceName, |
| boolean isPinRequested, |
| @Nullable String displayPin, |
| int displayId, |
| @NonNull P2pInvitationReceivedDialogCallback callback, |
| @NonNull WifiThreadRunner callbackThreadRunner) { |
| try { |
| return new DialogHandle( |
| new P2pInvitationReceivedDialogHandle( |
| deviceName, |
| isPinRequested, |
| displayPin, |
| displayId, |
| callback, |
| callbackThreadRunner) |
| ); |
| } catch (IllegalArgumentException e) { |
| Log.e(TAG, "Could not create DialogHandle for P2P Invitation Received dialog: " + e); |
| return null; |
| } |
| } |
| |
| /** |
| * Returns the reply to a P2P Invitation Received dialog to the callback of matching dialogId. |
| * Note: Must be invoked only from the main Wi-Fi thread. |
| * |
| * @param dialogId id of the replying dialog. |
| * @param accepted Whether the invitation was accepted. |
| * @param optionalPin PIN of the reply, or {@code null} if none was supplied. |
| */ |
| public void replyToP2pInvitationReceivedDialog( |
| int dialogId, |
| boolean accepted, |
| @Nullable String optionalPin) { |
| if (mVerboseLoggingEnabled) { |
| Log.i(TAG, "Response received for P2P Invitation Received dialog." |
| + " id=" + dialogId |
| + " accepted=" + accepted |
| + " pin=" + optionalPin); |
| } |
| DialogHandleInternal internalHandle = mActiveDialogHandles.get(dialogId); |
| if (internalHandle == null) { |
| if (mVerboseLoggingEnabled) { |
| Log.w(TAG, "No matching dialog handle for P2P Invitation Received dialog" |
| + " id=" + dialogId); |
| } |
| return; |
| } |
| if (!(internalHandle instanceof P2pInvitationReceivedDialogHandle)) { |
| if (mVerboseLoggingEnabled) { |
| Log.w(TAG, "Dialog handle with id " + dialogId |
| + " is not for a P2P Invitation Received dialog."); |
| } |
| return; |
| } |
| if (accepted) { |
| ((P2pInvitationReceivedDialogHandle) internalHandle).notifyOnAccepted(optionalPin); |
| } else { |
| ((P2pInvitationReceivedDialogHandle) internalHandle).notifyOnDeclined(); |
| } |
| } |
| |
| private class P2pInvitationSentDialogHandle extends DialogHandleInternal { |
| P2pInvitationSentDialogHandle( |
| final @NonNull String deviceName, |
| final @NonNull String displayPin, |
| int displayId) throws IllegalArgumentException { |
| super(getBaseLaunchIntent(WifiManager.DIALOG_TYPE_P2P_INVITATION_SENT) |
| .putExtra(WifiManager.EXTRA_P2P_DEVICE_NAME, deviceName) |
| .putExtra(WifiManager.EXTRA_P2P_DISPLAY_PIN, displayPin), |
| displayId); |
| if (deviceName == null) { |
| throw new IllegalArgumentException("Device name cannot be null!"); |
| } |
| if (displayPin == null) { |
| throw new IllegalArgumentException("Display PIN cannot be null!"); |
| } |
| } |
| } |
| |
| /** |
| * Creates a P2P Invitation Sent dialog. |
| * |
| * @param deviceName Name of the device the invitation was sent to. |
| * @param displayPin display PIN |
| * @param displayId display ID |
| * @return DialogHandle Handle for the dialog, or {@code null} if no dialog could |
| * be created. |
| */ |
| @AnyThread |
| public DialogHandle createP2pInvitationSentDialog( |
| @NonNull String deviceName, |
| @Nullable String displayPin, |
| int displayId) { |
| try { |
| return new DialogHandle(new P2pInvitationSentDialogHandle(deviceName, displayPin, |
| displayId)); |
| } catch (IllegalArgumentException e) { |
| Log.e(TAG, "Could not create DialogHandle for P2P Invitation Sent dialog: " + e); |
| return null; |
| } |
| } |
| } |