From c278f80c282e1ec4563b75b2c3f7c169e3d26023 Mon Sep 17 00:00:00 2001 From: Roshan Pius Date: Thu, 14 Nov 2019 07:19:12 -0800 Subject: [PATCH] NetworkScoreManager: Add @SystemApi for wifi mainline module Add formal API's for API's that the wifi mainline module uses: a) NetworkScoreManager.requestNetworkScores - Request new network scores. b) NetworkScoreManager.registerNetworkScoreCallback - @SystemApi wrapper over the existing registerNetworkScoreCache. c) NetworkKey.createFromScanResult - Create NetworkKey from ScanResult. Also, a) Converted the existing WifiNetworkScoreCache callback implementation to support both the new @SystemApi (used by wifi) and the old @hide API (used by settings). b) Stopped invoking dump on all the callbacks from NetworkScoreService. The dump of each callback should be invoked at their client site (i.e wifi service should dump state of the callback it registers with the service). c) Added a helper method |dumpWithLatestScanResults| to help dump the state of the WifiNetworkScoreCache from wifi service. Bug: 144487252 Test: a) Device boots up and connects to wifi network. b) Manually verified that network scores are being requested & updated with the new interface. Change-Id: Id5a66189150e7a088127519373a832f63bdd12ac --- api/system-current.txt | 18 ++- core/java/android/net/NetworkKey.java | 11 +- .../java/android/net/NetworkScoreManager.java | 111 +++++++++++++++++- .../android/server/NetworkScoreService.java | 13 -- .../net/wifi/WifiNetworkScoreCache.java | 18 ++- 5 files changed, 146 insertions(+), 25 deletions(-) diff --git a/api/system-current.txt b/api/system-current.txt index aed13b7b0af20..0478b91cb9f78 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4225,6 +4225,7 @@ package android.net { public class NetworkKey implements android.os.Parcelable { ctor public NetworkKey(android.net.WifiKey); + method @Nullable public static android.net.NetworkKey createFromScanResult(@Nullable android.net.wifi.ScanResult); method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator CREATOR; @@ -4247,16 +4248,23 @@ package android.net { method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, "android.permission.REQUEST_NETWORK_SCORES"}) public boolean clearScores() throws java.lang.SecurityException; method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, "android.permission.REQUEST_NETWORK_SCORES"}) public void disableScoring() throws java.lang.SecurityException; method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, "android.permission.REQUEST_NETWORK_SCORES"}) public String getActiveScorerPackage(); + method @RequiresPermission("android.permission.REQUEST_NETWORK_SCORES") public void registerNetworkScoreCallback(int, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.NetworkScoreManager.NetworkScoreCallback) throws java.lang.SecurityException; + method @RequiresPermission("android.permission.REQUEST_NETWORK_SCORES") public boolean requestScores(@NonNull android.net.NetworkKey[]) throws java.lang.SecurityException; method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, "android.permission.REQUEST_NETWORK_SCORES"}) public boolean setActiveScorer(String) throws java.lang.SecurityException; - method @RequiresPermission(android.Manifest.permission.SCORE_NETWORKS) public boolean updateScores(android.net.ScoredNetwork[]) throws java.lang.SecurityException; - field public static final String ACTION_CHANGE_ACTIVE = "android.net.scoring.CHANGE_ACTIVE"; + method @RequiresPermission(android.Manifest.permission.SCORE_NETWORKS) public boolean updateScores(@NonNull android.net.ScoredNetwork[]) throws java.lang.SecurityException; + field @Deprecated public static final String ACTION_CHANGE_ACTIVE = "android.net.scoring.CHANGE_ACTIVE"; field public static final String ACTION_CUSTOM_ENABLE = "android.net.scoring.CUSTOM_ENABLE"; field public static final String ACTION_RECOMMEND_NETWORKS = "android.net.action.RECOMMEND_NETWORKS"; field public static final String ACTION_SCORER_CHANGED = "android.net.scoring.SCORER_CHANGED"; - field public static final String ACTION_SCORE_NETWORKS = "android.net.scoring.SCORE_NETWORKS"; - field public static final String EXTRA_NETWORKS_TO_SCORE = "networksToScore"; + field @Deprecated public static final String ACTION_SCORE_NETWORKS = "android.net.scoring.SCORE_NETWORKS"; + field @Deprecated public static final String EXTRA_NETWORKS_TO_SCORE = "networksToScore"; field public static final String EXTRA_NEW_SCORER = "newScorer"; - field public static final String EXTRA_PACKAGE_NAME = "packageName"; + field @Deprecated public static final String EXTRA_PACKAGE_NAME = "packageName"; + } + + public static interface NetworkScoreManager.NetworkScoreCallback { + method public void clearScores(); + method public void updateScores(@NonNull java.util.List); } public class NetworkStack { diff --git a/core/java/android/net/NetworkKey.java b/core/java/android/net/NetworkKey.java index a101da7b4b9cf..fb4a4a6abfd88 100644 --- a/core/java/android/net/NetworkKey.java +++ b/core/java/android/net/NetworkKey.java @@ -16,6 +16,7 @@ package android.net; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -27,6 +28,8 @@ import android.os.Parcelable; import android.text.TextUtils; import android.util.Log; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Objects; /** @@ -48,6 +51,13 @@ public class NetworkKey implements Parcelable { /** A wifi network, for which {@link #wifiKey} will be populated. */ public static final int TYPE_WIFI = 1; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"TYPE_"}, value = { + TYPE_WIFI + }) + public @interface NetworkType {} + /** * The type of this network. * @see #TYPE_WIFI @@ -65,7 +75,6 @@ public class NetworkKey implements Parcelable { * * @return A new {@link NetworkKey} instance or null if the given * {@link ScanResult} instance is malformed. - * @hide */ @Nullable public static NetworkKey createFromScanResult(@Nullable ScanResult result) { diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java index 50dd468aa9053..f6dc52522cb24 100644 --- a/core/java/android/net/NetworkScoreManager.java +++ b/core/java/android/net/NetworkScoreManager.java @@ -17,7 +17,9 @@ package android.net; import android.Manifest.permission; +import android.annotation.CallbackExecutor; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; @@ -25,13 +27,16 @@ import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.Context; +import android.os.Binder; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; +import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; +import java.util.concurrent.Executor; /** * Class that manages communication between network subsystems and a network scorer. @@ -50,19 +55,25 @@ import java.util.List; @SystemApi @SystemService(Context.NETWORK_SCORE_SERVICE) public class NetworkScoreManager { + private static final String TAG = "NetworkScoreManager"; + /** * Activity action: ask the user to change the active network scorer. This will show a dialog * that asks the user whether they want to replace the current active scorer with the one * specified in {@link #EXTRA_PACKAGE_NAME}. The activity will finish with RESULT_OK if the * active scorer was changed or RESULT_CANCELED if it failed for any reason. + * @deprecated No longer sent. */ + @Deprecated @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_CHANGE_ACTIVE = "android.net.scoring.CHANGE_ACTIVE"; /** * Extra used with {@link #ACTION_CHANGE_ACTIVE} to specify the new scorer package. Set with * {@link android.content.Intent#putExtra(String, String)}. + * @deprecated No longer sent. */ + @Deprecated public static final String EXTRA_PACKAGE_NAME = "packageName"; /** @@ -73,7 +84,9 @@ public class NetworkScoreManager { * configured by the user as well as any open networks. * *

This is a protected intent that can only be sent by the system. + * @deprecated Use {@link #ACTION_RECOMMEND_NETWORKS} to bind scorer app instead. */ + @Deprecated @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_SCORE_NETWORKS = "android.net.scoring.SCORE_NETWORKS"; @@ -81,7 +94,9 @@ public class NetworkScoreManager { * Extra used with {@link #ACTION_SCORE_NETWORKS} to specify the networks to be scored, as an * array of {@link NetworkKey}s. Can be obtained with * {@link android.content.Intent#getParcelableArrayExtra(String)}}. + * @deprecated Use {@link #ACTION_RECOMMEND_NETWORKS} to bind scorer app instead. */ + @Deprecated public static final String EXTRA_NETWORKS_TO_SCORE = "networksToScore"; /** @@ -285,7 +300,7 @@ public class NetworkScoreManager { * @throws SecurityException if the caller is not the active scorer. */ @RequiresPermission(android.Manifest.permission.SCORE_NETWORKS) - public boolean updateScores(ScoredNetwork[] networks) throws SecurityException { + public boolean updateScores(@NonNull ScoredNetwork[] networks) throws SecurityException { try { return mService.updateScores(networks); } catch (RemoteException e) { @@ -359,13 +374,21 @@ public class NetworkScoreManager { /** * Request scoring for networks. * - * @return true if the broadcast was sent, or false if there is no active scorer. + *

+ * Note: The results (i.e scores) for these networks, when available will be provided via the + * callback registered with {@link #registerNetworkScoreCallback(int, int, Executor, + * NetworkScoreCallback)}. The calling module is responsible for registering a callback to + * receive the results before requesting new scores via this API. + * + * @return true if the request was successfully sent, or false if there is no active scorer. * @throws SecurityException if the caller does not hold the * {@link permission#REQUEST_NETWORK_SCORES} permission. + * * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.REQUEST_NETWORK_SCORES) - public boolean requestScores(NetworkKey[] networks) throws SecurityException { + public boolean requestScores(@NonNull NetworkKey[] networks) throws SecurityException { try { return mService.requestScores(networks); } catch (RemoteException e) { @@ -430,6 +453,88 @@ public class NetworkScoreManager { } } + /** + * Base class for network score cache callback. Should be extended by applications and set + * when calling {@link #registerNetworkScoreCallback(int, int, NetworkScoreCallback, + * Executor)} + * + * @hide + */ + @SystemApi + public interface NetworkScoreCallback { + /** + * Called when a new set of network scores are available. + * This is triggered in response when the client invokes + * {@link #requestScores(NetworkKey[])} to score a new set of networks. + * + * @param networks List of {@link ScoredNetwork} containing updated scores. + */ + void updateScores(@NonNull List networks); + + /** + * Invokes when all the previously provided scores are no longer valid. + */ + void clearScores(); + } + + /** + * Callback proxy for {@link NetworkScoreCallback} objects. + */ + private class NetworkScoreCallbackProxy extends INetworkScoreCache.Stub { + private final Executor mExecutor; + private final NetworkScoreCallback mCallback; + + NetworkScoreCallbackProxy(Executor executor, NetworkScoreCallback callback) { + mExecutor = executor; + mCallback = callback; + } + + @Override + public void updateScores(@NonNull List networks) { + Binder.clearCallingIdentity(); + mExecutor.execute(() -> { + mCallback.updateScores(networks); + }); + } + + @Override + public void clearScores() { + Binder.clearCallingIdentity(); + mExecutor.execute(() -> { + mCallback.clearScores(); + }); + } + } + + /** + * Register a network score callback. + * + * @param networkType the type of network this cache can handle. See {@link NetworkKey#type} + * @param filterType the {@link CacheUpdateFilter} to apply + * @param callback implementation of {@link NetworkScoreCallback} that will be invoked when the + * scores change. + * @param executor The executor on which to execute the callbacks. + * @throws SecurityException if the caller does not hold the + * {@link permission#REQUEST_NETWORK_SCORES} permission. + * @throws IllegalArgumentException if a callback is already registered for this type. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.REQUEST_NETWORK_SCORES) + public void registerNetworkScoreCallback(@NetworkKey.NetworkType int networkType, + @CacheUpdateFilter int filterType, + @NonNull @CallbackExecutor Executor executor, + @NonNull NetworkScoreCallback callback) throws SecurityException { + if (callback == null || executor == null) { + throw new IllegalArgumentException("callback / executor cannot be null"); + } + Log.v(TAG, "registerNetworkScoreCallback: callback=" + callback + ", executor=" + + executor); + // Use the @hide method. + registerNetworkScoreCache( + networkType, new NetworkScoreCallbackProxy(executor, callback), filterType); + } + /** * Determine whether the application with the given UID is the enabled scorer. * diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java index df5005eed66be..b26ef92557e11 100644 --- a/services/core/java/com/android/server/NetworkScoreService.java +++ b/services/core/java/com/android/server/NetworkScoreService.java @@ -58,12 +58,10 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.PackageMonitor; -import com.android.internal.os.TransferPipe; import com.android.internal.util.DumpUtils; import com.android.server.pm.permission.PermissionManagerServiceInternal; import java.io.FileDescriptor; -import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; @@ -903,17 +901,6 @@ public class NetworkScoreService extends INetworkScoreService.Stub { } writer.println("Current scorer: " + currentScorer); - sendCacheUpdateCallback(new BiConsumer() { - @Override - public void accept(INetworkScoreCache networkScoreCache, Object cookie) { - try { - TransferPipe.dumpAsync(networkScoreCache.asBinder(), fd, args); - } catch (IOException | RemoteException e) { - writer.println("Failed to dump score cache: " + e); - } - } - }, getScoreCacheLists()); - synchronized (mServiceConnectionLock) { if (mServiceConnection != null) { mServiceConnection.dump(fd, writer, args); diff --git a/wifi/java/android/net/wifi/WifiNetworkScoreCache.java b/wifi/java/android/net/wifi/WifiNetworkScoreCache.java index b22ae07015c85..5a212a824452b 100755 --- a/wifi/java/android/net/wifi/WifiNetworkScoreCache.java +++ b/wifi/java/android/net/wifi/WifiNetworkScoreCache.java @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.content.Context; import android.net.INetworkScoreCache; import android.net.NetworkKey; +import android.net.NetworkScoreManager; import android.net.ScoredNetwork; import android.os.Handler; import android.os.Process; @@ -40,7 +41,8 @@ import java.util.List; * * @hide */ -public class WifiNetworkScoreCache extends INetworkScoreCache.Stub { +public class WifiNetworkScoreCache extends INetworkScoreCache.Stub + implements NetworkScoreManager.NetworkScoreCallback { private static final String TAG = "WifiNetworkScoreCache"; private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); @@ -246,6 +248,17 @@ public class WifiNetworkScoreCache extends INetworkScoreCache.Stub { } @Override protected final void dump(FileDescriptor fd, PrintWriter writer, String[] args) { + WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); + dumpWithLatestScanResults(fd, writer, args, wifiManager.getScanResults()); + } + + /** + * This is directly invoked from within Wifi-Service (on it's instance of this class), hence + * avoid making the WifiManager.getScanResults() call to avoid a deadlock. + */ + public final void dumpWithLatestScanResults( + FileDescriptor fd, PrintWriter writer, String[] args, + List latestScanResults) { mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG); String header = String.format("WifiNetworkScoreCache (%s/%d)", mContext.getPackageName(), Process.myUid()); @@ -256,8 +269,7 @@ public class WifiNetworkScoreCache extends INetworkScoreCache.Stub { writer.println(" " + score); } writer.println(" Network scores for latest ScanResults:"); - WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); - for (ScanResult scanResult : wifiManager.getScanResults()) { + for (ScanResult scanResult : latestScanResults) { writer.println( " " + buildNetworkKey(scanResult) + ": " + getNetworkScore(scanResult)); }