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.encodeSsid(ssid);
    941                                 } catch (Exception e) {
    942                                     Log.e(TAG, "Could not encode SSID.", e);
    943                                 }
    944                             }
    945                             result.setResult(status, ssidString);
    946                         });
    947             } catch (RemoteException e) {
    948                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
    949                 supplicantServiceDiedHandler();
    950                 return null;
    951             }
    952 
    953             return result.getResult();
    954         }
    955     }
    956 
    957 
    958     /**
    959      * Reinvoke a device from a persistent group.
    960      *
    961      * @param networkId Used to specify the persistent group.
    962      * @param peerAddress MAC address of the device to reinvoke.
    963      *
    964      * @return true, if operation was successful.
    965      */
    966     public boolean reinvoke(int networkId, String peerAddress) {
    967         if (TextUtils.isEmpty(peerAddress) || networkId < 0) return false;
    968         synchronized (mLock) {
    969             if (!checkSupplicantP2pIfaceAndLogFailure("reinvoke")) return false;
    970             if (peerAddress == null) {
    971                 Log.e(TAG, "Cannot parse peer mac address.");
    972                 return false;
    973             }
    974             byte[] macAddress = null;
    975             try {
    976                 macAddress = NativeUtil.macAddressToByteArray(peerAddress);
    977             } catch (Exception e) {
    978                 Log.e(TAG, "Could not parse mac address.", e);
    979                 return false;
    980             }
    981 
    982             SupplicantResult<Void> result = new SupplicantResult(
    983                     "reinvoke(" + networkId + ", " + peerAddress + ")");
    984             try {
    985                 result.setResult(mISupplicantP2pIface.reinvoke(networkId, macAddress));
    986             } catch (RemoteException e) {
    987                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
    988                 supplicantServiceDiedHandler();
    989             }
    990 
    991             return result.isSuccess();
    992         }
    993     }
    994 
    995 
    996     /**
    997      * Set up a P2P group owner manually (i.e., without group owner
    998      * negotiation with a specific peer). This is also known as autonomous
    999      * group owner.
   1000      *
   1001      * @param networkId Used to specify the restart of a persistent group.
   1002      * @param isPersistent Used to request a persistent group to be formed.
   1003      *
   1004      * @return true, if operation was successful.
   1005      */
   1006     public boolean groupAdd(int networkId, boolean isPersistent) {
   1007         synchronized (mLock) {
   1008             if (!checkSupplicantP2pIfaceAndLogFailure("groupAdd")) return false;
   1009             SupplicantResult<Void> result =
   1010                     new SupplicantResult("groupAdd(" + networkId + ", " + isPersistent + ")");
   1011             try {
   1012                 result.setResult(mISupplicantP2pIface.addGroup(isPersistent, networkId));
   1013             } catch (RemoteException e) {
   1014                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   1015                 supplicantServiceDiedHandler();
   1016             }
   1017             return result.isSuccess();
   1018         }
   1019     }
   1020 
   1021     /**
   1022      * Set up a P2P group owner manually.
   1023      * This is a helper method that invokes groupAdd(networkId, isPersistent) internally.
   1024      *
   1025      * @param isPersistent Used to request a persistent group to be formed.
   1026      *
   1027      * @return true, if operation was successful.
   1028      */
   1029     public boolean groupAdd(boolean isPersistent) {
   1030         // Supplicant expects networkId to be -1 if not supplied.
   1031         return groupAdd(-1, isPersistent);
   1032     }
   1033 
   1034 
   1035     /**
   1036      * Terminate a P2P group. If a new virtual network interface was used for
   1037      * the group, it must also be removed. The network interface name of the
   1038      * group interface is used as a parameter for this command.
   1039      *
   1040      * @param groupName Group interface name to use.
   1041      *
   1042      * @return true, if operation was successful.
   1043      */
   1044     public boolean groupRemove(String groupName) {
   1045         if (TextUtils.isEmpty(groupName)) return false;
   1046         synchronized (mLock) {
   1047             if (!checkSupplicantP2pIfaceAndLogFailure("groupRemove")) return false;
   1048             SupplicantResult<Void> result = new SupplicantResult("groupRemove(" + groupName + ")");
   1049             try {
   1050                 result.setResult(mISupplicantP2pIface.removeGroup(groupName));
   1051             } catch (RemoteException e) {
   1052                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   1053                 supplicantServiceDiedHandler();
   1054             }
   1055             return result.isSuccess();
   1056         }
   1057     }
   1058 
   1059 
   1060     /**
   1061      * Gets the capability of the group which the device is a
   1062      * member of.
   1063      *
   1064      * @param peerAddress MAC address of the peer.
   1065      *
   1066      * @return combination of |GroupCapabilityMask| values.
   1067      */
   1068     public int getGroupCapability(String peerAddress) {
   1069         synchronized (mLock) {
   1070             if (!checkSupplicantP2pIfaceAndLogFailure("getGroupCapability")) {
   1071                 return RESULT_NOT_VALID;
   1072             }
   1073 
   1074             if (peerAddress == null) {
   1075                 Log.e(TAG, "Cannot parse peer mac address.");
   1076                 return RESULT_NOT_VALID;
   1077             }
   1078             byte[] macAddress = null;
   1079             try {
   1080                 macAddress = NativeUtil.macAddressToByteArray(peerAddress);
   1081             } catch (Exception e) {
   1082                 Log.e(TAG, "Could not parse group address.", e);
   1083                 return RESULT_NOT_VALID;
   1084             }
   1085 
   1086             SupplicantResult<Integer> capability = new SupplicantResult(
   1087                     "getGroupCapability(" + peerAddress + ")");
   1088             try {
   1089                 mISupplicantP2pIface.getGroupCapability(
   1090                         macAddress, (SupplicantStatus status, int cap) -> {
   1091                             capability.setResult(status, cap);
   1092                         });
   1093             } catch (RemoteException e) {
   1094                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   1095                 supplicantServiceDiedHandler();
   1096             }
   1097 
   1098             if (!capability.isSuccess()) {
   1099                 return RESULT_NOT_VALID;
   1100             }
   1101 
   1102             return capability.getResult();
   1103         }
   1104     }
   1105 
   1106 
   1107     /**
   1108      * Configure Extended Listen Timing.
   1109      *
   1110      * If enabled, listen state must be entered every |intervalInMillis| for at
   1111      * least |periodInMillis|. Both values have acceptable range of 1-65535
   1112      * (with interval obviously having to be larger than or equal to duration).
   1113      * If the P2P module is not idle at the time the Extended Listen Timing
   1114      * timeout occurs, the Listen State operation must be skipped.
   1115      *
   1116      * @param enable Enables or disables listening.
   1117      * @param periodInMillis Period in milliseconds.
   1118      * @param intervalInMillis Interval in milliseconds.
   1119      *
   1120      * @return true, if operation was successful.
   1121      */
   1122     public boolean configureExtListen(boolean enable, int periodInMillis, int intervalInMillis) {
   1123         if (enable && intervalInMillis < periodInMillis) {
   1124             return false;
   1125         }
   1126         synchronized (mLock) {
   1127             if (!checkSupplicantP2pIfaceAndLogFailure("configureExtListen")) return false;
   1128 
   1129             // If listening is disabled, wpa supplicant expects zeroes.
   1130             if (!enable) {
   1131                 periodInMillis = 0;
   1132                 intervalInMillis = 0;
   1133             }
   1134 
   1135             // Verify that the integers are not negative. Leave actual parameter validation to
   1136             // supplicant.
   1137             if (periodInMillis < 0 || intervalInMillis < 0) {
   1138                 Log.e(TAG, "Invalid parameters supplied to configureExtListen: " + periodInMillis
   1139                         + ", " + intervalInMillis);
   1140                 return false;
   1141             }
   1142 
   1143             SupplicantResult<Void> result = new SupplicantResult(
   1144                     "configureExtListen(" + periodInMillis + ", " + intervalInMillis + ")");
   1145             try {
   1146                 result.setResult(
   1147                         mISupplicantP2pIface.configureExtListen(periodInMillis, intervalInMillis));
   1148             } catch (RemoteException e) {
   1149                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   1150                 supplicantServiceDiedHandler();
   1151             }
   1152 
   1153             return result.isSuccess();
   1154         }
   1155     }
   1156 
   1157 
   1158     /**
   1159      * Set P2P Listen channel and operating chanel.
   1160      *
   1161      * @param listenChannel Wifi channel. eg, 1, 6, 11.
   1162      * @param operatingChannel Wifi channel. eg, 1, 6, 11.
   1163      *
   1164      * @return true, if operation was successful.
   1165      */
   1166     public boolean setListenChannel(int listenChannel, int operatingChannel) {
   1167         synchronized (mLock) {
   1168             if (!checkSupplicantP2pIfaceAndLogFailure("setListenChannel")) return false;
   1169 
   1170             if (listenChannel >= 1 && listenChannel <= 11) {
   1171                 SupplicantResult<Void> result = new SupplicantResult(
   1172                         "setListenChannel(" + listenChannel + ", " + DEFAULT_OPERATING_CLASS + ")");
   1173                 try {
   1174                     result.setResult(mISupplicantP2pIface.setListenChannel(
   1175                             listenChannel, DEFAULT_OPERATING_CLASS));
   1176                 } catch (RemoteException e) {
   1177                     Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   1178                     supplicantServiceDiedHandler();
   1179                 }
   1180                 if (!result.isSuccess()) {
   1181                     return false;
   1182                 }
   1183             } else if (listenChannel != 0) {
   1184                 // listenChannel == 0 does not set any listen channel.
   1185                 return false;
   1186             }
   1187 
   1188             if (operatingChannel >= 0 && operatingChannel <= 165) {
   1189                 ArrayList<ISupplicantP2pIface.FreqRange> ranges = new ArrayList<>();
   1190                 // operatingChannel == 0 enables all freqs.
   1191                 if (operatingChannel >= 1 && operatingChannel <= 165) {
   1192                     int freq = (operatingChannel <= 14 ? 2407 : 5000) + operatingChannel * 5;
   1193                     ISupplicantP2pIface.FreqRange range1 =  new ISupplicantP2pIface.FreqRange();
   1194                     range1.min = 1000;
   1195                     range1.max = freq - 5;
   1196                     ISupplicantP2pIface.FreqRange range2 =  new ISupplicantP2pIface.FreqRange();
   1197                     range2.min = freq + 5;
   1198                     range2.max = 6000;
   1199                     ranges.add(range1);
   1200                     ranges.add(range2);
   1201                 }
   1202                 SupplicantResult<Void> result = new SupplicantResult(
   1203                         "setDisallowedFrequencies(" + ranges + ")");
   1204                 try {
   1205                     result.setResult(mISupplicantP2pIface.setDisallowedFrequencies(ranges));
   1206                 } catch (RemoteException e) {
   1207                     Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   1208                     supplicantServiceDiedHandler();
   1209                 }
   1210                 return result.isSuccess();
   1211             }
   1212             return false;
   1213         }
   1214     }
   1215 
   1216 
   1217     /**
   1218      * This command can be used to add a upnp/bonjour service.
   1219      *
   1220      * @param servInfo List of service queries.
   1221      *
   1222      * @return true, if operation was successful.
   1223      */
   1224     public boolean serviceAdd(WifiP2pServiceInfo servInfo) {
   1225         synchronized (mLock) {
   1226             if (!checkSupplicantP2pIfaceAndLogFailure("serviceAdd")) return false;
   1227 
   1228             if (servInfo == null) {
   1229                 Log.e(TAG, "Null service info passed.");
   1230                 return false;
   1231             }
   1232 
   1233             for (String s : servInfo.getSupplicantQueryList()) {
   1234                 if (s == null) {
   1235                     Log.e(TAG, "Invalid service description (null).");
   1236                     return false;
   1237                 }
   1238 
   1239                 String[] data = s.split(" ");
   1240                 if (data.length < 3) {
   1241                     Log.e(TAG, "Service specification invalid: " + s);
   1242                     return false;
   1243                 }
   1244 
   1245                 SupplicantResult<Void> result = null;
   1246                 try {
   1247                     if ("upnp".equals(data[0])) {
   1248                         int version = 0;
   1249                         try {
   1250                             version = Integer.parseInt(data[1], 16);
   1251                         } catch (NumberFormatException e) {
   1252                             Log.e(TAG, "UPnP Service specification invalid: " + s, e);
   1253                             return false;
   1254                         }
   1255 
   1256                         result = new SupplicantResult(
   1257                                 "addUpnpService(" + data[1] + ", " + data[2] + ")");
   1258                         result.setResult(mISupplicantP2pIface.addUpnpService(version, data[2]));
   1259                     } else if ("bonjour".equals(data[0])) {
   1260                         if (data[1] != null && data[2] != null) {
   1261                             ArrayList<Byte> request = null;
   1262                             ArrayList<Byte> response = null;
   1263                             try {
   1264                                 request = NativeUtil.byteArrayToArrayList(
   1265                                         NativeUtil.hexStringToByteArray(data[1]));
   1266                                 response = NativeUtil.byteArrayToArrayList(
   1267                                         NativeUtil.hexStringToByteArray(data[2]));
   1268                             } catch (Exception e) {
   1269                                 Log.e(TAG, "Invalid bonjour service description.");
   1270                                 return false;
   1271                             }
   1272                             result = new SupplicantResult(
   1273                                     "addBonjourService(" + data[1] + ", " + data[2] + ")");
   1274                             result.setResult(
   1275                                     mISupplicantP2pIface.addBonjourService(request, response));
   1276                         }
   1277                     } else {
   1278                         return false;
   1279                     }
   1280                 } catch (RemoteException e) {
   1281                     Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   1282                     supplicantServiceDiedHandler();
   1283                 }
   1284 
   1285                 if (result == null || !result.isSuccess()) return false;
   1286             }
   1287 
   1288             return true;
   1289         }
   1290     }
   1291 
   1292 
   1293     /**
   1294      * This command can be used to remove a upnp/bonjour service.
   1295      *
   1296      * @param servInfo List of service queries.
   1297      *
   1298      * @return true, if operation was successful.
   1299      */
   1300     public boolean serviceRemove(WifiP2pServiceInfo servInfo) {
   1301         synchronized (mLock) {
   1302             if (!checkSupplicantP2pIfaceAndLogFailure("serviceRemove")) return false;
   1303 
   1304             if (servInfo == null) {
   1305                 Log.e(TAG, "Null service info passed.");
   1306                 return false;
   1307             }
   1308 
   1309             for (String s : servInfo.getSupplicantQueryList()) {
   1310                 if (s == null) {
   1311                     Log.e(TAG, "Invalid service description (null).");
   1312                     return false;
   1313                 }
   1314 
   1315                 String[] data = s.split(" ");
   1316                 if (data.length < 3) {
   1317                     Log.e(TAG, "Service specification invalid: " + s);
   1318                     return false;
   1319                 }
   1320 
   1321                 SupplicantResult<Void> result = null;
   1322                 try {
   1323                     if ("upnp".equals(data[0])) {
   1324                         int version = 0;
   1325                         try {
   1326                             version = Integer.parseInt(data[1], 16);
   1327                         } catch (NumberFormatException e) {
   1328                             Log.e(TAG, "UPnP Service specification invalid: " + s, e);
   1329                             return false;
   1330                         }
   1331                         result = new SupplicantResult(
   1332                                 "removeUpnpService(" + data[1] + ", " + data[2] + ")");
   1333                         result.setResult(mISupplicantP2pIface.removeUpnpService(version, data[2]));
   1334                     } else if ("bonjour".equals(data[0])) {
   1335                         if (data[1] != null) {
   1336                             ArrayList<Byte> request = null;
   1337                             try {
   1338                                 request = NativeUtil.byteArrayToArrayList(
   1339                                     NativeUtil.hexStringToByteArray(data[1]));
   1340                             } catch (Exception e) {
   1341                                 Log.e(TAG, "Invalid bonjour service description.");
   1342                                 return false;
   1343                             }
   1344                             result = new SupplicantResult("removeBonjourService(" + data[1] + ")");
   1345                             result.setResult(mISupplicantP2pIface.removeBonjourService(request));
   1346                         }
   1347                     } else {
   1348                         Log.e(TAG, "Unknown / unsupported P2P service requested: " + data[0]);
   1349                         return false;
   1350                     }
   1351                 } catch (RemoteException e) {
   1352                     Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   1353                     supplicantServiceDiedHandler();
   1354                 }
   1355 
   1356                 if (result == null || !result.isSuccess()) return false;
   1357             }
   1358 
   1359             return true;
   1360         }
   1361     }
   1362 
   1363 
   1364     /**
   1365      * Schedule a P2P service discovery request. The parameters for this command
   1366      * are the device address of the peer device (or 00:00:00:00:00:00 for
   1367      * wildcard query that is sent to every discovered P2P peer that supports
   1368      * service discovery) and P2P Service Query TLV(s) as hexdump.
   1369      *
   1370      * @param peerAddress MAC address of the device to discover.
   1371      * @param query Hex dump of the query data.
   1372      * @return identifier Identifier for the request. Can be used to cancel the
   1373      *         request.
   1374      */
   1375     public String requestServiceDiscovery(String peerAddress, String query) {
   1376         synchronized (mLock) {
   1377             if (!checkSupplicantP2pIfaceAndLogFailure("requestServiceDiscovery")) return null;
   1378 
   1379             if (peerAddress == null) {
   1380                 Log.e(TAG, "Cannot parse peer mac address.");
   1381                 return null;
   1382             }
   1383             byte[] macAddress = null;
   1384             try {
   1385                 macAddress = NativeUtil.macAddressToByteArray(peerAddress);
   1386             } catch (Exception e) {
   1387                 Log.e(TAG, "Could not process peer MAC address.", e);
   1388                 return null;
   1389             }
   1390 
   1391             if (query == null) {
   1392                 Log.e(TAG, "Cannot parse service discovery query: " + query);
   1393                 return null;
   1394             }
   1395             ArrayList<Byte> binQuery = null;
   1396             try {
   1397                 binQuery = NativeUtil.byteArrayToArrayList(NativeUtil.hexStringToByteArray(query));
   1398             } catch (Exception e) {
   1399                 Log.e(TAG, "Could not parse service query.", e);
   1400                 return null;
   1401             }
   1402 
   1403             SupplicantResult<Long> result = new SupplicantResult(
   1404                     "requestServiceDiscovery(" + peerAddress + ", " + query + ")");
   1405             try {
   1406                 mISupplicantP2pIface.requestServiceDiscovery(
   1407                         macAddress, binQuery,
   1408                         (SupplicantStatus status, long identifier) -> {
   1409                             result.setResult(status, new Long(identifier));
   1410                         });
   1411             } catch (RemoteException e) {
   1412                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   1413                 supplicantServiceDiedHandler();
   1414             }
   1415 
   1416             Long value = result.getResult();
   1417             if (value == null) return null;
   1418             return value.toString();
   1419         }
   1420     }
   1421 
   1422 
   1423     /**
   1424      * Cancel a previous service discovery request.
   1425      *
   1426      * @param identifier Identifier for the request to cancel.
   1427      * @return true, if operation was successful.
   1428      */
   1429     public boolean cancelServiceDiscovery(String identifier) {
   1430         synchronized (mLock) {
   1431             if (!checkSupplicantP2pIfaceAndLogFailure("cancelServiceDiscovery")) return false;
   1432             if (identifier == null) {
   1433                 Log.e(TAG, "cancelServiceDiscovery requires a valid tag.");
   1434                 return false;
   1435             }
   1436 
   1437             long id = 0;
   1438             try {
   1439                 id = Long.parseLong(identifier);
   1440             } catch (NumberFormatException e) {
   1441                 Log.e(TAG, "Service discovery identifier invalid: " + identifier, e);
   1442                 return false;
   1443             }
   1444 
   1445             SupplicantResult<Void> result = new SupplicantResult(
   1446                     "cancelServiceDiscovery(" + identifier + ")");
   1447             try {
   1448                 result.setResult(mISupplicantP2pIface.cancelServiceDiscovery(id));
   1449             } catch (RemoteException e) {
   1450                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   1451                 supplicantServiceDiedHandler();
   1452             }
   1453 
   1454             return result.isSuccess();
   1455         }
   1456     }
   1457 
   1458 
   1459     /**
   1460      * Send driver command to set Miracast mode.
   1461      *
   1462      * @param mode Mode of Miracast.
   1463      * @return true, if operation was successful.
   1464      */
   1465     public boolean setMiracastMode(int mode) {
   1466         synchronized (mLock) {
   1467             if (!checkSupplicantP2pIfaceAndLogFailure("setMiracastMode")) return false;
   1468             byte targetMode = ISupplicantP2pIface.MiracastMode.DISABLED;
   1469 
   1470             switch (mode) {
   1471                 case WifiP2pManager.MIRACAST_SOURCE:
   1472                     targetMode = ISupplicantP2pIface.MiracastMode.SOURCE;
   1473                     break;
   1474 
   1475                 case WifiP2pManager.MIRACAST_SINK:
   1476                     targetMode = ISupplicantP2pIface.MiracastMode.SINK;
   1477                     break;
   1478             }
   1479 
   1480             SupplicantResult<Void> result = new SupplicantResult(
   1481                     "setMiracastMode(" + mode + ")");
   1482             try {
   1483                 result.setResult(mISupplicantP2pIface.setMiracastMode(targetMode));
   1484             } catch (RemoteException e) {
   1485                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   1486                 supplicantServiceDiedHandler();
   1487             }
   1488 
   1489             return result.isSuccess();
   1490         }
   1491     }
   1492 
   1493 
   1494     /**
   1495      * Initiate WPS Push Button setup.
   1496      * The PBC operation requires that a button is also pressed at the
   1497      * AP/Registrar at about the same time (2 minute window).
   1498      *
   1499      * @param groupIfName Group interface name to use.
   1500      * @param bssid BSSID of the AP. Use empty bssid to indicate wildcard.
   1501      * @return true, if operation was successful.
   1502      */
   1503     public boolean startWpsPbc(String groupIfName, String bssid) {
   1504         if (TextUtils.isEmpty(groupIfName)) {
   1505             Log.e(TAG, "Group name required when requesting WPS PBC. Got (" + groupIfName + ")");
   1506             return false;
   1507         }
   1508         synchronized (mLock) {
   1509             if (!checkSupplicantP2pIfaceAndLogFailure("startWpsPbc")) return false;
   1510             // Null values should be fine, since bssid can be empty.
   1511             byte[] macAddress = null;
   1512             try {
   1513                 macAddress = NativeUtil.macAddressToByteArray(bssid);
   1514             } catch (Exception e) {
   1515                 Log.e(TAG, "Could not parse BSSID.", e);
   1516                 return false;
   1517             }
   1518 
   1519             SupplicantResult<Void> result = new SupplicantResult(
   1520                     "startWpsPbc(" + groupIfName + ", " + bssid + ")");
   1521             try {
   1522                 result.setResult(mISupplicantP2pIface.startWpsPbc(groupIfName, macAddress));
   1523             } catch (RemoteException e) {
   1524                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   1525                 supplicantServiceDiedHandler();
   1526             }
   1527 
   1528             return result.isSuccess();
   1529         }
   1530     }
   1531 
   1532 
   1533     /**
   1534      * Initiate WPS Pin Keypad setup.
   1535      *
   1536      * @param groupIfName Group interface name to use.
   1537      * @param pin 8 digit pin to be used.
   1538      * @return true, if operation was successful.
   1539      */
   1540     public boolean startWpsPinKeypad(String groupIfName, String pin) {
   1541         if (TextUtils.isEmpty(groupIfName) || TextUtils.isEmpty(pin)) return false;
   1542         synchronized (mLock) {
   1543             if (!checkSupplicantP2pIfaceAndLogFailure("startWpsPinKeypad")) return false;
   1544             if (groupIfName == null) {
   1545                 Log.e(TAG, "Group name required when requesting WPS KEYPAD.");
   1546                 return false;
   1547             }
   1548             if (pin == null) {
   1549                 Log.e(TAG, "PIN required when requesting WPS KEYPAD.");
   1550                 return false;
   1551             }
   1552 
   1553             SupplicantResult<Void> result = new SupplicantResult(
   1554                     "startWpsPinKeypad(" + groupIfName + ", " + pin + ")");
   1555             try {
   1556                 result.setResult(mISupplicantP2pIface.startWpsPinKeypad(groupIfName, pin));
   1557             } catch (RemoteException e) {
   1558                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   1559                 supplicantServiceDiedHandler();
   1560             }
   1561 
   1562             return result.isSuccess();
   1563         }
   1564     }
   1565 
   1566 
   1567     /**
   1568      * Initiate WPS Pin Display setup.
   1569      *
   1570      * @param groupIfName Group interface name to use.
   1571      * @param bssid BSSID of the AP. Use empty bssid to indicate wildcard.
   1572      * @return generated pin if operation was successful, null otherwise.
   1573      */
   1574     public String startWpsPinDisplay(String groupIfName, String bssid) {
   1575         if (TextUtils.isEmpty(groupIfName)) return null;
   1576         synchronized (mLock) {
   1577             if (!checkSupplicantP2pIfaceAndLogFailure("startWpsPinDisplay")) return null;
   1578             if (groupIfName == null) {
   1579                 Log.e(TAG, "Group name required when requesting WPS KEYPAD.");
   1580                 return null;
   1581             }
   1582 
   1583             // Null values should be fine, since bssid can be empty.
   1584             byte[] macAddress = null;
   1585             try {
   1586                 macAddress = NativeUtil.macAddressToByteArray(bssid);
   1587             } catch (Exception e) {
   1588                 Log.e(TAG, "Could not parse BSSID.", e);
   1589                 return null;
   1590             }
   1591 
   1592             SupplicantResult<String> result = new SupplicantResult(
   1593                     "startWpsPinDisplay(" + groupIfName + ", " + bssid + ")");
   1594             try {
   1595                 mISupplicantP2pIface.startWpsPinDisplay(
   1596                         groupIfName, macAddress,
   1597                         (SupplicantStatus status, String generatedPin) -> {
   1598                             result.setResult(status, generatedPin);
   1599                         });
   1600             } catch (RemoteException e) {
   1601                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   1602                 supplicantServiceDiedHandler();
   1603             }
   1604 
   1605             return result.getResult();
   1606         }
   1607     }
   1608 
   1609 
   1610     /**
   1611      * Cancel any ongoing WPS operations.
   1612      *
   1613      * @param groupIfName Group interface name to use.
   1614      * @return true, if operation was successful.
   1615      */
   1616     public boolean cancelWps(String groupIfName) {
   1617         synchronized (mLock) {
   1618             if (!checkSupplicantP2pIfaceAndLogFailure("cancelWps")) return false;
   1619             if (groupIfName == null) {
   1620                 Log.e(TAG, "Group name required when requesting WPS KEYPAD.");
   1621                 return false;
   1622             }
   1623 
   1624             SupplicantResult<Void> result = new SupplicantResult(
   1625                     "cancelWps(" + groupIfName + ")");
   1626             try {
   1627                 result.setResult(mISupplicantP2pIface.cancelWps(groupIfName));
   1628             } catch (RemoteException e) {
   1629                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   1630                 supplicantServiceDiedHandler();
   1631             }
   1632 
   1633             return result.isSuccess();
   1634         }
   1635     }
   1636 
   1637 
   1638     /**
   1639      * Enable/Disable Wifi Display.
   1640      *
   1641      * @param enable true to enable, false to disable.
   1642      * @return true, if operation was successful.
   1643      */
   1644     public boolean enableWfd(boolean enable) {
   1645         synchronized (mLock) {
   1646             if (!checkSupplicantP2pIfaceAndLogFailure("enableWfd")) return false;
   1647 
   1648             SupplicantResult<Void> result = new SupplicantResult(
   1649                     "enableWfd(" + enable + ")");
   1650             try {
   1651                 result.setResult(mISupplicantP2pIface.enableWfd(enable));
   1652             } catch (RemoteException e) {
   1653                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   1654                 supplicantServiceDiedHandler();
   1655             }
   1656 
   1657             return result.isSuccess();
   1658         }
   1659     }
   1660 
   1661 
   1662     /**
   1663      * Set Wifi Display device info.
   1664      *
   1665      * @param info WFD device info as described in section 5.1.2 of WFD technical
   1666      *        specification v1.0.0.
   1667      * @return true, if operation was successful.
   1668      */
   1669     public boolean setWfdDeviceInfo(String info) {
   1670         synchronized (mLock) {
   1671             if (!checkSupplicantP2pIfaceAndLogFailure("setWfdDeviceInfo")) return false;
   1672 
   1673             if (info == null) {
   1674                 Log.e(TAG, "Cannot parse null WFD info string.");
   1675                 return false;
   1676             }
   1677             byte[] wfdInfo = null;
   1678             try {
   1679                 wfdInfo = NativeUtil.hexStringToByteArray(info);
   1680             } catch (Exception e) {
   1681                 Log.e(TAG, "Could not parse WFD Device Info string.");
   1682                 return false;
   1683             }
   1684 
   1685             SupplicantResult<Void> result = new SupplicantResult(
   1686                     "setWfdDeviceInfo(" + info + ")");
   1687             try {
   1688                 result.setResult(mISupplicantP2pIface.setWfdDeviceInfo(wfdInfo));
   1689             } catch (RemoteException e) {
   1690                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   1691                 supplicantServiceDiedHandler();
   1692             }
   1693 
   1694             return result.isSuccess();
   1695         }
   1696     }
   1697 
   1698     /**
   1699      * Remove network with provided id.
   1700      *
   1701      * @param networkId Id of the network to lookup.
   1702      * @return true, if operation was successful.
   1703      */
   1704     public boolean removeNetwork(int networkId) {
   1705         synchronized (mLock) {
   1706             if (!checkSupplicantP2pIfaceAndLogFailure("removeNetwork")) return false;
   1707 
   1708             SupplicantResult<Void> result = new SupplicantResult(
   1709                     "removeNetwork(" + networkId + ")");
   1710             try {
   1711                 result.setResult(mISupplicantP2pIface.removeNetwork(networkId));
   1712             } catch (RemoteException e) {
   1713                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   1714                 supplicantServiceDiedHandler();
   1715             }
   1716 
   1717             return result.isSuccess();
   1718         }
   1719     }
   1720 
   1721     /**
   1722      * List the networks saved in wpa_supplicant.
   1723      *
   1724      * @return List of network ids.
   1725      */
   1726     private List<Integer> listNetworks() {
   1727         synchronized (mLock) {
   1728             if (!checkSupplicantP2pIfaceAndLogFailure("listNetworks")) return null;
   1729             SupplicantResult<ArrayList> result = new SupplicantResult("listNetworks()");
   1730             try {
   1731                 mISupplicantP2pIface.listNetworks(
   1732                         (SupplicantStatus status, ArrayList<Integer> networkIds) -> {
   1733                             result.setResult(status, networkIds);
   1734                         });
   1735             } catch (RemoteException e) {
   1736                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   1737                 supplicantServiceDiedHandler();
   1738             }
   1739             return result.getResult();
   1740         }
   1741     }
   1742 
   1743     /**
   1744      * Get the supplicant P2p network object for the specified network ID.
   1745      *
   1746      * @param networkId Id of the network to lookup.
   1747      * @return ISupplicantP2pNetwork instance on success, null on failure.
   1748      */
   1749     private ISupplicantP2pNetwork getNetwork(int networkId) {
   1750         synchronized (mLock) {
   1751             if (!checkSupplicantP2pIfaceAndLogFailure("getNetwork")) return null;
   1752             SupplicantResult<ISupplicantNetwork> result =
   1753                     new SupplicantResult("getNetwork(" + networkId + ")");
   1754             try {
   1755                 mISupplicantP2pIface.getNetwork(
   1756                         networkId,
   1757                         (SupplicantStatus status, ISupplicantNetwork network) -> {
   1758                             result.setResult(status, network);
   1759                         });
   1760             } catch (RemoteException e) {
   1761                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   1762                 supplicantServiceDiedHandler();
   1763             }
   1764             if (result.getResult() == null) {
   1765                 Log.e(TAG, "getNetwork got null network");
   1766                 return null;
   1767             }
   1768             return getP2pNetworkMockable(result.getResult());
   1769         }
   1770     }
   1771 
   1772     /**
   1773      * Get the persistent group list from wpa_supplicant's p2p mgmt interface
   1774      *
   1775      * @param groups WifiP2pGroupList to store persistent groups in
   1776      * @return true, if list has been modified.
   1777      */
   1778     public boolean loadGroups(WifiP2pGroupList groups) {
   1779         synchronized (mLock) {
   1780             if (!checkSupplicantP2pIfaceAndLogFailure("loadGroups")) return false;
   1781             List<Integer> networkIds = listNetworks();
   1782             if (networkIds == null || networkIds.isEmpty()) {
   1783                 return false;
   1784             }
   1785             for (Integer networkId : networkIds) {
   1786                 ISupplicantP2pNetwork network = getNetwork(networkId);
   1787                 if (network == null) {
   1788                     Log.e(TAG, "Failed to retrieve network object for " + networkId);
   1789                     continue;
   1790                 }
   1791                 SupplicantResult<Boolean> resultIsCurrent =
   1792                         new SupplicantResult("isCurrent(" + networkId + ")");
   1793                 try {
   1794                     network.isCurrent(
   1795                             (SupplicantStatus status, boolean isCurrent) -> {
   1796                                 resultIsCurrent.setResult(status, isCurrent);
   1797                             });
   1798                 } catch (RemoteException e) {
   1799                     Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   1800                     supplicantServiceDiedHandler();
   1801                 }
   1802                 /** Skip the current network, if we're somehow getting networks from the p2p GO
   1803                     interface, instead of p2p mgmt interface*/
   1804                 if (!resultIsCurrent.isSuccess() || resultIsCurrent.getResult()) {
   1805                     Log.i(TAG, "Skipping current network");
   1806                     continue;
   1807                 }
   1808 
   1809                 WifiP2pGroup group = new WifiP2pGroup();
   1810                 group.setNetworkId(networkId);
   1811 
   1812                 // Now get the ssid, bssid and other flags for this network.
   1813                 SupplicantResult<ArrayList> resultSsid =
   1814                         new SupplicantResult("getSsid(" + networkId + ")");
   1815                 try {
   1816                     network.getSsid(
   1817                             (SupplicantStatus status, ArrayList<Byte> ssid) -> {
   1818                                 resultSsid.setResult(status, ssid);
   1819                             });
   1820                 } catch (RemoteException e) {
   1821                     Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   1822                     supplicantServiceDiedHandler();
   1823                 }
   1824                 if (resultSsid.isSuccess() && resultSsid.getResult() != null
   1825                         && !resultSsid.getResult().isEmpty()) {
   1826                     group.setNetworkName(NativeUtil.removeEnclosingQuotes(
   1827                             NativeUtil.encodeSsid(resultSsid.getResult())));
   1828                 }
   1829 
   1830                 SupplicantResult<byte[]> resultBssid =
   1831                         new SupplicantResult("getBssid(" + networkId + ")");
   1832                 try {
   1833                     network.getBssid(
   1834                             (SupplicantStatus status, byte[] bssid) -> {
   1835                                 resultBssid.setResult(status, bssid);
   1836                             });
   1837                 } catch (RemoteException e) {
   1838                     Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   1839                     supplicantServiceDiedHandler();
   1840                 }
   1841                 if (resultBssid.isSuccess() && !ArrayUtils.isEmpty(resultBssid.getResult())) {
   1842                     WifiP2pDevice device = new WifiP2pDevice();
   1843                     device.deviceAddress =
   1844                             NativeUtil.macAddressFromByteArray(resultBssid.getResult());
   1845                     group.setOwner(device);
   1846                 }
   1847 
   1848                 SupplicantResult<Boolean> resultIsGo =
   1849                         new SupplicantResult("isGo(" + networkId + ")");
   1850                 try {
   1851                     network.isGo(
   1852                             (SupplicantStatus status, boolean isGo) -> {
   1853                                 resultIsGo.setResult(status, isGo);
   1854                             });
   1855                 } catch (RemoteException e) {
   1856                     Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   1857                     supplicantServiceDiedHandler();
   1858                 }
   1859                 if (resultIsGo.isSuccess()) {
   1860                     group.setIsGroupOwner(resultIsGo.getResult());
   1861                 }
   1862                 groups.add(group);
   1863             }
   1864         }
   1865         return true;
   1866     }
   1867 
   1868     /**
   1869      * Set WPS device name.
   1870      *
   1871      * @param name String to be set.
   1872      * @return true if request is sent successfully, false otherwise.
   1873      */
   1874     public boolean setWpsDeviceName(String name) {
   1875         if (name == null) {
   1876             return false;
   1877         }
   1878         synchronized (mLock) {
   1879             if (!checkSupplicantP2pIfaceAndLogFailure("setWpsDeviceName")) return false;
   1880             SupplicantResult<Void> result = new SupplicantResult(
   1881                     "setWpsDeviceName(" + name + ")");
   1882             try {
   1883                 result.setResult(mISupplicantP2pIface.setWpsDeviceName(name));
   1884             } catch (RemoteException e) {
   1885                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   1886                 supplicantServiceDiedHandler();
   1887             }
   1888             return result.isSuccess();
   1889         }
   1890     }
   1891 
   1892     /**
   1893      * Set WPS device type.
   1894      *
   1895      * @param typeStr Type specified as a string. Used format: <categ>-<OUI>-<subcateg>
   1896      * @return true if request is sent successfully, false otherwise.
   1897      */
   1898     public boolean setWpsDeviceType(String typeStr) {
   1899         try {
   1900             Matcher match = WPS_DEVICE_TYPE_PATTERN.matcher(typeStr);
   1901             if (!match.find() || match.groupCount() != 3) {
   1902                 Log.e(TAG, "Malformed WPS device type " + typeStr);
   1903                 return false;
   1904             }
   1905             short categ = Short.parseShort(match.group(1));
   1906             byte[] oui = NativeUtil.hexStringToByteArray(match.group(2));
   1907             short subCateg = Short.parseShort(match.group(3));
   1908 
   1909             byte[] bytes = new byte[8];
   1910             ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
   1911             byteBuffer.putShort(categ);
   1912             byteBuffer.put(oui);
   1913             byteBuffer.putShort(subCateg);
   1914             synchronized (mLock) {
   1915                 if (!checkSupplicantP2pIfaceAndLogFailure("setWpsDeviceType")) return false;
   1916                 SupplicantResult<Void> result = new SupplicantResult(
   1917                         "setWpsDeviceType(" + typeStr + ")");
   1918                 try {
   1919                     result.setResult(mISupplicantP2pIface.setWpsDeviceType(bytes));
   1920                 } catch (RemoteException e) {
   1921                     Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   1922                     supplicantServiceDiedHandler();
   1923                 }
   1924                 return result.isSuccess();
   1925             }
   1926         } catch (IllegalArgumentException e) {
   1927             Log.e(TAG, "Illegal argument " + typeStr, e);
   1928             return false;
   1929         }
   1930     }
   1931 
   1932     /**
   1933      * Set WPS config methods
   1934      *
   1935      * @param configMethodsStr List of config methods.
   1936      * @return true if request is sent successfully, false otherwise.
   1937      */
   1938     public boolean setWpsConfigMethods(String configMethodsStr) {
   1939         synchronized (mLock) {
   1940             if (!checkSupplicantP2pIfaceAndLogFailure("setWpsConfigMethods")) return false;
   1941             SupplicantResult<Void> result =
   1942                     new SupplicantResult("setWpsConfigMethods(" + configMethodsStr + ")");
   1943             short configMethodsMask = 0;
   1944             String[] configMethodsStrArr = configMethodsStr.split("\\s+");
   1945             for (int i = 0; i < configMethodsStrArr.length; i++) {
   1946                 configMethodsMask |= stringToWpsConfigMethod(configMethodsStrArr[i]);
   1947             }
   1948             try {
   1949                 result.setResult(mISupplicantP2pIface.setWpsConfigMethods(configMethodsMask));
   1950             } catch (RemoteException e) {
   1951                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   1952                 supplicantServiceDiedHandler();
   1953             }
   1954             return result.isSuccess();
   1955         }
   1956     }
   1957 
   1958     /**
   1959      * Get NFC handover request message.
   1960      *
   1961      * @return select message if created successfully, null otherwise.
   1962      */
   1963     public String getNfcHandoverRequest() {
   1964         synchronized (mLock) {
   1965             if (!checkSupplicantP2pIfaceAndLogFailure("getNfcHandoverRequest")) return null;
   1966             SupplicantResult<ArrayList> result = new SupplicantResult(
   1967                     "getNfcHandoverRequest()");
   1968             try {
   1969                 mISupplicantP2pIface.createNfcHandoverRequestMessage(
   1970                         (SupplicantStatus status, ArrayList<Byte> message) -> {
   1971                             result.setResult(status, message);
   1972                         });
   1973             } catch (RemoteException e) {
   1974                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   1975                 supplicantServiceDiedHandler();
   1976             }
   1977             if (!result.isSuccess()) {
   1978                 return null;
   1979 
   1980             }
   1981             return NativeUtil.hexStringFromByteArray(
   1982                     NativeUtil.byteArrayFromArrayList(result.getResult()));
   1983         }
   1984     }
   1985 
   1986     /**
   1987      * Get NFC handover select message.
   1988      *
   1989      * @return select message if created successfully, null otherwise.
   1990      */
   1991     public String getNfcHandoverSelect() {
   1992         synchronized (mLock) {
   1993             if (!checkSupplicantP2pIfaceAndLogFailure("getNfcHandoverSelect")) return null;
   1994             SupplicantResult<ArrayList> result = new SupplicantResult(
   1995                     "getNfcHandoverSelect()");
   1996             try {
   1997                 mISupplicantP2pIface.createNfcHandoverSelectMessage(
   1998                         (SupplicantStatus status, ArrayList<Byte> message) -> {
   1999                             result.setResult(status, message);
   2000                         });
   2001             } catch (RemoteException e) {
   2002                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   2003                 supplicantServiceDiedHandler();
   2004             }
   2005             if (!result.isSuccess()) {
   2006                 return null;
   2007 
   2008             }
   2009             return NativeUtil.hexStringFromByteArray(
   2010                     NativeUtil.byteArrayFromArrayList(result.getResult()));
   2011         }
   2012     }
   2013 
   2014     /**
   2015      * Report NFC handover select message.
   2016      *
   2017      * @return true if reported successfully, false otherwise.
   2018      */
   2019     public boolean initiatorReportNfcHandover(String selectMessage) {
   2020         if (selectMessage == null) return false;
   2021         synchronized (mLock) {
   2022             if (!checkSupplicantP2pIfaceAndLogFailure("initiatorReportNfcHandover")) return false;
   2023             SupplicantResult<Void> result = new SupplicantResult(
   2024                     "initiatorReportNfcHandover(" + selectMessage + ")");
   2025             try {
   2026                 result.setResult(mISupplicantP2pIface.reportNfcHandoverInitiation(
   2027                         NativeUtil.byteArrayToArrayList(NativeUtil.hexStringToByteArray(
   2028                             selectMessage))));
   2029             } catch (RemoteException e) {
   2030                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   2031                 supplicantServiceDiedHandler();
   2032             } catch (IllegalArgumentException e) {
   2033                 Log.e(TAG, "Illegal argument " + selectMessage, e);
   2034                 return false;
   2035             }
   2036             return result.isSuccess();
   2037         }
   2038     }
   2039 
   2040     /**
   2041      * Report NFC handover request message.
   2042      *
   2043      * @return true if reported successfully, false otherwise.
   2044      */
   2045     public boolean responderReportNfcHandover(String requestMessage) {
   2046         if (requestMessage == null) return false;
   2047         synchronized (mLock) {
   2048             if (!checkSupplicantP2pIfaceAndLogFailure("responderReportNfcHandover")) return false;
   2049             SupplicantResult<Void> result = new SupplicantResult(
   2050                     "responderReportNfcHandover(" + requestMessage + ")");
   2051             try {
   2052                 result.setResult(mISupplicantP2pIface.reportNfcHandoverResponse(
   2053                         NativeUtil.byteArrayToArrayList(NativeUtil.hexStringToByteArray(
   2054                             requestMessage))));
   2055             } catch (RemoteException e) {
   2056                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   2057                 supplicantServiceDiedHandler();
   2058             } catch (IllegalArgumentException e) {
   2059                 Log.e(TAG, "Illegal argument " + requestMessage, e);
   2060                 return false;
   2061             }
   2062             return result.isSuccess();
   2063         }
   2064     }
   2065 
   2066     /**
   2067      * Set the client list for the provided network.
   2068      *
   2069      * @param networkId Id of the network.
   2070      * @param clientListStr Space separated list of clients.
   2071      * @return true, if operation was successful.
   2072      */
   2073     public boolean setClientList(int networkId, String clientListStr) {
   2074         synchronized (mLock) {
   2075             if (!checkSupplicantP2pIfaceAndLogFailure("setClientList")) return false;
   2076             if (TextUtils.isEmpty(clientListStr)) {
   2077                 Log.e(TAG, "Invalid client list");
   2078                 return false;
   2079             }
   2080             ISupplicantP2pNetwork network = getNetwork(networkId);
   2081             if (network == null) {
   2082                 Log.e(TAG, "Invalid network id ");
   2083                 return false;
   2084             }
   2085             SupplicantResult<Void> result = new SupplicantResult(
   2086                     "setClientList(" + networkId + ", " + clientListStr + ")");
   2087             try {
   2088                 ArrayList<byte[]> clients = new ArrayList<>();
   2089                 for (String clientStr : Arrays.asList(clientListStr.split("\\s+"))) {
   2090                     clients.add(NativeUtil.macAddressToByteArray(clientStr));
   2091                 }
   2092                 result.setResult(network.setClientList(clients));
   2093             } catch (RemoteException e) {
   2094                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   2095                 supplicantServiceDiedHandler();
   2096             } catch (IllegalArgumentException e) {
   2097                 Log.e(TAG, "Illegal argument " + clientListStr, e);
   2098                 return false;
   2099             }
   2100             return result.isSuccess();
   2101         }
   2102     }
   2103 
   2104     /**
   2105      * Set the client list for the provided network.
   2106      *
   2107      * @param networkId Id of the network.
   2108      * @return  Space separated list of clients if successfull, null otherwise.
   2109      */
   2110     public String getClientList(int networkId) {
   2111         synchronized (mLock) {
   2112             if (!checkSupplicantP2pIfaceAndLogFailure("getClientList")) return null;
   2113             ISupplicantP2pNetwork network = getNetwork(networkId);
   2114             if (network == null) {
   2115                 Log.e(TAG, "Invalid network id ");
   2116                 return null;
   2117             }
   2118             SupplicantResult<ArrayList> result = new SupplicantResult(
   2119                     "getClientList(" + networkId + ")");
   2120             try {
   2121                 network.getClientList(
   2122                         (SupplicantStatus status, ArrayList<byte[]> clients) -> {
   2123                             result.setResult(status, clients);
   2124                         });
   2125             } catch (RemoteException e) {
   2126                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   2127                 supplicantServiceDiedHandler();
   2128             }
   2129             if (!result.isSuccess()) {
   2130                 return null;
   2131             }
   2132             ArrayList<byte[]> clients = result.getResult();
   2133             return clients.stream()
   2134                     .map(NativeUtil::macAddressFromByteArray)
   2135                     .collect(Collectors.joining(" "));
   2136         }
   2137     }
   2138 
   2139     /**
   2140      * Persist the current configurations to disk.
   2141      *
   2142      * @return true, if operation was successful.
   2143      */
   2144     public boolean saveConfig() {
   2145         synchronized (mLock) {
   2146             if (!checkSupplicantP2pIfaceAndLogFailure("saveConfig")) return false;
   2147             SupplicantResult<Void> result = new SupplicantResult("saveConfig()");
   2148             try {
   2149                 result.setResult(mISupplicantP2pIface.saveConfig());
   2150             } catch (RemoteException e) {
   2151                 Log.e(TAG, "ISupplicantP2pIface exception: " + e);
   2152                 supplicantServiceDiedHandler();
   2153             }
   2154             return result.isSuccess();
   2155         }
   2156     }
   2157 
   2158     /**
   2159      * Converts the Wps config method string to the equivalent enum value.
   2160      */
   2161     private static short stringToWpsConfigMethod(String configMethod) {
   2162         switch (configMethod) {
   2163             case "usba":
   2164                 return WpsConfigMethods.USBA;
   2165             case "ethernet":
   2166                 return WpsConfigMethods.ETHERNET;
   2167             case "label":
   2168                 return WpsConfigMethods.LABEL;
   2169             case "display":
   2170                 return WpsConfigMethods.DISPLAY;
   2171             case "int_nfc_token":
   2172                 return WpsConfigMethods.INT_NFC_TOKEN;
   2173             case "ext_nfc_token":
   2174                 return WpsConfigMethods.EXT_NFC_TOKEN;
   2175             case "nfc_interface":
   2176                 return WpsConfigMethods.NFC_INTERFACE;
   2177             case "push_button":
   2178                 return WpsConfigMethods.PUSHBUTTON;
   2179             case "keypad":
   2180                 return WpsConfigMethods.KEYPAD;
   2181             case "virtual_push_button":
   2182                 return WpsConfigMethods.VIRT_PUSHBUTTON;
   2183             case "physical_push_button":
   2184                 return WpsConfigMethods.PHY_PUSHBUTTON;
   2185             case "p2ps":
   2186                 return WpsConfigMethods.P2PS;
   2187             case "virtual_display":
   2188                 return WpsConfigMethods.VIRT_DISPLAY;
   2189             case "physical_display":
   2190                 return WpsConfigMethods.PHY_DISPLAY;
   2191             default:
   2192                 throw new IllegalArgumentException(
   2193                         "Invalid WPS config method: " + configMethod);
   2194         }
   2195     }
   2196 
   2197     /** Container class allowing propagation of status and/or value
   2198      * from callbacks.
   2199      *
   2200      * Primary purpose is to allow callback lambdas to provide results
   2201      * to parent methods.
   2202      */
   2203     private static class SupplicantResult<E> {
   2204         private String mMethodName;
   2205         private SupplicantStatus mStatus;
   2206         private E mValue;
   2207 
   2208         SupplicantResult(String methodName) {
   2209             mMethodName = methodName;
   2210             mStatus = null;
   2211             mValue = null;
   2212             logd("entering " + mMethodName);
   2213         }
   2214 
   2215         public void setResult(SupplicantStatus status, E value) {
   2216             logCompletion(mMethodName, status);
   2217             logd("leaving " + mMethodName + " with result = " + value);
   2218             mStatus = status;
   2219             mValue = value;
   2220         }
   2221 
   2222         public void setResult(SupplicantStatus status) {
   2223             logCompletion(mMethodName, status);
   2224             logd("leaving " + mMethodName);
   2225             mStatus = status;
   2226         }
   2227 
   2228         public boolean isSuccess() {
   2229             return (mStatus != null && mStatus.code == SupplicantStatusCode.SUCCESS);
   2230         }
   2231 
   2232         public E getResult() {
   2233             return (isSuccess() ? mValue : null);
   2234         }
   2235     }
   2236 }
   2237