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