diff options
| -rw-r--r-- | Android.bp | 2 | ||||
| -rw-r--r-- | api/system-current.txt | 14 | ||||
| -rw-r--r-- | wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl | 36 | ||||
| -rw-r--r-- | wifi/java/android/net/wifi/INetworkRequestUserSelectionCallback.aidl | 31 | ||||
| -rw-r--r-- | wifi/java/android/net/wifi/IWifiManager.aidl | 5 | ||||
| -rw-r--r-- | wifi/java/android/net/wifi/WifiManager.java | 237 | ||||
| -rw-r--r-- | wifi/tests/src/android/net/wifi/WifiManagerTest.java | 89 |
7 files changed, 413 insertions, 1 deletions
diff --git a/Android.bp b/Android.bp index 56d6557ee6e1..940e3d53819b 100644 --- a/Android.bp +++ b/Android.bp @@ -592,6 +592,8 @@ java_defaults { "telephony/java/com/android/internal/telephony/euicc/ISetDefaultSmdpAddressCallback.aidl", "telephony/java/com/android/internal/telephony/euicc/ISetNicknameCallback.aidl", "telephony/java/com/android/internal/telephony/euicc/ISwitchToProfileCallback.aidl", + "wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl", + "wifi/java/android/net/wifi/INetworkRequestUserSelectionCallback.aidl", "wifi/java/android/net/wifi/ISoftApCallback.aidl", "wifi/java/android/net/wifi/ITrafficStateCallback.aidl", "wifi/java/android/net/wifi/IWifiManager.aidl", diff --git a/api/system-current.txt b/api/system-current.txt index 9897db2f034b..22f19dc19a88 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -3594,8 +3594,10 @@ package android.net.wifi { method public boolean isPortableHotspotSupported(); method public boolean isWifiApEnabled(); method public boolean isWifiScannerSupported(); + method public void registerNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback, android.os.Handler); method public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration); method public boolean startScan(android.os.WorkSource); + method public void unregisterNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback); field public static final int CHANGE_REASON_ADDED = 0; // 0x0 field public static final int CHANGE_REASON_CONFIG_CHANGE = 2; // 0x2 field public static final int CHANGE_REASON_REMOVED = 1; // 0x1 @@ -3623,6 +3625,18 @@ package android.net.wifi { method public abstract void onSuccess(); } + public static abstract interface WifiManager.NetworkRequestMatchCallback { + method public abstract void onMatch(java.util.List<android.net.wifi.WifiConfiguration>); + method public abstract void onUserSelectionCallbackRegistration(android.net.wifi.WifiManager.NetworkRequestUserSelectionCallback); + method public abstract void onUserSelectionConnectFailure(android.net.wifi.WifiConfiguration); + method public abstract void onUserSelectionConnectSuccess(android.net.wifi.WifiConfiguration); + } + + public static abstract interface WifiManager.NetworkRequestUserSelectionCallback { + method public abstract void reject(); + method public abstract void select(android.net.wifi.WifiConfiguration); + } + public class WifiNetworkConnectionStatistics implements android.os.Parcelable { ctor public WifiNetworkConnectionStatistics(int, int); ctor public WifiNetworkConnectionStatistics(); diff --git a/wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl b/wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl new file mode 100644 index 000000000000..f472a021b031 --- /dev/null +++ b/wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2018 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 android.net.wifi; + +import android.net.wifi.INetworkRequestUserSelectionCallback; +import android.net.wifi.WifiConfiguration; + +/** + * Interface for network request match callback. + * + * @hide + */ +oneway interface INetworkRequestMatchCallback +{ + void onUserSelectionCallbackRegistration(in INetworkRequestUserSelectionCallback userSelectionCallback); + + void onMatch(in List<WifiConfiguration> wificonfigurations); + + void onUserSelectionConnectSuccess(in WifiConfiguration wificonfiguration); + + void onUserSelectionConnectFailure(in WifiConfiguration wificonfiguration); +} diff --git a/wifi/java/android/net/wifi/INetworkRequestUserSelectionCallback.aidl b/wifi/java/android/net/wifi/INetworkRequestUserSelectionCallback.aidl new file mode 100644 index 000000000000..524cefbb295f --- /dev/null +++ b/wifi/java/android/net/wifi/INetworkRequestUserSelectionCallback.aidl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2018 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 android.net.wifi; + +import android.net.wifi.WifiConfiguration; + +/** + * Interface for providing user selection in response to + * network request match callback. + * @hide + */ +oneway interface INetworkRequestUserSelectionCallback +{ + void select(in WifiConfiguration wificonfiguration); + + void reject(); +} diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index 12f50c8af8a9..1fd68ec1df70 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -25,6 +25,7 @@ import android.net.wifi.hotspot2.IProvisioningCallback; import android.net.DhcpInfo; import android.net.Network; +import android.net.wifi.INetworkRequestMatchCallback; import android.net.wifi.ISoftApCallback; import android.net.wifi.ITrafficStateCallback; import android.net.wifi.PasspointManagementObjectDefinition; @@ -185,5 +186,9 @@ interface IWifiManager void registerTrafficStateCallback(in IBinder binder, in ITrafficStateCallback callback, int callbackIdentifier); void unregisterTrafficStateCallback(int callbackIdentifier); + + void registerNetworkRequestMatchCallback(in IBinder binder, in INetworkRequestMatchCallback callback, int callbackIdentifier); + + void unregisterNetworkRequestMatchCallback(int callbackIdentifier); } diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index f72705aaafae..9ce548601f23 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -1212,6 +1212,243 @@ public class WifiManager { } /** + * Interface for indicating user selection from the list of networks presented in the + * {@link NetworkRequestMatchCallback#onMatch(List)}. + * + * The platform will implement this callback and pass it along with the + * {@link NetworkRequestMatchCallback#onUserSelectionCallbackRegistration( + * NetworkRequestUserSelectionCallback)}. The UI component handling + * {@link NetworkRequestMatchCallback} will invoke {@link #select(WifiConfiguration)} or + * {@link #reject()} to return the user's selection back to the platform via this callback. + * @hide + */ + @SystemApi + public interface NetworkRequestUserSelectionCallback { + /** + * User selected this network to connect to. + * @param wifiConfiguration WifiConfiguration object corresponding to the network + * user selected. + */ + void select(@NonNull WifiConfiguration wifiConfiguration); + + /** + * User rejected the app's request. + */ + void reject(); + } + + /** + * Interface for network request callback. Should be implemented by applications and passed when + * calling {@link #registerNetworkRequestMatchCallback(NetworkRequestMatchCallback, Handler)}. + * + * This is meant to be implemented by a UI component to present the user with a list of networks + * matching the app's request. The user is allowed to pick one of these networks to connect to + * or reject the request by the app. + * @hide + */ + @SystemApi + public interface NetworkRequestMatchCallback { + /** + * Invoked to register a callback to be invoked to convey user selection. The callback + * object paased in this method is to be invoked by the UI component after the service sends + * a list of matching scan networks using {@link #onMatch(List)} and user picks a network + * from that list. + * + * @param userSelectionCallback Callback object to send back the user selection. + */ + void onUserSelectionCallbackRegistration( + @NonNull NetworkRequestUserSelectionCallback userSelectionCallback); + + /** + * Invoked when a network request initiated by an app matches some networks in scan results. + * This may be invoked multiple times for a single network request as the platform finds new + * networks in scan results. + * + * @param wifiConfigurations List of {@link WifiConfiguration} objects corresponding to the + * networks matching the request. + */ + void onMatch(@NonNull List<WifiConfiguration> wifiConfigurations); + + /** + * Invoked on a successful connection with the network that the user selected + * via {@link NetworkRequestUserSelectionCallback}. + * + * @param wifiConfiguration WifiConfiguration object corresponding to the network that the + * user selected. + */ + void onUserSelectionConnectSuccess(@NonNull WifiConfiguration wifiConfiguration); + + /** + * Invoked on failure to establish connection with the network that the user selected + * via {@link NetworkRequestUserSelectionCallback}. + * + * @param wifiConfiguration WifiConfiguration object corresponding to the network + * user selected. + */ + void onUserSelectionConnectFailure(@NonNull WifiConfiguration wifiConfiguration); + } + + /** + * Callback proxy for NetworkRequestUserSelectionCallback objects. + * @hide + */ + private class NetworkRequestUserSelectionCallbackProxy implements + NetworkRequestUserSelectionCallback { + private final INetworkRequestUserSelectionCallback mCallback; + + NetworkRequestUserSelectionCallbackProxy( + INetworkRequestUserSelectionCallback callback) { + mCallback = callback; + } + + @Override + public void select(@NonNull WifiConfiguration wifiConfiguration) { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "NetworkRequestUserSelectionCallbackProxy: select " + + "wificonfiguration: " + wifiConfiguration); + } + try { + mCallback.select(wifiConfiguration); + } catch (RemoteException e) { + Log.e(TAG, "Failed to invoke onSelected", e); + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void reject() { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "NetworkRequestUserSelectionCallbackProxy: reject"); + } + try { + mCallback.reject(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to invoke onRejected", e); + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * Callback proxy for NetworkRequestMatchCallback objects. + * @hide + */ + private class NetworkRequestMatchCallbackProxy extends INetworkRequestMatchCallback.Stub { + private final Handler mHandler; + private final NetworkRequestMatchCallback mCallback; + + NetworkRequestMatchCallbackProxy(Looper looper, NetworkRequestMatchCallback callback) { + mHandler = new Handler(looper); + mCallback = callback; + } + + @Override + public void onUserSelectionCallbackRegistration( + INetworkRequestUserSelectionCallback userSelectionCallback) { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "NetworkRequestMatchCallbackProxy: " + + "onUserSelectionCallbackRegistration callback: " + userSelectionCallback); + } + mHandler.post(() -> { + mCallback.onUserSelectionCallbackRegistration( + new NetworkRequestUserSelectionCallbackProxy(userSelectionCallback)); + }); + } + + @Override + public void onMatch(List<WifiConfiguration> wifiConfigurations) { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "NetworkRequestMatchCallbackProxy: onMatch wificonfigurations: " + + wifiConfigurations); + } + mHandler.post(() -> { + mCallback.onMatch(wifiConfigurations); + }); + } + + @Override + public void onUserSelectionConnectSuccess(WifiConfiguration wifiConfiguration) { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "NetworkRequestMatchCallbackProxy: onUserSelectionConnectSuccess " + + " wificonfiguration: " + wifiConfiguration); + } + mHandler.post(() -> { + mCallback.onUserSelectionConnectSuccess(wifiConfiguration); + }); + } + + @Override + public void onUserSelectionConnectFailure(WifiConfiguration wifiConfiguration) { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "NetworkRequestMatchCallbackProxy: onUserSelectionConnectFailure" + + " wificonfiguration: " + wifiConfiguration); + } + mHandler.post(() -> { + mCallback.onUserSelectionConnectFailure(wifiConfiguration); + }); + } + } + + /** + * Registers a callback for NetworkRequest matches. See {@link NetworkRequestMatchCallback}. + * Caller can unregister a previously registered callback using + * {@link #unregisterNetworkRequestMatchCallback(NetworkRequestMatchCallback)} + * <p> + * Applications should have the + * {@link android.Manifest.permission#NETWORK_SETTINGS} permission. Callers + * without the permission will trigger a {@link java.lang.SecurityException}. + * <p> + * + * @param callback Callback for network match events + * @param handler The Handler on whose thread to execute the callbacks of the {@code callback} + * object. If null, then the application's main thread will be used. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + public void registerNetworkRequestMatchCallback(@NonNull NetworkRequestMatchCallback callback, + @Nullable Handler handler) { + if (callback == null) throw new IllegalArgumentException("callback cannot be null"); + Log.v(TAG, "registerNetworkRequestMatchCallback: callback=" + callback + + ", handler=" + handler); + + Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper(); + Binder binder = new Binder(); + try { + mService.registerNetworkRequestMatchCallback( + binder, new NetworkRequestMatchCallbackProxy(looper, callback), + callback.hashCode()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Unregisters a callback for NetworkRequest matches. See {@link NetworkRequestMatchCallback}. + * <p> + * Applications should have the + * {@link android.Manifest.permission#NETWORK_SETTINGS} permission. Callers + * without the permission will trigger a {@link java.lang.SecurityException}. + * <p> + * + * @param callback Callback for network match events + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + public void unregisterNetworkRequestMatchCallback( + @NonNull NetworkRequestMatchCallback callback) { + if (callback == null) throw new IllegalArgumentException("callback cannot be null"); + Log.v(TAG, "unregisterNetworkRequestMatchCallback: callback=" + callback); + + try { + mService.unregisterNetworkRequestMatchCallback(callback.hashCode()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Provide a list of network suggestions to the device. See {@link WifiNetworkSuggestion} * for a detailed explanation of the parameters. *<p> diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java index e40b657aded0..ea41bb39c4d5 100644 --- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java @@ -43,6 +43,8 @@ import android.net.wifi.WifiManager.LocalOnlyHotspotCallback; import android.net.wifi.WifiManager.LocalOnlyHotspotObserver; import android.net.wifi.WifiManager.LocalOnlyHotspotReservation; import android.net.wifi.WifiManager.LocalOnlyHotspotSubscription; +import android.net.wifi.WifiManager.NetworkRequestMatchCallback; +import android.net.wifi.WifiManager.NetworkRequestUserSelectionCallback; import android.net.wifi.WifiManager.SoftApCallback; import android.net.wifi.WifiManager.TrafficStateCallback; import android.os.Handler; @@ -59,6 +61,8 @@ import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; + /** * Unit tests for {@link android.net.wifi.WifiManager}. */ @@ -67,16 +71,19 @@ public class WifiManagerTest { private static final int ERROR_NOT_SET = -1; private static final int ERROR_TEST_REASON = 5; + private static final int TEST_UID = 14553; private static final String TEST_PACKAGE_NAME = "TestPackage"; private static final String TEST_COUNTRY_CODE = "US"; @Mock Context mContext; - @Mock IWifiManager mWifiService; + @Mock + android.net.wifi.IWifiManager mWifiService; @Mock ApplicationInfo mApplicationInfo; @Mock WifiConfiguration mApConfig; @Mock IBinder mAppBinder; @Mock SoftApCallback mSoftApCallback; @Mock TrafficStateCallback mTrafficStateCallback; + @Mock NetworkRequestMatchCallback mNetworkRequestMatchCallback; private Handler mHandler; private TestLooper mLooper; @@ -1163,4 +1170,84 @@ i * Verify that a call to cancel WPS immediately returns a failure. assertEquals(1, altLooper.dispatchAll()); verify(mTrafficStateCallback).onStateChanged(TrafficStateCallback.DATA_ACTIVITY_INOUT); } + + /** + * Verify the call to registerNetworkRequestMatchCallback goes to WifiServiceImpl. + */ + @Test + public void registerNetworkRequestMatchCallbackCallGoesToWifiServiceImpl() + throws Exception { + when(mContext.getMainLooper()).thenReturn(mLooper.getLooper()); + ArgumentCaptor<INetworkRequestMatchCallback.Stub> callbackCaptor = + ArgumentCaptor.forClass(INetworkRequestMatchCallback.Stub.class); + mWifiManager.registerNetworkRequestMatchCallback(mNetworkRequestMatchCallback, null); + verify(mWifiService).registerNetworkRequestMatchCallback( + any(IBinder.class), callbackCaptor.capture(), anyInt()); + + INetworkRequestUserSelectionCallback iUserSelectionCallback = + mock(INetworkRequestUserSelectionCallback.class); + + assertEquals(0, mLooper.dispatchAll()); + callbackCaptor.getValue().onMatch(new ArrayList<WifiConfiguration>()); + assertEquals(1, mLooper.dispatchAll()); + verify(mNetworkRequestMatchCallback).onMatch(anyList()); + + callbackCaptor.getValue().onUserSelectionConnectSuccess(new WifiConfiguration()); + assertEquals(1, mLooper.dispatchAll()); + verify(mNetworkRequestMatchCallback).onUserSelectionConnectSuccess( + any(WifiConfiguration.class)); + + callbackCaptor.getValue().onUserSelectionConnectFailure(new WifiConfiguration()); + assertEquals(1, mLooper.dispatchAll()); + verify(mNetworkRequestMatchCallback).onUserSelectionConnectFailure( + any(WifiConfiguration.class)); + } + + /** + * Verify the call to unregisterNetworkRequestMatchCallback goes to WifiServiceImpl. + */ + @Test + public void unregisterNetworkRequestMatchCallbackCallGoesToWifiServiceImpl() throws Exception { + ArgumentCaptor<Integer> callbackIdentifier = ArgumentCaptor.forClass(Integer.class); + mWifiManager.registerNetworkRequestMatchCallback(mNetworkRequestMatchCallback, mHandler); + verify(mWifiService).registerNetworkRequestMatchCallback( + any(IBinder.class), any(INetworkRequestMatchCallback.class), + callbackIdentifier.capture()); + + mWifiManager.unregisterNetworkRequestMatchCallback(mNetworkRequestMatchCallback); + verify(mWifiService).unregisterNetworkRequestMatchCallback( + eq((int) callbackIdentifier.getValue())); + } + + /** + * Verify the call to NetworkRequestUserSelectionCallback goes to + * WifiServiceImpl. + */ + @Test + public void networkRequestUserSelectionCallbackCallGoesToWifiServiceImpl() + throws Exception { + when(mContext.getMainLooper()).thenReturn(mLooper.getLooper()); + ArgumentCaptor<INetworkRequestMatchCallback.Stub> callbackCaptor = + ArgumentCaptor.forClass(INetworkRequestMatchCallback.Stub.class); + mWifiManager.registerNetworkRequestMatchCallback(mNetworkRequestMatchCallback, null); + verify(mWifiService).registerNetworkRequestMatchCallback( + any(IBinder.class), callbackCaptor.capture(), anyInt()); + + INetworkRequestUserSelectionCallback iUserSelectionCallback = + mock(INetworkRequestUserSelectionCallback.class); + ArgumentCaptor<NetworkRequestUserSelectionCallback> userSelectionCallbackCaptor = + ArgumentCaptor.forClass(NetworkRequestUserSelectionCallback.class); + callbackCaptor.getValue().onUserSelectionCallbackRegistration( + iUserSelectionCallback); + assertEquals(1, mLooper.dispatchAll()); + verify(mNetworkRequestMatchCallback).onUserSelectionCallbackRegistration( + userSelectionCallbackCaptor.capture()); + + WifiConfiguration selected = new WifiConfiguration(); + userSelectionCallbackCaptor.getValue().select(selected); + verify(iUserSelectionCallback).select(selected); + + userSelectionCallbackCaptor.getValue().reject(); + verify(iUserSelectionCallback).reject(); + } } |