• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wifi.p2p;
18 
19 import android.annotation.NonNull;
20 import android.hardware.wifi.supplicant.V1_0.ISupplicant;
21 import android.hardware.wifi.supplicant.V1_0.ISupplicantIface;
22 import android.hardware.wifi.supplicant.V1_0.ISupplicantNetwork;
23 import android.hardware.wifi.supplicant.V1_0.ISupplicantP2pIface;
24 import android.hardware.wifi.supplicant.V1_0.ISupplicantP2pIfaceCallback;
25 import android.hardware.wifi.supplicant.V1_0.ISupplicantP2pNetwork;
26 import android.hardware.wifi.supplicant.V1_0.IfaceType;
27 import android.hardware.wifi.supplicant.V1_0.SupplicantStatus;
28 import android.hardware.wifi.supplicant.V1_0.SupplicantStatusCode;
29 import android.hardware.wifi.supplicant.V1_0.WpsConfigMethods;
30 import android.hidl.manager.V1_0.IServiceManager;
31 import android.hidl.manager.V1_0.IServiceNotification;
32 import android.net.wifi.CoexUnsafeChannel;
33 import android.net.wifi.ScanResult;
34 import android.net.wifi.WpsInfo;
35 import android.net.wifi.p2p.WifiP2pConfig;
36 import android.net.wifi.p2p.WifiP2pDevice;
37 import android.net.wifi.p2p.WifiP2pGroup;
38 import android.net.wifi.p2p.WifiP2pGroupList;
39 import android.net.wifi.p2p.WifiP2pManager;
40 import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
41 import android.os.IHwBinder.DeathRecipient;
42 import android.os.RemoteException;
43 import android.text.TextUtils;
44 import android.util.Log;
45 
46 import com.android.internal.annotations.VisibleForTesting;
47 import com.android.modules.utils.build.SdkLevel;
48 import com.android.server.wifi.util.ArrayUtils;
49 import com.android.server.wifi.util.NativeUtil;
50 
51 import java.nio.ByteBuffer;
52 import java.nio.ByteOrder;
53 import java.util.ArrayList;
54 import java.util.Arrays;
55 import java.util.List;
56 import java.util.NoSuchElementException;
57 import java.util.Set;
58 import java.util.regex.Matcher;
59 import java.util.regex.Pattern;
60 import java.util.stream.Collectors;
61 
62 /**
63  * Native calls sending requests to the P2P Hals, and callbacks for receiving P2P events
64  */
65 public class SupplicantP2pIfaceHalHidlImpl implements ISupplicantP2pIfaceHal {
66     private static final String TAG = "SupplicantP2pIfaceHalHidlImpl";
67     @VisibleForTesting
68     public static final String HAL_INSTANCE_NAME = "default";
69     private static boolean sVerboseLoggingEnabled = true;
70     private static boolean sHalVerboseLoggingEnabled = true;
71     private static final int RESULT_NOT_VALID = -1;
72     private static final int DEFAULT_OPERATING_CLASS = 81;
73     /**
74      * Regex pattern for extracting the wps device type bytes.
75      * Matches a strings like the following: "<categ>-<OUI>-<subcateg>";
76      */
77     private static final Pattern WPS_DEVICE_TYPE_PATTERN =
78             Pattern.compile("^(\\d{1,2})-([0-9a-fA-F]{8})-(\\d{1,2})$");
79 
80     private final Object mLock = new Object();
81 
82     // Supplicant HAL HIDL interface objects
83     private IServiceManager mIServiceManager = null;
84     private ISupplicant mISupplicant = null;
85     private ISupplicantIface mHidlSupplicantIface = null;
86     private ISupplicantP2pIface mISupplicantP2pIface = null;
87     private final IServiceNotification mServiceNotificationCallback =
88             new IServiceNotification.Stub() {
89         public void onRegistration(String fqName, String name, boolean preexisting) {
90             synchronized (mLock) {
91                 if (sVerboseLoggingEnabled) {
92                     Log.i(TAG, "IServiceNotification.onRegistration for: " + fqName
93                             + ", " + name + " preexisting=" + preexisting);
94                 }
95                 if (!initSupplicantService()) {
96                     Log.e(TAG, "Initializing ISupplicant failed.");
97                     supplicantServiceDiedHandler();
98                 } else {
99                     Log.i(TAG, "Completed initialization of ISupplicant interfaces.");
100                 }
101             }
102         }
103     };
104     private final DeathRecipient mServiceManagerDeathRecipient =
105             cookie -> {
106                 Log.w(TAG, "IServiceManager died: cookie=" + cookie);
107                 synchronized (mLock) {
108                     supplicantServiceDiedHandler();
109                     mIServiceManager = null; // Will need to register a new ServiceNotification
110                 }
111             };
112     private final DeathRecipient mSupplicantDeathRecipient =
113             cookie -> {
114                 Log.w(TAG, "ISupplicant/ISupplicantP2pIface died: cookie=" + cookie);
115                 synchronized (mLock) {
116                     supplicantServiceDiedHandler();
117                 }
118             };
119 
120     private final WifiP2pMonitor mMonitor;
121     private ISupplicantP2pIfaceCallback mCallback = null;
122 
SupplicantP2pIfaceHalHidlImpl(WifiP2pMonitor monitor)123     public SupplicantP2pIfaceHalHidlImpl(WifiP2pMonitor monitor) {
124         mMonitor = monitor;
125     }
126 
linkToServiceManagerDeath()127     private boolean linkToServiceManagerDeath() {
128         if (mIServiceManager == null) return false;
129         try {
130             if (!mIServiceManager.linkToDeath(mServiceManagerDeathRecipient, 0)) {
131                 Log.wtf(TAG, "Error on linkToDeath on IServiceManager");
132                 supplicantServiceDiedHandler();
133                 mIServiceManager = null; // Will need to register a new ServiceNotification
134                 return false;
135             }
136         } catch (RemoteException e) {
137             Log.e(TAG, "IServiceManager.linkToDeath exception", e);
138             return false;
139         }
140         return true;
141     }
142 
143     /**
144      * Enable verbose logging for all sub modules.
145      *
146      */
enableVerboseLogging(boolean verboseEnabled, boolean halVerboseEnabled)147     public static void enableVerboseLogging(boolean verboseEnabled, boolean halVerboseEnabled) {
148         sVerboseLoggingEnabled = verboseEnabled;
149         sHalVerboseLoggingEnabled = halVerboseEnabled;
150         SupplicantP2pIfaceCallback.enableVerboseLogging(verboseEnabled, halVerboseEnabled);
151         SupplicantP2pIfaceCallbackV1_4.enableVerboseLogging(verboseEnabled, halVerboseEnabled);
152     }
153 
154     /**
155      * Set the debug log level for wpa_supplicant
156      *
157      * @param turnOnVerbose Whether to turn on verbose logging or not.
158      * @param globalShowKeys Whether show keys is true in WifiGlobals.
159      * @return true if request is sent successfully, false otherwise.
160      */
setLogLevel(boolean turnOnVerbose, boolean globalShowKeys)161     public boolean setLogLevel(boolean turnOnVerbose, boolean globalShowKeys) {
162         synchronized (mLock) {
163             int logLevel = turnOnVerbose
164                     ? ISupplicant.DebugLevel.DEBUG
165                     : ISupplicant.DebugLevel.INFO;
166             return setDebugParams(logLevel, false,
167                     turnOnVerbose && globalShowKeys);
168         }
169     }
170 
171     /** See ISupplicant.hal for documentation */
setDebugParams(int level, boolean showTimestamp, boolean showKeys)172     private boolean setDebugParams(int level, boolean showTimestamp, boolean showKeys) {
173         synchronized (mLock) {
174             if (mISupplicant == null) {
175                 Log.e(TAG, "Got null ISupplicant service. Stopping supplicant HIDL startup");
176                 return false;
177             }
178             try {
179                 SupplicantStatus status =
180                         mISupplicant.setDebugParams(level, showTimestamp, showKeys);
181                 if (status == null || status.code != SupplicantStatusCode.SUCCESS) {
182                     Log.e(TAG, "Failed to set debug params " + status);
183                     return false;
184                 }
185             } catch (RemoteException e) {
186                 Log.e(TAG, "Exception while setting debug params for ISupplicant service: " + e);
187                 supplicantServiceDiedHandler();
188                 return false;
189             }
190         }
191         return true;
192     }
193 
194     /**
195      * Registers a service notification for the ISupplicant service, which triggers initialization
196      * of the ISupplicantP2pIface
197      * @return true if the service notification was successfully registered
198      */
initialize()199     public boolean initialize() {
200         if (sVerboseLoggingEnabled) Log.i(TAG, "Registering ISupplicant service ready callback.");
201         synchronized (mLock) {
202             if (mIServiceManager != null) {
203                 Log.i(TAG, "Supplicant HAL already initialized.");
204                 // Already have an IServiceManager and serviceNotification registered, don't
205                 // don't register another.
206                 return true;
207             }
208             mISupplicant = null;
209             mISupplicantP2pIface = null;
210             try {
211                 mIServiceManager = getServiceManagerMockable();
212                 if (mIServiceManager == null) {
213                     Log.e(TAG, "Failed to get HIDL Service Manager");
214                     return false;
215                 }
216                 if (!linkToServiceManagerDeath()) {
217                     return false;
218                 }
219                 /* TODO(b/33639391) : Use the new ISupplicant.registerForNotifications() once it
220                    exists */
221                 if (!mIServiceManager.registerForNotifications(
222                         ISupplicant.kInterfaceName, "", mServiceNotificationCallback)) {
223                     Log.e(TAG, "Failed to register for notifications to "
224                             + ISupplicant.kInterfaceName);
225                     mIServiceManager = null; // Will need to register a new ServiceNotification
226                     return false;
227                 }
228 
229                 // Successful completion by the end of the 'try' block. This will prevent reporting
230                 // proper initialization after exception is caught.
231                 return true;
232             } catch (RemoteException e) {
233                 Log.e(TAG, "Exception while trying to register a listener for ISupplicant service: "
234                         + e);
235                 supplicantServiceDiedHandler();
236             }
237             return false;
238         }
239     }
240 
linkToSupplicantDeath()241     private boolean linkToSupplicantDeath() {
242         if (mISupplicant == null) return false;
243         try {
244             if (!mISupplicant.linkToDeath(mSupplicantDeathRecipient, 0)) {
245                 Log.wtf(TAG, "Error on linkToDeath on ISupplicant");
246                 supplicantServiceDiedHandler();
247                 return false;
248             }
249         } catch (RemoteException e) {
250             Log.e(TAG, "ISupplicant.linkToDeath exception", e);
251             return false;
252         }
253         return true;
254     }
255 
initSupplicantService()256     private boolean initSupplicantService() {
257         synchronized (mLock) {
258             try {
259                 mISupplicant = getSupplicantMockable();
260             } catch (RemoteException e) {
261                 Log.e(TAG, "ISupplicant.getService exception: " + e);
262                 return false;
263             }
264             if (mISupplicant == null) {
265                 Log.e(TAG, "Got null ISupplicant service. Stopping supplicant HIDL startup");
266                 return false;
267             }
268             if (!linkToSupplicantDeath()) {
269                 return false;
270             }
271         }
272         return true;
273     }
274 
linkToSupplicantP2pIfaceDeath()275     private boolean linkToSupplicantP2pIfaceDeath() {
276         if (mISupplicantP2pIface == null) return false;
277         try {
278             if (!mISupplicantP2pIface.linkToDeath(mSupplicantDeathRecipient, 0)) {
279                 Log.wtf(TAG, "Error on linkToDeath on ISupplicantP2pIface");
280                 supplicantServiceDiedHandler();
281                 return false;
282             }
283         } catch (RemoteException e) {
284             Log.e(TAG, "ISupplicantP2pIface.linkToDeath exception", e);
285             return false;
286         }
287         return true;
288     }
289 
290     /**
291      * Setup the P2p iface.
292      *
293      * @param ifaceName Name of the interface.
294      * @return true on success, false otherwise.
295      */
setupIface(@onNull String ifaceName)296     public boolean setupIface(@NonNull String ifaceName) {
297         synchronized (mLock) {
298             if (mISupplicantP2pIface != null) return false;
299             ISupplicantIface ifaceHwBinder;
300             if (isV1_1()) {
301                 ifaceHwBinder = addIfaceV1_1(ifaceName);
302             } else {
303                 ifaceHwBinder = getIfaceV1_0(ifaceName);
304             }
305             if (ifaceHwBinder == null) {
306                 Log.e(TAG, "initSupplicantP2pIface got null iface");
307                 return false;
308             }
309             mISupplicantP2pIface = getP2pIfaceMockable(ifaceHwBinder);
310             if (!linkToSupplicantP2pIfaceDeath()) {
311                 return false;
312             }
313             if (mISupplicantP2pIface != null && mMonitor != null) {
314                 if (null != getP2pIfaceMockableV1_4()) {
315                     android.hardware.wifi.supplicant.V1_4.ISupplicantP2pIfaceCallback callback =
316                             new SupplicantP2pIfaceCallbackV1_4(ifaceName);
317                     if (!registerCallbackV1_4(callback)) {
318                         Log.e(TAG, "Callback registration failed. Initialization incomplete.");
319                         return false;
320                     }
321                     mCallback = callback;
322                 } else {
323                     mCallback = new SupplicantP2pIfaceCallback(ifaceName);
324                     if (!registerCallback(mCallback)) {
325                         Log.e(TAG, "Callback registration failed. Initialization incomplete.");
326                         return false;
327                     }
328                 }
329             }
330             return true;
331         }
332     }
333 
334     protected class SupplicantP2pIfaceCallback extends SupplicantP2pIfaceCallbackHidlImpl {
SupplicantP2pIfaceCallback(@onNull String ifaceName)335         SupplicantP2pIfaceCallback(@NonNull String ifaceName) {
336             super(SupplicantP2pIfaceHalHidlImpl.this, ifaceName, mMonitor);
337         }
338     }
339 
340     protected class SupplicantP2pIfaceCallbackV1_4 extends SupplicantP2pIfaceCallbackHidlV1_4Impl {
SupplicantP2pIfaceCallbackV1_4(@onNull String ifaceName)341         SupplicantP2pIfaceCallbackV1_4(@NonNull String ifaceName) {
342             super(SupplicantP2pIfaceHalHidlImpl.this, ifaceName, mMonitor);
343         }
344     }
345 
getIfaceV1_0(@onNull String ifaceName)346     private ISupplicantIface getIfaceV1_0(@NonNull String ifaceName) {
347         if (null == mISupplicant) {
348             Log.e(TAG, "Can't call getIface: ISupplicant is null");
349             return null;
350         }
351         /** List all supplicant Ifaces */
352         final ArrayList<ISupplicant.IfaceInfo> supplicantIfaces = new ArrayList();
353         try {
354             mISupplicant.listInterfaces((SupplicantStatus status,
355                                          ArrayList<ISupplicant.IfaceInfo> ifaces) -> {
356                 if (status.code != SupplicantStatusCode.SUCCESS) {
357                     Log.e(TAG, "Getting Supplicant Interfaces failed: " + status.code);
358                     return;
359                 }
360                 supplicantIfaces.addAll(ifaces);
361             });
362         } catch (RemoteException e) {
363             Log.e(TAG, "ISupplicant.listInterfaces exception: " + e);
364             return null;
365         }
366         if (supplicantIfaces.size() == 0) {
367             Log.e(TAG, "Got zero HIDL supplicant ifaces. Stopping supplicant HIDL startup.");
368             supplicantServiceDiedHandler();
369             return null;
370         }
371         SupplicantResult<ISupplicantIface> supplicantIface =
372                 new SupplicantResult("getInterface()");
373         for (ISupplicant.IfaceInfo ifaceInfo : supplicantIfaces) {
374             if (ifaceInfo.type == IfaceType.P2P && ifaceName.equals(ifaceInfo.name)) {
375                 try {
376                     mISupplicant.getInterface(ifaceInfo,
377                             (SupplicantStatus status, ISupplicantIface iface) -> {
378                                 if (status.code != SupplicantStatusCode.SUCCESS) {
379                                     Log.e(TAG, "Failed to get ISupplicantIface " + status.code);
380                                     return;
381                                 }
382                                 supplicantIface.setResult(status, iface);
383                             });
384                 } catch (RemoteException | IllegalArgumentException e) {
385                     Log.e(TAG, "ISupplicant.getInterface exception: " + e);
386                     supplicantServiceDiedHandler();
387                     return null;
388                 }
389                 break;
390             }
391         }
392         return supplicantIface.getResult();
393     }
394 
addIfaceV1_1(@onNull String ifaceName)395     private ISupplicantIface addIfaceV1_1(@NonNull String ifaceName) {
396         synchronized (mLock) {
397             ISupplicant.IfaceInfo ifaceInfo = new ISupplicant.IfaceInfo();
398             ifaceInfo.name = ifaceName;
399             ifaceInfo.type = IfaceType.P2P;
400             SupplicantResult<ISupplicantIface> supplicantIface =
401                     new SupplicantResult("addInterface(" + ifaceInfo + ")");
402             try {
403                 android.hardware.wifi.supplicant.V1_1.ISupplicant supplicant_v1_1 =
404                         getSupplicantMockableV1_1();
405                 if (supplicant_v1_1 == null) {
406                     Log.e(TAG, "Can't call addIface: ISupplicantP2pIface is null");
407                     return null;
408                 }
409                 supplicant_v1_1.addInterface(ifaceInfo,
410                         (SupplicantStatus status, ISupplicantIface iface) -> {
411                             if (status.code != SupplicantStatusCode.SUCCESS
412                                     && status.code != SupplicantStatusCode.FAILURE_IFACE_EXISTS) {
413                                 Log.e(TAG, "Failed to get ISupplicantIface " + status.code);
414                                 return;
415                             }
416                             supplicantIface.setResult(status, iface);
417                         });
418             } catch (RemoteException e) {
419                 Log.e(TAG, "ISupplicant.addInterface exception: " + e);
420                 supplicantServiceDiedHandler();
421                 return null;
422             }
423             return supplicantIface.getResult();
424         }
425     }
426 
427     /**
428      * Teardown the P2P interface.
429      *
430      * @param ifaceName Name of the interface.
431      * @return true on success, false otherwise.
432      */
teardownIface(@onNull String ifaceName)433     public boolean teardownIface(@NonNull String ifaceName) {
434         synchronized (mLock) {
435             if (mISupplicantP2pIface == null) return false;
436             // Only supported for V1.1
437             if (isV1_1()) {
438                 return removeIfaceV1_1(ifaceName);
439             }
440             return true;
441         }
442     }
443 
444     /**
445      * Remove the P2p iface.
446      *
447      * @return true on success, false otherwise.
448      */
removeIfaceV1_1(@onNull String ifaceName)449     private boolean removeIfaceV1_1(@NonNull String ifaceName) {
450         synchronized (mLock) {
451             try {
452                 android.hardware.wifi.supplicant.V1_1.ISupplicant supplicant_v1_1 =
453                         getSupplicantMockableV1_1();
454                 if (supplicant_v1_1 == null) {
455                     Log.e(TAG, "Can't call removeIface: ISupplicantP2pIface is null");
456                     return false;
457                 }
458                 ISupplicant.IfaceInfo ifaceInfo = new ISupplicant.IfaceInfo();
459                 ifaceInfo.name = ifaceName;
460                 ifaceInfo.type = IfaceType.P2P;
461                 SupplicantStatus status = supplicant_v1_1.removeInterface(ifaceInfo);
462                 if (status.code != SupplicantStatusCode.SUCCESS) {
463                     Log.e(TAG, "Failed to remove iface " + status.code);
464                     return false;
465                 }
466                 mCallback = null;
467             } catch (RemoteException e) {
468                 Log.e(TAG, "ISupplicant.removeInterface exception: " + e);
469                 supplicantServiceDiedHandler();
470                 return false;
471             }
472             mISupplicantP2pIface = null;
473             return true;
474         }
475     }
476 
supplicantServiceDiedHandler()477     private void supplicantServiceDiedHandler() {
478         synchronized (mLock) {
479             mISupplicant = null;
480             mISupplicantP2pIface = null;
481         }
482     }
483 
484 
485     /**
486      * Signals whether initialization started successfully.
487      */
isInitializationStarted()488     public boolean isInitializationStarted() {
489         synchronized (mLock) {
490             return mIServiceManager != null;
491         }
492     }
493 
494     /**
495      * Signals whether initialization completed successfully. Only necessary for testing, is not
496      * needed to guard calls etc.
497      */
isInitializationComplete()498     public boolean isInitializationComplete() {
499         return mISupplicant != null;
500     }
501 
502     /**
503      * Indicates whether the HIDL service is declared. Uses the IServiceManager to check
504      * if the device is running a version >= V1_0 of the HAL from the VINTF for the device.
505      */
serviceDeclared()506     public static boolean serviceDeclared() {
507         try {
508             IServiceManager serviceManager = IServiceManager.getService();
509             String interfaceName = android.hardware.wifi.supplicant.V1_0.ISupplicant.kInterfaceName;
510             if (serviceManager.getTransport(interfaceName, HAL_INSTANCE_NAME)
511                     != IServiceManager.Transport.EMPTY) {
512                 return true;
513             }
514             return false;
515         } catch (RemoteException e) {
516             Log.e(TAG, "Unable to check for existence of HIDL service.");
517             return false;
518         }
519     }
520 
521     /**
522      * Wrapper functions to access static HAL methods, created to be mockable in unit tests
523      */
getServiceManagerMockable()524     protected IServiceManager getServiceManagerMockable() throws RemoteException {
525         return IServiceManager.getService();
526     }
527 
getSupplicantMockable()528     protected ISupplicant getSupplicantMockable() throws RemoteException {
529         try {
530             return ISupplicant.getService();
531         } catch (NoSuchElementException e) {
532             Log.e(TAG, "Failed to get ISupplicant", e);
533             return null;
534         }
535     }
536 
getSupplicantMockableV1_1()537     protected android.hardware.wifi.supplicant.V1_1.ISupplicant getSupplicantMockableV1_1()
538             throws RemoteException {
539         synchronized (mLock) {
540             try {
541                 return android.hardware.wifi.supplicant.V1_1.ISupplicant.castFrom(
542                         mISupplicant);
543             } catch (NoSuchElementException e) {
544                 Log.e(TAG, "Failed to get ISupplicant", e);
545                 return null;
546             }
547         }
548     }
549 
getP2pIfaceMockable(ISupplicantIface iface)550     protected ISupplicantP2pIface getP2pIfaceMockable(ISupplicantIface iface) {
551         return ISupplicantP2pIface.asInterface(iface.asBinder());
552     }
553 
554     protected android.hardware.wifi.supplicant.V1_2.ISupplicantP2pIface
getP2pIfaceMockableV1_2()555             getP2pIfaceMockableV1_2() {
556         if (mISupplicantP2pIface == null) return null;
557         return android.hardware.wifi.supplicant.V1_2.ISupplicantP2pIface.castFrom(
558                 mISupplicantP2pIface);
559     }
560 
561     protected android.hardware.wifi.supplicant.V1_4.ISupplicantP2pIface
getP2pIfaceMockableV1_4()562             getP2pIfaceMockableV1_4() {
563         if (mISupplicantP2pIface == null) return null;
564         return android.hardware.wifi.supplicant.V1_4.ISupplicantP2pIface.castFrom(
565                 mISupplicantP2pIface);
566     }
567 
getP2pNetworkMockable(ISupplicantNetwork network)568     protected ISupplicantP2pNetwork getP2pNetworkMockable(ISupplicantNetwork network) {
569         return ISupplicantP2pNetwork.asInterface(network.asBinder());
570     }
571 
572     /**
573      * Check if the device is running V1_1 supplicant service.
574      * @return
575      */
isV1_1()576     private boolean isV1_1() {
577         synchronized (mLock) {
578             try {
579                 return (getSupplicantMockableV1_1() != null);
580             } catch (RemoteException e) {
581                 Log.e(TAG, "ISupplicant.getService exception: " + e);
582                 supplicantServiceDiedHandler();
583                 return false;
584             }
585         }
586     }
587 
logd(String s)588     protected static void logd(String s) {
589         if (sVerboseLoggingEnabled) Log.d(TAG, s);
590     }
591 
logw(String s)592     protected static void logw(String s) {
593         Log.w(TAG, s);
594     }
595 
logCompletion(String operation, int code, String debugMessage)596     protected static <S> void logCompletion(String operation, int code, String debugMessage) {
597         if (code == SupplicantStatusCode.SUCCESS) {
598             logd(operation + " completed successfully.");
599         } else {
600             Log.w(TAG, operation + " failed: " + code + " (" + debugMessage + ")");
601         }
602     }
603 
604 
605     /**
606      * Returns false if SupplicantP2pIface is null, and logs failure to call methodStr
607      */
checkSupplicantP2pIfaceAndLogFailure(String method)608     private boolean checkSupplicantP2pIfaceAndLogFailure(String method) {
609         if (mISupplicantP2pIface == null) {
610             Log.e(TAG, "Can't call " + method + ": ISupplicantP2pIface is null");
611             return false;
612         }
613         return true;
614     }
615 
616     /**
617      * Returns SupplicantP2pIface on success, logs failure to call methodStr
618      * and returns false otherwise
619      */
620     private android.hardware.wifi.supplicant.V1_2.ISupplicantP2pIface
getSupplicantP2pIfaceAndLogFailureV1_2(String method)621             getSupplicantP2pIfaceAndLogFailureV1_2(String method) {
622         synchronized (mLock) {
623             android.hardware.wifi.supplicant.V1_2.ISupplicantP2pIface p2pIfaceV12 =
624                     getP2pIfaceMockableV1_2();
625             if (p2pIfaceV12 == null) {
626                 Log.e(TAG, "Can't call " + method + ": ISupplicantP2pIface is null");
627                 return null;
628             }
629             return p2pIfaceV12;
630         }
631     }
632 
633     /**
634      * Returns SupplicantP2pIface on success, logs failure to call methodStr
635      * and returns false otherwise
636      */
637     private android.hardware.wifi.supplicant.V1_4.ISupplicantP2pIface
getSupplicantP2pIfaceAndLogFailureV1_4(String method)638             getSupplicantP2pIfaceAndLogFailureV1_4(String method) {
639         synchronized (mLock) {
640             android.hardware.wifi.supplicant.V1_4.ISupplicantP2pIface p2pIfaceV12 =
641                     getP2pIfaceMockableV1_4();
642             if (p2pIfaceV12 == null) {
643                 Log.e(TAG, "Can't call " + method + ": ISupplicantP2pIface is null");
644                 return null;
645             }
646             return p2pIfaceV12;
647         }
648     }
649 
wpsInfoToConfigMethod(int info)650     private int wpsInfoToConfigMethod(int info) {
651         switch (info) {
652             case WpsInfo.PBC:
653                 return ISupplicantP2pIface.WpsProvisionMethod.PBC;
654 
655             case WpsInfo.DISPLAY:
656                 return ISupplicantP2pIface.WpsProvisionMethod.DISPLAY;
657 
658             case WpsInfo.KEYPAD:
659             case WpsInfo.LABEL:
660                 return ISupplicantP2pIface.WpsProvisionMethod.KEYPAD;
661 
662             default:
663                 Log.e(TAG, "Unsupported WPS provision method: " + info);
664                 return RESULT_NOT_VALID;
665         }
666     }
667 
668     /**
669      * Retrieves the name of the network interface.
670      *
671      * @return name Name of the network interface, e.g., wlan0
672      */
getName()673     public String getName() {
674         synchronized (mLock) {
675             if (!checkSupplicantP2pIfaceAndLogFailure("getName")) return null;
676             SupplicantResult<String> result = new SupplicantResult("getName()");
677 
678             try {
679                 mISupplicantP2pIface.getName(
680                         (SupplicantStatus status, String name) -> {
681                             result.setResult(status, name);
682                         });
683             } catch (RemoteException e) {
684                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
685                 supplicantServiceDiedHandler();
686             }
687             return result.getResult();
688         }
689     }
690 
691 
692     /**
693      * Register for callbacks from this interface.
694      *
695      * These callbacks are invoked for events that are specific to this interface.
696      * Registration of multiple callback objects is supported. These objects must
697      * be automatically deleted when the corresponding client process is dead or
698      * if this interface is removed.
699      *
700      * @param receiver An instance of the |ISupplicantP2pIfaceCallback| HIDL
701      *        interface object.
702      * @return boolean value indicating whether operation was successful.
703      */
registerCallback(ISupplicantP2pIfaceCallback receiver)704     public boolean registerCallback(ISupplicantP2pIfaceCallback receiver) {
705         synchronized (mLock) {
706             if (!checkSupplicantP2pIfaceAndLogFailure("registerCallback")) return false;
707             SupplicantResult<Void> result = new SupplicantResult("registerCallback()");
708             try {
709                 result.setResult(mISupplicantP2pIface.registerCallback(receiver));
710             } catch (RemoteException e) {
711                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
712                 supplicantServiceDiedHandler();
713             }
714             return result.isSuccess();
715         }
716     }
717 
718 
719     /**
720      * Register for callbacks from this interface.
721      *
722      * These callbacks are invoked for events that are specific to this interface.
723      * Registration of multiple callback objects is supported. These objects must
724      * be automatically deleted when the corresponding client process is dead or
725      * if this interface is removed.
726      *
727      * @param receiver An instance of the |ISupplicantP2pIfaceCallback| HIDL
728      *        interface object.
729      * @return boolean value indicating whether operation was successful.
730      */
registerCallbackV1_4( android.hardware.wifi.supplicant.V1_4.ISupplicantP2pIfaceCallback receiver)731     public boolean registerCallbackV1_4(
732             android.hardware.wifi.supplicant.V1_4.ISupplicantP2pIfaceCallback receiver) {
733         synchronized (mLock) {
734             if (!checkSupplicantP2pIfaceAndLogFailure("registerCallbackV1_4")) return false;
735             android.hardware.wifi.supplicant.V1_4.ISupplicantP2pIface ifaceV14 =
736                     getP2pIfaceMockableV1_4();
737             if (null == ifaceV14) return false;
738             SupplicantResultV1_4<Void> result =
739                     new SupplicantResultV1_4("registerCallbackV1_4()");
740             try {
741                 result.setResult(ifaceV14.registerCallback_1_4(receiver));
742             } catch (RemoteException e) {
743                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
744                 supplicantServiceDiedHandler();
745             }
746             return result.isSuccess();
747         }
748     }
749 
750     /**
751      * Initiate a P2P service discovery with a (optional) timeout.
752      *
753      * @param timeout Max time to be spent is performing discovery.
754      *        Set to 0 to indefinitely continue discovery until an explicit
755      *        |stopFind| is sent.
756      * @return boolean value indicating whether operation was successful.
757      */
find(int timeout)758     public boolean find(int timeout) {
759         synchronized (mLock) {
760             if (!checkSupplicantP2pIfaceAndLogFailure("find")) return false;
761 
762             if (timeout < 0) {
763                 Log.e(TAG, "Invalid timeout value: " + timeout);
764                 return false;
765             }
766             SupplicantResult<Void> result = new SupplicantResult("find(" + timeout + ")");
767             try {
768                 result.setResult(mISupplicantP2pIface.find(timeout));
769             } catch (RemoteException e) {
770                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
771                 supplicantServiceDiedHandler();
772             }
773             return result.isSuccess();
774         }
775     }
776 
777     /**
778      * Initiate a P2P device discovery with a scan type, a (optional) frequency, and a (optional)
779      * timeout.
780      *
781      * @param type indicates what channels to scan.
782      *        Valid values are {@link WifiP2pManager#WIFI_P2P_SCAN_FULL} for doing full P2P scan,
783      *        {@link WifiP2pManager#WIFI_P2P_SCAN_SOCIAL} for scanning social channels,
784      *        {@link WifiP2pManager#WIFI_P2P_SCAN_SINGLE_FREQ} for scanning a specified frequency.
785      * @param freq is the frequency to be scanned.
786      *        The possible values are:
787      *        <ul>
788      *        <li> A valid frequency for {@link WifiP2pManager#WIFI_P2P_SCAN_SINGLE_FREQ}</li>
789      *        <li> {@link WifiP2pManager#WIFI_P2P_SCAN_FREQ_UNSPECIFIED} for
790      *          {@link WifiP2pManager#WIFI_P2P_SCAN_FULL} and
791      *          {@link WifiP2pManager#WIFI_P2P_SCAN_SOCIAL}</li>
792      *        </ul>
793      * @param timeout Max time to be spent is performing discovery.
794      *        Set to 0 to indefinitely continue discovery until an explicit
795      *        |stopFind| is sent.
796      * @return false, for it's not supported in HIDL.
797      */
find(@ifiP2pManager.WifiP2pScanType int type, int freq, int timeout)798     public boolean find(@WifiP2pManager.WifiP2pScanType int type, int freq, int timeout) {
799         return false;
800     }
801 
802     /**
803      * Stop an ongoing P2P service discovery.
804      *
805      * @return boolean value indicating whether operation was successful.
806      */
stopFind()807     public boolean stopFind() {
808         synchronized (mLock) {
809             if (!checkSupplicantP2pIfaceAndLogFailure("stopFind")) return false;
810             SupplicantResult<Void> result = new SupplicantResult("stopFind()");
811             try {
812                 result.setResult(mISupplicantP2pIface.stopFind());
813             } catch (RemoteException e) {
814                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
815                 supplicantServiceDiedHandler();
816             }
817             return result.isSuccess();
818         }
819     }
820 
821 
822     /**
823      * Flush P2P peer table and state.
824      *
825      * @return boolean value indicating whether operation was successful.
826      */
flush()827     public boolean flush() {
828         synchronized (mLock) {
829             if (!checkSupplicantP2pIfaceAndLogFailure("flush")) return false;
830             SupplicantResult<Void> result = new SupplicantResult("flush()");
831             try {
832                 result.setResult(mISupplicantP2pIface.flush());
833             } catch (RemoteException e) {
834                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
835                 supplicantServiceDiedHandler();
836             }
837             return result.isSuccess();
838         }
839     }
840 
841 
842     /**
843      * This command can be used to flush all services from the
844      * device.
845      *
846      * @return boolean value indicating whether operation was successful.
847      */
serviceFlush()848     public boolean serviceFlush() {
849         synchronized (mLock) {
850             if (!checkSupplicantP2pIfaceAndLogFailure("serviceFlush")) return false;
851             SupplicantResult<Void> result = new SupplicantResult("serviceFlush()");
852             try {
853                 result.setResult(mISupplicantP2pIface.flushServices());
854             } catch (RemoteException e) {
855                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
856                 supplicantServiceDiedHandler();
857             }
858             return result.isSuccess();
859         }
860     }
861 
862 
863     /**
864      * Turn on/off power save mode for the interface.
865      *
866      * @param groupIfName Group interface name to use.
867      * @param enable Indicate if power save is to be turned on/off.
868      *
869      * @return boolean value indicating whether operation was successful.
870      */
setPowerSave(String groupIfName, boolean enable)871     public boolean setPowerSave(String groupIfName, boolean enable) {
872         synchronized (mLock) {
873             if (!checkSupplicantP2pIfaceAndLogFailure("setPowerSave")) return false;
874             SupplicantResult<Void> result = new SupplicantResult(
875                     "setPowerSave(" + groupIfName + ", " + enable + ")");
876             try {
877                 result.setResult(mISupplicantP2pIface.setPowerSave(groupIfName, enable));
878             } catch (RemoteException e) {
879                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
880                 supplicantServiceDiedHandler();
881             }
882             return result.isSuccess();
883         }
884     }
885 
886 
887     /**
888      * Set the Maximum idle time in seconds for P2P groups.
889      * This value controls how long a P2P group is maintained after there
890      * is no other members in the group. As a group owner, this means no
891      * associated stations in the group. As a P2P client, this means no
892      * group owner seen in scan results.
893      *
894      * @param groupIfName Group interface name to use.
895      * @param timeoutInSec Timeout value in seconds.
896      *
897      * @return boolean value indicating whether operation was successful.
898      */
setGroupIdle(String groupIfName, int timeoutInSec)899     public boolean setGroupIdle(String groupIfName, int timeoutInSec) {
900         synchronized (mLock) {
901             if (!checkSupplicantP2pIfaceAndLogFailure("setGroupIdle")) return false;
902             // Basic checking here. Leave actual parameter validation to supplicant.
903             if (timeoutInSec < 0) {
904                 Log.e(TAG, "Invalid group timeout value " + timeoutInSec);
905                 return false;
906             }
907 
908             SupplicantResult<Void> result = new SupplicantResult(
909                     "setGroupIdle(" + groupIfName + ", " + timeoutInSec + ")");
910             try {
911                 result.setResult(mISupplicantP2pIface.setGroupIdle(groupIfName, timeoutInSec));
912             } catch (RemoteException e) {
913                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
914                 supplicantServiceDiedHandler();
915             }
916             return result.isSuccess();
917         }
918     }
919 
920 
921     /**
922      * Set the postfix to be used for P2P SSID's.
923      *
924      * @param postfix String to be appended to SSID.
925      *
926      * @return boolean value indicating whether operation was successful.
927      */
setSsidPostfix(String postfix)928     public boolean setSsidPostfix(String postfix) {
929         synchronized (mLock) {
930             if (!checkSupplicantP2pIfaceAndLogFailure("setSsidPostfix")) return false;
931             // Basic checking here. Leave actual parameter validation to supplicant.
932             if (postfix == null) {
933                 Log.e(TAG, "Invalid SSID postfix value (null).");
934                 return false;
935             }
936 
937             SupplicantResult<Void> result = new SupplicantResult("setSsidPostfix(" + postfix + ")");
938             try {
939                 result.setResult(mISupplicantP2pIface.setSsidPostfix(
940                         NativeUtil.decodeSsid("\"" + postfix + "\"")));
941             } catch (RemoteException e) {
942                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
943                 supplicantServiceDiedHandler();
944             } catch (IllegalArgumentException e) {
945                 Log.e(TAG, "Could not decode SSID.", e);
946                 return false;
947             }
948 
949             return result.isSuccess();
950         }
951     }
952 
953 
954     /**
955      * Start P2P group formation with a discovered P2P peer. This includes
956      * optional group owner negotiation, group interface setup, provisioning,
957      * and establishing data connection.
958      *
959      * @param config Configuration to use to connect to remote device.
960      * @param joinExistingGroup Indicates that this is a command to join an
961      *        existing group as a client. It skips the group owner negotiation
962      *        part. This must send a Provision Discovery Request message to the
963      *        target group owner before associating for WPS provisioning.
964      *
965      * @return String containing generated pin, if selected provision method
966      *        uses PIN.
967      */
connect(WifiP2pConfig config, boolean joinExistingGroup)968     public String connect(WifiP2pConfig config, boolean joinExistingGroup) {
969         if (config == null) return null;
970         synchronized (mLock) {
971             if (!checkSupplicantP2pIfaceAndLogFailure("setSsidPostfix")) return null;
972 
973             if (config == null) {
974                 Log.e(TAG, "Could not connect: null config.");
975                 return null;
976             }
977 
978             if (config.deviceAddress == null) {
979                 Log.e(TAG, "Could not parse null mac address.");
980                 return null;
981             }
982 
983             if (config.wps.setup == WpsInfo.PBC && !TextUtils.isEmpty(config.wps.pin)) {
984                 Log.e(TAG, "Expected empty pin for PBC.");
985                 return null;
986             }
987 
988             byte[] peerAddress = null;
989             try {
990                 peerAddress = NativeUtil.macAddressToByteArray(config.deviceAddress);
991             } catch (Exception e) {
992                 Log.e(TAG, "Could not parse peer mac address.", e);
993                 return null;
994             }
995 
996             int provisionMethod = wpsInfoToConfigMethod(config.wps.setup);
997             if (provisionMethod == RESULT_NOT_VALID) {
998                 Log.e(TAG, "Invalid WPS config method: " + config.wps.setup);
999                 return null;
1000             }
1001             // NOTE: preSelectedPin cannot be null, otherwise hal would crash.
1002             String preSelectedPin = TextUtils.isEmpty(config.wps.pin) ? "" : config.wps.pin;
1003             boolean persistent = (config.netId == WifiP2pGroup.NETWORK_ID_PERSISTENT);
1004 
1005             if (config.groupOwnerIntent < 0 || config.groupOwnerIntent > 15) {
1006                 Log.e(TAG, "Invalid group owner intent: " + config.groupOwnerIntent);
1007                 return null;
1008             }
1009 
1010             SupplicantResult<String> result = new SupplicantResult(
1011                     "connect(" + config.deviceAddress + ")");
1012             try {
1013                 mISupplicantP2pIface.connect(
1014                         peerAddress, provisionMethod, preSelectedPin, joinExistingGroup,
1015                         persistent, config.groupOwnerIntent,
1016                         (SupplicantStatus status, String generatedPin) -> {
1017                             result.setResult(status, generatedPin);
1018                         });
1019             } catch (RemoteException e) {
1020                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
1021                 supplicantServiceDiedHandler();
1022             }
1023             return result.getResult();
1024         }
1025     }
1026 
1027     /**
1028      * Cancel an ongoing P2P group formation and joining-a-group related
1029      * operation. This operation unauthorizes the specific peer device (if any
1030      * had been authorized to start group formation), stops P2P find (if in
1031      * progress), stops pending operations for join-a-group, and removes the
1032      * P2P group interface (if one was used) that is in the WPS provisioning
1033      * step. If the WPS provisioning step has been completed, the group is not
1034      * terminated.
1035      *
1036      * @return boolean value indicating whether operation was successful.
1037      */
cancelConnect()1038     public boolean cancelConnect() {
1039         synchronized (mLock) {
1040             if (!checkSupplicantP2pIfaceAndLogFailure("cancelConnect")) return false;
1041             SupplicantResult<Void> result = new SupplicantResult("cancelConnect()");
1042             try {
1043                 result.setResult(mISupplicantP2pIface.cancelConnect());
1044             } catch (RemoteException e) {
1045                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
1046                 supplicantServiceDiedHandler();
1047             }
1048             return result.isSuccess();
1049         }
1050     }
1051 
1052 
1053     /**
1054      * Send P2P provision discovery request to the specified peer. The
1055      * parameters for this command are the P2P device address of the peer and the
1056      * desired configuration method.
1057      *
1058      * @param config Config class describing peer setup.
1059      *
1060      * @return boolean value indicating whether operation was successful.
1061      */
provisionDiscovery(WifiP2pConfig config)1062     public boolean provisionDiscovery(WifiP2pConfig config) {
1063         if (config == null) return false;
1064         synchronized (mLock) {
1065             if (!checkSupplicantP2pIfaceAndLogFailure("provisionDiscovery")) return false;
1066 
1067             int targetMethod = wpsInfoToConfigMethod(config.wps.setup);
1068             if (targetMethod == RESULT_NOT_VALID) {
1069                 Log.e(TAG, "Unrecognized WPS configuration method: " + config.wps.setup);
1070                 return false;
1071             }
1072             if (targetMethod == ISupplicantP2pIface.WpsProvisionMethod.DISPLAY) {
1073                 // We are doing display, so provision discovery is keypad.
1074                 targetMethod = ISupplicantP2pIface.WpsProvisionMethod.KEYPAD;
1075             } else if (targetMethod == ISupplicantP2pIface.WpsProvisionMethod.KEYPAD) {
1076                 // We are doing keypad, so provision discovery is display.
1077                 targetMethod = ISupplicantP2pIface.WpsProvisionMethod.DISPLAY;
1078             }
1079 
1080             if (config.deviceAddress == null) {
1081                 Log.e(TAG, "Cannot parse null mac address.");
1082                 return false;
1083             }
1084             byte[] macAddress = null;
1085             try {
1086                 macAddress = NativeUtil.macAddressToByteArray(config.deviceAddress);
1087             } catch (Exception e) {
1088                 Log.e(TAG, "Could not parse peer mac address.", e);
1089                 return false;
1090             }
1091 
1092             SupplicantResult<Void> result = new SupplicantResult(
1093                     "provisionDiscovery(" + config.deviceAddress + ", " + config.wps.setup + ")");
1094             try {
1095                 result.setResult(mISupplicantP2pIface.provisionDiscovery(macAddress, targetMethod));
1096             } catch (RemoteException e) {
1097                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
1098                 supplicantServiceDiedHandler();
1099             }
1100 
1101             return result.isSuccess();
1102         }
1103     }
1104 
1105 
1106     /**
1107      * Invite a device to a persistent group.
1108      * If the peer device is the group owner of the persistent group, the peer
1109      * parameter is not needed. Otherwise it is used to specify which
1110      * device to invite. |goDeviceAddress| parameter may be used to override
1111      * the group owner device address for Invitation Request should it not be
1112      * known for some reason (this should not be needed in most cases).
1113      *
1114      * @param group Group object to use.
1115      * @param peerAddress MAC address of the device to invite.
1116      *
1117      * @return boolean value indicating whether operation was successful.
1118      */
invite(WifiP2pGroup group, String peerAddress)1119     public boolean invite(WifiP2pGroup group, String peerAddress) {
1120         if (TextUtils.isEmpty(peerAddress)) return false;
1121         synchronized (mLock) {
1122             if (!checkSupplicantP2pIfaceAndLogFailure("invite")) return false;
1123             if (group == null) {
1124                 Log.e(TAG, "Cannot invite to null group.");
1125                 return false;
1126             }
1127 
1128             if (group.getOwner() == null) {
1129                 Log.e(TAG, "Cannot invite to group with null owner.");
1130                 return false;
1131             }
1132 
1133             if (group.getOwner().deviceAddress == null) {
1134                 Log.e(TAG, "Group owner has no mac address.");
1135                 return false;
1136             }
1137 
1138             byte[] ownerMacAddress = null;
1139             try {
1140                 ownerMacAddress = NativeUtil.macAddressToByteArray(group.getOwner().deviceAddress);
1141             } catch (Exception e) {
1142                 Log.e(TAG, "Group owner mac address parse error.", e);
1143                 return false;
1144             }
1145 
1146             if (peerAddress == null) {
1147                 Log.e(TAG, "Cannot parse peer mac address.");
1148                 return false;
1149             }
1150 
1151             byte[] peerMacAddress;
1152             try {
1153                 peerMacAddress = NativeUtil.macAddressToByteArray(peerAddress);
1154             } catch (Exception e) {
1155                 Log.e(TAG, "Peer mac address parse error.", e);
1156                 return false;
1157             }
1158 
1159             SupplicantResult<Void> result = new SupplicantResult(
1160                     "invite(" + group.getInterface() + ", " + group.getOwner().deviceAddress
1161                             + ", " + peerAddress + ")");
1162             try {
1163                 result.setResult(mISupplicantP2pIface.invite(
1164                         group.getInterface(), ownerMacAddress, peerMacAddress));
1165             } catch (RemoteException e) {
1166                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
1167                 supplicantServiceDiedHandler();
1168             }
1169             return result.isSuccess();
1170         }
1171     }
1172 
1173 
1174     /**
1175      * Reject connection attempt from a peer (specified with a device
1176      * address). This is a mechanism to reject a pending group owner negotiation
1177      * with a peer and request to automatically block any further connection or
1178      * discovery of the peer.
1179      *
1180      * @param peerAddress MAC address of the device to reject.
1181      *
1182      * @return boolean value indicating whether operation was successful.
1183      */
reject(String peerAddress)1184     public boolean reject(String peerAddress) {
1185         synchronized (mLock) {
1186             if (!checkSupplicantP2pIfaceAndLogFailure("reject")) return false;
1187 
1188             if (peerAddress == null) {
1189                 Log.e(TAG, "Cannot parse rejected peer's mac address.");
1190                 return false;
1191             }
1192             byte[] macAddress = null;
1193             try {
1194                 macAddress = NativeUtil.macAddressToByteArray(peerAddress);
1195             } catch (Exception e) {
1196                 Log.e(TAG, "Could not parse peer mac address.", e);
1197                 return false;
1198             }
1199 
1200             SupplicantResult<Void> result =
1201                     new SupplicantResult("reject(" + peerAddress + ")");
1202             try {
1203                 result.setResult(mISupplicantP2pIface.reject(macAddress));
1204             } catch (RemoteException e) {
1205                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
1206                 supplicantServiceDiedHandler();
1207             }
1208 
1209             return result.isSuccess();
1210         }
1211     }
1212 
1213 
1214     /**
1215      * Gets the MAC address of the device.
1216      *
1217      * @return MAC address of the device.
1218      */
getDeviceAddress()1219     public String getDeviceAddress() {
1220         synchronized (mLock) {
1221             if (!checkSupplicantP2pIfaceAndLogFailure("getDeviceAddress")) return null;
1222             SupplicantResult<String> result = new SupplicantResult("getDeviceAddress()");
1223             try {
1224                 mISupplicantP2pIface.getDeviceAddress((SupplicantStatus status, byte[] address) -> {
1225                     String parsedAddress = null;
1226                     try {
1227                         parsedAddress = NativeUtil.macAddressFromByteArray(address);
1228                     } catch (Exception e) {
1229                         Log.e(TAG, "Could not process reported address.", e);
1230                     }
1231                     result.setResult(status, parsedAddress);
1232                 });
1233             } catch (RemoteException e) {
1234                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
1235                 supplicantServiceDiedHandler();
1236                 return null;
1237             }
1238 
1239             return result.getResult();
1240         }
1241     }
1242 
1243 
1244     /**
1245      * Gets the operational SSID of the device.
1246      *
1247      * @param address MAC address of the peer.
1248      *
1249      * @return SSID of the device.
1250      */
getSsid(String address)1251     public String getSsid(String address) {
1252         synchronized (mLock) {
1253             if (!checkSupplicantP2pIfaceAndLogFailure("getSsid")) return null;
1254 
1255             if (address == null) {
1256                 Log.e(TAG, "Cannot parse peer mac address.");
1257                 return null;
1258             }
1259             byte[] macAddress = null;
1260             try {
1261                 macAddress = NativeUtil.macAddressToByteArray(address);
1262             } catch (Exception e) {
1263                 Log.e(TAG, "Could not parse mac address.", e);
1264                 return null;
1265             }
1266 
1267             SupplicantResult<String> result =
1268                     new SupplicantResult("getSsid(" + address + ")");
1269             try {
1270                 mISupplicantP2pIface.getSsid(
1271                         macAddress, (SupplicantStatus status, ArrayList<Byte> ssid) -> {
1272                             String ssidString = null;
1273                             if (ssid != null) {
1274                                 try {
1275                                     ssidString = NativeUtil.removeEnclosingQuotes(
1276                                             NativeUtil.encodeSsid(ssid));
1277                                 } catch (Exception e) {
1278                                     Log.e(TAG, "Could not encode SSID.", e);
1279                                 }
1280                             }
1281                             result.setResult(status, ssidString);
1282                         });
1283             } catch (RemoteException e) {
1284                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
1285                 supplicantServiceDiedHandler();
1286                 return null;
1287             }
1288 
1289             return result.getResult();
1290         }
1291     }
1292 
1293 
1294     /**
1295      * Reinvoke a device from a persistent group.
1296      *
1297      * @param networkId Used to specify the persistent group.
1298      * @param peerAddress MAC address of the device to reinvoke.
1299      *
1300      * @return true, if operation was successful.
1301      */
reinvoke(int networkId, String peerAddress)1302     public boolean reinvoke(int networkId, String peerAddress) {
1303         if (TextUtils.isEmpty(peerAddress) || networkId < 0) return false;
1304         synchronized (mLock) {
1305             if (!checkSupplicantP2pIfaceAndLogFailure("reinvoke")) return false;
1306             if (peerAddress == null) {
1307                 Log.e(TAG, "Cannot parse peer mac address.");
1308                 return false;
1309             }
1310             byte[] macAddress = null;
1311             try {
1312                 macAddress = NativeUtil.macAddressToByteArray(peerAddress);
1313             } catch (Exception e) {
1314                 Log.e(TAG, "Could not parse mac address.", e);
1315                 return false;
1316             }
1317 
1318             SupplicantResult<Void> result = new SupplicantResult(
1319                     "reinvoke(" + networkId + ", " + peerAddress + ")");
1320             try {
1321                 result.setResult(mISupplicantP2pIface.reinvoke(networkId, macAddress));
1322             } catch (RemoteException e) {
1323                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
1324                 supplicantServiceDiedHandler();
1325             }
1326 
1327             return result.isSuccess();
1328         }
1329     }
1330 
1331     /**
1332      * Set up a P2P group owner manually (i.e., without group owner
1333      * negotiation with a specific peer). This is also known as autonomous
1334      * group owner.
1335      *
1336      * @param networkId Used to specify the restart of a persistent group.
1337      * @param isPersistent Used to request a persistent group to be formed.
1338      *
1339      * @return true, if operation was successful.
1340      */
groupAdd(int networkId, boolean isPersistent)1341     public boolean groupAdd(int networkId, boolean isPersistent) {
1342         synchronized (mLock) {
1343             if (!checkSupplicantP2pIfaceAndLogFailure("groupAdd")) return false;
1344             SupplicantResult<Void> result =
1345                     new SupplicantResult("groupAdd(" + networkId + ", " + isPersistent + ")");
1346             try {
1347                 result.setResult(mISupplicantP2pIface.addGroup(isPersistent, networkId));
1348             } catch (RemoteException e) {
1349                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
1350                 supplicantServiceDiedHandler();
1351             }
1352             return result.isSuccess();
1353         }
1354     }
1355 
1356     /**
1357      * Set up a P2P group as Group Owner or join a group with a configuration.
1358      *
1359      * @param networkName SSID of the group to be formed
1360      * @param passphrase passphrase of the group to be formed
1361      * @param isPersistent Used to request a persistent group to be formed.
1362      * @param freq preferred frequency or band of the group to be formed
1363      * @param peerAddress peerAddress Group Owner MAC address, only applied for Group Client.
1364      *        If the MAC is "00:00:00:00:00:00", the device will try to find a peer
1365      *        whose SSID matches ssid.
1366      * @param join join a group or create a group
1367      *
1368      * @return true, if operation was successful.
1369      */
groupAdd(String networkName, String passphrase, boolean isPersistent, int freq, String peerAddress, boolean join)1370     public boolean groupAdd(String networkName, String passphrase,
1371             boolean isPersistent, int freq, String peerAddress, boolean join) {
1372         synchronized (mLock) {
1373             android.hardware.wifi.supplicant.V1_2.ISupplicantP2pIface ifaceV12 =
1374                     getSupplicantP2pIfaceAndLogFailureV1_2("groupAdd_1_2");
1375             if (ifaceV12 == null) return false;
1376 
1377             java.util.ArrayList<Byte> ssid = NativeUtil.decodeSsid("\"" + networkName + "\"");
1378             byte[] macAddress = null;
1379             try {
1380                 macAddress = NativeUtil.macAddressToByteArray(peerAddress);
1381             } catch (Exception e) {
1382                 Log.e(TAG, "Could not parse mac address.", e);
1383                 return false;
1384             }
1385 
1386             SupplicantResult<Void> result =
1387                     new SupplicantResult("groupAdd(" + networkName + ", "
1388                         + (TextUtils.isEmpty(passphrase) ? "<Empty>" : "<Non-Empty>")
1389                         + ", " + isPersistent + ", " + freq
1390                         + ", " + peerAddress + ", " + join + ")");
1391             try {
1392                 result.setResult(ifaceV12.addGroup_1_2(
1393                         ssid, passphrase, isPersistent, freq, macAddress, join));
1394             } catch (RemoteException e) {
1395                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
1396                 supplicantServiceDiedHandler();
1397             }
1398             return result.isSuccess();
1399         }
1400     }
1401 
1402     /**
1403      * Terminate a P2P group. If a new virtual network interface was used for
1404      * the group, it must also be removed. The network interface name of the
1405      * group interface is used as a parameter for this command.
1406      *
1407      * @param groupName Group interface name to use.
1408      *
1409      * @return true, if operation was successful.
1410      */
groupRemove(String groupName)1411     public boolean groupRemove(String groupName) {
1412         if (TextUtils.isEmpty(groupName)) return false;
1413         synchronized (mLock) {
1414             if (!checkSupplicantP2pIfaceAndLogFailure("groupRemove")) return false;
1415             SupplicantResult<Void> result = new SupplicantResult("groupRemove(" + groupName + ")");
1416             try {
1417                 result.setResult(mISupplicantP2pIface.removeGroup(groupName));
1418             } catch (RemoteException e) {
1419                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
1420                 supplicantServiceDiedHandler();
1421             }
1422             return result.isSuccess();
1423         }
1424     }
1425 
1426 
1427     /**
1428      * Gets the capability of the group which the device is a
1429      * member of.
1430      *
1431      * @param peerAddress MAC address of the peer.
1432      *
1433      * @return combination of |GroupCapabilityMask| values.
1434      */
getGroupCapability(String peerAddress)1435     public int getGroupCapability(String peerAddress) {
1436         synchronized (mLock) {
1437             if (!checkSupplicantP2pIfaceAndLogFailure("getGroupCapability")) {
1438                 return RESULT_NOT_VALID;
1439             }
1440 
1441             if (peerAddress == null) {
1442                 Log.e(TAG, "Cannot parse peer mac address.");
1443                 return RESULT_NOT_VALID;
1444             }
1445             byte[] macAddress = null;
1446             try {
1447                 macAddress = NativeUtil.macAddressToByteArray(peerAddress);
1448             } catch (Exception e) {
1449                 Log.e(TAG, "Could not parse group address.", e);
1450                 return RESULT_NOT_VALID;
1451             }
1452 
1453             SupplicantResult<Integer> capability = new SupplicantResult(
1454                     "getGroupCapability(" + peerAddress + ")");
1455             try {
1456                 mISupplicantP2pIface.getGroupCapability(
1457                         macAddress, (SupplicantStatus status, int cap) -> {
1458                             capability.setResult(status, cap);
1459                         });
1460             } catch (RemoteException e) {
1461                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
1462                 supplicantServiceDiedHandler();
1463             }
1464 
1465             if (!capability.isSuccess()) {
1466                 return RESULT_NOT_VALID;
1467             }
1468 
1469             return capability.getResult();
1470         }
1471     }
1472 
1473 
1474     /**
1475      * Configure Extended Listen Timing.
1476      *
1477      * If enabled, listen state must be entered every |intervalInMillis| for at
1478      * least |periodInMillis|. Both values have acceptable range of 1-65535
1479      * (with interval obviously having to be larger than or equal to duration).
1480      * If the P2P module is not idle at the time the Extended Listen Timing
1481      * timeout occurs, the Listen State operation must be skipped.
1482      *
1483      * @param enable Enables or disables listening.
1484      * @param periodInMillis Period in milliseconds.
1485      * @param intervalInMillis Interval in milliseconds.
1486      *
1487      * @return true, if operation was successful.
1488      */
configureExtListen(boolean enable, int periodInMillis, int intervalInMillis)1489     public boolean configureExtListen(boolean enable, int periodInMillis, int intervalInMillis) {
1490         if (enable && intervalInMillis < periodInMillis) {
1491             return false;
1492         }
1493         synchronized (mLock) {
1494             if (!checkSupplicantP2pIfaceAndLogFailure("configureExtListen")) return false;
1495 
1496             // If listening is disabled, wpa supplicant expects zeroes.
1497             if (!enable) {
1498                 periodInMillis = 0;
1499                 intervalInMillis = 0;
1500             }
1501 
1502             // Verify that the integers are not negative. Leave actual parameter validation to
1503             // supplicant.
1504             if (periodInMillis < 0 || intervalInMillis < 0) {
1505                 Log.e(TAG, "Invalid parameters supplied to configureExtListen: " + periodInMillis
1506                         + ", " + intervalInMillis);
1507                 return false;
1508             }
1509 
1510             SupplicantResult<Void> result = new SupplicantResult(
1511                     "configureExtListen(" + periodInMillis + ", " + intervalInMillis + ")");
1512             try {
1513                 result.setResult(
1514                         mISupplicantP2pIface.configureExtListen(periodInMillis, intervalInMillis));
1515             } catch (RemoteException e) {
1516                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
1517                 supplicantServiceDiedHandler();
1518             }
1519 
1520             return result.isSuccess();
1521         }
1522     }
1523 
1524 
1525     /**
1526      * Set P2P Listen channel.
1527      *
1528      * @param listenChannel Wifi channel. eg, 1, 6, 11.
1529      *
1530      * @return true, if operation was successful.
1531      */
setListenChannel(int listenChannel)1532     public boolean setListenChannel(int listenChannel) {
1533         synchronized (mLock) {
1534             if (!checkSupplicantP2pIfaceAndLogFailure("setListenChannel")) return false;
1535 
1536             // There is no original channel recorded in supplicant, so just return true.
1537             if (0 == listenChannel) return true;
1538 
1539             // Using channels other than 1, 6, and 11 would result in discovery issue.
1540             if (listenChannel != 1 && listenChannel != 6 && listenChannel != 11) {
1541                 return false;
1542             }
1543 
1544             SupplicantResult<Void> result = new SupplicantResult(
1545                     "setListenChannel(" + listenChannel + ", " + DEFAULT_OPERATING_CLASS + ")");
1546             try {
1547                 result.setResult(mISupplicantP2pIface.setListenChannel(
1548                         listenChannel, DEFAULT_OPERATING_CLASS));
1549             } catch (RemoteException e) {
1550                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
1551                 supplicantServiceDiedHandler();
1552             }
1553             return result.isSuccess();
1554         }
1555     }
1556 
1557     /**
1558      * Set P2P operating channel.
1559      *
1560      * @param operatingChannel the desired operating channel.
1561      * @param unsafeChannels channels which p2p cannot use.
1562      *
1563      * @return true, if operation was successful.
1564      */
setOperatingChannel(int operatingChannel, @NonNull List<CoexUnsafeChannel> unsafeChannels)1565     public boolean setOperatingChannel(int operatingChannel,
1566             @NonNull List<CoexUnsafeChannel> unsafeChannels) {
1567         synchronized (mLock) {
1568             if (!checkSupplicantP2pIfaceAndLogFailure("setOperatingChannel")) return false;
1569             if (null == unsafeChannels) return false;
1570 
1571             ArrayList<ISupplicantP2pIface.FreqRange> ranges = new ArrayList<>();
1572             if (operatingChannel >= 1 && operatingChannel <= 165) {
1573                 int freq = (operatingChannel <= 14 ? 2407 : 5000) + operatingChannel * 5;
1574                 ISupplicantP2pIface.FreqRange range1 =  new ISupplicantP2pIface.FreqRange();
1575                 range1.min = 1000;
1576                 range1.max = freq - 5;
1577                 ISupplicantP2pIface.FreqRange range2 =  new ISupplicantP2pIface.FreqRange();
1578                 range2.min = freq + 5;
1579                 range2.max = 6000;
1580                 ranges.add(range1);
1581                 ranges.add(range2);
1582             }
1583             if (SdkLevel.isAtLeastS()) {
1584                 for (CoexUnsafeChannel cuc: unsafeChannels) {
1585                     int centerFreq = ScanResult.convertChannelToFrequencyMhzIfSupported(
1586                             cuc.getChannel(), cuc.getBand());
1587                     ISupplicantP2pIface.FreqRange range = new ISupplicantP2pIface.FreqRange();
1588                     // The range boundaries are inclusive in native frequency inclusion check.
1589                     // Minusing one to avoid affecting neighbors.
1590                     range.min = centerFreq - 5 - 1;
1591                     range.max = centerFreq + 5 - 1;
1592                     ranges.add(range);
1593                 }
1594             }
1595             SupplicantResult<Void> result = new SupplicantResult(
1596                     "setDisallowedFrequencies(" + ranges + ")");
1597             try {
1598                 result.setResult(mISupplicantP2pIface.setDisallowedFrequencies(ranges));
1599             } catch (RemoteException e) {
1600                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
1601                 supplicantServiceDiedHandler();
1602             }
1603             return result.isSuccess();
1604         }
1605     }
1606 
1607     /**
1608      * This command can be used to add a upnp/bonjour service.
1609      *
1610      * @param servInfo List of service queries.
1611      *
1612      * @return true, if operation was successful.
1613      */
serviceAdd(WifiP2pServiceInfo servInfo)1614     public boolean serviceAdd(WifiP2pServiceInfo servInfo) {
1615         synchronized (mLock) {
1616             if (!checkSupplicantP2pIfaceAndLogFailure("serviceAdd")) return false;
1617 
1618             if (servInfo == null) {
1619                 Log.e(TAG, "Null service info passed.");
1620                 return false;
1621             }
1622 
1623             for (String s : servInfo.getSupplicantQueryList()) {
1624                 if (s == null) {
1625                     Log.e(TAG, "Invalid service description (null).");
1626                     return false;
1627                 }
1628 
1629                 String[] data = s.split(" ");
1630                 if (data.length < 3) {
1631                     Log.e(TAG, "Service specification invalid: " + s);
1632                     return false;
1633                 }
1634 
1635                 SupplicantResult<Void> result = null;
1636                 try {
1637                     if ("upnp".equals(data[0])) {
1638                         int version = 0;
1639                         try {
1640                             version = Integer.parseInt(data[1], 16);
1641                         } catch (NumberFormatException e) {
1642                             Log.e(TAG, "UPnP Service specification invalid: " + s, e);
1643                             return false;
1644                         }
1645 
1646                         result = new SupplicantResult(
1647                                 "addUpnpService(" + data[1] + ", " + data[2] + ")");
1648                         result.setResult(mISupplicantP2pIface.addUpnpService(version, data[2]));
1649                     } else if ("bonjour".equals(data[0])) {
1650                         if (data[1] != null && data[2] != null) {
1651                             ArrayList<Byte> request = null;
1652                             ArrayList<Byte> response = null;
1653                             try {
1654                                 request = NativeUtil.byteArrayToArrayList(
1655                                         NativeUtil.hexStringToByteArray(data[1]));
1656                                 response = NativeUtil.byteArrayToArrayList(
1657                                         NativeUtil.hexStringToByteArray(data[2]));
1658                             } catch (Exception e) {
1659                                 Log.e(TAG, "Invalid bonjour service description.");
1660                                 return false;
1661                             }
1662                             result = new SupplicantResult(
1663                                     "addBonjourService(" + data[1] + ", " + data[2] + ")");
1664                             result.setResult(
1665                                     mISupplicantP2pIface.addBonjourService(request, response));
1666                         }
1667                     } else {
1668                         return false;
1669                     }
1670                 } catch (RemoteException e) {
1671                     Log.e(TAG, "ISupplicantP2pIface exception: " + e);
1672                     supplicantServiceDiedHandler();
1673                 }
1674 
1675                 if (result == null || !result.isSuccess()) return false;
1676             }
1677 
1678             return true;
1679         }
1680     }
1681 
1682 
1683     /**
1684      * This command can be used to remove a upnp/bonjour service.
1685      *
1686      * @param servInfo List of service queries.
1687      *
1688      * @return true, if operation was successful.
1689      */
serviceRemove(WifiP2pServiceInfo servInfo)1690     public boolean serviceRemove(WifiP2pServiceInfo servInfo) {
1691         synchronized (mLock) {
1692             if (!checkSupplicantP2pIfaceAndLogFailure("serviceRemove")) return false;
1693 
1694             if (servInfo == null) {
1695                 Log.e(TAG, "Null service info passed.");
1696                 return false;
1697             }
1698 
1699             for (String s : servInfo.getSupplicantQueryList()) {
1700                 if (s == null) {
1701                     Log.e(TAG, "Invalid service description (null).");
1702                     return false;
1703                 }
1704 
1705                 String[] data = s.split(" ");
1706                 if (data.length < 3) {
1707                     Log.e(TAG, "Service specification invalid: " + s);
1708                     return false;
1709                 }
1710 
1711                 SupplicantResult<Void> result = null;
1712                 try {
1713                     if ("upnp".equals(data[0])) {
1714                         int version = 0;
1715                         try {
1716                             version = Integer.parseInt(data[1], 16);
1717                         } catch (NumberFormatException e) {
1718                             Log.e(TAG, "UPnP Service specification invalid: " + s, e);
1719                             return false;
1720                         }
1721                         result = new SupplicantResult(
1722                                 "removeUpnpService(" + data[1] + ", " + data[2] + ")");
1723                         result.setResult(mISupplicantP2pIface.removeUpnpService(version, data[2]));
1724                     } else if ("bonjour".equals(data[0])) {
1725                         if (data[1] != null) {
1726                             ArrayList<Byte> request = null;
1727                             try {
1728                                 request = NativeUtil.byteArrayToArrayList(
1729                                     NativeUtil.hexStringToByteArray(data[1]));
1730                             } catch (Exception e) {
1731                                 Log.e(TAG, "Invalid bonjour service description.");
1732                                 return false;
1733                             }
1734                             result = new SupplicantResult("removeBonjourService(" + data[1] + ")");
1735                             result.setResult(mISupplicantP2pIface.removeBonjourService(request));
1736                         }
1737                     } else {
1738                         Log.e(TAG, "Unknown / unsupported P2P service requested: " + data[0]);
1739                         return false;
1740                     }
1741                 } catch (RemoteException e) {
1742                     Log.e(TAG, "ISupplicantP2pIface exception: " + e);
1743                     supplicantServiceDiedHandler();
1744                 }
1745 
1746                 if (result == null || !result.isSuccess()) return false;
1747             }
1748 
1749             return true;
1750         }
1751     }
1752 
1753 
1754     /**
1755      * Schedule a P2P service discovery request. The parameters for this command
1756      * are the device address of the peer device (or 00:00:00:00:00:00 for
1757      * wildcard query that is sent to every discovered P2P peer that supports
1758      * service discovery) and P2P Service Query TLV(s) as hexdump.
1759      *
1760      * @param peerAddress MAC address of the device to discover.
1761      * @param query Hex dump of the query data.
1762      * @return identifier Identifier for the request. Can be used to cancel the
1763      *         request.
1764      */
requestServiceDiscovery(String peerAddress, String query)1765     public String requestServiceDiscovery(String peerAddress, String query) {
1766         synchronized (mLock) {
1767             if (!checkSupplicantP2pIfaceAndLogFailure("requestServiceDiscovery")) return null;
1768 
1769             if (peerAddress == null) {
1770                 Log.e(TAG, "Cannot parse peer mac address.");
1771                 return null;
1772             }
1773             byte[] macAddress = null;
1774             try {
1775                 macAddress = NativeUtil.macAddressToByteArray(peerAddress);
1776             } catch (Exception e) {
1777                 Log.e(TAG, "Could not process peer MAC address.", e);
1778                 return null;
1779             }
1780 
1781             if (query == null) {
1782                 Log.e(TAG, "Cannot parse service discovery query: " + query);
1783                 return null;
1784             }
1785             ArrayList<Byte> binQuery = null;
1786             try {
1787                 binQuery = NativeUtil.byteArrayToArrayList(NativeUtil.hexStringToByteArray(query));
1788             } catch (Exception e) {
1789                 Log.e(TAG, "Could not parse service query.", e);
1790                 return null;
1791             }
1792 
1793             SupplicantResult<Long> result = new SupplicantResult(
1794                     "requestServiceDiscovery(" + peerAddress + ", " + query + ")");
1795             try {
1796                 mISupplicantP2pIface.requestServiceDiscovery(
1797                         macAddress, binQuery,
1798                         (SupplicantStatus status, long identifier) -> {
1799                             result.setResult(status, new Long(identifier));
1800                         });
1801             } catch (RemoteException e) {
1802                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
1803                 supplicantServiceDiedHandler();
1804             }
1805 
1806             Long value = result.getResult();
1807             if (value == null) return null;
1808             return value.toString();
1809         }
1810     }
1811 
1812 
1813     /**
1814      * Cancel a previous service discovery request.
1815      *
1816      * @param identifier Identifier for the request to cancel.
1817      * @return true, if operation was successful.
1818      */
cancelServiceDiscovery(String identifier)1819     public boolean cancelServiceDiscovery(String identifier) {
1820         synchronized (mLock) {
1821             if (!checkSupplicantP2pIfaceAndLogFailure("cancelServiceDiscovery")) return false;
1822             if (identifier == null) {
1823                 Log.e(TAG, "cancelServiceDiscovery requires a valid tag.");
1824                 return false;
1825             }
1826 
1827             long id = 0;
1828             try {
1829                 id = Long.parseLong(identifier);
1830             } catch (NumberFormatException e) {
1831                 Log.e(TAG, "Service discovery identifier invalid: " + identifier, e);
1832                 return false;
1833             }
1834 
1835             SupplicantResult<Void> result = new SupplicantResult(
1836                     "cancelServiceDiscovery(" + identifier + ")");
1837             try {
1838                 result.setResult(mISupplicantP2pIface.cancelServiceDiscovery(id));
1839             } catch (RemoteException e) {
1840                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
1841                 supplicantServiceDiedHandler();
1842             }
1843 
1844             return result.isSuccess();
1845         }
1846     }
1847 
1848 
1849     /**
1850      * Send driver command to set Miracast mode.
1851      *
1852      * @param mode Mode of Miracast.
1853      * @return true, if operation was successful.
1854      */
setMiracastMode(int mode)1855     public boolean setMiracastMode(int mode) {
1856         synchronized (mLock) {
1857             if (!checkSupplicantP2pIfaceAndLogFailure("setMiracastMode")) return false;
1858             byte targetMode = ISupplicantP2pIface.MiracastMode.DISABLED;
1859 
1860             switch (mode) {
1861                 case WifiP2pManager.MIRACAST_SOURCE:
1862                     targetMode = ISupplicantP2pIface.MiracastMode.SOURCE;
1863                     break;
1864 
1865                 case WifiP2pManager.MIRACAST_SINK:
1866                     targetMode = ISupplicantP2pIface.MiracastMode.SINK;
1867                     break;
1868             }
1869 
1870             SupplicantResult<Void> result = new SupplicantResult(
1871                     "setMiracastMode(" + mode + ")");
1872             try {
1873                 result.setResult(mISupplicantP2pIface.setMiracastMode(targetMode));
1874             } catch (RemoteException e) {
1875                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
1876                 supplicantServiceDiedHandler();
1877             }
1878 
1879             return result.isSuccess();
1880         }
1881     }
1882 
1883 
1884     /**
1885      * Initiate WPS Push Button setup.
1886      * The PBC operation requires that a button is also pressed at the
1887      * AP/Registrar at about the same time (2 minute window).
1888      *
1889      * @param groupIfName Group interface name to use.
1890      * @param bssid BSSID of the AP. Use empty bssid to indicate wildcard.
1891      * @return true, if operation was successful.
1892      */
startWpsPbc(String groupIfName, String bssid)1893     public boolean startWpsPbc(String groupIfName, String bssid) {
1894         if (TextUtils.isEmpty(groupIfName)) {
1895             Log.e(TAG, "Group name required when requesting WPS PBC. Got (" + groupIfName + ")");
1896             return false;
1897         }
1898         synchronized (mLock) {
1899             if (!checkSupplicantP2pIfaceAndLogFailure("startWpsPbc")) return false;
1900             // Null values should be fine, since bssid can be empty.
1901             byte[] macAddress = null;
1902             try {
1903                 macAddress = NativeUtil.macAddressToByteArray(bssid);
1904             } catch (Exception e) {
1905                 Log.e(TAG, "Could not parse BSSID.", e);
1906                 return false;
1907             }
1908 
1909             SupplicantResult<Void> result = new SupplicantResult(
1910                     "startWpsPbc(" + groupIfName + ", " + bssid + ")");
1911             try {
1912                 result.setResult(mISupplicantP2pIface.startWpsPbc(groupIfName, macAddress));
1913             } catch (RemoteException e) {
1914                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
1915                 supplicantServiceDiedHandler();
1916             }
1917 
1918             return result.isSuccess();
1919         }
1920     }
1921 
1922 
1923     /**
1924      * Initiate WPS Pin Keypad setup.
1925      *
1926      * @param groupIfName Group interface name to use.
1927      * @param pin 8 digit pin to be used.
1928      * @return true, if operation was successful.
1929      */
startWpsPinKeypad(String groupIfName, String pin)1930     public boolean startWpsPinKeypad(String groupIfName, String pin) {
1931         if (TextUtils.isEmpty(groupIfName) || TextUtils.isEmpty(pin)) return false;
1932         synchronized (mLock) {
1933             if (!checkSupplicantP2pIfaceAndLogFailure("startWpsPinKeypad")) return false;
1934             if (groupIfName == null) {
1935                 Log.e(TAG, "Group name required when requesting WPS KEYPAD.");
1936                 return false;
1937             }
1938             if (pin == null) {
1939                 Log.e(TAG, "PIN required when requesting WPS KEYPAD.");
1940                 return false;
1941             }
1942 
1943             SupplicantResult<Void> result = new SupplicantResult(
1944                     "startWpsPinKeypad(" + groupIfName + ", " + pin + ")");
1945             try {
1946                 result.setResult(mISupplicantP2pIface.startWpsPinKeypad(groupIfName, pin));
1947             } catch (RemoteException e) {
1948                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
1949                 supplicantServiceDiedHandler();
1950             }
1951 
1952             return result.isSuccess();
1953         }
1954     }
1955 
1956 
1957     /**
1958      * Initiate WPS Pin Display setup.
1959      *
1960      * @param groupIfName Group interface name to use.
1961      * @param bssid BSSID of the AP. Use empty bssid to indicate wildcard.
1962      * @return generated pin if operation was successful, null otherwise.
1963      */
startWpsPinDisplay(String groupIfName, String bssid)1964     public String startWpsPinDisplay(String groupIfName, String bssid) {
1965         if (TextUtils.isEmpty(groupIfName)) return null;
1966         synchronized (mLock) {
1967             if (!checkSupplicantP2pIfaceAndLogFailure("startWpsPinDisplay")) return null;
1968             if (groupIfName == null) {
1969                 Log.e(TAG, "Group name required when requesting WPS KEYPAD.");
1970                 return null;
1971             }
1972 
1973             // Null values should be fine, since bssid can be empty.
1974             byte[] macAddress = null;
1975             try {
1976                 macAddress = NativeUtil.macAddressToByteArray(bssid);
1977             } catch (Exception e) {
1978                 Log.e(TAG, "Could not parse BSSID.", e);
1979                 return null;
1980             }
1981 
1982             SupplicantResult<String> result = new SupplicantResult(
1983                     "startWpsPinDisplay(" + groupIfName + ", " + bssid + ")");
1984             try {
1985                 mISupplicantP2pIface.startWpsPinDisplay(
1986                         groupIfName, macAddress,
1987                         (SupplicantStatus status, String generatedPin) -> {
1988                             result.setResult(status, generatedPin);
1989                         });
1990             } catch (RemoteException e) {
1991                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
1992                 supplicantServiceDiedHandler();
1993             }
1994 
1995             return result.getResult();
1996         }
1997     }
1998 
1999 
2000     /**
2001      * Cancel any ongoing WPS operations.
2002      *
2003      * @param groupIfName Group interface name to use.
2004      * @return true, if operation was successful.
2005      */
cancelWps(String groupIfName)2006     public boolean cancelWps(String groupIfName) {
2007         synchronized (mLock) {
2008             if (!checkSupplicantP2pIfaceAndLogFailure("cancelWps")) return false;
2009             if (groupIfName == null) {
2010                 Log.e(TAG, "Group name required when requesting WPS KEYPAD.");
2011                 return false;
2012             }
2013 
2014             SupplicantResult<Void> result = new SupplicantResult(
2015                     "cancelWps(" + groupIfName + ")");
2016             try {
2017                 result.setResult(mISupplicantP2pIface.cancelWps(groupIfName));
2018             } catch (RemoteException e) {
2019                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
2020                 supplicantServiceDiedHandler();
2021             }
2022 
2023             return result.isSuccess();
2024         }
2025     }
2026 
2027 
2028     /**
2029      * Enable/Disable Wifi Display.
2030      *
2031      * @param enable true to enable, false to disable.
2032      * @return true, if operation was successful.
2033      */
enableWfd(boolean enable)2034     public boolean enableWfd(boolean enable) {
2035         synchronized (mLock) {
2036             if (!checkSupplicantP2pIfaceAndLogFailure("enableWfd")) return false;
2037 
2038             SupplicantResult<Void> result = new SupplicantResult(
2039                     "enableWfd(" + enable + ")");
2040             try {
2041                 result.setResult(mISupplicantP2pIface.enableWfd(enable));
2042             } catch (RemoteException e) {
2043                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
2044                 supplicantServiceDiedHandler();
2045             }
2046 
2047             return result.isSuccess();
2048         }
2049     }
2050 
2051 
2052     /**
2053      * Set Wifi Display device info.
2054      *
2055      * @param info WFD device info as described in section 5.1.2 of WFD technical
2056      *        specification v1.0.0.
2057      * @return true, if operation was successful.
2058      */
setWfdDeviceInfo(String info)2059     public boolean setWfdDeviceInfo(String info) {
2060         synchronized (mLock) {
2061             if (!checkSupplicantP2pIfaceAndLogFailure("setWfdDeviceInfo")) return false;
2062 
2063             if (info == null) {
2064                 Log.e(TAG, "Cannot parse null WFD info string.");
2065                 return false;
2066             }
2067             byte[] wfdInfo = null;
2068             try {
2069                 wfdInfo = NativeUtil.hexStringToByteArray(info);
2070             } catch (Exception e) {
2071                 Log.e(TAG, "Could not parse WFD Device Info string.");
2072                 return false;
2073             }
2074 
2075             SupplicantResult<Void> result = new SupplicantResult(
2076                     "setWfdDeviceInfo(" + info + ")");
2077             try {
2078                 result.setResult(mISupplicantP2pIface.setWfdDeviceInfo(wfdInfo));
2079             } catch (RemoteException e) {
2080                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
2081                 supplicantServiceDiedHandler();
2082             }
2083 
2084             return result.isSuccess();
2085         }
2086     }
2087 
2088     /**
2089      * Remove network with provided id.
2090      *
2091      * @param networkId Id of the network to lookup.
2092      * @return true, if operation was successful.
2093      */
removeNetwork(int networkId)2094     public boolean removeNetwork(int networkId) {
2095         synchronized (mLock) {
2096             if (!checkSupplicantP2pIfaceAndLogFailure("removeNetwork")) return false;
2097 
2098             SupplicantResult<Void> result = new SupplicantResult(
2099                     "removeNetwork(" + networkId + ")");
2100             try {
2101                 result.setResult(mISupplicantP2pIface.removeNetwork(networkId));
2102             } catch (RemoteException e) {
2103                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
2104                 supplicantServiceDiedHandler();
2105             }
2106 
2107             return result.isSuccess();
2108         }
2109     }
2110 
2111     /**
2112      * List the networks saved in wpa_supplicant.
2113      *
2114      * @return List of network ids.
2115      */
listNetworks()2116     private List<Integer> listNetworks() {
2117         synchronized (mLock) {
2118             if (!checkSupplicantP2pIfaceAndLogFailure("listNetworks")) return null;
2119             SupplicantResult<ArrayList> result = new SupplicantResult("listNetworks()");
2120             try {
2121                 mISupplicantP2pIface.listNetworks(
2122                         (SupplicantStatus status, ArrayList<Integer> networkIds) -> {
2123                             result.setResult(status, networkIds);
2124                         });
2125             } catch (RemoteException e) {
2126                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
2127                 supplicantServiceDiedHandler();
2128             }
2129             return result.getResult();
2130         }
2131     }
2132 
2133     /**
2134      * Get the supplicant P2p network object for the specified network ID.
2135      *
2136      * @param networkId Id of the network to lookup.
2137      * @return ISupplicantP2pNetwork instance on success, null on failure.
2138      */
getNetwork(int networkId)2139     private ISupplicantP2pNetwork getNetwork(int networkId) {
2140         synchronized (mLock) {
2141             if (!checkSupplicantP2pIfaceAndLogFailure("getNetwork")) return null;
2142             SupplicantResult<ISupplicantNetwork> result =
2143                     new SupplicantResult("getNetwork(" + networkId + ")");
2144             try {
2145                 mISupplicantP2pIface.getNetwork(
2146                         networkId,
2147                         (SupplicantStatus status, ISupplicantNetwork network) -> {
2148                             result.setResult(status, network);
2149                         });
2150             } catch (RemoteException e) {
2151                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
2152                 supplicantServiceDiedHandler();
2153             }
2154             if (result.getResult() == null) {
2155                 Log.e(TAG, "getNetwork got null network");
2156                 return null;
2157             }
2158             return getP2pNetworkMockable(result.getResult());
2159         }
2160     }
2161 
2162     /**
2163      * Get the persistent group list from wpa_supplicant's p2p mgmt interface
2164      *
2165      * @param groups WifiP2pGroupList to store persistent groups in
2166      * @return true, if list has been modified.
2167      */
loadGroups(WifiP2pGroupList groups)2168     public boolean loadGroups(WifiP2pGroupList groups) {
2169         synchronized (mLock) {
2170             if (!checkSupplicantP2pIfaceAndLogFailure("loadGroups")) return false;
2171             List<Integer> networkIds = listNetworks();
2172             if (networkIds == null || networkIds.isEmpty()) {
2173                 return false;
2174             }
2175             for (Integer networkId : networkIds) {
2176                 ISupplicantP2pNetwork network = getNetwork(networkId);
2177                 if (network == null) {
2178                     Log.e(TAG, "Failed to retrieve network object for " + networkId);
2179                     continue;
2180                 }
2181                 SupplicantResult<Boolean> resultIsCurrent =
2182                         new SupplicantResult("isCurrent(" + networkId + ")");
2183                 try {
2184                     network.isCurrent(
2185                             (SupplicantStatus status, boolean isCurrent) -> {
2186                                 resultIsCurrent.setResult(status, isCurrent);
2187                             });
2188                 } catch (RemoteException e) {
2189                     Log.e(TAG, "ISupplicantP2pIface exception: " + e);
2190                     supplicantServiceDiedHandler();
2191                 }
2192                 /** Skip the current network, if we're somehow getting networks from the p2p GO
2193                     interface, instead of p2p mgmt interface*/
2194                 if (!resultIsCurrent.isSuccess() || resultIsCurrent.getResult()) {
2195                     Log.i(TAG, "Skipping current network");
2196                     continue;
2197                 }
2198 
2199                 WifiP2pGroup group = new WifiP2pGroup();
2200                 group.setNetworkId(networkId);
2201 
2202                 // Now get the ssid, bssid and other flags for this network.
2203                 SupplicantResult<ArrayList> resultSsid =
2204                         new SupplicantResult("getSsid(" + networkId + ")");
2205                 try {
2206                     network.getSsid(
2207                             (SupplicantStatus status, ArrayList<Byte> ssid) -> {
2208                                 resultSsid.setResult(status, ssid);
2209                             });
2210                 } catch (RemoteException e) {
2211                     Log.e(TAG, "ISupplicantP2pIface exception: " + e);
2212                     supplicantServiceDiedHandler();
2213                 }
2214                 if (resultSsid.isSuccess() && resultSsid.getResult() != null
2215                         && !resultSsid.getResult().isEmpty()) {
2216                     group.setNetworkName(NativeUtil.removeEnclosingQuotes(
2217                             NativeUtil.encodeSsid(resultSsid.getResult())));
2218                 }
2219 
2220                 SupplicantResult<byte[]> resultBssid =
2221                         new SupplicantResult("getBssid(" + networkId + ")");
2222                 try {
2223                     network.getBssid(
2224                             (SupplicantStatus status, byte[] bssid) -> {
2225                                 resultBssid.setResult(status, bssid);
2226                             });
2227                 } catch (RemoteException e) {
2228                     Log.e(TAG, "ISupplicantP2pIface exception: " + e);
2229                     supplicantServiceDiedHandler();
2230                 }
2231                 if (resultBssid.isSuccess() && !ArrayUtils.isEmpty(resultBssid.getResult())) {
2232                     WifiP2pDevice device = new WifiP2pDevice();
2233                     device.deviceAddress =
2234                             NativeUtil.macAddressFromByteArray(resultBssid.getResult());
2235                     group.setOwner(device);
2236                 }
2237 
2238                 SupplicantResult<Boolean> resultIsGo =
2239                         new SupplicantResult("isGo(" + networkId + ")");
2240                 try {
2241                     network.isGo(
2242                             (SupplicantStatus status, boolean isGo) -> {
2243                                 resultIsGo.setResult(status, isGo);
2244                             });
2245                 } catch (RemoteException e) {
2246                     Log.e(TAG, "ISupplicantP2pIface exception: " + e);
2247                     supplicantServiceDiedHandler();
2248                 }
2249                 if (resultIsGo.isSuccess()) {
2250                     group.setIsGroupOwner(resultIsGo.getResult());
2251                 }
2252                 groups.add(group);
2253             }
2254         }
2255         return true;
2256     }
2257 
2258     /**
2259      * Set WPS device name.
2260      *
2261      * @param name String to be set.
2262      * @return true if request is sent successfully, false otherwise.
2263      */
setWpsDeviceName(String name)2264     public boolean setWpsDeviceName(String name) {
2265         if (name == null) {
2266             return false;
2267         }
2268         synchronized (mLock) {
2269             if (!checkSupplicantP2pIfaceAndLogFailure("setWpsDeviceName")) return false;
2270             SupplicantResult<Void> result = new SupplicantResult(
2271                     "setWpsDeviceName(" + name + ")");
2272             try {
2273                 result.setResult(mISupplicantP2pIface.setWpsDeviceName(name));
2274             } catch (RemoteException e) {
2275                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
2276                 supplicantServiceDiedHandler();
2277             }
2278             return result.isSuccess();
2279         }
2280     }
2281 
2282     /**
2283      * Set WPS device type.
2284      *
2285      * @param typeStr Type specified as a string. Used format: <categ>-<OUI>-<subcateg>
2286      * @return true if request is sent successfully, false otherwise.
2287      */
setWpsDeviceType(String typeStr)2288     public boolean setWpsDeviceType(String typeStr) {
2289         try {
2290             Matcher match = WPS_DEVICE_TYPE_PATTERN.matcher(typeStr);
2291             if (!match.find() || match.groupCount() != 3) {
2292                 Log.e(TAG, "Malformed WPS device type " + typeStr);
2293                 return false;
2294             }
2295             short categ = Short.parseShort(match.group(1));
2296             byte[] oui = NativeUtil.hexStringToByteArray(match.group(2));
2297             short subCateg = Short.parseShort(match.group(3));
2298 
2299             byte[] bytes = new byte[8];
2300             ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
2301             byteBuffer.putShort(categ);
2302             byteBuffer.put(oui);
2303             byteBuffer.putShort(subCateg);
2304             synchronized (mLock) {
2305                 if (!checkSupplicantP2pIfaceAndLogFailure("setWpsDeviceType")) return false;
2306                 SupplicantResult<Void> result = new SupplicantResult(
2307                         "setWpsDeviceType(" + typeStr + ")");
2308                 try {
2309                     result.setResult(mISupplicantP2pIface.setWpsDeviceType(bytes));
2310                 } catch (RemoteException e) {
2311                     Log.e(TAG, "ISupplicantP2pIface exception: " + e);
2312                     supplicantServiceDiedHandler();
2313                 }
2314                 return result.isSuccess();
2315             }
2316         } catch (IllegalArgumentException e) {
2317             Log.e(TAG, "Illegal argument " + typeStr, e);
2318             return false;
2319         }
2320     }
2321 
2322     /**
2323      * Set WPS config methods
2324      *
2325      * @param configMethodsStr List of config methods.
2326      * @return true if request is sent successfully, false otherwise.
2327      */
setWpsConfigMethods(String configMethodsStr)2328     public boolean setWpsConfigMethods(String configMethodsStr) {
2329         synchronized (mLock) {
2330             if (!checkSupplicantP2pIfaceAndLogFailure("setWpsConfigMethods")) return false;
2331             SupplicantResult<Void> result =
2332                     new SupplicantResult("setWpsConfigMethods(" + configMethodsStr + ")");
2333             short configMethodsMask = 0;
2334             String[] configMethodsStrArr = configMethodsStr.split("\\s+");
2335             for (int i = 0; i < configMethodsStrArr.length; i++) {
2336                 configMethodsMask |= stringToWpsConfigMethod(configMethodsStrArr[i]);
2337             }
2338             try {
2339                 result.setResult(mISupplicantP2pIface.setWpsConfigMethods(configMethodsMask));
2340             } catch (RemoteException e) {
2341                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
2342                 supplicantServiceDiedHandler();
2343             }
2344             return result.isSuccess();
2345         }
2346     }
2347 
2348     /**
2349      * Get NFC handover request message.
2350      *
2351      * @return select message if created successfully, null otherwise.
2352      */
getNfcHandoverRequest()2353     public String getNfcHandoverRequest() {
2354         synchronized (mLock) {
2355             if (!checkSupplicantP2pIfaceAndLogFailure("getNfcHandoverRequest")) return null;
2356             SupplicantResult<ArrayList> result = new SupplicantResult(
2357                     "getNfcHandoverRequest()");
2358             try {
2359                 mISupplicantP2pIface.createNfcHandoverRequestMessage(
2360                         (SupplicantStatus status, ArrayList<Byte> message) -> {
2361                             result.setResult(status, message);
2362                         });
2363             } catch (RemoteException e) {
2364                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
2365                 supplicantServiceDiedHandler();
2366             }
2367             if (!result.isSuccess()) {
2368                 return null;
2369 
2370             }
2371             return NativeUtil.hexStringFromByteArray(
2372                     NativeUtil.byteArrayFromArrayList(result.getResult()));
2373         }
2374     }
2375 
2376     /**
2377      * Get NFC handover select message.
2378      *
2379      * @return select message if created successfully, null otherwise.
2380      */
getNfcHandoverSelect()2381     public String getNfcHandoverSelect() {
2382         synchronized (mLock) {
2383             if (!checkSupplicantP2pIfaceAndLogFailure("getNfcHandoverSelect")) return null;
2384             SupplicantResult<ArrayList> result = new SupplicantResult(
2385                     "getNfcHandoverSelect()");
2386             try {
2387                 mISupplicantP2pIface.createNfcHandoverSelectMessage(
2388                         (SupplicantStatus status, ArrayList<Byte> message) -> {
2389                             result.setResult(status, message);
2390                         });
2391             } catch (RemoteException e) {
2392                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
2393                 supplicantServiceDiedHandler();
2394             }
2395             if (!result.isSuccess()) {
2396                 return null;
2397 
2398             }
2399             return NativeUtil.hexStringFromByteArray(
2400                     NativeUtil.byteArrayFromArrayList(result.getResult()));
2401         }
2402     }
2403 
2404     /**
2405      * Report NFC handover select message.
2406      *
2407      * @return true if reported successfully, false otherwise.
2408      */
initiatorReportNfcHandover(String selectMessage)2409     public boolean initiatorReportNfcHandover(String selectMessage) {
2410         if (selectMessage == null) return false;
2411         synchronized (mLock) {
2412             if (!checkSupplicantP2pIfaceAndLogFailure("initiatorReportNfcHandover")) return false;
2413             SupplicantResult<Void> result = new SupplicantResult(
2414                     "initiatorReportNfcHandover(" + selectMessage + ")");
2415             try {
2416                 result.setResult(mISupplicantP2pIface.reportNfcHandoverInitiation(
2417                         NativeUtil.byteArrayToArrayList(NativeUtil.hexStringToByteArray(
2418                             selectMessage))));
2419             } catch (RemoteException e) {
2420                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
2421                 supplicantServiceDiedHandler();
2422             } catch (IllegalArgumentException e) {
2423                 Log.e(TAG, "Illegal argument " + selectMessage, e);
2424                 return false;
2425             }
2426             return result.isSuccess();
2427         }
2428     }
2429 
2430     /**
2431      * Report NFC handover request message.
2432      *
2433      * @return true if reported successfully, false otherwise.
2434      */
responderReportNfcHandover(String requestMessage)2435     public boolean responderReportNfcHandover(String requestMessage) {
2436         if (requestMessage == null) return false;
2437         synchronized (mLock) {
2438             if (!checkSupplicantP2pIfaceAndLogFailure("responderReportNfcHandover")) return false;
2439             SupplicantResult<Void> result = new SupplicantResult(
2440                     "responderReportNfcHandover(" + requestMessage + ")");
2441             try {
2442                 result.setResult(mISupplicantP2pIface.reportNfcHandoverResponse(
2443                         NativeUtil.byteArrayToArrayList(NativeUtil.hexStringToByteArray(
2444                             requestMessage))));
2445             } catch (RemoteException e) {
2446                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
2447                 supplicantServiceDiedHandler();
2448             } catch (IllegalArgumentException e) {
2449                 Log.e(TAG, "Illegal argument " + requestMessage, e);
2450                 return false;
2451             }
2452             return result.isSuccess();
2453         }
2454     }
2455 
2456     /**
2457      * Set the client list for the provided network.
2458      *
2459      * @param networkId Id of the network.
2460      * @param clientListStr Space separated list of clients.
2461      * @return true, if operation was successful.
2462      */
setClientList(int networkId, String clientListStr)2463     public boolean setClientList(int networkId, String clientListStr) {
2464         synchronized (mLock) {
2465             if (!checkSupplicantP2pIfaceAndLogFailure("setClientList")) return false;
2466             if (TextUtils.isEmpty(clientListStr)) {
2467                 Log.e(TAG, "Invalid client list");
2468                 return false;
2469             }
2470             ISupplicantP2pNetwork network = getNetwork(networkId);
2471             if (network == null) {
2472                 Log.e(TAG, "Invalid network id ");
2473                 return false;
2474             }
2475             SupplicantResult<Void> result = new SupplicantResult(
2476                     "setClientList(" + networkId + ", " + clientListStr + ")");
2477             try {
2478                 ArrayList<byte[]> clients = new ArrayList<>();
2479                 for (String clientStr : Arrays.asList(clientListStr.split("\\s+"))) {
2480                     clients.add(NativeUtil.macAddressToByteArray(clientStr));
2481                 }
2482                 result.setResult(network.setClientList(clients));
2483             } catch (RemoteException e) {
2484                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
2485                 supplicantServiceDiedHandler();
2486             } catch (IllegalArgumentException e) {
2487                 Log.e(TAG, "Illegal argument " + clientListStr, e);
2488                 return false;
2489             }
2490             return result.isSuccess();
2491         }
2492     }
2493 
2494     /**
2495      * Set the client list for the provided network.
2496      *
2497      * @param networkId Id of the network.
2498      * @return Space separated list of clients if successful, null otherwise.
2499      */
getClientList(int networkId)2500     public String getClientList(int networkId) {
2501         synchronized (mLock) {
2502             if (!checkSupplicantP2pIfaceAndLogFailure("getClientList")) return null;
2503             ISupplicantP2pNetwork network = getNetwork(networkId);
2504             if (network == null) {
2505                 Log.e(TAG, "Invalid network id ");
2506                 return null;
2507             }
2508             SupplicantResult<ArrayList> result = new SupplicantResult(
2509                     "getClientList(" + networkId + ")");
2510             try {
2511                 network.getClientList(
2512                         (SupplicantStatus status, ArrayList<byte[]> clients) -> {
2513                             result.setResult(status, clients);
2514                         });
2515             } catch (RemoteException e) {
2516                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
2517                 supplicantServiceDiedHandler();
2518             }
2519             if (!result.isSuccess()) {
2520                 return null;
2521             }
2522             ArrayList<byte[]> clients = result.getResult();
2523             return clients.stream()
2524                     .map(NativeUtil::macAddressFromByteArray)
2525                     .collect(Collectors.joining(" "));
2526         }
2527     }
2528 
2529     /**
2530      * Persist the current configurations to disk.
2531      *
2532      * @return true, if operation was successful.
2533      */
saveConfig()2534     public boolean saveConfig() {
2535         synchronized (mLock) {
2536             if (!checkSupplicantP2pIfaceAndLogFailure("saveConfig")) return false;
2537             SupplicantResult<Void> result = new SupplicantResult("saveConfig()");
2538             try {
2539                 result.setResult(mISupplicantP2pIface.saveConfig());
2540             } catch (RemoteException e) {
2541                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
2542                 supplicantServiceDiedHandler();
2543             }
2544             return result.isSuccess();
2545         }
2546     }
2547 
2548 
2549     /**
2550      * Enable/Disable P2P MAC randomization.
2551      *
2552      * @param enable true to enable, false to disable.
2553      * @return true, if operation was successful.
2554      */
setMacRandomization(boolean enable)2555     public boolean setMacRandomization(boolean enable) {
2556         synchronized (mLock) {
2557             android.hardware.wifi.supplicant.V1_2.ISupplicantP2pIface ifaceV12 =
2558                     getSupplicantP2pIfaceAndLogFailureV1_2("setMacRandomization");
2559             if (ifaceV12 == null) return false;
2560 
2561             SupplicantResult<Void> result = new SupplicantResult(
2562                     "setMacRandomization(" + enable + ")");
2563             try {
2564                 result.setResult(ifaceV12.setMacRandomization(enable));
2565             } catch (RemoteException e) {
2566                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
2567                 supplicantServiceDiedHandler();
2568             }
2569 
2570             return result.isSuccess();
2571         }
2572     }
2573 
2574     /**
2575      * Set Wifi Display R2 device info.
2576      *
2577      * @param info WFD R2 device info as described in section 5.1.12 of WFD technical
2578      *        specification v2.1.
2579      * @return true, if operation was successful.
2580      */
setWfdR2DeviceInfo(String info)2581     public boolean setWfdR2DeviceInfo(String info) {
2582         synchronized (mLock) {
2583             if (info == null) {
2584                 Log.e(TAG, "Cannot parse null WFD info string.");
2585                 return false;
2586             }
2587             byte[] wfdR2Info = null;
2588             try {
2589                 wfdR2Info = NativeUtil.hexStringToByteArray(info);
2590             } catch (Exception e) {
2591                 Log.e(TAG, "Could not parse WFD R2 Device Info string.");
2592                 return false;
2593             }
2594 
2595             android.hardware.wifi.supplicant.V1_4.ISupplicantP2pIface ifaceV14 =
2596                     getSupplicantP2pIfaceAndLogFailureV1_4("setWfdR2DeviceInfo");
2597             if (ifaceV14 == null) return false;
2598             SupplicantResultV1_4<Void> result = new SupplicantResultV1_4(
2599                     "setWfdR2DeviceInfo(" + info + ")");
2600             try {
2601                 result.setResult(ifaceV14.setWfdR2DeviceInfo(wfdR2Info));
2602             } catch (RemoteException e) {
2603                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
2604                 supplicantServiceDiedHandler();
2605             }
2606             return result.isSuccess();
2607         }
2608     }
2609 
2610     /**
2611      * Remove the client with the MAC address from the group.
2612      *
2613      * @param peerAddress Mac address of the client.
2614      * @param isLegacyClient Indicate if client is a legacy client or not.
2615      * @return true if success
2616      */
removeClient(String peerAddress, boolean isLegacyClient)2617     public boolean removeClient(String peerAddress, boolean isLegacyClient) {
2618         Log.d(TAG, "removeClient() is not supported.");
2619         return false;
2620     }
2621 
2622     /**
2623      * Set vendor-specific information elements to wpa_supplicant.
2624      *
2625      * @param vendorElements The list of vendor-specific information elements.
2626      *
2627      * @return boolean The value indicating whether operation was successful.
2628      */
setVendorElements(Set<ScanResult.InformationElement> vendorElements)2629     public boolean setVendorElements(Set<ScanResult.InformationElement> vendorElements) {
2630         Log.d(TAG, "setVendorElements() is not supported.");
2631         return false;
2632     }
2633 
2634     /**
2635      * Configure the IP addresses in supplicant for P2P GO to provide the IP address to
2636      * client in EAPOL handshake. Refer Wi-Fi P2P Technical Specification v1.7 - Section  4.2.8
2637      * IP Address Allocation in EAPOL-Key Frames (4-Way Handshake) for more details.
2638      * The IP addresses are IPV4 addresses and higher-order address bytes are in the
2639      * lower-order int bytes (e.g. 1.2.3.4 is represented as 0x04030201)
2640      *
2641      * @param ipAddressGo The P2P Group Owner IP address.
2642      * @param ipAddressMask The P2P Group owner subnet mask.
2643      * @param ipAddressStart The starting address in the IP address pool.
2644      * @param ipAddressEnd The ending address in the IP address pool.
2645      * @return boolean value indicating whether operation was successful.
2646      */
configureEapolIpAddressAllocationParams(int ipAddressGo, int ipAddressMask, int ipAddressStart, int ipAddressEnd)2647     public boolean configureEapolIpAddressAllocationParams(int ipAddressGo, int ipAddressMask,
2648             int ipAddressStart, int ipAddressEnd) {
2649         Log.d(TAG, "configureEapolIpAddressAllocationParams() is not supported.");
2650         return false;
2651     }
2652 
2653     /**
2654      * Converts the Wps config method string to the equivalent enum value.
2655      */
stringToWpsConfigMethod(String configMethod)2656     private static short stringToWpsConfigMethod(String configMethod) {
2657         switch (configMethod) {
2658             case "usba":
2659                 return WpsConfigMethods.USBA;
2660             case "ethernet":
2661                 return WpsConfigMethods.ETHERNET;
2662             case "label":
2663                 return WpsConfigMethods.LABEL;
2664             case "display":
2665                 return WpsConfigMethods.DISPLAY;
2666             case "int_nfc_token":
2667                 return WpsConfigMethods.INT_NFC_TOKEN;
2668             case "ext_nfc_token":
2669                 return WpsConfigMethods.EXT_NFC_TOKEN;
2670             case "nfc_interface":
2671                 return WpsConfigMethods.NFC_INTERFACE;
2672             case "push_button":
2673                 return WpsConfigMethods.PUSHBUTTON;
2674             case "keypad":
2675                 return WpsConfigMethods.KEYPAD;
2676             case "virtual_push_button":
2677                 return WpsConfigMethods.VIRT_PUSHBUTTON;
2678             case "physical_push_button":
2679                 return WpsConfigMethods.PHY_PUSHBUTTON;
2680             case "p2ps":
2681                 return WpsConfigMethods.P2PS;
2682             case "virtual_display":
2683                 return WpsConfigMethods.VIRT_DISPLAY;
2684             case "physical_display":
2685                 return WpsConfigMethods.PHY_DISPLAY;
2686             default:
2687                 throw new IllegalArgumentException(
2688                         "Invalid WPS config method: " + configMethod);
2689         }
2690     }
2691 
2692     /** Container class allowing propagation of status and/or value
2693      * from callbacks.
2694      *
2695      * Primary purpose is to allow callback lambdas to provide results
2696      * to parent methods.
2697      */
2698     private static class SupplicantResultBase<S, E> {
2699         private String mMethodName;
2700         private S mStatus;
2701         private E mValue;
2702 
SupplicantResultBase(String methodName)2703         SupplicantResultBase(String methodName) {
2704             mMethodName = methodName;
2705             mStatus = null;
2706             mValue = null;
2707             logd("entering " + mMethodName);
2708         }
2709 
setResult(S status, E value)2710         public void setResult(S status, E value) {
2711             if (status == null) {
2712                 logw(mMethodName + " failed: no status code returned.");
2713             } else {
2714                 logCompletion(mMethodName, getCode(status), getDebugMessage(status));
2715             }
2716             logd("leaving " + mMethodName + " with result = " + value);
2717             mStatus = status;
2718             mValue = value;
2719         }
2720 
setResult(S status)2721         public void setResult(S status) {
2722             if (status == null) {
2723                 logw(mMethodName + " failed: no status code returned.");
2724             } else {
2725                 logCompletion(mMethodName, getCode(status), getDebugMessage(status));
2726             }
2727             logd("leaving " + mMethodName);
2728             mStatus = status;
2729         }
2730 
isSuccess()2731         public boolean isSuccess() {
2732             return (mStatus != null
2733                     && (getCode(mStatus) == SupplicantStatusCode.SUCCESS
2734                     || getCode(mStatus) == SupplicantStatusCode.FAILURE_IFACE_EXISTS));
2735         }
2736 
getResult()2737         public E getResult() {
2738             return (isSuccess() ? mValue : null);
2739         }
2740 
getCode(Object obj)2741         protected int getCode(Object obj) {
2742             SupplicantStatus status = (SupplicantStatus) obj;
2743             return status.code;
2744         }
2745 
getDebugMessage(Object obj)2746         protected String getDebugMessage(Object obj) {
2747             SupplicantStatus status = (SupplicantStatus) obj;
2748             return status.debugMessage;
2749         }
2750     }
2751 
2752     private static class SupplicantResult<E>
2753             extends SupplicantResultBase<SupplicantStatus, E> {
SupplicantResult(String iface)2754         SupplicantResult(String iface) {
2755             super(iface);
2756         }
2757     }
2758 
2759     private static class SupplicantResultV1_4<E>
2760             extends SupplicantResultBase<
2761                     android.hardware.wifi.supplicant.V1_4.SupplicantStatus, E> {
SupplicantResultV1_4(String iface)2762         SupplicantResultV1_4(String iface) {
2763             super(iface);
2764         }
2765 
getCode(Object obj)2766         protected int getCode(Object obj) {
2767             android.hardware.wifi.supplicant.V1_4.SupplicantStatus status =
2768                     (android.hardware.wifi.supplicant.V1_4.SupplicantStatus) obj;
2769             return status.code;
2770         }
2771 
getDebugMessage(Object obj)2772         protected String getDebugMessage(Object obj) {
2773             android.hardware.wifi.supplicant.V1_4.SupplicantStatus status =
2774                     (android.hardware.wifi.supplicant.V1_4.SupplicantStatus) obj;
2775             return status.debugMessage;
2776         }
2777     }
2778 }
2779