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