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