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