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