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