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