| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.wifi.p2p; |
| |
| import android.annotation.NonNull; |
| import android.hardware.wifi.supplicant.V1_0.ISupplicant; |
| import android.hardware.wifi.supplicant.V1_0.ISupplicantIface; |
| import android.hardware.wifi.supplicant.V1_0.ISupplicantNetwork; |
| import android.hardware.wifi.supplicant.V1_0.ISupplicantP2pIface; |
| import android.hardware.wifi.supplicant.V1_0.ISupplicantP2pIfaceCallback; |
| import android.hardware.wifi.supplicant.V1_0.ISupplicantP2pNetwork; |
| import android.hardware.wifi.supplicant.V1_0.IfaceType; |
| import android.hardware.wifi.supplicant.V1_0.SupplicantStatus; |
| import android.hardware.wifi.supplicant.V1_0.SupplicantStatusCode; |
| import android.hardware.wifi.supplicant.V1_0.WpsConfigMethods; |
| import android.hidl.manager.V1_0.IServiceManager; |
| import android.hidl.manager.V1_0.IServiceNotification; |
| import android.net.wifi.WpsInfo; |
| import android.net.wifi.p2p.WifiP2pConfig; |
| import android.net.wifi.p2p.WifiP2pDevice; |
| import android.net.wifi.p2p.WifiP2pGroup; |
| import android.net.wifi.p2p.WifiP2pGroupList; |
| import android.net.wifi.p2p.WifiP2pManager; |
| import android.net.wifi.p2p.nsd.WifiP2pServiceInfo; |
| import android.os.HwRemoteBinder; |
| import android.os.RemoteException; |
| import android.text.TextUtils; |
| import android.util.Log; |
| |
| import com.android.internal.util.ArrayUtils; |
| import com.android.server.wifi.util.NativeUtil; |
| |
| import java.nio.ByteBuffer; |
| import java.nio.ByteOrder; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.NoSuchElementException; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| import java.util.stream.Collectors; |
| |
| /** |
| * Native calls sending requests to the P2P Hals, and callbacks for receiving P2P events |
| * |
| * {@hide} |
| */ |
| public class SupplicantP2pIfaceHal { |
| private static final String TAG = "SupplicantP2pIfaceHal"; |
| private static boolean sVerboseLoggingEnabled = true; |
| private static final int RESULT_NOT_VALID = -1; |
| private static final int DEFAULT_GROUP_OWNER_INTENT = 6; |
| private static final int DEFAULT_OPERATING_CLASS = 81; |
| /** |
| * Regex pattern for extracting the wps device type bytes. |
| * Matches a strings like the following: "<categ>-<OUI>-<subcateg>"; |
| */ |
| private static final Pattern WPS_DEVICE_TYPE_PATTERN = |
| Pattern.compile("^(\\d{1,2})-([0-9a-fA-F]{8})-(\\d{1,2})$"); |
| |
| private Object mLock = new Object(); |
| |
| // Supplicant HAL HIDL interface objects |
| private IServiceManager mIServiceManager = null; |
| private ISupplicant mISupplicant = null; |
| private ISupplicantIface mHidlSupplicantIface = null; |
| private ISupplicantP2pIface mISupplicantP2pIface = null; |
| private final IServiceNotification mServiceNotificationCallback = |
| new IServiceNotification.Stub() { |
| public void onRegistration(String fqName, String name, boolean preexisting) { |
| synchronized (mLock) { |
| if (sVerboseLoggingEnabled) { |
| Log.i(TAG, "IServiceNotification.onRegistration for: " + fqName |
| + ", " + name + " preexisting=" + preexisting); |
| } |
| if (!initSupplicantService()) { |
| Log.e(TAG, "initalizing ISupplicant failed."); |
| supplicantServiceDiedHandler(); |
| } else { |
| Log.i(TAG, "Completed initialization of ISupplicant interfaces."); |
| } |
| } |
| } |
| }; |
| private final HwRemoteBinder.DeathRecipient mServiceManagerDeathRecipient = |
| cookie -> { |
| Log.w(TAG, "IServiceManager died: cookie=" + cookie); |
| synchronized (mLock) { |
| supplicantServiceDiedHandler(); |
| mIServiceManager = null; // Will need to register a new ServiceNotification |
| } |
| }; |
| private final HwRemoteBinder.DeathRecipient mSupplicantDeathRecipient = |
| cookie -> { |
| Log.w(TAG, "ISupplicant/ISupplicantStaIface died: cookie=" + cookie); |
| synchronized (mLock) { |
| supplicantServiceDiedHandler(); |
| } |
| }; |
| |
| private final WifiP2pMonitor mMonitor; |
| private SupplicantP2pIfaceCallback mCallback = null; |
| |
| public SupplicantP2pIfaceHal(WifiP2pMonitor monitor) { |
| mMonitor = monitor; |
| } |
| |
| private boolean linkToServiceManagerDeath() { |
| if (mIServiceManager == null) return false; |
| try { |
| if (!mIServiceManager.linkToDeath(mServiceManagerDeathRecipient, 0)) { |
| Log.wtf(TAG, "Error on linkToDeath on IServiceManager"); |
| supplicantServiceDiedHandler(); |
| mIServiceManager = null; // Will need to register a new ServiceNotification |
| return false; |
| } |
| } catch (RemoteException e) { |
| Log.e(TAG, "IServiceManager.linkToDeath exception", e); |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * Enable verbose logging for all sub modules. |
| */ |
| public static void enableVerboseLogging(int verbose) { |
| sVerboseLoggingEnabled = verbose > 0; |
| SupplicantP2pIfaceCallback.enableVerboseLogging(verbose); |
| } |
| |
| /** |
| * Registers a service notification for the ISupplicant service, which triggers intialization of |
| * the ISupplicantP2pIface |
| * @return true if the service notification was successfully registered |
| */ |
| public boolean initialize() { |
| if (sVerboseLoggingEnabled) Log.i(TAG, "Registering ISupplicant service ready callback."); |
| synchronized (mLock) { |
| if (mIServiceManager != null) { |
| Log.i(TAG, "Supplicant HAL already initialized."); |
| // Already have an IServiceManager and serviceNotification registered, don't |
| // don't register another. |
| return true; |
| } |
| mISupplicant = null; |
| mISupplicantP2pIface = null; |
| try { |
| mIServiceManager = getServiceManagerMockable(); |
| if (mIServiceManager == null) { |
| Log.e(TAG, "Failed to get HIDL Service Manager"); |
| return false; |
| } |
| if (!linkToServiceManagerDeath()) { |
| return false; |
| } |
| /* TODO(b/33639391) : Use the new ISupplicant.registerForNotifications() once it |
| exists */ |
| if (!mIServiceManager.registerForNotifications( |
| ISupplicant.kInterfaceName, "", mServiceNotificationCallback)) { |
| Log.e(TAG, "Failed to register for notifications to " |
| + ISupplicant.kInterfaceName); |
| mIServiceManager = null; // Will need to register a new ServiceNotification |
| return false; |
| } |
| |
| // Successful completion by the end of the 'try' block. This will prevent reporting |
| // proper initialization after exception is caught. |
| return true; |
| } catch (RemoteException e) { |
| Log.e(TAG, "Exception while trying to register a listener for ISupplicant service: " |
| + e); |
| supplicantServiceDiedHandler(); |
| } |
| return false; |
| } |
| } |
| |
| private boolean linkToSupplicantDeath() { |
| if (mISupplicant == null) return false; |
| try { |
| if (!mISupplicant.linkToDeath(mSupplicantDeathRecipient, 0)) { |
| Log.wtf(TAG, "Error on linkToDeath on ISupplicant"); |
| supplicantServiceDiedHandler(); |
| return false; |
| } |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicant.linkToDeath exception", e); |
| return false; |
| } |
| return true; |
| } |
| |
| private boolean initSupplicantService() { |
| synchronized (mLock) { |
| try { |
| mISupplicant = getSupplicantMockable(); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicant.getService exception: " + e); |
| return false; |
| } |
| if (mISupplicant == null) { |
| Log.e(TAG, "Got null ISupplicant service. Stopping supplicant HIDL startup"); |
| return false; |
| } |
| if (!linkToSupplicantDeath()) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private boolean linkToSupplicantP2pIfaceDeath() { |
| if (mISupplicantP2pIface == null) return false; |
| try { |
| if (!mISupplicantP2pIface.linkToDeath(mSupplicantDeathRecipient, 0)) { |
| Log.wtf(TAG, "Error on linkToDeath on ISupplicantP2pIface"); |
| supplicantServiceDiedHandler(); |
| return false; |
| } |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface.linkToDeath exception", e); |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * Setup the P2p iface. |
| * |
| * @param ifaceName Name of the interface. |
| * @return true on success, false otherwise. |
| */ |
| public boolean setupIface(@NonNull String ifaceName) { |
| synchronized (mLock) { |
| if (mISupplicantP2pIface != null) return false; |
| ISupplicantIface ifaceHwBinder; |
| if (isV1_1()) { |
| ifaceHwBinder = addIfaceV1_1(ifaceName); |
| } else { |
| ifaceHwBinder = getIfaceV1_0(ifaceName); |
| } |
| if (ifaceHwBinder == null) { |
| Log.e(TAG, "initSupplicantP2pIface got null iface"); |
| return false; |
| } |
| mISupplicantP2pIface = getP2pIfaceMockable(ifaceHwBinder); |
| if (!linkToSupplicantP2pIfaceDeath()) { |
| return false; |
| } |
| if (mISupplicantP2pIface != null && mMonitor != null) { |
| mCallback = new SupplicantP2pIfaceCallback(ifaceName, mMonitor); |
| if (!registerCallback(mCallback)) { |
| Log.e(TAG, "Callback registration failed. Initialization incomplete."); |
| return false; |
| } |
| } |
| return true; |
| } |
| } |
| |
| private ISupplicantIface getIfaceV1_0(@NonNull String ifaceName) { |
| /** List all supplicant Ifaces */ |
| final ArrayList<ISupplicant.IfaceInfo> supplicantIfaces = new ArrayList(); |
| try { |
| mISupplicant.listInterfaces((SupplicantStatus status, |
| ArrayList<ISupplicant.IfaceInfo> ifaces) -> { |
| if (status.code != SupplicantStatusCode.SUCCESS) { |
| Log.e(TAG, "Getting Supplicant Interfaces failed: " + status.code); |
| return; |
| } |
| supplicantIfaces.addAll(ifaces); |
| }); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicant.listInterfaces exception: " + e); |
| return null; |
| } |
| if (supplicantIfaces.size() == 0) { |
| Log.e(TAG, "Got zero HIDL supplicant ifaces. Stopping supplicant HIDL startup."); |
| supplicantServiceDiedHandler(); |
| return null; |
| } |
| SupplicantResult<ISupplicantIface> supplicantIface = |
| new SupplicantResult("getInterface()"); |
| for (ISupplicant.IfaceInfo ifaceInfo : supplicantIfaces) { |
| if (ifaceInfo.type == IfaceType.P2P && ifaceName.equals(ifaceInfo.name)) { |
| try { |
| mISupplicant.getInterface(ifaceInfo, |
| (SupplicantStatus status, ISupplicantIface iface) -> { |
| if (status.code != SupplicantStatusCode.SUCCESS) { |
| Log.e(TAG, "Failed to get ISupplicantIface " + status.code); |
| return; |
| } |
| supplicantIface.setResult(status, iface); |
| }); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicant.getInterface exception: " + e); |
| supplicantServiceDiedHandler(); |
| return null; |
| } |
| break; |
| } |
| } |
| return supplicantIface.getResult(); |
| } |
| |
| private ISupplicantIface addIfaceV1_1(@NonNull String ifaceName) { |
| synchronized (mLock) { |
| ISupplicant.IfaceInfo ifaceInfo = new ISupplicant.IfaceInfo(); |
| ifaceInfo.name = ifaceName; |
| ifaceInfo.type = IfaceType.P2P; |
| SupplicantResult<ISupplicantIface> supplicantIface = |
| new SupplicantResult("addInterface(" + ifaceInfo + ")"); |
| try { |
| android.hardware.wifi.supplicant.V1_1.ISupplicant supplicant_v1_1 = |
| getSupplicantMockableV1_1(); |
| if (supplicant_v1_1 == null) { |
| Log.e(TAG, "Can't call addIface: ISupplicantP2pIface is null"); |
| return null; |
| } |
| supplicant_v1_1.addInterface(ifaceInfo, |
| (SupplicantStatus status, ISupplicantIface iface) -> { |
| if (status.code != SupplicantStatusCode.SUCCESS |
| && status.code != SupplicantStatusCode.FAILURE_IFACE_EXISTS) { |
| Log.e(TAG, "Failed to get ISupplicantIface " + status.code); |
| return; |
| } |
| supplicantIface.setResult(status, iface); |
| }); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicant.addInterface exception: " + e); |
| supplicantServiceDiedHandler(); |
| return null; |
| } |
| return supplicantIface.getResult(); |
| } |
| } |
| |
| /** |
| * Teardown the P2P interface. |
| * |
| * @param ifaceName Name of the interface. |
| * @return true on success, false otherwise. |
| */ |
| public boolean teardownIface(@NonNull String ifaceName) { |
| synchronized (mLock) { |
| if (mISupplicantP2pIface == null) return false; |
| // Only supported for V1.1 |
| if (isV1_1()) { |
| return removeIfaceV1_1(ifaceName); |
| } |
| return true; |
| } |
| } |
| |
| /** |
| * Remove the P2p iface. |
| * |
| * @return true on success, false otherwise. |
| */ |
| private boolean removeIfaceV1_1(@NonNull String ifaceName) { |
| synchronized (mLock) { |
| try { |
| android.hardware.wifi.supplicant.V1_1.ISupplicant supplicant_v1_1 = |
| getSupplicantMockableV1_1(); |
| if (supplicant_v1_1 == null) { |
| Log.e(TAG, "Can't call removeIface: ISupplicantP2pIface is null"); |
| return false; |
| } |
| ISupplicant.IfaceInfo ifaceInfo = new ISupplicant.IfaceInfo(); |
| ifaceInfo.name = ifaceName; |
| ifaceInfo.type = IfaceType.P2P; |
| SupplicantStatus status = supplicant_v1_1.removeInterface(ifaceInfo); |
| if (status.code != SupplicantStatusCode.SUCCESS) { |
| Log.e(TAG, "Failed to remove iface " + status.code); |
| return false; |
| } |
| mCallback = null; |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicant.removeInterface exception: " + e); |
| supplicantServiceDiedHandler(); |
| return false; |
| } |
| mISupplicantP2pIface = null; |
| return true; |
| } |
| } |
| |
| private void supplicantServiceDiedHandler() { |
| synchronized (mLock) { |
| mISupplicant = null; |
| mISupplicantP2pIface = null; |
| } |
| } |
| |
| |
| /** |
| * Signals whether Initialization completed successfully. |
| */ |
| public boolean isInitializationStarted() { |
| synchronized (mLock) { |
| return mIServiceManager != null; |
| } |
| } |
| |
| /** |
| * Signals whether Initialization completed successfully. Only necessary for testing, is not |
| * needed to guard calls etc. |
| */ |
| public boolean isInitializationComplete() { |
| return mISupplicant != null; |
| } |
| |
| /** |
| * Wrapper functions to access static HAL methods, created to be mockable in unit tests |
| */ |
| protected IServiceManager getServiceManagerMockable() throws RemoteException { |
| return IServiceManager.getService(); |
| } |
| |
| protected ISupplicant getSupplicantMockable() throws RemoteException { |
| try { |
| return ISupplicant.getService(); |
| } catch (NoSuchElementException e) { |
| Log.e(TAG, "Failed to get ISupplicant", e); |
| return null; |
| } |
| } |
| |
| protected android.hardware.wifi.supplicant.V1_1.ISupplicant getSupplicantMockableV1_1() |
| throws RemoteException { |
| synchronized (mLock) { |
| try { |
| return android.hardware.wifi.supplicant.V1_1.ISupplicant.castFrom( |
| ISupplicant.getService()); |
| } catch (NoSuchElementException e) { |
| Log.e(TAG, "Failed to get ISupplicant", e); |
| return null; |
| } |
| } |
| } |
| |
| protected ISupplicantP2pIface getP2pIfaceMockable(ISupplicantIface iface) { |
| return ISupplicantP2pIface.asInterface(iface.asBinder()); |
| } |
| |
| protected android.hardware.wifi.supplicant.V1_2.ISupplicantP2pIface |
| getP2pIfaceMockableV1_2() { |
| if (mISupplicantP2pIface == null) return null; |
| return android.hardware.wifi.supplicant.V1_2.ISupplicantP2pIface.castFrom( |
| mISupplicantP2pIface); |
| } |
| |
| protected ISupplicantP2pNetwork getP2pNetworkMockable(ISupplicantNetwork network) { |
| return ISupplicantP2pNetwork.asInterface(network.asBinder()); |
| } |
| |
| /** |
| * Check if the device is running V1_1 supplicant service. |
| * @return |
| */ |
| private boolean isV1_1() { |
| synchronized (mLock) { |
| try { |
| return (getSupplicantMockableV1_1() != null); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicant.getService exception: " + e); |
| supplicantServiceDiedHandler(); |
| return false; |
| } |
| } |
| } |
| |
| protected static void logd(String s) { |
| if (sVerboseLoggingEnabled) Log.d(TAG, s); |
| } |
| |
| protected static void logCompletion(String operation, SupplicantStatus status) { |
| if (status == null) { |
| Log.w(TAG, operation + " failed: no status code returned."); |
| } else if (status.code == SupplicantStatusCode.SUCCESS) { |
| logd(operation + " completed successfully."); |
| } else { |
| Log.w(TAG, operation + " failed: " + status.code + " (" + status.debugMessage + ")"); |
| } |
| } |
| |
| |
| /** |
| * Returns false if SupplicantP2pIface is null, and logs failure to call methodStr |
| */ |
| private boolean checkSupplicantP2pIfaceAndLogFailure(String method) { |
| if (mISupplicantP2pIface == null) { |
| Log.e(TAG, "Can't call " + method + ": ISupplicantP2pIface is null"); |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * Returns false if SupplicantP2pIface is null, and logs failure to call methodStr |
| */ |
| private boolean checkSupplicantP2pIfaceAndLogFailureV1_2(String method) { |
| if (getP2pIfaceMockableV1_2() == null) { |
| Log.e(TAG, "Can't call " + method + ": ISupplicantP2pIface is null"); |
| return false; |
| } |
| return true; |
| } |
| |
| private int wpsInfoToConfigMethod(int info) { |
| switch (info) { |
| case WpsInfo.PBC: |
| return ISupplicantP2pIface.WpsProvisionMethod.PBC; |
| |
| case WpsInfo.DISPLAY: |
| return ISupplicantP2pIface.WpsProvisionMethod.DISPLAY; |
| |
| case WpsInfo.KEYPAD: |
| case WpsInfo.LABEL: |
| return ISupplicantP2pIface.WpsProvisionMethod.KEYPAD; |
| |
| default: |
| Log.e(TAG, "Unsupported WPS provision method: " + info); |
| return RESULT_NOT_VALID; |
| } |
| } |
| |
| /** |
| * Retrieves the name of the network interface. |
| * |
| * @return name Name of the network interface, e.g., wlan0 |
| */ |
| public String getName() { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("getName")) return null; |
| SupplicantResult<String> result = new SupplicantResult("getName()"); |
| |
| try { |
| mISupplicantP2pIface.getName( |
| (SupplicantStatus status, String name) -> { |
| result.setResult(status, name); |
| }); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| return result.getResult(); |
| } |
| } |
| |
| |
| /** |
| * Register for callbacks from this interface. |
| * |
| * These callbacks are invoked for events that are specific to this interface. |
| * Registration of multiple callback objects is supported. These objects must |
| * be automatically deleted when the corresponding client process is dead or |
| * if this interface is removed. |
| * |
| * @param receiver An instance of the |ISupplicantP2pIfaceCallback| HIDL |
| * interface object. |
| * @return boolean value indicating whether operation was successful. |
| */ |
| public boolean registerCallback(ISupplicantP2pIfaceCallback receiver) { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("registerCallback")) return false; |
| SupplicantResult<Void> result = new SupplicantResult("registerCallback()"); |
| try { |
| result.setResult(mISupplicantP2pIface.registerCallback(receiver)); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| return result.isSuccess(); |
| } |
| } |
| |
| |
| /** |
| * Initiate a P2P service discovery with a (optional) timeout. |
| * |
| * @param timeout Max time to be spent is peforming discovery. |
| * Set to 0 to indefinely continue discovery untill and explicit |
| * |stopFind| is sent. |
| * @return boolean value indicating whether operation was successful. |
| */ |
| public boolean find(int timeout) { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("find")) return false; |
| |
| if (timeout < 0) { |
| Log.e(TAG, "Invalid timeout value: " + timeout); |
| return false; |
| } |
| SupplicantResult<Void> result = new SupplicantResult("find(" + timeout + ")"); |
| try { |
| result.setResult(mISupplicantP2pIface.find(timeout)); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| return result.isSuccess(); |
| } |
| } |
| |
| |
| /** |
| * Stop an ongoing P2P service discovery. |
| * |
| * @return boolean value indicating whether operation was successful. |
| */ |
| public boolean stopFind() { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("stopFind")) return false; |
| SupplicantResult<Void> result = new SupplicantResult("stopFind()"); |
| try { |
| result.setResult(mISupplicantP2pIface.stopFind()); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| return result.isSuccess(); |
| } |
| } |
| |
| |
| /** |
| * Flush P2P peer table and state. |
| * |
| * @return boolean value indicating whether operation was successful. |
| */ |
| public boolean flush() { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("flush")) return false; |
| SupplicantResult<Void> result = new SupplicantResult("flush()"); |
| try { |
| result.setResult(mISupplicantP2pIface.flush()); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| return result.isSuccess(); |
| } |
| } |
| |
| |
| /** |
| * This command can be used to flush all services from the |
| * device. |
| * |
| * @return boolean value indicating whether operation was successful. |
| */ |
| public boolean serviceFlush() { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("serviceFlush")) return false; |
| SupplicantResult<Void> result = new SupplicantResult("serviceFlush()"); |
| try { |
| result.setResult(mISupplicantP2pIface.flushServices()); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| return result.isSuccess(); |
| } |
| } |
| |
| |
| /** |
| * Turn on/off power save mode for the interface. |
| * |
| * @param groupIfName Group interface name to use. |
| * @param enable Indicate if power save is to be turned on/off. |
| * |
| * @return boolean value indicating whether operation was successful. |
| */ |
| public boolean setPowerSave(String groupIfName, boolean enable) { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("setPowerSave")) return false; |
| SupplicantResult<Void> result = new SupplicantResult( |
| "setPowerSave(" + groupIfName + ", " + enable + ")"); |
| try { |
| result.setResult(mISupplicantP2pIface.setPowerSave(groupIfName, enable)); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| return result.isSuccess(); |
| } |
| } |
| |
| |
| /** |
| * Set the Maximum idle time in seconds for P2P groups. |
| * This value controls how long a P2P group is maintained after there |
| * is no other members in the group. As a group owner, this means no |
| * associated stations in the group. As a P2P client, this means no |
| * group owner seen in scan results. |
| * |
| * @param groupIfName Group interface name to use. |
| * @param timeoutInSec Timeout value in seconds. |
| * |
| * @return boolean value indicating whether operation was successful. |
| */ |
| public boolean setGroupIdle(String groupIfName, int timeoutInSec) { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("setGroupIdle")) return false; |
| // Basic checking here. Leave actual parameter validation to supplicant. |
| if (timeoutInSec < 0) { |
| Log.e(TAG, "Invalid group timeout value " + timeoutInSec); |
| return false; |
| } |
| |
| SupplicantResult<Void> result = new SupplicantResult( |
| "setGroupIdle(" + groupIfName + ", " + timeoutInSec + ")"); |
| try { |
| result.setResult(mISupplicantP2pIface.setGroupIdle(groupIfName, timeoutInSec)); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| return result.isSuccess(); |
| } |
| } |
| |
| |
| /** |
| * Set the postfix to be used for P2P SSID's. |
| * |
| * @param postfix String to be appended to SSID. |
| * |
| * @return boolean value indicating whether operation was successful. |
| */ |
| public boolean setSsidPostfix(String postfix) { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("setSsidPostfix")) return false; |
| // Basic checking here. Leave actual parameter validation to supplicant. |
| if (postfix == null) { |
| Log.e(TAG, "Invalid SSID postfix value (null)."); |
| return false; |
| } |
| |
| SupplicantResult<Void> result = new SupplicantResult("setSsidPostfix(" + postfix + ")"); |
| try { |
| result.setResult(mISupplicantP2pIface.setSsidPostfix( |
| NativeUtil.decodeSsid("\"" + postfix + "\""))); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } catch (IllegalArgumentException e) { |
| Log.e(TAG, "Could not decode SSID.", e); |
| return false; |
| } |
| |
| return result.isSuccess(); |
| } |
| } |
| |
| |
| /** |
| * Start P2P group formation with a discovered P2P peer. This includes |
| * optional group owner negotiation, group interface setup, provisioning, |
| * and establishing data connection. |
| * |
| * @param config Configuration to use to connect to remote device. |
| * @param joinExistingGroup Indicates that this is a command to join an |
| * existing group as a client. It skips the group owner negotiation |
| * part. This must send a Provision Discovery Request message to the |
| * target group owner before associating for WPS provisioning. |
| * |
| * @return String containing generated pin, if selected provision method |
| * uses PIN. |
| */ |
| public String connect(WifiP2pConfig config, boolean joinExistingGroup) { |
| if (config == null) return null; |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("setSsidPostfix")) return null; |
| |
| if (config == null) { |
| Log.e(TAG, "Could not connect: null config."); |
| return null; |
| } |
| |
| if (config.deviceAddress == null) { |
| Log.e(TAG, "Could not parse null mac address."); |
| return null; |
| } |
| |
| if (config.wps.setup == WpsInfo.PBC && !TextUtils.isEmpty(config.wps.pin)) { |
| Log.e(TAG, "Expected empty pin for PBC."); |
| return null; |
| } |
| |
| byte[] peerAddress = null; |
| try { |
| peerAddress = NativeUtil.macAddressToByteArray(config.deviceAddress); |
| } catch (Exception e) { |
| Log.e(TAG, "Could not parse peer mac address.", e); |
| return null; |
| } |
| |
| int provisionMethod = wpsInfoToConfigMethod(config.wps.setup); |
| if (provisionMethod == RESULT_NOT_VALID) { |
| Log.e(TAG, "Invalid WPS config method: " + config.wps.setup); |
| return null; |
| } |
| // NOTE: preSelectedPin cannot be null, otherwise hal would crash. |
| String preSelectedPin = TextUtils.isEmpty(config.wps.pin) ? "" : config.wps.pin; |
| boolean persistent = (config.netId == WifiP2pGroup.PERSISTENT_NET_ID); |
| |
| int goIntent = 0; |
| if (!joinExistingGroup) { |
| int groupOwnerIntent = config.groupOwnerIntent; |
| if (groupOwnerIntent < 0 || groupOwnerIntent > 15) { |
| groupOwnerIntent = DEFAULT_GROUP_OWNER_INTENT; |
| } |
| goIntent = groupOwnerIntent; |
| } |
| |
| SupplicantResult<String> result = new SupplicantResult( |
| "connect(" + config.deviceAddress + ")"); |
| try { |
| mISupplicantP2pIface.connect( |
| peerAddress, provisionMethod, preSelectedPin, joinExistingGroup, |
| persistent, goIntent, |
| (SupplicantStatus status, String generatedPin) -> { |
| result.setResult(status, generatedPin); |
| }); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| return result.getResult(); |
| } |
| } |
| |
| /** |
| * Cancel an ongoing P2P group formation and joining-a-group related |
| * operation. This operation unauthorizes the specific peer device (if any |
| * had been authorized to start group formation), stops P2P find (if in |
| * progress), stops pending operations for join-a-group, and removes the |
| * P2P group interface (if one was used) that is in the WPS provisioning |
| * step. If the WPS provisioning step has been completed, the group is not |
| * terminated. |
| * |
| * @return boolean value indicating whether operation was successful. |
| */ |
| public boolean cancelConnect() { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("cancelConnect")) return false; |
| SupplicantResult<Void> result = new SupplicantResult("cancelConnect()"); |
| try { |
| result.setResult(mISupplicantP2pIface.cancelConnect()); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| return result.isSuccess(); |
| } |
| } |
| |
| |
| /** |
| * Send P2P provision discovery request to the specified peer. The |
| * parameters for this command are the P2P device address of the peer and the |
| * desired configuration method. |
| * |
| * @param config Config class describing peer setup. |
| * |
| * @return boolean value indicating whether operation was successful. |
| */ |
| public boolean provisionDiscovery(WifiP2pConfig config) { |
| if (config == null) return false; |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("provisionDiscovery")) return false; |
| |
| int targetMethod = wpsInfoToConfigMethod(config.wps.setup); |
| if (targetMethod == RESULT_NOT_VALID) { |
| Log.e(TAG, "Unrecognized WPS configuration method: " + config.wps.setup); |
| return false; |
| } |
| if (targetMethod == ISupplicantP2pIface.WpsProvisionMethod.DISPLAY) { |
| // We are doing display, so provision discovery is keypad. |
| targetMethod = ISupplicantP2pIface.WpsProvisionMethod.KEYPAD; |
| } else if (targetMethod == ISupplicantP2pIface.WpsProvisionMethod.KEYPAD) { |
| // We are doing keypad, so provision discovery is display. |
| targetMethod = ISupplicantP2pIface.WpsProvisionMethod.DISPLAY; |
| } |
| |
| if (config.deviceAddress == null) { |
| Log.e(TAG, "Cannot parse null mac address."); |
| return false; |
| } |
| byte[] macAddress = null; |
| try { |
| macAddress = NativeUtil.macAddressToByteArray(config.deviceAddress); |
| } catch (Exception e) { |
| Log.e(TAG, "Could not parse peer mac address.", e); |
| return false; |
| } |
| |
| SupplicantResult<Void> result = new SupplicantResult( |
| "provisionDiscovery(" + config.deviceAddress + ", " + config.wps.setup + ")"); |
| try { |
| result.setResult(mISupplicantP2pIface.provisionDiscovery(macAddress, targetMethod)); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| |
| return result.isSuccess(); |
| } |
| } |
| |
| |
| /** |
| * Invite a device to a persistent group. |
| * If the peer device is the group owner of the persistent group, the peer |
| * parameter is not needed. Otherwise it is used to specify which |
| * device to invite. |goDeviceAddress| parameter may be used to override |
| * the group owner device address for Invitation Request should it not be |
| * known for some reason (this should not be needed in most cases). |
| * |
| * @param group Group object to use. |
| * @param peerAddress MAC address of the device to invite. |
| * |
| * @return boolean value indicating whether operation was successful. |
| */ |
| public boolean invite(WifiP2pGroup group, String peerAddress) { |
| if (TextUtils.isEmpty(peerAddress)) return false; |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("invite")) return false; |
| if (group == null) { |
| Log.e(TAG, "Cannot invite to null group."); |
| return false; |
| } |
| |
| if (group.getOwner() == null) { |
| Log.e(TAG, "Cannot invite to group with null owner."); |
| return false; |
| } |
| |
| if (group.getOwner().deviceAddress == null) { |
| Log.e(TAG, "Group owner has no mac address."); |
| return false; |
| } |
| |
| byte[] ownerMacAddress = null; |
| try { |
| ownerMacAddress = NativeUtil.macAddressToByteArray(group.getOwner().deviceAddress); |
| } catch (Exception e) { |
| Log.e(TAG, "Group owner mac address parse error.", e); |
| return false; |
| } |
| |
| if (peerAddress == null) { |
| Log.e(TAG, "Cannot parse peer mac address."); |
| return false; |
| } |
| |
| byte[] peerMacAddress; |
| try { |
| peerMacAddress = NativeUtil.macAddressToByteArray(peerAddress); |
| } catch (Exception e) { |
| Log.e(TAG, "Peer mac address parse error.", e); |
| return false; |
| } |
| |
| SupplicantResult<Void> result = new SupplicantResult( |
| "invite(" + group.getInterface() + ", " + group.getOwner().deviceAddress |
| + ", " + peerAddress + ")"); |
| try { |
| result.setResult(mISupplicantP2pIface.invite( |
| group.getInterface(), ownerMacAddress, peerMacAddress)); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| return result.isSuccess(); |
| } |
| } |
| |
| |
| /** |
| * Reject connection attempt from a peer (specified with a device |
| * address). This is a mechanism to reject a pending group owner negotiation |
| * with a peer and request to automatically block any further connection or |
| * discovery of the peer. |
| * |
| * @param peerAddress MAC address of the device to reject. |
| * |
| * @return boolean value indicating whether operation was successful. |
| */ |
| public boolean reject(String peerAddress) { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("reject")) return false; |
| |
| if (peerAddress == null) { |
| Log.e(TAG, "Cannot parse rejected peer's mac address."); |
| return false; |
| } |
| byte[] macAddress = null; |
| try { |
| macAddress = NativeUtil.macAddressToByteArray(peerAddress); |
| } catch (Exception e) { |
| Log.e(TAG, "Could not parse peer mac address.", e); |
| return false; |
| } |
| |
| SupplicantResult<Void> result = |
| new SupplicantResult("reject(" + peerAddress + ")"); |
| try { |
| result.setResult(mISupplicantP2pIface.reject(macAddress)); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| |
| return result.isSuccess(); |
| } |
| } |
| |
| |
| /** |
| * Gets the MAC address of the device. |
| * |
| * @return MAC address of the device. |
| */ |
| public String getDeviceAddress() { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("getDeviceAddress")) return null; |
| SupplicantResult<String> result = new SupplicantResult("getDeviceAddress()"); |
| try { |
| mISupplicantP2pIface.getDeviceAddress((SupplicantStatus status, byte[] address) -> { |
| String parsedAddress = null; |
| try { |
| parsedAddress = NativeUtil.macAddressFromByteArray(address); |
| } catch (Exception e) { |
| Log.e(TAG, "Could not process reported address.", e); |
| } |
| result.setResult(status, parsedAddress); |
| }); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| return null; |
| } |
| |
| return result.getResult(); |
| } |
| } |
| |
| |
| /** |
| * Gets the operational SSID of the device. |
| * |
| * @param address MAC address of the peer. |
| * |
| * @return SSID of the device. |
| */ |
| public String getSsid(String address) { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("getSsid")) return null; |
| |
| if (address == null) { |
| Log.e(TAG, "Cannot parse peer mac address."); |
| return null; |
| } |
| byte[] macAddress = null; |
| try { |
| macAddress = NativeUtil.macAddressToByteArray(address); |
| } catch (Exception e) { |
| Log.e(TAG, "Could not parse mac address.", e); |
| return null; |
| } |
| |
| SupplicantResult<String> result = |
| new SupplicantResult("getSsid(" + address + ")"); |
| try { |
| mISupplicantP2pIface.getSsid( |
| macAddress, (SupplicantStatus status, ArrayList<Byte> ssid) -> { |
| String ssidString = null; |
| if (ssid != null) { |
| try { |
| ssidString = NativeUtil.removeEnclosingQuotes( |
| NativeUtil.encodeSsid(ssid)); |
| } catch (Exception e) { |
| Log.e(TAG, "Could not encode SSID.", e); |
| } |
| } |
| result.setResult(status, ssidString); |
| }); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| return null; |
| } |
| |
| return result.getResult(); |
| } |
| } |
| |
| |
| /** |
| * Reinvoke a device from a persistent group. |
| * |
| * @param networkId Used to specify the persistent group. |
| * @param peerAddress MAC address of the device to reinvoke. |
| * |
| * @return true, if operation was successful. |
| */ |
| public boolean reinvoke(int networkId, String peerAddress) { |
| if (TextUtils.isEmpty(peerAddress) || networkId < 0) return false; |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("reinvoke")) return false; |
| if (peerAddress == null) { |
| Log.e(TAG, "Cannot parse peer mac address."); |
| return false; |
| } |
| byte[] macAddress = null; |
| try { |
| macAddress = NativeUtil.macAddressToByteArray(peerAddress); |
| } catch (Exception e) { |
| Log.e(TAG, "Could not parse mac address.", e); |
| return false; |
| } |
| |
| SupplicantResult<Void> result = new SupplicantResult( |
| "reinvoke(" + networkId + ", " + peerAddress + ")"); |
| try { |
| result.setResult(mISupplicantP2pIface.reinvoke(networkId, macAddress)); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| |
| return result.isSuccess(); |
| } |
| } |
| |
| |
| /** |
| * Set up a P2P group owner manually (i.e., without group owner |
| * negotiation with a specific peer). This is also known as autonomous |
| * group owner. |
| * |
| * @param networkId Used to specify the restart of a persistent group. |
| * @param isPersistent Used to request a persistent group to be formed. |
| * |
| * @return true, if operation was successful. |
| */ |
| public boolean groupAdd(int networkId, boolean isPersistent) { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("groupAdd")) return false; |
| SupplicantResult<Void> result = |
| new SupplicantResult("groupAdd(" + networkId + ", " + isPersistent + ")"); |
| try { |
| result.setResult(mISupplicantP2pIface.addGroup(isPersistent, networkId)); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| return result.isSuccess(); |
| } |
| } |
| |
| /** |
| * Set up a P2P group as Group Owner or join a group with a configuration. |
| * |
| * @param networkName SSID of the group to be formed |
| * @param passphrase passphrase of the group to be formed |
| * @param isPersistent Used to request a persistent group to be formed. |
| * @param freq prefered frequencty or band of the group to be formed |
| * @param peerAddress peerAddress Group Owner MAC address, only applied for Group Client. |
| * If the MAC is "00:00:00:00:00:00", the device will try to find a peer |
| * whose SSID matches ssid. |
| * @param join join a group or create a group |
| * |
| * @return true, if operation was successful. |
| */ |
| public boolean groupAdd(String networkName, String passphrase, |
| boolean isPersistent, int freq, String peerAddress, boolean join) { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailureV1_2("groupAdd_1_2")) return false; |
| java.util.ArrayList<Byte> ssid = NativeUtil.decodeSsid("\"" + networkName + "\""); |
| byte[] macAddress = null; |
| try { |
| macAddress = NativeUtil.macAddressToByteArray(peerAddress); |
| } catch (Exception e) { |
| Log.e(TAG, "Could not parse mac address.", e); |
| return false; |
| } |
| |
| android.hardware.wifi.supplicant.V1_2.ISupplicantP2pIface ifaceV12 = |
| getP2pIfaceMockableV1_2(); |
| SupplicantResult<Void> result = |
| new SupplicantResult("groupAdd(" + networkName + ", " |
| + (TextUtils.isEmpty(passphrase) ? "<Empty>" : "<Non-Empty>") |
| + ", " + isPersistent + ", " + freq |
| + ", " + peerAddress + ", " + join + ")"); |
| try { |
| result.setResult(ifaceV12.addGroup_1_2( |
| ssid, passphrase, isPersistent, freq, macAddress, join)); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| return result.isSuccess(); |
| } |
| } |
| |
| /** |
| * Set up a P2P group owner manually. |
| * This is a helper method that invokes groupAdd(networkId, isPersistent) internally. |
| * |
| * @param isPersistent Used to request a persistent group to be formed. |
| * |
| * @return true, if operation was successful. |
| */ |
| public boolean groupAdd(boolean isPersistent) { |
| // Supplicant expects networkId to be -1 if not supplied. |
| return groupAdd(-1, isPersistent); |
| } |
| |
| |
| /** |
| * Terminate a P2P group. If a new virtual network interface was used for |
| * the group, it must also be removed. The network interface name of the |
| * group interface is used as a parameter for this command. |
| * |
| * @param groupName Group interface name to use. |
| * |
| * @return true, if operation was successful. |
| */ |
| public boolean groupRemove(String groupName) { |
| if (TextUtils.isEmpty(groupName)) return false; |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("groupRemove")) return false; |
| SupplicantResult<Void> result = new SupplicantResult("groupRemove(" + groupName + ")"); |
| try { |
| result.setResult(mISupplicantP2pIface.removeGroup(groupName)); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| return result.isSuccess(); |
| } |
| } |
| |
| |
| /** |
| * Gets the capability of the group which the device is a |
| * member of. |
| * |
| * @param peerAddress MAC address of the peer. |
| * |
| * @return combination of |GroupCapabilityMask| values. |
| */ |
| public int getGroupCapability(String peerAddress) { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("getGroupCapability")) { |
| return RESULT_NOT_VALID; |
| } |
| |
| if (peerAddress == null) { |
| Log.e(TAG, "Cannot parse peer mac address."); |
| return RESULT_NOT_VALID; |
| } |
| byte[] macAddress = null; |
| try { |
| macAddress = NativeUtil.macAddressToByteArray(peerAddress); |
| } catch (Exception e) { |
| Log.e(TAG, "Could not parse group address.", e); |
| return RESULT_NOT_VALID; |
| } |
| |
| SupplicantResult<Integer> capability = new SupplicantResult( |
| "getGroupCapability(" + peerAddress + ")"); |
| try { |
| mISupplicantP2pIface.getGroupCapability( |
| macAddress, (SupplicantStatus status, int cap) -> { |
| capability.setResult(status, cap); |
| }); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| |
| if (!capability.isSuccess()) { |
| return RESULT_NOT_VALID; |
| } |
| |
| return capability.getResult(); |
| } |
| } |
| |
| |
| /** |
| * Configure Extended Listen Timing. |
| * |
| * If enabled, listen state must be entered every |intervalInMillis| for at |
| * least |periodInMillis|. Both values have acceptable range of 1-65535 |
| * (with interval obviously having to be larger than or equal to duration). |
| * If the P2P module is not idle at the time the Extended Listen Timing |
| * timeout occurs, the Listen State operation must be skipped. |
| * |
| * @param enable Enables or disables listening. |
| * @param periodInMillis Period in milliseconds. |
| * @param intervalInMillis Interval in milliseconds. |
| * |
| * @return true, if operation was successful. |
| */ |
| public boolean configureExtListen(boolean enable, int periodInMillis, int intervalInMillis) { |
| if (enable && intervalInMillis < periodInMillis) { |
| return false; |
| } |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("configureExtListen")) return false; |
| |
| // If listening is disabled, wpa supplicant expects zeroes. |
| if (!enable) { |
| periodInMillis = 0; |
| intervalInMillis = 0; |
| } |
| |
| // Verify that the integers are not negative. Leave actual parameter validation to |
| // supplicant. |
| if (periodInMillis < 0 || intervalInMillis < 0) { |
| Log.e(TAG, "Invalid parameters supplied to configureExtListen: " + periodInMillis |
| + ", " + intervalInMillis); |
| return false; |
| } |
| |
| SupplicantResult<Void> result = new SupplicantResult( |
| "configureExtListen(" + periodInMillis + ", " + intervalInMillis + ")"); |
| try { |
| result.setResult( |
| mISupplicantP2pIface.configureExtListen(periodInMillis, intervalInMillis)); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| |
| return result.isSuccess(); |
| } |
| } |
| |
| |
| /** |
| * Set P2P Listen channel and operating chanel. |
| * |
| * @param listenChannel Wifi channel. eg, 1, 6, 11. |
| * @param operatingChannel Wifi channel. eg, 1, 6, 11. |
| * |
| * @return true, if operation was successful. |
| */ |
| public boolean setListenChannel(int listenChannel, int operatingChannel) { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("setListenChannel")) return false; |
| |
| if (listenChannel >= 1 && listenChannel <= 11) { |
| SupplicantResult<Void> result = new SupplicantResult( |
| "setListenChannel(" + listenChannel + ", " + DEFAULT_OPERATING_CLASS + ")"); |
| try { |
| result.setResult(mISupplicantP2pIface.setListenChannel( |
| listenChannel, DEFAULT_OPERATING_CLASS)); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| if (!result.isSuccess()) { |
| return false; |
| } |
| } else if (listenChannel != 0) { |
| // listenChannel == 0 does not set any listen channel. |
| return false; |
| } |
| |
| if (operatingChannel >= 0 && operatingChannel <= 165) { |
| ArrayList<ISupplicantP2pIface.FreqRange> ranges = new ArrayList<>(); |
| // operatingChannel == 0 enables all freqs. |
| if (operatingChannel >= 1 && operatingChannel <= 165) { |
| int freq = (operatingChannel <= 14 ? 2407 : 5000) + operatingChannel * 5; |
| ISupplicantP2pIface.FreqRange range1 = new ISupplicantP2pIface.FreqRange(); |
| range1.min = 1000; |
| range1.max = freq - 5; |
| ISupplicantP2pIface.FreqRange range2 = new ISupplicantP2pIface.FreqRange(); |
| range2.min = freq + 5; |
| range2.max = 6000; |
| ranges.add(range1); |
| ranges.add(range2); |
| } |
| SupplicantResult<Void> result = new SupplicantResult( |
| "setDisallowedFrequencies(" + ranges + ")"); |
| try { |
| result.setResult(mISupplicantP2pIface.setDisallowedFrequencies(ranges)); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| return result.isSuccess(); |
| } |
| return false; |
| } |
| } |
| |
| |
| /** |
| * This command can be used to add a upnp/bonjour service. |
| * |
| * @param servInfo List of service queries. |
| * |
| * @return true, if operation was successful. |
| */ |
| public boolean serviceAdd(WifiP2pServiceInfo servInfo) { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("serviceAdd")) return false; |
| |
| if (servInfo == null) { |
| Log.e(TAG, "Null service info passed."); |
| return false; |
| } |
| |
| for (String s : servInfo.getSupplicantQueryList()) { |
| if (s == null) { |
| Log.e(TAG, "Invalid service description (null)."); |
| return false; |
| } |
| |
| String[] data = s.split(" "); |
| if (data.length < 3) { |
| Log.e(TAG, "Service specification invalid: " + s); |
| return false; |
| } |
| |
| SupplicantResult<Void> result = null; |
| try { |
| if ("upnp".equals(data[0])) { |
| int version = 0; |
| try { |
| version = Integer.parseInt(data[1], 16); |
| } catch (NumberFormatException e) { |
| Log.e(TAG, "UPnP Service specification invalid: " + s, e); |
| return false; |
| } |
| |
| result = new SupplicantResult( |
| "addUpnpService(" + data[1] + ", " + data[2] + ")"); |
| result.setResult(mISupplicantP2pIface.addUpnpService(version, data[2])); |
| } else if ("bonjour".equals(data[0])) { |
| if (data[1] != null && data[2] != null) { |
| ArrayList<Byte> request = null; |
| ArrayList<Byte> response = null; |
| try { |
| request = NativeUtil.byteArrayToArrayList( |
| NativeUtil.hexStringToByteArray(data[1])); |
| response = NativeUtil.byteArrayToArrayList( |
| NativeUtil.hexStringToByteArray(data[2])); |
| } catch (Exception e) { |
| Log.e(TAG, "Invalid bonjour service description."); |
| return false; |
| } |
| result = new SupplicantResult( |
| "addBonjourService(" + data[1] + ", " + data[2] + ")"); |
| result.setResult( |
| mISupplicantP2pIface.addBonjourService(request, response)); |
| } |
| } else { |
| return false; |
| } |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| |
| if (result == null || !result.isSuccess()) return false; |
| } |
| |
| return true; |
| } |
| } |
| |
| |
| /** |
| * This command can be used to remove a upnp/bonjour service. |
| * |
| * @param servInfo List of service queries. |
| * |
| * @return true, if operation was successful. |
| */ |
| public boolean serviceRemove(WifiP2pServiceInfo servInfo) { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("serviceRemove")) return false; |
| |
| if (servInfo == null) { |
| Log.e(TAG, "Null service info passed."); |
| return false; |
| } |
| |
| for (String s : servInfo.getSupplicantQueryList()) { |
| if (s == null) { |
| Log.e(TAG, "Invalid service description (null)."); |
| return false; |
| } |
| |
| String[] data = s.split(" "); |
| if (data.length < 3) { |
| Log.e(TAG, "Service specification invalid: " + s); |
| return false; |
| } |
| |
| SupplicantResult<Void> result = null; |
| try { |
| if ("upnp".equals(data[0])) { |
| int version = 0; |
| try { |
| version = Integer.parseInt(data[1], 16); |
| } catch (NumberFormatException e) { |
| Log.e(TAG, "UPnP Service specification invalid: " + s, e); |
| return false; |
| } |
| result = new SupplicantResult( |
| "removeUpnpService(" + data[1] + ", " + data[2] + ")"); |
| result.setResult(mISupplicantP2pIface.removeUpnpService(version, data[2])); |
| } else if ("bonjour".equals(data[0])) { |
| if (data[1] != null) { |
| ArrayList<Byte> request = null; |
| try { |
| request = NativeUtil.byteArrayToArrayList( |
| NativeUtil.hexStringToByteArray(data[1])); |
| } catch (Exception e) { |
| Log.e(TAG, "Invalid bonjour service description."); |
| return false; |
| } |
| result = new SupplicantResult("removeBonjourService(" + data[1] + ")"); |
| result.setResult(mISupplicantP2pIface.removeBonjourService(request)); |
| } |
| } else { |
| Log.e(TAG, "Unknown / unsupported P2P service requested: " + data[0]); |
| return false; |
| } |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| |
| if (result == null || !result.isSuccess()) return false; |
| } |
| |
| return true; |
| } |
| } |
| |
| |
| /** |
| * Schedule a P2P service discovery request. The parameters for this command |
| * are the device address of the peer device (or 00:00:00:00:00:00 for |
| * wildcard query that is sent to every discovered P2P peer that supports |
| * service discovery) and P2P Service Query TLV(s) as hexdump. |
| * |
| * @param peerAddress MAC address of the device to discover. |
| * @param query Hex dump of the query data. |
| * @return identifier Identifier for the request. Can be used to cancel the |
| * request. |
| */ |
| public String requestServiceDiscovery(String peerAddress, String query) { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("requestServiceDiscovery")) return null; |
| |
| if (peerAddress == null) { |
| Log.e(TAG, "Cannot parse peer mac address."); |
| return null; |
| } |
| byte[] macAddress = null; |
| try { |
| macAddress = NativeUtil.macAddressToByteArray(peerAddress); |
| } catch (Exception e) { |
| Log.e(TAG, "Could not process peer MAC address.", e); |
| return null; |
| } |
| |
| if (query == null) { |
| Log.e(TAG, "Cannot parse service discovery query: " + query); |
| return null; |
| } |
| ArrayList<Byte> binQuery = null; |
| try { |
| binQuery = NativeUtil.byteArrayToArrayList(NativeUtil.hexStringToByteArray(query)); |
| } catch (Exception e) { |
| Log.e(TAG, "Could not parse service query.", e); |
| return null; |
| } |
| |
| SupplicantResult<Long> result = new SupplicantResult( |
| "requestServiceDiscovery(" + peerAddress + ", " + query + ")"); |
| try { |
| mISupplicantP2pIface.requestServiceDiscovery( |
| macAddress, binQuery, |
| (SupplicantStatus status, long identifier) -> { |
| result.setResult(status, new Long(identifier)); |
| }); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| |
| Long value = result.getResult(); |
| if (value == null) return null; |
| return value.toString(); |
| } |
| } |
| |
| |
| /** |
| * Cancel a previous service discovery request. |
| * |
| * @param identifier Identifier for the request to cancel. |
| * @return true, if operation was successful. |
| */ |
| public boolean cancelServiceDiscovery(String identifier) { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("cancelServiceDiscovery")) return false; |
| if (identifier == null) { |
| Log.e(TAG, "cancelServiceDiscovery requires a valid tag."); |
| return false; |
| } |
| |
| long id = 0; |
| try { |
| id = Long.parseLong(identifier); |
| } catch (NumberFormatException e) { |
| Log.e(TAG, "Service discovery identifier invalid: " + identifier, e); |
| return false; |
| } |
| |
| SupplicantResult<Void> result = new SupplicantResult( |
| "cancelServiceDiscovery(" + identifier + ")"); |
| try { |
| result.setResult(mISupplicantP2pIface.cancelServiceDiscovery(id)); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| |
| return result.isSuccess(); |
| } |
| } |
| |
| |
| /** |
| * Send driver command to set Miracast mode. |
| * |
| * @param mode Mode of Miracast. |
| * @return true, if operation was successful. |
| */ |
| public boolean setMiracastMode(int mode) { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("setMiracastMode")) return false; |
| byte targetMode = ISupplicantP2pIface.MiracastMode.DISABLED; |
| |
| switch (mode) { |
| case WifiP2pManager.MIRACAST_SOURCE: |
| targetMode = ISupplicantP2pIface.MiracastMode.SOURCE; |
| break; |
| |
| case WifiP2pManager.MIRACAST_SINK: |
| targetMode = ISupplicantP2pIface.MiracastMode.SINK; |
| break; |
| } |
| |
| SupplicantResult<Void> result = new SupplicantResult( |
| "setMiracastMode(" + mode + ")"); |
| try { |
| result.setResult(mISupplicantP2pIface.setMiracastMode(targetMode)); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| |
| return result.isSuccess(); |
| } |
| } |
| |
| |
| /** |
| * Initiate WPS Push Button setup. |
| * The PBC operation requires that a button is also pressed at the |
| * AP/Registrar at about the same time (2 minute window). |
| * |
| * @param groupIfName Group interface name to use. |
| * @param bssid BSSID of the AP. Use empty bssid to indicate wildcard. |
| * @return true, if operation was successful. |
| */ |
| public boolean startWpsPbc(String groupIfName, String bssid) { |
| if (TextUtils.isEmpty(groupIfName)) { |
| Log.e(TAG, "Group name required when requesting WPS PBC. Got (" + groupIfName + ")"); |
| return false; |
| } |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("startWpsPbc")) return false; |
| // Null values should be fine, since bssid can be empty. |
| byte[] macAddress = null; |
| try { |
| macAddress = NativeUtil.macAddressToByteArray(bssid); |
| } catch (Exception e) { |
| Log.e(TAG, "Could not parse BSSID.", e); |
| return false; |
| } |
| |
| SupplicantResult<Void> result = new SupplicantResult( |
| "startWpsPbc(" + groupIfName + ", " + bssid + ")"); |
| try { |
| result.setResult(mISupplicantP2pIface.startWpsPbc(groupIfName, macAddress)); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| |
| return result.isSuccess(); |
| } |
| } |
| |
| |
| /** |
| * Initiate WPS Pin Keypad setup. |
| * |
| * @param groupIfName Group interface name to use. |
| * @param pin 8 digit pin to be used. |
| * @return true, if operation was successful. |
| */ |
| public boolean startWpsPinKeypad(String groupIfName, String pin) { |
| if (TextUtils.isEmpty(groupIfName) || TextUtils.isEmpty(pin)) return false; |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("startWpsPinKeypad")) return false; |
| if (groupIfName == null) { |
| Log.e(TAG, "Group name required when requesting WPS KEYPAD."); |
| return false; |
| } |
| if (pin == null) { |
| Log.e(TAG, "PIN required when requesting WPS KEYPAD."); |
| return false; |
| } |
| |
| SupplicantResult<Void> result = new SupplicantResult( |
| "startWpsPinKeypad(" + groupIfName + ", " + pin + ")"); |
| try { |
| result.setResult(mISupplicantP2pIface.startWpsPinKeypad(groupIfName, pin)); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| |
| return result.isSuccess(); |
| } |
| } |
| |
| |
| /** |
| * Initiate WPS Pin Display setup. |
| * |
| * @param groupIfName Group interface name to use. |
| * @param bssid BSSID of the AP. Use empty bssid to indicate wildcard. |
| * @return generated pin if operation was successful, null otherwise. |
| */ |
| public String startWpsPinDisplay(String groupIfName, String bssid) { |
| if (TextUtils.isEmpty(groupIfName)) return null; |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("startWpsPinDisplay")) return null; |
| if (groupIfName == null) { |
| Log.e(TAG, "Group name required when requesting WPS KEYPAD."); |
| return null; |
| } |
| |
| // Null values should be fine, since bssid can be empty. |
| byte[] macAddress = null; |
| try { |
| macAddress = NativeUtil.macAddressToByteArray(bssid); |
| } catch (Exception e) { |
| Log.e(TAG, "Could not parse BSSID.", e); |
| return null; |
| } |
| |
| SupplicantResult<String> result = new SupplicantResult( |
| "startWpsPinDisplay(" + groupIfName + ", " + bssid + ")"); |
| try { |
| mISupplicantP2pIface.startWpsPinDisplay( |
| groupIfName, macAddress, |
| (SupplicantStatus status, String generatedPin) -> { |
| result.setResult(status, generatedPin); |
| }); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| |
| return result.getResult(); |
| } |
| } |
| |
| |
| /** |
| * Cancel any ongoing WPS operations. |
| * |
| * @param groupIfName Group interface name to use. |
| * @return true, if operation was successful. |
| */ |
| public boolean cancelWps(String groupIfName) { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("cancelWps")) return false; |
| if (groupIfName == null) { |
| Log.e(TAG, "Group name required when requesting WPS KEYPAD."); |
| return false; |
| } |
| |
| SupplicantResult<Void> result = new SupplicantResult( |
| "cancelWps(" + groupIfName + ")"); |
| try { |
| result.setResult(mISupplicantP2pIface.cancelWps(groupIfName)); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| |
| return result.isSuccess(); |
| } |
| } |
| |
| |
| /** |
| * Enable/Disable Wifi Display. |
| * |
| * @param enable true to enable, false to disable. |
| * @return true, if operation was successful. |
| */ |
| public boolean enableWfd(boolean enable) { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("enableWfd")) return false; |
| |
| SupplicantResult<Void> result = new SupplicantResult( |
| "enableWfd(" + enable + ")"); |
| try { |
| result.setResult(mISupplicantP2pIface.enableWfd(enable)); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| |
| return result.isSuccess(); |
| } |
| } |
| |
| |
| /** |
| * Set Wifi Display device info. |
| * |
| * @param info WFD device info as described in section 5.1.2 of WFD technical |
| * specification v1.0.0. |
| * @return true, if operation was successful. |
| */ |
| public boolean setWfdDeviceInfo(String info) { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("setWfdDeviceInfo")) return false; |
| |
| if (info == null) { |
| Log.e(TAG, "Cannot parse null WFD info string."); |
| return false; |
| } |
| byte[] wfdInfo = null; |
| try { |
| wfdInfo = NativeUtil.hexStringToByteArray(info); |
| } catch (Exception e) { |
| Log.e(TAG, "Could not parse WFD Device Info string."); |
| return false; |
| } |
| |
| SupplicantResult<Void> result = new SupplicantResult( |
| "setWfdDeviceInfo(" + info + ")"); |
| try { |
| result.setResult(mISupplicantP2pIface.setWfdDeviceInfo(wfdInfo)); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| |
| return result.isSuccess(); |
| } |
| } |
| |
| /** |
| * Remove network with provided id. |
| * |
| * @param networkId Id of the network to lookup. |
| * @return true, if operation was successful. |
| */ |
| public boolean removeNetwork(int networkId) { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("removeNetwork")) return false; |
| |
| SupplicantResult<Void> result = new SupplicantResult( |
| "removeNetwork(" + networkId + ")"); |
| try { |
| result.setResult(mISupplicantP2pIface.removeNetwork(networkId)); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| |
| return result.isSuccess(); |
| } |
| } |
| |
| /** |
| * List the networks saved in wpa_supplicant. |
| * |
| * @return List of network ids. |
| */ |
| private List<Integer> listNetworks() { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("listNetworks")) return null; |
| SupplicantResult<ArrayList> result = new SupplicantResult("listNetworks()"); |
| try { |
| mISupplicantP2pIface.listNetworks( |
| (SupplicantStatus status, ArrayList<Integer> networkIds) -> { |
| result.setResult(status, networkIds); |
| }); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| return result.getResult(); |
| } |
| } |
| |
| /** |
| * Get the supplicant P2p network object for the specified network ID. |
| * |
| * @param networkId Id of the network to lookup. |
| * @return ISupplicantP2pNetwork instance on success, null on failure. |
| */ |
| private ISupplicantP2pNetwork getNetwork(int networkId) { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("getNetwork")) return null; |
| SupplicantResult<ISupplicantNetwork> result = |
| new SupplicantResult("getNetwork(" + networkId + ")"); |
| try { |
| mISupplicantP2pIface.getNetwork( |
| networkId, |
| (SupplicantStatus status, ISupplicantNetwork network) -> { |
| result.setResult(status, network); |
| }); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| if (result.getResult() == null) { |
| Log.e(TAG, "getNetwork got null network"); |
| return null; |
| } |
| return getP2pNetworkMockable(result.getResult()); |
| } |
| } |
| |
| /** |
| * Get the persistent group list from wpa_supplicant's p2p mgmt interface |
| * |
| * @param groups WifiP2pGroupList to store persistent groups in |
| * @return true, if list has been modified. |
| */ |
| public boolean loadGroups(WifiP2pGroupList groups) { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("loadGroups")) return false; |
| List<Integer> networkIds = listNetworks(); |
| if (networkIds == null || networkIds.isEmpty()) { |
| return false; |
| } |
| for (Integer networkId : networkIds) { |
| ISupplicantP2pNetwork network = getNetwork(networkId); |
| if (network == null) { |
| Log.e(TAG, "Failed to retrieve network object for " + networkId); |
| continue; |
| } |
| SupplicantResult<Boolean> resultIsCurrent = |
| new SupplicantResult("isCurrent(" + networkId + ")"); |
| try { |
| network.isCurrent( |
| (SupplicantStatus status, boolean isCurrent) -> { |
| resultIsCurrent.setResult(status, isCurrent); |
| }); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| /** Skip the current network, if we're somehow getting networks from the p2p GO |
| interface, instead of p2p mgmt interface*/ |
| if (!resultIsCurrent.isSuccess() || resultIsCurrent.getResult()) { |
| Log.i(TAG, "Skipping current network"); |
| continue; |
| } |
| |
| WifiP2pGroup group = new WifiP2pGroup(); |
| group.setNetworkId(networkId); |
| |
| // Now get the ssid, bssid and other flags for this network. |
| SupplicantResult<ArrayList> resultSsid = |
| new SupplicantResult("getSsid(" + networkId + ")"); |
| try { |
| network.getSsid( |
| (SupplicantStatus status, ArrayList<Byte> ssid) -> { |
| resultSsid.setResult(status, ssid); |
| }); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| if (resultSsid.isSuccess() && resultSsid.getResult() != null |
| && !resultSsid.getResult().isEmpty()) { |
| group.setNetworkName(NativeUtil.removeEnclosingQuotes( |
| NativeUtil.encodeSsid(resultSsid.getResult()))); |
| } |
| |
| SupplicantResult<byte[]> resultBssid = |
| new SupplicantResult("getBssid(" + networkId + ")"); |
| try { |
| network.getBssid( |
| (SupplicantStatus status, byte[] bssid) -> { |
| resultBssid.setResult(status, bssid); |
| }); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| if (resultBssid.isSuccess() && !ArrayUtils.isEmpty(resultBssid.getResult())) { |
| WifiP2pDevice device = new WifiP2pDevice(); |
| device.deviceAddress = |
| NativeUtil.macAddressFromByteArray(resultBssid.getResult()); |
| group.setOwner(device); |
| } |
| |
| SupplicantResult<Boolean> resultIsGo = |
| new SupplicantResult("isGo(" + networkId + ")"); |
| try { |
| network.isGo( |
| (SupplicantStatus status, boolean isGo) -> { |
| resultIsGo.setResult(status, isGo); |
| }); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| if (resultIsGo.isSuccess()) { |
| group.setIsGroupOwner(resultIsGo.getResult()); |
| } |
| groups.add(group); |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Set WPS device name. |
| * |
| * @param name String to be set. |
| * @return true if request is sent successfully, false otherwise. |
| */ |
| public boolean setWpsDeviceName(String name) { |
| if (name == null) { |
| return false; |
| } |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("setWpsDeviceName")) return false; |
| SupplicantResult<Void> result = new SupplicantResult( |
| "setWpsDeviceName(" + name + ")"); |
| try { |
| result.setResult(mISupplicantP2pIface.setWpsDeviceName(name)); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| return result.isSuccess(); |
| } |
| } |
| |
| /** |
| * Set WPS device type. |
| * |
| * @param typeStr Type specified as a string. Used format: <categ>-<OUI>-<subcateg> |
| * @return true if request is sent successfully, false otherwise. |
| */ |
| public boolean setWpsDeviceType(String typeStr) { |
| try { |
| Matcher match = WPS_DEVICE_TYPE_PATTERN.matcher(typeStr); |
| if (!match.find() || match.groupCount() != 3) { |
| Log.e(TAG, "Malformed WPS device type " + typeStr); |
| return false; |
| } |
| short categ = Short.parseShort(match.group(1)); |
| byte[] oui = NativeUtil.hexStringToByteArray(match.group(2)); |
| short subCateg = Short.parseShort(match.group(3)); |
| |
| byte[] bytes = new byte[8]; |
| ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN); |
| byteBuffer.putShort(categ); |
| byteBuffer.put(oui); |
| byteBuffer.putShort(subCateg); |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("setWpsDeviceType")) return false; |
| SupplicantResult<Void> result = new SupplicantResult( |
| "setWpsDeviceType(" + typeStr + ")"); |
| try { |
| result.setResult(mISupplicantP2pIface.setWpsDeviceType(bytes)); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| return result.isSuccess(); |
| } |
| } catch (IllegalArgumentException e) { |
| Log.e(TAG, "Illegal argument " + typeStr, e); |
| return false; |
| } |
| } |
| |
| /** |
| * Set WPS config methods |
| * |
| * @param configMethodsStr List of config methods. |
| * @return true if request is sent successfully, false otherwise. |
| */ |
| public boolean setWpsConfigMethods(String configMethodsStr) { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("setWpsConfigMethods")) return false; |
| SupplicantResult<Void> result = |
| new SupplicantResult("setWpsConfigMethods(" + configMethodsStr + ")"); |
| short configMethodsMask = 0; |
| String[] configMethodsStrArr = configMethodsStr.split("\\s+"); |
| for (int i = 0; i < configMethodsStrArr.length; i++) { |
| configMethodsMask |= stringToWpsConfigMethod(configMethodsStrArr[i]); |
| } |
| try { |
| result.setResult(mISupplicantP2pIface.setWpsConfigMethods(configMethodsMask)); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| return result.isSuccess(); |
| } |
| } |
| |
| /** |
| * Get NFC handover request message. |
| * |
| * @return select message if created successfully, null otherwise. |
| */ |
| public String getNfcHandoverRequest() { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("getNfcHandoverRequest")) return null; |
| SupplicantResult<ArrayList> result = new SupplicantResult( |
| "getNfcHandoverRequest()"); |
| try { |
| mISupplicantP2pIface.createNfcHandoverRequestMessage( |
| (SupplicantStatus status, ArrayList<Byte> message) -> { |
| result.setResult(status, message); |
| }); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| if (!result.isSuccess()) { |
| return null; |
| |
| } |
| return NativeUtil.hexStringFromByteArray( |
| NativeUtil.byteArrayFromArrayList(result.getResult())); |
| } |
| } |
| |
| /** |
| * Get NFC handover select message. |
| * |
| * @return select message if created successfully, null otherwise. |
| */ |
| public String getNfcHandoverSelect() { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("getNfcHandoverSelect")) return null; |
| SupplicantResult<ArrayList> result = new SupplicantResult( |
| "getNfcHandoverSelect()"); |
| try { |
| mISupplicantP2pIface.createNfcHandoverSelectMessage( |
| (SupplicantStatus status, ArrayList<Byte> message) -> { |
| result.setResult(status, message); |
| }); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| if (!result.isSuccess()) { |
| return null; |
| |
| } |
| return NativeUtil.hexStringFromByteArray( |
| NativeUtil.byteArrayFromArrayList(result.getResult())); |
| } |
| } |
| |
| /** |
| * Report NFC handover select message. |
| * |
| * @return true if reported successfully, false otherwise. |
| */ |
| public boolean initiatorReportNfcHandover(String selectMessage) { |
| if (selectMessage == null) return false; |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("initiatorReportNfcHandover")) return false; |
| SupplicantResult<Void> result = new SupplicantResult( |
| "initiatorReportNfcHandover(" + selectMessage + ")"); |
| try { |
| result.setResult(mISupplicantP2pIface.reportNfcHandoverInitiation( |
| NativeUtil.byteArrayToArrayList(NativeUtil.hexStringToByteArray( |
| selectMessage)))); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } catch (IllegalArgumentException e) { |
| Log.e(TAG, "Illegal argument " + selectMessage, e); |
| return false; |
| } |
| return result.isSuccess(); |
| } |
| } |
| |
| /** |
| * Report NFC handover request message. |
| * |
| * @return true if reported successfully, false otherwise. |
| */ |
| public boolean responderReportNfcHandover(String requestMessage) { |
| if (requestMessage == null) return false; |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("responderReportNfcHandover")) return false; |
| SupplicantResult<Void> result = new SupplicantResult( |
| "responderReportNfcHandover(" + requestMessage + ")"); |
| try { |
| result.setResult(mISupplicantP2pIface.reportNfcHandoverResponse( |
| NativeUtil.byteArrayToArrayList(NativeUtil.hexStringToByteArray( |
| requestMessage)))); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } catch (IllegalArgumentException e) { |
| Log.e(TAG, "Illegal argument " + requestMessage, e); |
| return false; |
| } |
| return result.isSuccess(); |
| } |
| } |
| |
| /** |
| * Set the client list for the provided network. |
| * |
| * @param networkId Id of the network. |
| * @param clientListStr Space separated list of clients. |
| * @return true, if operation was successful. |
| */ |
| public boolean setClientList(int networkId, String clientListStr) { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("setClientList")) return false; |
| if (TextUtils.isEmpty(clientListStr)) { |
| Log.e(TAG, "Invalid client list"); |
| return false; |
| } |
| ISupplicantP2pNetwork network = getNetwork(networkId); |
| if (network == null) { |
| Log.e(TAG, "Invalid network id "); |
| return false; |
| } |
| SupplicantResult<Void> result = new SupplicantResult( |
| "setClientList(" + networkId + ", " + clientListStr + ")"); |
| try { |
| ArrayList<byte[]> clients = new ArrayList<>(); |
| for (String clientStr : Arrays.asList(clientListStr.split("\\s+"))) { |
| clients.add(NativeUtil.macAddressToByteArray(clientStr)); |
| } |
| result.setResult(network.setClientList(clients)); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } catch (IllegalArgumentException e) { |
| Log.e(TAG, "Illegal argument " + clientListStr, e); |
| return false; |
| } |
| return result.isSuccess(); |
| } |
| } |
| |
| /** |
| * Set the client list for the provided network. |
| * |
| * @param networkId Id of the network. |
| * @return Space separated list of clients if successfull, null otherwise. |
| */ |
| public String getClientList(int networkId) { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("getClientList")) return null; |
| ISupplicantP2pNetwork network = getNetwork(networkId); |
| if (network == null) { |
| Log.e(TAG, "Invalid network id "); |
| return null; |
| } |
| SupplicantResult<ArrayList> result = new SupplicantResult( |
| "getClientList(" + networkId + ")"); |
| try { |
| network.getClientList( |
| (SupplicantStatus status, ArrayList<byte[]> clients) -> { |
| result.setResult(status, clients); |
| }); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| if (!result.isSuccess()) { |
| return null; |
| } |
| ArrayList<byte[]> clients = result.getResult(); |
| return clients.stream() |
| .map(NativeUtil::macAddressFromByteArray) |
| .collect(Collectors.joining(" ")); |
| } |
| } |
| |
| /** |
| * Persist the current configurations to disk. |
| * |
| * @return true, if operation was successful. |
| */ |
| public boolean saveConfig() { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailure("saveConfig")) return false; |
| SupplicantResult<Void> result = new SupplicantResult("saveConfig()"); |
| try { |
| result.setResult(mISupplicantP2pIface.saveConfig()); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| return result.isSuccess(); |
| } |
| } |
| |
| |
| /** |
| * Enable/Disable P2P MAC randomization. |
| * |
| * @param enable true to enable, false to disable. |
| * @return true, if operation was successful. |
| */ |
| public boolean setMacRandomization(boolean enable) { |
| synchronized (mLock) { |
| if (!checkSupplicantP2pIfaceAndLogFailureV1_2("setMacRandomization")) return false; |
| |
| android.hardware.wifi.supplicant.V1_2.ISupplicantP2pIface ifaceV12 = |
| getP2pIfaceMockableV1_2(); |
| SupplicantResult<Void> result = new SupplicantResult( |
| "setMacRandomization(" + enable + ")"); |
| try { |
| result.setResult(ifaceV12.setMacRandomization(enable)); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ISupplicantP2pIface exception: " + e); |
| supplicantServiceDiedHandler(); |
| } |
| |
| return result.isSuccess(); |
| } |
| } |
| |
| |
| /** |
| * Converts the Wps config method string to the equivalent enum value. |
| */ |
| private static short stringToWpsConfigMethod(String configMethod) { |
| switch (configMethod) { |
| case "usba": |
| return WpsConfigMethods.USBA; |
| case "ethernet": |
| return WpsConfigMethods.ETHERNET; |
| case "label": |
| return WpsConfigMethods.LABEL; |
| case "display": |
| return WpsConfigMethods.DISPLAY; |
| case "int_nfc_token": |
| return WpsConfigMethods.INT_NFC_TOKEN; |
| case "ext_nfc_token": |
| return WpsConfigMethods.EXT_NFC_TOKEN; |
| case "nfc_interface": |
| return WpsConfigMethods.NFC_INTERFACE; |
| case "push_button": |
| return WpsConfigMethods.PUSHBUTTON; |
| case "keypad": |
| return WpsConfigMethods.KEYPAD; |
| case "virtual_push_button": |
| return WpsConfigMethods.VIRT_PUSHBUTTON; |
| case "physical_push_button": |
| return WpsConfigMethods.PHY_PUSHBUTTON; |
| case "p2ps": |
| return WpsConfigMethods.P2PS; |
| case "virtual_display": |
| return WpsConfigMethods.VIRT_DISPLAY; |
| case "physical_display": |
| return WpsConfigMethods.PHY_DISPLAY; |
| default: |
| throw new IllegalArgumentException( |
| "Invalid WPS config method: " + configMethod); |
| } |
| } |
| |
| /** Container class allowing propagation of status and/or value |
| * from callbacks. |
| * |
| * Primary purpose is to allow callback lambdas to provide results |
| * to parent methods. |
| */ |
| private static class SupplicantResult<E> { |
| private String mMethodName; |
| private SupplicantStatus mStatus; |
| private E mValue; |
| |
| SupplicantResult(String methodName) { |
| mMethodName = methodName; |
| mStatus = null; |
| mValue = null; |
| logd("entering " + mMethodName); |
| } |
| |
| public void setResult(SupplicantStatus status, E value) { |
| logCompletion(mMethodName, status); |
| logd("leaving " + mMethodName + " with result = " + value); |
| mStatus = status; |
| mValue = value; |
| } |
| |
| public void setResult(SupplicantStatus status) { |
| logCompletion(mMethodName, status); |
| logd("leaving " + mMethodName); |
| mStatus = status; |
| } |
| |
| public boolean isSuccess() { |
| return (mStatus != null |
| && (mStatus.code == SupplicantStatusCode.SUCCESS |
| || mStatus.code == SupplicantStatusCode.FAILURE_IFACE_EXISTS)); |
| } |
| |
| public E getResult() { |
| return (isSuccess() ? mValue : null); |
| } |
| } |
| } |