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