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