From 1bbcd4ef884e14007de07df558425196e1abd9a7 Mon Sep 17 00:00:00 2001 From: Nate Jiang Date: Wed, 9 Dec 2020 22:43:33 -0800 Subject: [PATCH] [Suggestion] Add listener for user approval status change Bug: 160648511 Test: atest android.net.wifi Change-Id: I2a1959a2504e274de83f9cd0e44300222bbe8aac --- wifi/api/current.txt | 6 ++ ...ISuggestionUserApprovalStatusListener.aidl | 27 ++++++ wifi/java/android/net/wifi/IWifiManager.aidl | 5 + wifi/java/android/net/wifi/WifiManager.java | 91 ++++++++++++++++++ .../src/android/net/wifi/WifiManagerTest.java | 96 +++++++++++++++++-- 5 files changed, 219 insertions(+), 6 deletions(-) create mode 100644 wifi/java/android/net/wifi/ISuggestionUserApprovalStatusListener.aidl diff --git a/wifi/api/current.txt b/wifi/api/current.txt index e11b33efcc334..23b4646e01d9c 100644 --- a/wifi/api/current.txt +++ b/wifi/api/current.txt @@ -309,6 +309,7 @@ package android.net.wifi { method @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public int addNetworkSuggestions(@NonNull java.util.List); method public void addOrUpdatePasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration); method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE}) public void addSuggestionConnectionStatusListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SuggestionConnectionStatusListener); + method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean addSuggestionUserApprovalStatusListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SuggestionUserApprovalStatusListener); method @Deprecated public static int calculateSignalLevel(int, int); method @IntRange(from=0) public int calculateSignalLevel(int); method @Deprecated public void cancelWps(android.net.wifi.WifiManager.WpsCallback); @@ -359,6 +360,7 @@ package android.net.wifi { method @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public int removeNetworkSuggestions(@NonNull java.util.List); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_CARRIER_PROVISIONING}) public void removePasspointConfiguration(String); method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void removeSuggestionConnectionStatusListener(@NonNull android.net.wifi.WifiManager.SuggestionConnectionStatusListener); + method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void removeSuggestionUserApprovalStatusListener(@NonNull android.net.wifi.WifiManager.SuggestionUserApprovalStatusListener); method @Deprecated public boolean saveConfiguration(); method public void setTdlsEnabled(java.net.InetAddress, boolean); method public void setTdlsEnabledWithMacAddress(String, boolean); @@ -459,6 +461,10 @@ package android.net.wifi { method public void onConnectionStatus(@NonNull android.net.wifi.WifiNetworkSuggestion, int); } + public static interface WifiManager.SuggestionUserApprovalStatusListener { + method public void onUserApprovalStatusChange(); + } + public class WifiManager.WifiLock { method public void acquire(); method public boolean isHeld(); diff --git a/wifi/java/android/net/wifi/ISuggestionUserApprovalStatusListener.aidl b/wifi/java/android/net/wifi/ISuggestionUserApprovalStatusListener.aidl new file mode 100644 index 0000000000000..5aa3a90ee2bb7 --- /dev/null +++ b/wifi/java/android/net/wifi/ISuggestionUserApprovalStatusListener.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2020 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; + +/** + * Interface for suggestion user approval status listener. + * + * @hide + */ +oneway interface ISuggestionUserApprovalStatusListener +{ + void onUserApprovalStatusChange(); +} diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index e7b8475405ad7..8fa35c38f30f7 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -35,6 +35,7 @@ import android.net.wifi.IOnWifiUsabilityStatsListener; import android.net.wifi.IScanResultsCallback; import android.net.wifi.ISoftApCallback; import android.net.wifi.ISuggestionConnectionStatusListener; +import android.net.wifi.ISuggestionUserApprovalStatusListener; import android.net.wifi.ITrafficStateCallback; import android.net.wifi.IWifiConnectedNetworkScorer; import android.net.wifi.ScanResult; @@ -302,4 +303,8 @@ interface IWifiManager boolean isCarrierNetworkOffloadEnabled(int subscriptionId, boolean merged); void restartWifiSubsystem(String reason); + + boolean addSuggestionUserApprovalStatusListener(in IBinder binder, in ISuggestionUserApprovalStatusListener listener, int listenerIdentifier, String packageName, String featureId); + + void removeSuggestionUserApprovalStatusListener(int listenerIdentifier, String packageName); } diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 9fd70cbf6bc00..7e76a200492db 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -6852,4 +6852,95 @@ public class WifiManager { } } + /** + * Interface for network suggestion user approval status change listener. + * Should be implemented by applications and registered using + * {@link #addSuggestionUserApprovalStatusListener(Executor, + * SuggestionUserApprovalStatusListener)} ( + */ + public interface SuggestionUserApprovalStatusListener { + + /** + * Called when the user approval status of the App has changed. The current status can be + * queried by {@link #getNetworkSuggestionUserApprovalStatus()} + */ + void onUserApprovalStatusChange(); + } + + private class SuggestionUserApprovalStatusListenerProxy extends + ISuggestionUserApprovalStatusListener.Stub { + private final Executor mExecutor; + private final SuggestionUserApprovalStatusListener mListener; + + SuggestionUserApprovalStatusListenerProxy(@NonNull Executor executor, + @NonNull SuggestionUserApprovalStatusListener listener) { + mExecutor = executor; + mListener = listener; + } + + @Override + public void onUserApprovalStatusChange() { + mExecutor.execute(() -> mListener.onUserApprovalStatusChange()); + } + + } + + /** + * Add a listener for Wi-Fi network suggestion user approval status. + * See {@link SuggestionUserApprovalStatusListener}. + * Caller will receive a callback when the user approval status of the caller has changed. + * Caller can remove a previously registered listener using + * {@link WifiManager#removeSuggestionUserApprovalStatusListener( + * SuggestionUserApprovalStatusListener)} + * A caller can add multiple listeners to monitor the event. + * @param executor The executor to execute the listener of the {@code listener} object. + * @param listener listener for suggestion user approval status changes. + * @return true if succeed otherwise false. + */ + @RequiresPermission(ACCESS_WIFI_STATE) + public boolean addSuggestionUserApprovalStatusListener( + @NonNull @CallbackExecutor Executor executor, + @NonNull SuggestionUserApprovalStatusListener listener) { + if (!SdkLevel.isAtLeastS()) { + throw new UnsupportedOperationException(); + } + if (listener == null) throw new IllegalArgumentException("Listener cannot be null"); + if (executor == null) throw new IllegalArgumentException("Executor cannot be null"); + Log.v(TAG, "addSuggestionUserApprovalStatusListener listener=" + listener + + ", executor=" + executor); + try { + return mService.addSuggestionUserApprovalStatusListener(new Binder(), + new SuggestionUserApprovalStatusListenerProxy(executor, listener), + listener.hashCode(), mContext.getOpPackageName(), mContext.getAttributionTag()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + } + + /** + * Allow callers to remove a previously registered listener using + * {@link #addSuggestionUserApprovalStatusListener(Executor, + * SuggestionUserApprovalStatusListener)}. After calling this method, + * applications will no longer receive network suggestion user approval status change through + * that listener. + * + * @param listener listener to remove. + */ + @RequiresPermission(ACCESS_WIFI_STATE) + public void removeSuggestionUserApprovalStatusListener( + @NonNull SuggestionUserApprovalStatusListener listener) { + if (!SdkLevel.isAtLeastS()) { + throw new UnsupportedOperationException(); + } + if (listener == null) throw new IllegalArgumentException("Listener cannot be null"); + Log.v(TAG, "removeSuggestionUserApprovalStatusListener: listener=" + listener); + try { + mService.removeSuggestionUserApprovalStatusListener(listener.hashCode(), + mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java index 52e91394d8bf5..e48fc6d6e26f5 100644 --- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java @@ -89,6 +89,7 @@ import android.net.wifi.WifiManager.OnWifiUsabilityStatsListener; import android.net.wifi.WifiManager.ScanResultsCallback; import android.net.wifi.WifiManager.SoftApCallback; import android.net.wifi.WifiManager.SuggestionConnectionStatusListener; +import android.net.wifi.WifiManager.SuggestionUserApprovalStatusListener; import android.net.wifi.WifiManager.TrafficStateCallback; import android.net.wifi.WifiManager.WifiConnectedNetworkScorer; import android.os.Build; @@ -147,12 +148,13 @@ public class WifiManagerTest { @Mock NetworkRequestMatchCallback mNetworkRequestMatchCallback; @Mock OnWifiUsabilityStatsListener mOnWifiUsabilityStatsListener; @Mock OnWifiActivityEnergyInfoListener mOnWifiActivityEnergyInfoListener; - @Mock SuggestionConnectionStatusListener mListener; + @Mock SuggestionConnectionStatusListener mSuggestionConnectionListener; @Mock Runnable mRunnable; @Mock Executor mExecutor; @Mock Executor mAnotherExecutor; @Mock ActivityManager mActivityManager; @Mock WifiConnectedNetworkScorer mWifiConnectedNetworkScorer; + @Mock SuggestionUserApprovalStatusListener mSuggestionUserApprovalStatusListener; private Handler mHandler; private TestLooper mLooper; @@ -2327,7 +2329,7 @@ public class WifiManagerTest { */ @Test(expected = IllegalArgumentException.class) public void testAddSuggestionConnectionStatusListenerWithNullExecutor() { - mWifiManager.addSuggestionConnectionStatusListener(null, mListener); + mWifiManager.addSuggestionConnectionStatusListener(null, mSuggestionConnectionListener); } /** @@ -2347,11 +2349,12 @@ public class WifiManagerTest { ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass(ISuggestionConnectionStatusListener.Stub.class); Executor executor = new SynchronousExecutor(); - mWifiManager.addSuggestionConnectionStatusListener(executor, mListener); + mWifiManager.addSuggestionConnectionStatusListener(executor, mSuggestionConnectionListener); verify(mWifiService).registerSuggestionConnectionStatusListener(any(IBinder.class), callbackCaptor.capture(), anyInt(), anyString(), nullable(String.class)); callbackCaptor.getValue().onConnectionStatus(mWifiNetworkSuggestion, errorCode); - verify(mListener).onConnectionStatus(any(WifiNetworkSuggestion.class), eq(errorCode)); + verify(mSuggestionConnectionListener).onConnectionStatus(any(WifiNetworkSuggestion.class), + eq(errorCode)); } /** @@ -2362,7 +2365,8 @@ public class WifiManagerTest { int errorCode = STATUS_SUGGESTION_CONNECTION_FAILURE_AUTHENTICATION; ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass(ISuggestionConnectionStatusListener.Stub.class); - mWifiManager.addSuggestionConnectionStatusListener(mExecutor, mListener); + mWifiManager.addSuggestionConnectionStatusListener(mExecutor, + mSuggestionConnectionListener); verify(mWifiService).registerSuggestionConnectionStatusListener(any(IBinder.class), callbackCaptor.capture(), anyInt(), anyString(), nullable(String.class)); callbackCaptor.getValue().onConnectionStatus(any(WifiNetworkSuggestion.class), errorCode); @@ -2382,7 +2386,7 @@ public class WifiManagerTest { */ @Test public void testRemoveSuggestionConnectionListener() throws Exception { - mWifiManager.removeSuggestionConnectionStatusListener(mListener); + mWifiManager.removeSuggestionConnectionStatusListener(mSuggestionConnectionListener); verify(mWifiService).unregisterSuggestionConnectionStatusListener(anyInt(), anyString()); } @@ -2609,4 +2613,84 @@ public class WifiManagerTest { verify(mWifiService).isCarrierNetworkOffloadEnabled(TEST_SUB_ID, false); } + + /** + * Verify an IllegalArgumentException is thrown if listener is not provided. + */ + @Test(expected = IllegalArgumentException.class) + public void testRemoveSuggestionUserApprovalStatusListenerWithNullListener() { + assumeTrue(SdkLevel.isAtLeastS()); + + mWifiManager.removeSuggestionUserApprovalStatusListener(null); + } + + + /** + * Verify removeSuggestionUserApprovalStatusListener. + */ + @Test + public void testRemoveSuggestionUserApprovalStatusListener() throws Exception { + assumeTrue(SdkLevel.isAtLeastS()); + + mWifiManager.removeSuggestionUserApprovalStatusListener( + mSuggestionUserApprovalStatusListener); + verify(mWifiService).removeSuggestionUserApprovalStatusListener(anyInt(), anyString()); + } + + /** + * Verify an IllegalArgumentException is thrown if executor not provided. + */ + @Test(expected = IllegalArgumentException.class) + public void testAddSuggestionUserApprovalStatusListenerWithNullExecutor() { + assumeTrue(SdkLevel.isAtLeastS()); + + mWifiManager.addSuggestionUserApprovalStatusListener(null, + mSuggestionUserApprovalStatusListener); + } + + /** + * Verify an IllegalArgumentException is thrown if listener is not provided. + */ + @Test(expected = IllegalArgumentException.class) + public void testAddSuggestionUserApprovalStatusListenerWithNullListener() { + assumeTrue(SdkLevel.isAtLeastS()); + + mWifiManager.addSuggestionUserApprovalStatusListener(mExecutor, null); + } + + /** + * Verify client provided listener is being called to the right listener. + */ + @Test + public void testAddSuggestionUserApprovalStatusListenerAndReceiveEvent() throws Exception { + assumeTrue(SdkLevel.isAtLeastS()); + + ArgumentCaptor callbackCaptor = + ArgumentCaptor.forClass(ISuggestionUserApprovalStatusListener.Stub.class); + Executor executor = new SynchronousExecutor(); + mWifiManager.addSuggestionUserApprovalStatusListener(executor, + mSuggestionUserApprovalStatusListener); + verify(mWifiService).addSuggestionUserApprovalStatusListener(any(IBinder.class), + callbackCaptor.capture(), anyInt(), anyString(), nullable(String.class)); + callbackCaptor.getValue().onUserApprovalStatusChange(); + verify(mSuggestionUserApprovalStatusListener).onUserApprovalStatusChange(); + } + + /** + * Verify client provided listener is being called to the right executor. + */ + @Test + public void testAddSuggestionUserApprovalStatusListenerWithTheTargetExecutor() + throws Exception { + assumeTrue(SdkLevel.isAtLeastS()); + ArgumentCaptor callbackCaptor = + ArgumentCaptor.forClass(ISuggestionUserApprovalStatusListener.Stub.class); + mWifiManager.addSuggestionUserApprovalStatusListener(mExecutor, + mSuggestionUserApprovalStatusListener); + verify(mWifiService).addSuggestionUserApprovalStatusListener(any(IBinder.class), + callbackCaptor.capture(), anyInt(), anyString(), nullable(String.class)); + callbackCaptor.getValue().onUserApprovalStatusChange(); + verify(mExecutor).execute(any(Runnable.class)); + } + }