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