summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/system-current.txt3
-rw-r--r--core/java/android/net/NetworkRecommendationProvider.java2
-rw-r--r--core/java/android/net/NetworkScoreManager.java20
-rw-r--r--core/java/android/net/NetworkScorerAppManager.java261
-rw-r--r--core/java/android/net/RecommendationRequest.java20
-rw-r--r--core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java2
-rw-r--r--core/java/android/util/apk/ZipUtils.java2
-rw-r--r--core/jni/android_util_Process.cpp2
-rw-r--r--core/res/res/drawable/ic_signal_wifi_badged_0_bars.xml29
-rw-r--r--core/res/res/drawable/ic_signal_wifi_badged_1_bar.xml32
-rw-r--r--core/res/res/drawable/ic_signal_wifi_badged_2_bars.xml32
-rw-r--r--core/res/res/drawable/ic_signal_wifi_badged_3_bars.xml32
-rw-r--r--core/res/res/drawable/ic_signal_wifi_badged_4_bars.xml28
-rw-r--r--core/res/res/drawable/ic_signal_wifi_badged_4k.xml31
-rw-r--r--core/res/res/drawable/ic_signal_wifi_badged_hd.xml31
-rw-r--r--core/res/res/drawable/ic_signal_wifi_badged_ld.xml31
-rw-r--r--core/res/res/drawable/ic_signal_wifi_badged_sd.xml31
-rw-r--r--core/res/res/values/symbols.xml9
-rw-r--r--core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java294
-rw-r--r--core/tests/coretests/src/android/net/RecommendationRequestTest.java84
-rw-r--r--services/core/java/com/android/server/BluetoothManagerService.java3
-rw-r--r--services/core/java/com/android/server/NetworkScoreService.java301
-rw-r--r--services/core/java/com/android/server/pm/Installer.java16
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java25
-rw-r--r--services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java278
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java10
-rw-r--r--wifi/java/android/net/wifi/WifiConfiguration.java1
-rwxr-xr-xwifi/java/android/net/wifi/WifiNetworkScoreCache.java102
-rw-r--r--wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java272
29 files changed, 1303 insertions, 681 deletions
diff --git a/api/system-current.txt b/api/system-current.txt
index 8bf880b0a746..f96a58b759a6 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -25673,7 +25673,7 @@ package android.net {
field public static final java.lang.String EXTRA_SEQUENCE = "android.net.extra.SEQUENCE";
}
- public static final class NetworkRecommendationProvider.ResultCallback {
+ public static class NetworkRecommendationProvider.ResultCallback {
method public void onResult(android.net.RecommendationResult);
}
@@ -26855,6 +26855,7 @@ package android.net.wifi {
field public boolean hiddenSSID;
field public java.lang.String lastUpdateName;
field public int lastUpdateUid;
+ field public boolean meteredHint;
field public int networkId;
field public int numAssociation;
field public int numScorerOverride;
diff --git a/core/java/android/net/NetworkRecommendationProvider.java b/core/java/android/net/NetworkRecommendationProvider.java
index af5a052c6bf7..16ae867d81e7 100644
--- a/core/java/android/net/NetworkRecommendationProvider.java
+++ b/core/java/android/net/NetworkRecommendationProvider.java
@@ -75,7 +75,7 @@ public abstract class NetworkRecommendationProvider {
* A callback implementing applications should invoke when a {@link RecommendationResult}
* is available.
*/
- public static final class ResultCallback {
+ public static class ResultCallback {
private final IRemoteCallback mCallback;
private final int mSequence;
private final AtomicBoolean mCallbackRun;
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index e08767cf28fe..18259564974a 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -183,7 +183,7 @@ public class NetworkScoreManager {
if (app == null) {
return null;
}
- return app.mPackageName;
+ return app.packageName;
}
/**
@@ -272,19 +272,11 @@ public class NetworkScoreManager {
* @hide
*/
public boolean requestScores(NetworkKey[] networks) throws SecurityException {
- String activeScorer = getActiveScorerPackage();
- if (activeScorer == null) {
- return false;
+ try {
+ return mService.requestScores(networks);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
- Intent intent = new Intent(ACTION_SCORE_NETWORKS);
- intent.setPackage(activeScorer);
- intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- intent.putExtra(EXTRA_NETWORKS_TO_SCORE, networks);
- // A scorer should never become active if its package doesn't hold SCORE_NETWORKS, but
- // ensure the package still holds it to be extra safe.
- // TODO: http://b/23422763
- mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM, Manifest.permission.SCORE_NETWORKS);
- return true;
}
/**
@@ -344,6 +336,8 @@ public class NetworkScoreManager {
/**
* Request a recommendation for which network to connect to.
*
+ * <p>It is not safe to call this method from the main thread.
+ *
* @param request a {@link RecommendationRequest} instance containing additional
* request details
* @return a {@link RecommendationResult} instance containing the recommended network
diff --git a/core/java/android/net/NetworkScorerAppManager.java b/core/java/android/net/NetworkScorerAppManager.java
index ebb31c9056a9..4282ca75f2b6 100644
--- a/core/java/android/net/NetworkScorerAppManager.java
+++ b/core/java/android/net/NetworkScorerAppManager.java
@@ -19,160 +19,176 @@ package android.net;
import android.Manifest;
import android.Manifest.permission;
import android.annotation.Nullable;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
-
+import com.android.internal.R;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
- * Internal class for managing the primary network scorer application.
- *
- * TODO: Rename this to something more generic.
+ * Internal class for discovering and managing the network scorer/recommendation application.
*
* @hide
*/
public class NetworkScorerAppManager {
private static final String TAG = "NetworkScorerAppManager";
-
- private static final Intent SCORE_INTENT =
- new Intent(NetworkScoreManager.ACTION_SCORE_NETWORKS);
-
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
private final Context mContext;
public NetworkScorerAppManager(Context context) {
mContext = context;
}
+ /**
+ * Holds metadata about a discovered network scorer/recommendation application.
+ */
public static class NetworkScorerAppData {
/** Package name of this scorer app. */
- public final String mPackageName;
+ public final String packageName;
/** UID of the scorer app. */
- public final int mPackageUid;
-
- /** Name of this scorer app for display. */
- public final CharSequence mScorerName;
+ public final int packageUid;
/**
- * Optional class name of a configuration activity. Null if none is set.
- *
- * @see NetworkScoreManager#ACTION_CUSTOM_ENABLE
+ * Name of the recommendation service we can bind to.
*/
- public final String mConfigurationActivityClassName;
+ public final String recommendationServiceClassName;
- /**
- * Optional class name of the scoring service we can bind to. Null if none is set.
- */
- public final String mScoringServiceClassName;
-
- public NetworkScorerAppData(String packageName, int packageUid, CharSequence scorerName,
- @Nullable String configurationActivityClassName,
- @Nullable String scoringServiceClassName) {
- mScorerName = scorerName;
- mPackageName = packageName;
- mPackageUid = packageUid;
- mConfigurationActivityClassName = configurationActivityClassName;
- mScoringServiceClassName = scoringServiceClassName;
+ public NetworkScorerAppData(String packageName, int packageUid,
+ String recommendationServiceClassName) {
+ this.packageName = packageName;
+ this.packageUid = packageUid;
+ this.recommendationServiceClassName = recommendationServiceClassName;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("NetworkScorerAppData{");
- sb.append("mPackageName='").append(mPackageName).append('\'');
- sb.append(", mPackageUid=").append(mPackageUid);
- sb.append(", mScorerName=").append(mScorerName);
- sb.append(", mConfigurationActivityClassName='").append(mConfigurationActivityClassName)
- .append('\'');
- sb.append(", mScoringServiceClassName='").append(mScoringServiceClassName).append('\'');
+ sb.append("mPackageName='").append(packageName).append('\'');
+ sb.append(", packageUid=").append(packageUid);
+ sb.append(", recommendationServiceClassName='")
+ .append(recommendationServiceClassName).append('\'');
sb.append('}');
return sb.toString();
}
}
/**
- * Returns the list of available scorer apps.
+ * @return A {@link NetworkScorerAppData} instance containing information about the
+ * best configured network recommendation provider installed or {@code null}
+ * if none of the configured packages can recommend networks.
*
- * <p>A network scorer is any application which:
+ * <p>A network recommendation provider is any application which:
* <ul>
+ * <li>Is listed in the <code>config_networkRecommendationPackageNames</code> config.
* <li>Declares the {@link android.Manifest.permission#SCORE_NETWORKS} permission.
- * <li>Includes a receiver for {@link NetworkScoreManager#ACTION_SCORE_NETWORKS} guarded by the
- * {@link android.Manifest.permission#BROADCAST_NETWORK_PRIVILEGED} permission.
+ * <li>Includes a Service for {@link NetworkScoreManager#ACTION_RECOMMEND_NETWORKS}.
* </ul>
- *
- * @return the list of scorers, or the empty list if there are no valid scorers.
*/
- public Collection<NetworkScorerAppData> getAllValidScorers() {
- // Network scorer apps can only run as the primary user so exit early if we're not the
- // primary user.
+ public NetworkScorerAppData getNetworkRecommendationProviderData() {
+ // Network recommendation apps can only run as the primary user right now.
+ // http://b/23422763
if (UserHandle.getCallingUserId() != UserHandle.USER_SYSTEM) {
- return Collections.emptyList();
+ return null;
}
- List<NetworkScorerAppData> scorers = new ArrayList<>();
- PackageManager pm = mContext.getPackageManager();
- // Only apps installed under the primary user of the device can be scorers.
- // TODO: http://b/23422763
- List<ResolveInfo> receivers =
- pm.queryBroadcastReceiversAsUser(SCORE_INTENT, 0 /* flags */, UserHandle.USER_SYSTEM);
- for (ResolveInfo receiver : receivers) {
- // This field is a misnomer, see android.content.pm.ResolveInfo#activityInfo
- final ActivityInfo receiverInfo = receiver.activityInfo;
- if (receiverInfo == null) {
- // Should never happen with queryBroadcastReceivers, but invalid nonetheless.
- continue;
+ final List<String> potentialPkgs = getPotentialRecommendationProviderPackages();
+ if (potentialPkgs.isEmpty()) {
+ if (DEBUG) {
+ Log.d(TAG, "No Network Recommendation Providers specified.");
}
- if (!permission.BROADCAST_NETWORK_PRIVILEGED.equals(receiverInfo.permission)) {
- // Receiver doesn't require the BROADCAST_NETWORK_PRIVILEGED permission, which
- // means anyone could trigger network scoring and flood the framework with score
- // requests.
- continue;
+ return null;
+ }
+
+ final PackageManager pm = mContext.getPackageManager();
+ for (int i = 0; i < potentialPkgs.size(); i++) {
+ final String potentialPkg = potentialPkgs.get(i);
+
+ // Look for the recommendation service class and required receiver.
+ final ResolveInfo resolveServiceInfo = findRecommendationService(potentialPkg);
+ if (resolveServiceInfo != null) {
+ return new NetworkScorerAppData(potentialPkg,
+ resolveServiceInfo.serviceInfo.applicationInfo.uid,
+ resolveServiceInfo.serviceInfo.name);
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, potentialPkg + " does not have the required components, skipping.");
+ }
}
- if (pm.checkPermission(permission.SCORE_NETWORKS, receiverInfo.packageName) !=
- PackageManager.PERMISSION_GRANTED) {
- // Application doesn't hold the SCORE_NETWORKS permission, so the user never
- // approved it as a network scorer.
- continue;
+ }
+
+ // None of the configured packages are valid.
+ return null;
+ }
+
+ /**
+ * @return A priority order list of package names that have been granted the
+ * permission needed for them to act as a network recommendation provider.
+ * The packages in the returned list may not contain the other required
+ * network recommendation provider components so additional checks are required
+ * before making a package the network recommendation provider.
+ */
+ public List<String> getPotentialRecommendationProviderPackages() {
+ final String[] packageArray = mContext.getResources().getStringArray(
+ R.array.config_networkRecommendationPackageNames);
+ if (packageArray == null || packageArray.length == 0) {
+ if (DEBUG) {
+ Log.d(TAG, "No Network Recommendation Providers specified.");
}
+ return Collections.emptyList();
+ }
+
+ if (VERBOSE) {
+ Log.d(TAG, "Configured packages: " + TextUtils.join(", ", packageArray));
+ }
- // Optionally, this package may specify a configuration activity.
- String configurationActivityClassName = null;
- Intent intent = new Intent(NetworkScoreManager.ACTION_CUSTOM_ENABLE);
- intent.setPackage(receiverInfo.packageName);
- List<ResolveInfo> configActivities = pm.queryIntentActivities(intent, 0 /* flags */);
- if (configActivities != null && !configActivities.isEmpty()) {
- ActivityInfo activityInfo = configActivities.get(0).activityInfo;
- if (activityInfo != null) {
- configurationActivityClassName = activityInfo.name;
+ List<String> packages = new ArrayList<>();
+ final PackageManager pm = mContext.getPackageManager();
+ for (String potentialPkg : packageArray) {
+ if (pm.checkPermission(permission.SCORE_NETWORKS, potentialPkg)
+ == PackageManager.PERMISSION_GRANTED) {
+ packages.add(potentialPkg);
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, potentialPkg + " has not been granted " + permission.SCORE_NETWORKS
+ + ", skipping.");
}
}
+ }
- // Find the scoring service class we can bind to, if any.
- String scoringServiceClassName = null;
- Intent serviceIntent = new Intent(NetworkScoreManager.ACTION_SCORE_NETWORKS);
- serviceIntent.setPackage(receiverInfo.packageName);
- ResolveInfo resolveServiceInfo = pm.resolveService(serviceIntent, 0 /* flags */);
- if (resolveServiceInfo != null && resolveServiceInfo.serviceInfo != null) {
- scoringServiceClassName = resolveServiceInfo.serviceInfo.name;
- }
+ return packages;
+ }
- // NOTE: loadLabel will attempt to load the receiver's label and fall back to the
- // app label if none is present.
- scorers.add(new NetworkScorerAppData(receiverInfo.packageName,
- receiverInfo.applicationInfo.uid, receiverInfo.loadLabel(pm),
- configurationActivityClassName, scoringServiceClassName));
+ private ResolveInfo findRecommendationService(String packageName) {
+ final PackageManager pm = mContext.getPackageManager();
+ final int resolveFlags = 0;
+
+ final Intent serviceIntent = new Intent(NetworkScoreManager.ACTION_RECOMMEND_NETWORKS);
+ serviceIntent.setPackage(packageName);
+ final ResolveInfo resolveServiceInfo =
+ pm.resolveService(serviceIntent, resolveFlags);
+
+ if (VERBOSE) {
+ Log.d(TAG, "Resolved " + serviceIntent + " to " + resolveServiceInfo);
+ }
+
+ if (resolveServiceInfo != null && resolveServiceInfo.serviceInfo != null) {
+ return resolveServiceInfo;
}
- return scorers;
+ if (VERBOSE) {
+ Log.v(TAG, packageName + " does not have a service for " + serviceIntent);
+ }
+ return null;
}
/**
@@ -182,10 +198,15 @@ public class NetworkScorerAppManager {
* selected) or if the previously-set scorer is no longer a valid scorer app (e.g. because
* it was disabled or uninstalled).
*/
+ @Nullable
public NetworkScorerAppData getActiveScorer() {
- String scorerPackage = Settings.Global.getString(mContext.getContentResolver(),
- Settings.Global.NETWORK_SCORER_APP);
- return getScorer(scorerPackage);
+ if (isNetworkRecommendationsDisabled()) {
+ // If recommendations are disabled then there can't be an active scorer.
+ return null;
+ }
+
+ // Otherwise return the recommendation provider (which may be null).
+ return getNetworkRecommendationProviderData();
}
/**
@@ -195,33 +216,13 @@ public class NetworkScorerAppManager {
*
* @param packageName the packageName of the new scorer to use. If null, scoring will be
* disabled. Otherwise, the scorer will only be set if it is a valid scorer application.
- * @return true if the scorer was changed, or false if the package is not a valid scorer.
+ * @return true if the scorer was changed, or false if the package is not a valid scorer or
+ * a valid network recommendation provider exists.
+ * @deprecated Scorers are now selected from a configured list.
*/
+ @Deprecated
public boolean setActiveScorer(String packageName) {
- String oldPackageName = Settings.Global.getString(mContext.getContentResolver(),
- Settings.Global.NETWORK_SCORER_APP);
- if (TextUtils.equals(oldPackageName, packageName)) {
- // No change.
- return true;
- }
-
- Log.i(TAG, "Changing network scorer from " + oldPackageName + " to " + packageName);
-
- if (packageName == null) {
- Settings.Global.putString(mContext.getContentResolver(),
- Settings.Global.NETWORK_SCORER_APP, null);
- return true;
- } else {
- // We only make the change if the new package is valid.
- if (getScorer(packageName) != null) {
- Settings.Global.putString(mContext.getContentResolver(),
- Settings.Global.NETWORK_SCORER_APP, packageName);
- return true;
- } else {
- Log.w(TAG, "Requested network scorer is not valid: " + packageName);
- return false;
- }
- }
+ return false;
}
/** Determine whether the application with the given UID is the enabled scorer. */
@@ -230,7 +231,7 @@ public class NetworkScorerAppManager {
if (defaultApp == null) {
return false;
}
- if (callingUid != defaultApp.mPackageUid) {
+ if (callingUid != defaultApp.packageUid) {
return false;
}
// To be extra safe, ensure the caller holds the SCORE_NETWORKS permission. It always
@@ -239,17 +240,9 @@ public class NetworkScorerAppManager {
PackageManager.PERMISSION_GRANTED;
}
- /** Returns the {@link NetworkScorerAppData} for the given app, or null if it's not a scorer. */
- public NetworkScorerAppData getScorer(String packageName) {
- if (TextUtils.isEmpty(packageName)) {
- return null;
- }
- Collection<NetworkScorerAppData> applications = getAllValidScorers();
- for (NetworkScorerAppData app : applications) {
- if (packageName.equals(app.mPackageName)) {
- return app;
- }
- }
- return null;
+ private boolean isNetworkRecommendationsDisabled() {
+ final ContentResolver cr = mContext.getContentResolver();
+ // A value of 1 indicates enabled.
+ return Settings.Global.getInt(cr, Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 0) != 1;
}
}
diff --git a/core/java/android/net/RecommendationRequest.java b/core/java/android/net/RecommendationRequest.java
index 05ca1aa437f9..a96f90d57746 100644
--- a/core/java/android/net/RecommendationRequest.java
+++ b/core/java/android/net/RecommendationRequest.java
@@ -105,7 +105,16 @@ public final class RecommendationRequest implements Parcelable {
}
protected RecommendationRequest(Parcel in) {
- mScanResults = (ScanResult[]) in.readParcelableArray(ScanResult.class.getClassLoader());
+ final int resultCount = in.readInt();
+ if (resultCount > 0) {
+ mScanResults = new ScanResult[resultCount];
+ for (int i = 0; i < resultCount; i++) {
+ mScanResults[i] = in.readParcelable(ScanResult.class.getClassLoader());
+ }
+ } else {
+ mScanResults = null;
+ }
+
mCurrentSelectedConfig = in.readParcelable(WifiConfiguration.class.getClassLoader());
mRequiredCapabilities = in.readParcelable(NetworkCapabilities.class.getClassLoader());
}
@@ -117,7 +126,14 @@ public final class RecommendationRequest implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeParcelableArray(mScanResults, flags);
+ if (mScanResults != null) {
+ dest.writeInt(mScanResults.length);
+ for (int i = 0; i < mScanResults.length; i++) {
+ dest.writeParcelable(mScanResults[i], flags);
+ }
+ } else {
+ dest.writeInt(0);
+ }
dest.writeParcelable(mCurrentSelectedConfig, flags);
dest.writeParcelable(mRequiredCapabilities, flags);
}
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index 78d3b7bf81d2..0216a0752a9c 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -579,7 +579,7 @@ public class ApkSignatureSchemeV2Verifier {
throws SignatureNotFoundException {
// Look up the offset of ZIP Central Directory.
long centralDirOffset = ZipUtils.getZipEocdCentralDirectoryOffset(eocd);
- if (centralDirOffset >= eocdOffset) {
+ if (centralDirOffset > eocdOffset) {
throw new SignatureNotFoundException(
"ZIP Central Directory offset out of range: " + centralDirOffset
+ ". ZIP End of Central Directory offset: " + eocdOffset);
diff --git a/core/java/android/util/apk/ZipUtils.java b/core/java/android/util/apk/ZipUtils.java
index cdbac1802377..fa5477e4190b 100644
--- a/core/java/android/util/apk/ZipUtils.java
+++ b/core/java/android/util/apk/ZipUtils.java
@@ -160,7 +160,7 @@ abstract class ZipUtils {
}
int maxCommentLength = Math.min(archiveSize - ZIP_EOCD_REC_MIN_SIZE, UINT16_MAX_VALUE);
int eocdWithEmptyCommentStartPosition = archiveSize - ZIP_EOCD_REC_MIN_SIZE;
- for (int expectedCommentLength = 0; expectedCommentLength < maxCommentLength;
+ for (int expectedCommentLength = 0; expectedCommentLength <= maxCommentLength;
expectedCommentLength++) {
int eocdStartPos = eocdWithEmptyCommentStartPosition - expectedCommentLength;
if (zipContents.getInt(eocdStartPos) == ZIP_EOCD_REC_SIG) {
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 1cfbd97f82b9..b57f2362a284 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -240,7 +240,7 @@ void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jin
t_pri = getpriority(PRIO_PROCESS, t_pid);
if (t_pri <= ANDROID_PRIORITY_AUDIO) {
- int scheduler = sched_getscheduler(t_pid);
+ int scheduler = sched_getscheduler(t_pid) & ~SCHED_RESET_ON_FORK;
if ((scheduler == SCHED_FIFO) || (scheduler == SCHED_RR)) {
// This task wants to stay in its current audio group so it can keep its budget
// don't update its cpuset or cgroup
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_0_bars.xml b/core/res/res/drawable/ic_signal_wifi_badged_0_bars.xml
new file mode 100644
index 000000000000..bd1eb41bee18
--- /dev/null
+++ b/core/res/res/drawable/ic_signal_wifi_badged_0_bars.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+<vector xmlns:api24="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
+ android:viewportWidth="18"
+ android:viewportHeight="18"
+ android:width="18dp"
+ android:height="18dp">
+ <group
+ android:translateX="386"
+ android:translateY="-298">
+ <path
+ android:pathData="M-377 308.5c0 -2.5 2 -4.5 4.5 -4.5l3.5 0 0.79999 -1c-0.29999 -0.29999 -3.70001 -3 -8.79999 -3 -5.09998 0 -8.5 2.79999 -8.79999 3l8.79999 11 0 0 0 0 1.60001 -2c-0.9 -0.89999 -1.60001 -2.10001 -1.60001 -3.5z"
+ android:fillColor="#FFFFFF"
+ android:fillAlpha="0.3" />
+ </group>
+</vector>
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_1_bar.xml b/core/res/res/drawable/ic_signal_wifi_badged_1_bar.xml
new file mode 100644
index 000000000000..aedb12c310e6
--- /dev/null
+++ b/core/res/res/drawable/ic_signal_wifi_badged_1_bar.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+<vector xmlns:api24="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
+ android:viewportWidth="18"
+ android:viewportHeight="18"
+ android:width="18dp"
+ android:height="18dp">
+ <group
+ android:translateX="386"
+ android:translateY="-298">
+ <path
+ android:pathData="M-377 308.5c0 -2.5 2 -4.5 4.5 -4.5l3.5 0 0.79999 -1c-0.29999 -0.29999 -3.70001 -3 -8.79999 -3 -5.09998 0 -8.5 2.79999 -8.79999 3l8.79999 11 0 0 0 0 1.60001 -2c-0.9 -0.89999 -1.60001 -2.10001 -1.60001 -3.5z"
+ android:fillColor="#FFFFFF"
+ android:fillAlpha="0.3" />
+ <path
+ android:pathData="M-377 308.5c0 -0.20001 0 -0.29999 0.10001 -0.5 0 0 0 0 -0.10001 0 -2.10001 0 -3.60001 1.20001 -3.79999 1.29999L-377 314l1.60001 -2.10001c-0.9 -0.79998 -1.60001 -2 -1.60001 -3.39999z"
+ android:fillColor="#FFFFFF" />
+ </group>
+</vector>
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_2_bars.xml b/core/res/res/drawable/ic_signal_wifi_badged_2_bars.xml
new file mode 100644
index 000000000000..6f07cb5f5009
--- /dev/null
+++ b/core/res/res/drawable/ic_signal_wifi_badged_2_bars.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+<vector xmlns:api24="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
+ android:viewportWidth="18"
+ android:viewportHeight="18"
+ android:width="18dp"
+ android:height="18dp">
+ <group
+ android:translateX="386"
+ android:translateY="-298">
+ <path
+ android:pathData="M-377 308.5c0 -2.5 2 -4.5 4.5 -4.5l3.5 0 0.79999 -1c-0.29999 -0.29999 -3.70001 -3 -8.79999 -3 -5.09998 0 -8.5 2.79999 -8.79999 3l8.79999 11 0 0 0 0 1.60001 -2c-0.9 -0.89999 -1.60001 -2.10001 -1.60001 -3.5z"
+ android:fillColor="#FFFFFF"
+ android:fillAlpha="0.3" />
+ <path
+ android:pathData="M-377 308.5c0 -0.89999 0.29999 -1.70001 0.70001 -2.5 -0.20001 0 -0.5 0 -0.70001 0 -2.79999 0 -4.79999 1.60001 -5 1.79999l5 6.20001 0 0 0 0 1.60001 -2c-1 -0.89999 -1.60001 -2.10001 -1.60001 -3.5z"
+ android:fillColor="#FFFFFF" />
+ </group>
+</vector>
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_3_bars.xml b/core/res/res/drawable/ic_signal_wifi_badged_3_bars.xml
new file mode 100644
index 000000000000..c41a8ca8a47c
--- /dev/null
+++ b/core/res/res/drawable/ic_signal_wifi_badged_3_bars.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+<vector xmlns:api24="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
+ android:viewportWidth="18"
+ android:viewportHeight="18"
+ android:width="18dp"
+ android:height="18dp">
+ <group
+ android:translateX="386"
+ android:translateY="-298">
+ <path
+ android:pathData="M-377 308.5c0 -2.5 2 -4.5 4.5 -4.5l3.5 0 0.79999 -1c-0.29999 -0.29999 -3.70001 -3 -8.79999 -3 -5.09998 0 -8.5 2.79999 -8.79999 3l8.79999 11 0 0 0 0 1.60001 -2c-0.9 -0.89999 -1.60001 -2.10001 -1.60001 -3.5z"
+ android:fillColor="#FFFFFF"
+ android:fillAlpha="0.3" />
+ <path
+ android:pathData="M-375.39999 311.89999c-1 -0.79998 -1.60001 -2.1 -1.60001 -3.39999 0 -1.79999 1.10001 -3.39999 2.70001 -4.10001C-375.09998 304.19998 -376 304 -377 304c-3.60001 0 -6 1.89999 -6.29999 2.20001L-377 314l0 0 0 0"
+ android:fillColor="#FFFFFF" />
+ </group>
+</vector>
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_4_bars.xml b/core/res/res/drawable/ic_signal_wifi_badged_4_bars.xml
new file mode 100644
index 000000000000..ec0a52f6a43a
--- /dev/null
+++ b/core/res/res/drawable/ic_signal_wifi_badged_4_bars.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+<vector xmlns:api24="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
+ android:viewportWidth="18"
+ android:viewportHeight="18"
+ android:width="18dp"
+ android:height="18dp">
+ <group
+ android:translateX="386"
+ android:translateY="-298">
+ <path
+ android:pathData="M-377 308.5c0 -2.5 2 -4.5 4.5 -4.5l3.5 0 0.79999 -1c-0.29999 -0.29999 -3.70001 -3 -8.79999 -3 -5.09998 0 -8.5 2.79999 -8.79999 3l8.79999 11 0 0 0 0 1.60001 -2c-0.9 -0.89999 -1.60001 -2.10001 -1.60001 -3.5z"
+ android:fillColor="#FFFFFF" />
+ </group>
+</vector>
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_4k.xml b/core/res/res/drawable/ic_signal_wifi_badged_4k.xml
new file mode 100644
index 000000000000..78bd0a060d0e
--- /dev/null
+++ b/core/res/res/drawable/ic_signal_wifi_badged_4k.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+<vector xmlns:api24="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
+ android:viewportWidth="18"
+ android:viewportHeight="18"
+ android:width="18dp"
+ android:height="18dp">
+ <group
+ android:translateX="386"
+ android:translateY="-298">
+ <path
+ android:pathData="M-373.04999 308.79999l0.5 0 0 0.89999 -0.5 0 0 1.20001 -1.1 0 0 -1.20001 -1.9 0 -0.1 -0.70001 1.89999 -3.70001 1.10001 0 0 3.50003 0.1 0zm-1.89999 0l0.89999 0 0 -1.9 0 0 -0.89999 1.9z"
+ android:fillColor="#FFFFFF" />
+ <path
+ android:pathData="M-370.44998 308.70001l-0.5 0.60001 0 1.70001 -1.10001 0 0 -5.70001 1.10001 0 0 2.5 0.39999 -0.60001 1.10001 -1.89999 1.39999 0 -1.6 2.5 1.70001 3.20001 -1.29999 0 -1.20001 -2.30002z"
+ android:fillColor="#FFFFFF" />
+ </group>
+</vector>
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_hd.xml b/core/res/res/drawable/ic_signal_wifi_badged_hd.xml
new file mode 100644
index 000000000000..78085c2f5b4d
--- /dev/null
+++ b/core/res/res/drawable/ic_signal_wifi_badged_hd.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+<vector xmlns:api24="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
+ android:viewportWidth="18"
+ android:viewportHeight="18"
+ android:width="18dp"
+ android:height="18dp">
+ <group
+ android:translateX="386"
+ android:translateY="-298">
+ <path
+ android:pathData="M-371.79999 311l-1.1 0 0 -2.29999 -0.79999 0 0 2.29999 -1.10001 0 0 -5.70001 1.10001 0 0 2.39999 0.79999 0 0 -2.39999 1.1 0 0 5.70001z"
+ android:fillColor="#FFFFFF" />
+ <path
+ android:pathData="M-371.33557 310.98651l0 -5.68701 1.39068 0c0.27848 0 0.53336 0.0532 0.76465 0.16016 0.2313 0.10693 0.42954 0.2622 0.59568 0.46679 0.16519 0.20459 0.29357 0.45557 0.38421 0.75391 0.0906 0.29834 0.13593 0.63867 0.13593 1.02148l0 0.88672c0 0.38281 -0.0453 0.72363 -0.13593 1.021 -0.0906 0.29785 -0.21902 0.5498 -0.38421 0.7539 -0.16614 0.20411 -0.36628 0.35938 -0.59946 0.46485 -0.23316 0.10547 -0.49182 0.1582 -0.77786 0.1582l-1.37369 0zm1.06879 -4.76904l0 3.85107 0.26333 0c0.15491 0 0.28452 -0.0283 0.38971 -0.084 0.10516 -0.0557 0.19077 -0.14356 0.25681 -0.26367 0.066 -0.12012 0.11331 -0.27198 0.14184 -0.4585 0.0285 -0.18652 0.0424 -0.41113 0.0424 -0.67383l0 -0.89453c0 -0.26562 -0.0138 -0.49219 -0.0424 -0.67969 -0.0285 -0.1875 -0.0758 -0.33984 -0.14102 -0.45703 -0.0644 -0.11719 -0.1492 -0.20312 -0.25275 -0.25781 -0.10437 -0.0547 -0.23071 -0.082 -0.37991 -0.082l-0.27801 0z"
+ android:fillColor="#FFFFFF" />
+ </group>
+</vector>
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_ld.xml b/core/res/res/drawable/ic_signal_wifi_badged_ld.xml
new file mode 100644
index 000000000000..f660ab76e83a
--- /dev/null
+++ b/core/res/res/drawable/ic_signal_wifi_badged_ld.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+<vector xmlns:api24="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
+ android:viewportWidth="18"
+ android:viewportHeight="18"
+ android:width="18dp"
+ android:height="18dp">
+ <group
+ android:translateX="386"
+ android:translateY="-298">
+ <path
+ android:pathData="M-371.33557 310.98651l0 -5.68701 1.39068 0c0.27848 0 0.53336 0.0532 0.76465 0.16016 0.2313 0.10693 0.42954 0.2622 0.59568 0.46679 0.16519 0.20459 0.29357 0.45557 0.38421 0.75391 0.0906 0.29834 0.13593 0.63867 0.13593 1.02148l0 0.88672c0 0.38281 -0.0453 0.72363 -0.13593 1.021 -0.0906 0.29785 -0.21902 0.5498 -0.38421 0.7539 -0.16614 0.20411 -0.36628 0.35938 -0.59946 0.46485 -0.23316 0.10547 -0.49182 0.1582 -0.77786 0.1582l-1.37369 0zm1.06879 -4.76904l0 3.85107 0.26333 0c0.15491 0 0.28452 -0.0283 0.38971 -0.084 0.10516 -0.0557 0.19077 -0.14356 0.25681 -0.26367 0.066 -0.12012 0.11331 -0.27198 0.14184 -0.4585 0.0285 -0.18652 0.0424 -0.41113 0.0424 -0.67383l0 -0.89453c0 -0.26562 -0.0138 -0.49219 -0.0424 -0.67969 -0.0285 -0.1875 -0.0758 -0.33984 -0.14102 -0.45703 -0.0644 -0.11719 -0.1492 -0.20312 -0.25275 -0.25781 -0.10437 -0.0547 -0.23071 -0.082 -0.37991 -0.082l-0.27801 0z"
+ android:fillColor="#FFFFFF" />
+ <path
+ android:pathData="M-373.13333 310.13333l1.33334 0 0 0.86667 -2.46667 0 0 -5.66666 1.13333 0 0 4.79999z"
+ android:fillColor="#FFFFFF" />
+ </group>
+</vector>
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_sd.xml b/core/res/res/drawable/ic_signal_wifi_badged_sd.xml
new file mode 100644
index 000000000000..43b865325a7e
--- /dev/null
+++ b/core/res/res/drawable/ic_signal_wifi_badged_sd.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+<vector xmlns:api24="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
+ android:viewportWidth="18"
+ android:viewportHeight="18"
+ android:width="18dp"
+ android:height="18dp">
+ <group
+ android:translateX="386"
+ android:translateY="-298">
+ <path
+ android:pathData="M-371.33557 310.98651l0 -5.68701 1.39068 0c0.27848 0 0.53336 0.0532 0.76465 0.16016 0.2313 0.10693 0.42954 0.2622 0.59568 0.46679 0.16519 0.20459 0.29357 0.45557 0.38421 0.75391 0.0906 0.29834 0.13593 0.63867 0.13593 1.02148l0 0.88672c0 0.38281 -0.0453 0.72363 -0.13593 1.021 -0.0906 0.29785 -0.21902 0.5498 -0.38421 0.7539 -0.16614 0.20411 -0.36628 0.35938 -0.59946 0.46485 -0.23316 0.10547 -0.49182 0.1582 -0.77786 0.1582l-1.37369 0zm1.06879 -4.76904l0 3.85107 0.26333 0c0.15491 0 0.28452 -0.0283 0.38971 -0.084 0.10516 -0.0557 0.19077 -0.14356 0.25681 -0.26367 0.066 -0.12012 0.11331 -0.27198 0.14184 -0.4585 0.0285 -0.18652 0.0424 -0.41113 0.0424 -0.67383l0 -0.89453c0 -0.26562 -0.0138 -0.49219 -0.0424 -0.67969 -0.0285 -0.1875 -0.0758 -0.33984 -0.14102 -0.45703 -0.0644 -0.11719 -0.1492 -0.20312 -0.25275 -0.25781 -0.10437 -0.0547 -0.23071 -0.082 -0.37991 -0.082l-0.27801 0z"
+ android:fillColor="#FFFFFF" />
+ <path
+ android:pathData="M-372.87598 309.47461c0 -0.10645 -0.01 -0.20117 -0.0303 -0.28223 -0.0205 -0.0811 -0.0576 -0.15527 -0.11035 -0.22265 -0.0537 -0.0674 -0.12598 -0.12891 -0.21777 -0.18457 -0.0908 -0.0566 -0.20704 -0.11231 -0.34668 -0.16797 -0.24903 -0.0889 -0.47657 -0.18457 -0.68165 -0.28614 -0.20605 -0.10156 -0.38281 -0.2207 -0.53027 -0.35839 -0.14746 -0.13721 -0.26172 -0.29639 -0.34277 -0.47803 -0.0811 -0.18164 -0.12207 -0.39697 -0.12207 -0.646 0 -0.23144 0.042 -0.4414 0.12793 -0.63086 0.085 -0.18945 0.20312 -0.35205 0.35644 -0.48779 0.15235 -0.13623 0.33496 -0.2417 0.54883 -0.31641 0.21289 -0.0752 0.44824 -0.1123 0.70508 -0.1123 0.2666 0 0.50683 0.0425 0.71973 0.12744 0.21386 0.0854 0.39648 0.2041 0.54687 0.35645 0.15137 0.15234 0.26758 0.333 0.34766 0.54101 0.0791 0.2085 0.11914 0.43604 0.11914 0.68262l-1.07422 0c0 -0.11963 -0.0127 -0.22998 -0.0381 -0.33154 -0.0254 -0.10205 -0.0654 -0.18897 -0.12011 -0.26123 -0.0547 -0.0723 -0.125 -0.12891 -0.20997 -0.16993 -0.085 -0.0405 -0.1875 -0.0605 -0.30664 -0.0605 -0.11132 0 -0.208 0.0171 -0.29004 0.0513 -0.0811 0.0342 -0.14843 0.0815 -0.20117 0.14111 -0.0537 0.0596 -0.0928 0.13037 -0.11816 0.21143 -0.0254 0.0815 -0.0381 0.16894 -0.0381 0.26318 0 0.0937 0.0166 0.17725 0.0508 0.24951 0.0342 0.0723 0.0869 0.14014 0.15625 0.20361 0.0703 0.064 0.16016 0.125 0.26856 0.18311 0.10937 0.0586 0.23926 0.11963 0.38965 0.1831 0.24316 0.084 0.46093 0.17823 0.65136 0.2837 0.19043 0.10498 0.35059 0.22998 0.48047 0.37158 0.12891 0.14258 0.22754 0.30762 0.29493 0.49316 0.0674 0.1875 0.10156 0.40235 0.10156 0.64649 0 0.24121 -0.04 0.458 -0.12012 0.64843 -0.0801 0.19043 -0.19434 0.35059 -0.3418 0.48145 -0.14746 0.13086 -0.32617 0.23144 -0.53711 0.30176 -0.21093 0.0693 -0.44726 0.10449 -0.70898 0.10449 -0.23633 0 -0.46777 -0.0361 -0.69531 -0.1084 -0.22754 -0.0723 -0.43067 -0.18359 -0.61035 -0.33203 -0.17872 -0.14844 -0.32325 -0.33691 -0.43262 -0.56543 -0.10938 -0.22852 -0.16309 -0.49805 -0.16309 -0.80859l1.07813 0c0 0.17089 0.0166 0.31445 0.0498 0.43261 0.0332 0.11817 0.084 0.21485 0.15235 0.29004 0.0684 0.0752 0.15429 0.12891 0.25683 0.16211 0.10352 0.0332 0.22461 0.0488 0.36426 0.0488 0.11719 0 0.21582 -0.0156 0.2959 -0.0488 0.0801 -0.0332 0.14355 -0.0781 0.19238 -0.13574 0.0478 -0.0566 0.082 -0.125 0.10254 -0.2041 0.0205 -0.0781 0.0303 -0.16504 0.0303 -0.25879z"
+ android:fillColor="#FFFFFF" />
+ </group>
+</vector>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ddf8f25248a6..fe88cd1ff423 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1230,6 +1230,15 @@
<java-symbol type="drawable" name="platlogo" />
<java-symbol type="drawable" name="stat_notify_sync_error" />
<java-symbol type="drawable" name="stat_notify_wifi_in_range" />
+ <java-symbol type="drawable" name="ic_signal_wifi_badged_0_bars" />
+ <java-symbol type="drawable" name="ic_signal_wifi_badged_1_bar" />
+ <java-symbol type="drawable" name="ic_signal_wifi_badged_2_bars" />
+ <java-symbol type="drawable" name="ic_signal_wifi_badged_3_bars" />
+ <java-symbol type="drawable" name="ic_signal_wifi_badged_4_bars" />
+ <java-symbol type="drawable" name="ic_signal_wifi_badged_4k" />
+ <java-symbol type="drawable" name="ic_signal_wifi_badged_hd" />
+ <java-symbol type="drawable" name="ic_signal_wifi_badged_sd" />
+ <java-symbol type="drawable" name="ic_signal_wifi_badged_ld" />
<java-symbol type="drawable" name="stat_notify_rssi_in_range" />
<java-symbol type="drawable" name="stat_sys_gps_on" />
<java-symbol type="drawable" name="stat_sys_tether_wifi" />
diff --git a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
index 02c25170bb74..5bfff26b0813 100644
--- a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
+++ b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
@@ -16,32 +16,33 @@
package android.net;
+import static org.mockito.Mockito.when;
+
import android.Manifest.permission;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
import android.net.NetworkScorerAppManager.NetworkScorerAppData;
-import android.os.UserHandle;
+import android.provider.Settings;
import android.test.InstrumentationTestCase;
-
+import com.android.internal.R;
+import java.util.List;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-
public class NetworkScorerAppManagerTest extends InstrumentationTestCase {
@Mock private Context mMockContext;
@Mock private PackageManager mMockPm;
-
+ @Mock private Resources mResources;
+ @Mock private ContentResolver mContentResolver;
+ private Context mTargetContext;
private NetworkScorerAppManager mNetworkScorerAppManager;
@Override
@@ -49,154 +50,161 @@ public class NetworkScorerAppManagerTest extends InstrumentationTestCase {
super.setUp();
// Configuration needed to make mockito/dexcache work.
- System.setProperty("dexmaker.dexcache",
- getInstrumentation().getTargetContext().getCacheDir().getPath());
+ mTargetContext = getInstrumentation().getTargetContext();
+ System.setProperty("dexmaker.dexcache", mTargetContext.getCacheDir().getPath());
ClassLoader newClassLoader = getInstrumentation().getClass().getClassLoader();
Thread.currentThread().setContextClassLoader(newClassLoader);
MockitoAnnotations.initMocks(this);
- Mockito.when(mMockContext.getPackageManager()).thenReturn(mMockPm);
+ when(mMockContext.getPackageManager()).thenReturn(mMockPm);
+ when(mMockContext.getResources()).thenReturn(mResources);
+ when(mMockContext.getContentResolver()).thenReturn(mTargetContext.getContentResolver());
mNetworkScorerAppManager = new NetworkScorerAppManager(mMockContext);
}
- public void testGetAllValidScorers() throws Exception {
- // Package 1 - Valid scorer.
- ResolveInfoHolder package1 = buildResolveInfo("package1", 1, true, true, false, false);
-
- // Package 2 - Receiver does not have BROADCAST_NETWORK_PRIVILEGED permission.
- ResolveInfoHolder package2 = buildResolveInfo("package2", 2, false, true, false, false);
-
- // Package 3 - App does not have SCORE_NETWORKS permission.
- ResolveInfoHolder package3 = buildResolveInfo("package3", 3, true, false, false, false);
-
- // Package 4 - Valid scorer w/ optional config activity.
- ResolveInfoHolder package4 = buildResolveInfo("package4", 4, true, true, true, false);
-
- // Package 5 - Valid scorer w/ optional service to bind to.
- ResolveInfoHolder package5 = buildResolveInfo("package5", 5, true, true, false, true);
-
- List<ResolveInfoHolder> scorers = new ArrayList<>();
- scorers.add(package1);
- scorers.add(package2);
- scorers.add(package3);
- scorers.add(package4);
- scorers.add(package5);
- setScorers(scorers);
-
- Iterator<NetworkScorerAppData> result =
- mNetworkScorerAppManager.getAllValidScorers().iterator();
-
- assertTrue(result.hasNext());
- NetworkScorerAppData next = result.next();
- assertEquals("package1", next.mPackageName);
- assertEquals(1, next.mPackageUid);
- assertNull(next.mConfigurationActivityClassName);
-
- assertTrue(result.hasNext());
- next = result.next();
- assertEquals("package4", next.mPackageName);
- assertEquals(4, next.mPackageUid);
- assertEquals(".ConfigActivity", next.mConfigurationActivityClassName);
-
- assertTrue(result.hasNext());
- next = result.next();
- assertEquals("package5", next.mPackageName);
- assertEquals(5, next.mPackageUid);
- assertEquals(".ScoringService", next.mScoringServiceClassName);
-
- assertFalse(result.hasNext());
- }
-
- private void setScorers(List<ResolveInfoHolder> scorers) {
- List<ResolveInfo> receivers = new ArrayList<>();
- for (final ResolveInfoHolder scorer : scorers) {
- receivers.add(scorer.scorerResolveInfo);
- if (scorer.configActivityResolveInfo != null) {
- // This scorer has a config activity.
- Mockito.when(mMockPm.queryIntentActivities(
- Mockito.argThat(new ArgumentMatcher<Intent>() {
- @Override
- public boolean matches(Object object) {
- Intent intent = (Intent) object;
- return NetworkScoreManager.ACTION_CUSTOM_ENABLE.equals(
- intent.getAction())
- && scorer.scorerResolveInfo.activityInfo.packageName.equals(
- intent.getPackage());
- }
- }), Mockito.eq(0))).thenReturn(
- Collections.singletonList(scorer.configActivityResolveInfo));
- }
-
- if (scorer.serviceResolveInfo != null) {
- // This scorer has a service to bind to
- Mockito.when(mMockPm.resolveService(
- Mockito.argThat(new ArgumentMatcher<Intent>() {
- @Override
- public boolean matches(Object object) {
- Intent intent = (Intent) object;
- return NetworkScoreManager.ACTION_SCORE_NETWORKS.equals(
- intent.getAction())
- && scorer.scorerResolveInfo.activityInfo.packageName.equals(
- intent.getPackage());
- }
- }), Mockito.eq(0))).thenReturn(scorer.serviceResolveInfo);
- }
- }
+ public void testGetPotentialRecommendationProviderPackages_emptyConfig() throws Exception {
+ setNetworkRecommendationPackageNames(/*no configured packages*/);
+ assertTrue(mNetworkScorerAppManager.getPotentialRecommendationProviderPackages().isEmpty());
+ }
- Mockito.when(mMockPm.queryBroadcastReceiversAsUser(
- Mockito.argThat(new ArgumentMatcher<Intent>() {
- @Override
- public boolean matches(Object object) {
- Intent intent = (Intent) object;
- return NetworkScoreManager.ACTION_SCORE_NETWORKS.equals(intent.getAction());
- }
- }), Mockito.eq(0), Mockito.eq(UserHandle.USER_SYSTEM)))
- .thenReturn(receivers);
- }
-
- private ResolveInfoHolder buildResolveInfo(String packageName, int packageUid,
- boolean hasReceiverPermission, boolean hasScorePermission, boolean hasConfigActivity,
- boolean hasServiceInfo) throws Exception {
- Mockito.when(mMockPm.checkPermission(permission.SCORE_NETWORKS, packageName))
- .thenReturn(hasScorePermission ?
- PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED);
-
- ResolveInfo resolveInfo = new ResolveInfo();
- resolveInfo.activityInfo = new ActivityInfo();
- resolveInfo.activityInfo.packageName = packageName;
- resolveInfo.activityInfo.applicationInfo = new ApplicationInfo();
- resolveInfo.activityInfo.applicationInfo.uid = packageUid;
- if (hasReceiverPermission) {
- resolveInfo.activityInfo.permission = permission.BROADCAST_NETWORK_PRIVILEGED;
- }
+ public void testGetPotentialRecommendationProviderPackages_permissionNotGranted()
+ throws Exception {
+ setNetworkRecommendationPackageNames("package1");
+ mockScoreNetworksDenied("package1");
- ResolveInfo configActivityInfo = null;
- if (hasConfigActivity) {
- configActivityInfo = new ResolveInfo();
- configActivityInfo.activityInfo = new ActivityInfo();
- configActivityInfo.activityInfo.name = ".ConfigActivity";
- }
+ assertTrue(mNetworkScorerAppManager.getPotentialRecommendationProviderPackages().isEmpty());
+ }
- ResolveInfo serviceInfo = null;
- if (hasServiceInfo) {
- serviceInfo = new ResolveInfo();
- serviceInfo.serviceInfo = new ServiceInfo();
- serviceInfo.serviceInfo.name = ".ScoringService";
- }
+ public void testGetPotentialRecommendationProviderPackages_permissionGranted()
+ throws Exception {
+ setNetworkRecommendationPackageNames("package1");
+ mockScoreNetworksGranted("package1");
+
+ List<String> potentialProviderPackages =
+ mNetworkScorerAppManager.getPotentialRecommendationProviderPackages();
+
+ assertFalse(potentialProviderPackages.isEmpty());
+ assertEquals("package1", potentialProviderPackages.get(0));
+ }
+
+ public void testGetPotentialRecommendationProviderPackages_multipleConfigured()
+ throws Exception {
+ setNetworkRecommendationPackageNames("package1", "package2");
+ mockScoreNetworksDenied("package1");
+ mockScoreNetworksGranted("package2");
+
+ List<String> potentialProviderPackages =
+ mNetworkScorerAppManager.getPotentialRecommendationProviderPackages();
+
+ assertEquals(1, potentialProviderPackages.size());
+ assertEquals("package2", potentialProviderPackages.get(0));
+ }
+
+ public void testGetNetworkRecommendationProviderData_noPotentialPackages() throws Exception {
+ setNetworkRecommendationPackageNames(/*no configured packages*/);
+ assertNull(mNetworkScorerAppManager.getNetworkRecommendationProviderData());
+ }
+
+ public void testGetNetworkRecommendationProviderData_serviceMissing() throws Exception {
+ setNetworkRecommendationPackageNames("package1");
+ mockScoreNetworksGranted("package1");
- return new ResolveInfoHolder(resolveInfo, configActivityInfo, serviceInfo);
+ assertNull(mNetworkScorerAppManager.getNetworkRecommendationProviderData());
}
- private static class ResolveInfoHolder {
- final ResolveInfo scorerResolveInfo;
- final ResolveInfo configActivityResolveInfo;
- final ResolveInfo serviceResolveInfo;
+ public void testGetNetworkRecommendationProviderData_scoreNetworksNotGranted()
+ throws Exception {
+ setNetworkRecommendationPackageNames("package1");
+ mockScoreNetworksDenied("package1");
+ mockRecommendationServiceAvailable("package1", 924 /* packageUid */);
- public ResolveInfoHolder(ResolveInfo scorerResolveInfo,
- ResolveInfo configActivityResolveInfo, ResolveInfo serviceResolveInfo) {
- this.scorerResolveInfo = scorerResolveInfo;
- this.configActivityResolveInfo = configActivityResolveInfo;
- this.serviceResolveInfo = serviceResolveInfo;
+ assertNull(mNetworkScorerAppManager.getNetworkRecommendationProviderData());
+ }
+
+ public void testGetNetworkRecommendationProviderData_available() throws Exception {
+ setNetworkRecommendationPackageNames("package1");
+ mockScoreNetworksGranted("package1");
+ mockRecommendationServiceAvailable("package1", 924 /* packageUid */);
+
+ NetworkScorerAppData appData =
+ mNetworkScorerAppManager.getNetworkRecommendationProviderData();
+ assertNotNull(appData);
+ assertEquals("package1", appData.packageName);
+ assertEquals(924, appData.packageUid);
+ assertEquals(".RecommendationService", appData.recommendationServiceClassName);
+ }
+
+ public void testGetActiveScorer_providerAvailable() throws Exception {
+ setNetworkRecommendationPackageNames("package1");
+ mockScoreNetworksGranted("package1");
+ mockRecommendationServiceAvailable("package1", 924 /* packageUid */);
+
+ ContentResolver cr = mTargetContext.getContentResolver();
+ Settings.Global.putInt(cr, Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 1);
+
+ final NetworkScorerAppData activeScorer = mNetworkScorerAppManager.getActiveScorer();
+ assertNotNull(activeScorer);
+ assertEquals("package1", activeScorer.packageName);
+ assertEquals(924, activeScorer.packageUid);
+ assertEquals(".RecommendationService", activeScorer.recommendationServiceClassName);
+ }
+
+ public void testGetActiveScorer_providerNotAvailable()
+ throws Exception {
+ ContentResolver cr = mTargetContext.getContentResolver();
+ Settings.Global.putInt(cr, Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 1);
+
+ final NetworkScorerAppData activeScorer = mNetworkScorerAppManager.getActiveScorer();
+ assertNull(activeScorer);
+ }
+
+ public void testGetActiveScorer_recommendationsDisabled() throws Exception {
+ setNetworkRecommendationPackageNames("package1");
+ mockScoreNetworksGranted("package1");
+ mockRecommendationServiceAvailable("package1", 924 /* packageUid */);
+ ContentResolver cr = mTargetContext.getContentResolver();
+ Settings.Global.putInt(cr, Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 0);
+
+ final NetworkScorerAppData activeScorer = mNetworkScorerAppManager.getActiveScorer();
+ assertNull(activeScorer);
+ }
+
+ private void setNetworkRecommendationPackageNames(String... names) {
+ if (names == null) {
+ names = new String[0];
}
+ when(mResources.getStringArray(R.array.config_networkRecommendationPackageNames))
+ .thenReturn(names);
+ }
+
+ private void mockScoreNetworksGranted(String packageName) {
+ when(mMockPm.checkPermission(permission.SCORE_NETWORKS, packageName))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+ }
+
+ private void mockScoreNetworksDenied(String packageName) {
+ when(mMockPm.checkPermission(permission.SCORE_NETWORKS, packageName))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+ }
+
+ private void mockRecommendationServiceAvailable(final String packageName, int packageUid) {
+ final ResolveInfo serviceInfo = new ResolveInfo();
+ serviceInfo.serviceInfo = new ServiceInfo();
+ serviceInfo.serviceInfo.name = ".RecommendationService";
+ serviceInfo.serviceInfo.packageName = packageName;
+ serviceInfo.serviceInfo.applicationInfo = new ApplicationInfo();
+ serviceInfo.serviceInfo.applicationInfo.uid = packageUid;
+
+ final int flags = 0;
+ when(mMockPm.resolveService(
+ Mockito.argThat(new ArgumentMatcher<Intent>() {
+ @Override
+ public boolean matches(Object object) {
+ Intent intent = (Intent) object;
+ return NetworkScoreManager.ACTION_RECOMMEND_NETWORKS
+ .equals(intent.getAction())
+ && packageName.equals(intent.getPackage());
+ }
+ }), Mockito.eq(flags))).thenReturn(serviceInfo);
}
}
diff --git a/core/tests/coretests/src/android/net/RecommendationRequestTest.java b/core/tests/coretests/src/android/net/RecommendationRequestTest.java
new file mode 100644
index 000000000000..31560b0ac8c3
--- /dev/null
+++ b/core/tests/coretests/src/android/net/RecommendationRequestTest.java
@@ -0,0 +1,84 @@
+package android.net;
+
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.os.Parcel;
+import android.test.AndroidTestCase;
+
+public class RecommendationRequestTest extends AndroidTestCase {
+ private ScanResult[] mScanResults;
+ private WifiConfiguration mConfiguration;
+ private NetworkCapabilities mCapabilities;
+
+ @Override
+ public void setUp() throws Exception {
+ mScanResults = new ScanResult[2];
+ mScanResults[0] = new ScanResult();
+ mScanResults[1] = new ScanResult(
+ "ssid",
+ "bssid",
+ 0L /*hessid*/,
+ 1 /*anqpDominId*/,
+ "caps",
+ 2 /*level*/,
+ 3 /*frequency*/,
+ 4L /*tsf*/,
+ 5 /*distCm*/,
+ 6 /*distSdCm*/,
+ 7 /*channelWidth*/,
+ 8 /*centerFreq0*/,
+ 9 /*centerFreq1*/,
+ false /*is80211McRTTResponder*/);
+ mConfiguration = new WifiConfiguration();
+ mConfiguration.SSID = "RecommendationRequestTest";
+ mCapabilities = new NetworkCapabilities()
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED);
+ }
+
+ public void testParceling() throws Exception {
+ RecommendationRequest request = new RecommendationRequest.Builder()
+ .setCurrentRecommendedWifiConfig(mConfiguration)
+ .setScanResults(mScanResults)
+ .setNetworkCapabilities(mCapabilities)
+ .build();
+
+ RecommendationRequest parceled = passThroughParcel(request);
+ assertEquals(request.getCurrentSelectedConfig().SSID,
+ parceled.getCurrentSelectedConfig().SSID);
+ assertEquals(request.getRequiredCapabilities(), parceled.getRequiredCapabilities());
+ ScanResult[] parceledScanResults = parceled.getScanResults();
+ assertNotNull(parceledScanResults);
+ assertEquals(mScanResults.length, parceledScanResults.length);
+ for (int i = 0; i < mScanResults.length; i++) {
+ assertEquals(mScanResults[i].SSID, parceledScanResults[i].SSID);
+ }
+ }
+
+ public void testParceling_nullScanResults() throws Exception {
+ RecommendationRequest request = new RecommendationRequest.Builder()
+ .setCurrentRecommendedWifiConfig(mConfiguration)
+ .setNetworkCapabilities(mCapabilities)
+ .build();
+
+ RecommendationRequest parceled = passThroughParcel(request);
+ assertEquals(request.getCurrentSelectedConfig().SSID,
+ parceled.getCurrentSelectedConfig().SSID);
+ assertEquals(request.getRequiredCapabilities(), parceled.getRequiredCapabilities());
+ ScanResult[] parceledScanResults = parceled.getScanResults();
+ assertNull(parceledScanResults);
+ }
+
+ private RecommendationRequest passThroughParcel(RecommendationRequest request) {
+ Parcel p = Parcel.obtain();
+ RecommendationRequest output = null;
+ try {
+ request.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ output = RecommendationRequest.CREATOR.createFromParcel(p);
+ } finally {
+ p.recycle();
+ }
+ assertNotNull(output);
+ return output;
+ }
+}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 1035ac8f7c7b..86bbca7d0702 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -1282,8 +1282,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
if (mBluetooth != null) {
int state = mBluetooth.getState();
if (state == BluetoothAdapter.STATE_BLE_ON) {
- Slog.w(TAG, "BT is in BLE_ON State");
+ Slog.w(TAG, "BT Enable in BLE_ON State, going to ON");
mBluetooth.onLeServiceUp();
+ persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
break;
}
}
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index a1c3564abf8c..f712f121f1d7 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -16,7 +16,11 @@
package com.android.server;
+import static android.net.NetworkRecommendationProvider.EXTRA_RECOMMENDATION_RESULT;
+import static android.net.NetworkRecommendationProvider.EXTRA_SEQUENCE;
+
import android.Manifest.permission;
+import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -25,27 +29,29 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.net.INetworkRecommendationProvider;
import android.net.INetworkScoreCache;
import android.net.INetworkScoreService;
import android.net.NetworkKey;
-import android.net.NetworkScoreManager;
import android.net.NetworkScorerAppManager;
import android.net.NetworkScorerAppManager.NetworkScorerAppData;
import android.net.RecommendationRequest;
import android.net.RecommendationResult;
import android.net.ScoredNetwork;
+import android.net.Uri;
import android.net.wifi.WifiConfiguration;
-import android.os.Binder;
+import android.os.Bundle;
import android.os.IBinder;
+import android.os.IRemoteCallback;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.provider.Settings;
-import android.text.TextUtils;
+import android.provider.Settings.Global;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.TimedRemoteCaller;
-import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
@@ -59,6 +65,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
/**
@@ -67,17 +74,20 @@ import java.util.function.Consumer;
*/
public class NetworkScoreService extends INetworkScoreService.Stub {
private static final String TAG = "NetworkScoreService";
- private static final boolean DBG = false;
+ private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
private final Context mContext;
private final NetworkScorerAppManager mNetworkScorerAppManager;
+ private final RequestRecommendationCaller mRequestRecommendationCaller;
@GuardedBy("mScoreCaches")
private final Map<Integer, RemoteCallbackList<INetworkScoreCache>> mScoreCaches;
/** Lock used to update mPackageMonitor when scorer package changes occur. */
private final Object mPackageMonitorLock = new Object[0];
+ private final Object mServiceConnectionLock = new Object[0];
@GuardedBy("mPackageMonitorLock")
private NetworkScorerPackageMonitor mPackageMonitor;
+ @GuardedBy("mServiceConnectionLock")
private ScoringServiceConnection mServiceConnection;
private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
@@ -99,10 +109,10 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
* manages the service connection.
*/
private class NetworkScorerPackageMonitor extends PackageMonitor {
- final String mRegisteredPackage;
+ final List<String> mPackagesToWatch;
- private NetworkScorerPackageMonitor(String mRegisteredPackage) {
- this.mRegisteredPackage = mRegisteredPackage;
+ private NetworkScorerPackageMonitor(List<String> packagesToWatch) {
+ mPackagesToWatch = packagesToWatch;
}
@Override
@@ -136,7 +146,7 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
}
private void evaluateBinding(String scorerPackageName, boolean forceUnbind) {
- if (mRegisteredPackage.equals(scorerPackageName)) {
+ if (mPackagesToWatch.contains(scorerPackageName)) {
if (DBG) {
Log.d(TAG, "Evaluating binding for: " + scorerPackageName
+ ", forceUnbind=" + forceUnbind);
@@ -146,13 +156,14 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
if (activeScorer == null) {
// Package change has invalidated a scorer, this will also unbind any service
// connection.
- Log.i(TAG, "Package " + mRegisteredPackage +
- " is no longer valid, disabling scoring.");
- setScorerInternal(null);
- } else if (activeScorer.mScoringServiceClassName == null) {
- // The scoring service is not available, make sure it's unbound.
+ if (DBG) Log.d(TAG, "No active scorers available.");
unbindFromScoringServiceIfNeeded();
- } else { // The scoring service changed in some way.
+ } else if (activeScorer.packageName.equals(scorerPackageName)) {
+ if (DBG) {
+ Log.d(TAG, "Possible change to the active scorer: "
+ + activeScorer.packageName);
+ }
+ // The scoring service changed in some way.
if (forceUnbind) {
unbindFromScoringServiceIfNeeded();
}
@@ -162,6 +173,27 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
}
}
+ /**
+ * Reevaluates the service binding when the Settings toggle is changed.
+ */
+ private class SettingsObserver extends ContentObserver {
+
+ public SettingsObserver() {
+ super(null /*handler*/);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ onChange(selfChange, null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (DBG) Log.d(TAG, String.format("onChange(%s, %s)", selfChange, uri));
+ bindToScoringServiceIfNeeded();
+ }
+ }
+
public NetworkScoreService(Context context) {
this(context, new NetworkScorerAppManager(context));
}
@@ -176,24 +208,16 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
mContext.registerReceiverAsUser(
mUserIntentReceiver, UserHandle.SYSTEM, filter, null /* broadcastPermission*/,
null /* scheduler */);
+ // TODO(jjoslin): 12/15/16 - Make timeout configurable.
+ mRequestRecommendationCaller =
+ new RequestRecommendationCaller(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
}
/** Called when the system is ready to run third-party code but before it actually does so. */
void systemReady() {
if (DBG) Log.d(TAG, "systemReady");
- ContentResolver cr = mContext.getContentResolver();
- if (Settings.Global.getInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 0) == 0) {
- // On first run, we try to initialize the scorer to the one configured at build time.
- // This will be a no-op if the scorer isn't actually valid.
- String defaultPackage = mContext.getResources().getString(
- R.string.config_defaultNetworkScorerPackageName);
- if (!TextUtils.isEmpty(defaultPackage)) {
- mNetworkScorerAppManager.setActiveScorer(defaultPackage);
- }
- Settings.Global.putInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 1);
- }
-
registerPackageMonitorIfNeeded();
+ registerRecommendationSettingObserverIfNeeded();
}
/** Called when the system is ready for us to start third-party code. */
@@ -207,29 +231,40 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
bindToScoringServiceIfNeeded();
}
+ private void registerRecommendationSettingObserverIfNeeded() {
+ final List<String> providerPackages =
+ mNetworkScorerAppManager.getPotentialRecommendationProviderPackages();
+ if (!providerPackages.isEmpty()) {
+ final ContentResolver resolver = mContext.getContentResolver();
+ final Uri uri = Global.getUriFor(Global.NETWORK_RECOMMENDATIONS_ENABLED);
+ resolver.registerContentObserver(uri, false, new SettingsObserver());
+ }
+ }
+
private void registerPackageMonitorIfNeeded() {
if (DBG) Log.d(TAG, "registerPackageMonitorIfNeeded");
- NetworkScorerAppData scorer = mNetworkScorerAppManager.getActiveScorer();
+ final List<String> providerPackages =
+ mNetworkScorerAppManager.getPotentialRecommendationProviderPackages();
synchronized (mPackageMonitorLock) {
// Unregister the current monitor if needed.
if (mPackageMonitor != null) {
if (DBG) {
Log.d(TAG, "Unregistering package monitor for "
- + mPackageMonitor.mRegisteredPackage);
+ + mPackageMonitor.mPackagesToWatch);
}
mPackageMonitor.unregister();
mPackageMonitor = null;
}
- // Create and register the monitor if a scorer is active.
- if (scorer != null) {
- mPackageMonitor = new NetworkScorerPackageMonitor(scorer.mPackageName);
+ // Create and register the monitor if there are packages that could be providers.
+ if (!providerPackages.isEmpty()) {
+ mPackageMonitor = new NetworkScorerPackageMonitor(providerPackages);
// TODO: Need to update when we support per-user scorers. http://b/23422763
mPackageMonitor.register(mContext, null /* thread */, UserHandle.SYSTEM,
false /* externalStorage */);
if (DBG) {
Log.d(TAG, "Registered package monitor for "
- + mPackageMonitor.mRegisteredPackage);
+ + mPackageMonitor.mPackagesToWatch);
}
}
}
@@ -243,22 +278,24 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
private void bindToScoringServiceIfNeeded(NetworkScorerAppData scorerData) {
if (DBG) Log.d(TAG, "bindToScoringServiceIfNeeded(" + scorerData + ")");
- if (scorerData != null && scorerData.mScoringServiceClassName != null) {
- ComponentName componentName =
- new ComponentName(scorerData.mPackageName, scorerData.mScoringServiceClassName);
- // If we're connected to a different component then drop it.
- if (mServiceConnection != null
- && !mServiceConnection.mComponentName.equals(componentName)) {
- unbindFromScoringServiceIfNeeded();
- }
+ if (scorerData != null && scorerData.recommendationServiceClassName != null) {
+ ComponentName componentName = new ComponentName(scorerData.packageName,
+ scorerData.recommendationServiceClassName);
+ synchronized (mServiceConnectionLock) {
+ // If we're connected to a different component then drop it.
+ if (mServiceConnection != null
+ && !mServiceConnection.mComponentName.equals(componentName)) {
+ unbindFromScoringServiceIfNeeded();
+ }
- // If we're not connected at all then create a new connection.
- if (mServiceConnection == null) {
- mServiceConnection = new ScoringServiceConnection(componentName);
- }
+ // If we're not connected at all then create a new connection.
+ if (mServiceConnection == null) {
+ mServiceConnection = new ScoringServiceConnection(componentName);
+ }
- // Make sure the connection is connected (idempotent)
- mServiceConnection.connect(mContext);
+ // Make sure the connection is connected (idempotent)
+ mServiceConnection.connect(mContext);
+ }
} else { // otherwise make sure it isn't bound.
unbindFromScoringServiceIfNeeded();
}
@@ -266,10 +303,13 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
private void unbindFromScoringServiceIfNeeded() {
if (DBG) Log.d(TAG, "unbindFromScoringServiceIfNeeded");
- if (mServiceConnection != null) {
- mServiceConnection.disconnect(mContext);
+ synchronized (mServiceConnectionLock) {
+ if (mServiceConnection != null) {
+ mServiceConnection.disconnect(mContext);
+ }
+ mServiceConnection = null;
}
- mServiceConnection = null;
+ clearInternal();
}
@Override
@@ -349,7 +389,8 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
// mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
mContext.enforceCallingOrSelfPermission(permission.SCORE_NETWORKS, TAG);
- return setScorerInternal(packageName);
+ // Scorers (recommendation providers) are selected and no longer set.
+ return false;
}
@Override
@@ -359,56 +400,13 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
if (mNetworkScorerAppManager.isCallerActiveScorer(getCallingUid()) ||
mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED) ==
PackageManager.PERMISSION_GRANTED) {
- // The return value is discarded here because at this point, the call should always
- // succeed. The only reason for failure is if the new package is not a valid scorer, but
- // we're disabling scoring altogether here.
- setScorerInternal(null /* packageName */);
+ // no-op for now but we could write to the setting if needed.
} else {
throw new SecurityException(
"Caller is neither the active scorer nor the scorer manager.");
}
}
- /** Set the active scorer. Callers are responsible for checking permissions as appropriate. */
- private boolean setScorerInternal(String packageName) {
- if (DBG) Log.d(TAG, "setScorerInternal(" + packageName + ")");
- long token = Binder.clearCallingIdentity();
- try {
- unbindFromScoringServiceIfNeeded();
- // Preemptively clear scores even though the set operation could fail. We do this for
- // safety as scores should never be compared across apps; in practice, Settings should
- // only be allowing valid apps to be set as scorers, so failure here should be rare.
- clearInternal();
- // Get the scorer that is about to be replaced, if any, so we can notify it directly.
- NetworkScorerAppData prevScorer = mNetworkScorerAppManager.getActiveScorer();
- boolean result = mNetworkScorerAppManager.setActiveScorer(packageName);
- // Unconditionally attempt to bind to the current scorer. If setActiveScorer() failed
- // then we'll attempt to restore the previous binding (if any), otherwise an attempt
- // will be made to bind to the new scorer.
- bindToScoringServiceIfNeeded();
- if (result) { // new scorer successfully set
- registerPackageMonitorIfNeeded();
-
- Intent intent = new Intent(NetworkScoreManager.ACTION_SCORER_CHANGED);
- if (prevScorer != null) { // Directly notify the old scorer.
- intent.setPackage(prevScorer.mPackageName);
- // TODO: Need to update when we support per-user scorers. http://b/23422763
- mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
- }
-
- if (packageName != null) { // Then notify the new scorer
- intent.putExtra(NetworkScoreManager.EXTRA_NEW_SCORER, packageName);
- intent.setPackage(packageName);
- // TODO: Need to update when we support per-user scorers. http://b/23422763
- mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
- }
- }
- return result;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
/** Clear scores. Callers are responsible for checking permissions as appropriate. */
private void clearInternal() {
sendCallback(new Consumer<INetworkScoreCache>() {
@@ -464,7 +462,22 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
@Override
public RecommendationResult requestRecommendation(RecommendationRequest request) {
- // TODO(jjoslin): 11/25/16 - Update with real impl.
+ mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
+ throwIfCalledOnMainThread();
+ final INetworkRecommendationProvider provider = getRecommendationProvider();
+ if (provider != null) {
+ try {
+ return mRequestRecommendationCaller.getRecommendationResult(provider, request);
+ } catch (RemoteException | TimeoutException e) {
+ Log.w(TAG, "Failed to request a recommendation.", e);
+ // TODO(jjoslin): 12/15/16 - Keep track of failures.
+ }
+ }
+
+ if (DBG) {
+ Log.d(TAG, "Returning the default network recommendation.");
+ }
+
WifiConfiguration selectedConfig = null;
if (request != null) {
selectedConfig = request.getCurrentSelectedConfig();
@@ -474,7 +487,19 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
@Override
public boolean requestScores(NetworkKey[] networks) {
- // TODO(jjoslin): 12/13/16 - Implement
+ mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
+ final INetworkRecommendationProvider provider = getRecommendationProvider();
+ if (provider != null) {
+ try {
+ provider.requestScores(networks);
+ // TODO(jjoslin): 12/15/16 - Consider pushing null scores into the cache to prevent
+ // repeated requests for the same scores.
+ return true;
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to request scores.", e);
+ // TODO(jjoslin): 12/15/16 - Keep track of failures.
+ }
+ }
return false;
}
@@ -486,7 +511,7 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
writer.println("Scoring is disabled.");
return;
}
- writer.println("Current scorer: " + currentScorer.mPackageName);
+ writer.println("Current scorer: " + currentScorer.packageName);
sendCallback(new Consumer<INetworkScoreCache>() {
@Override
@@ -499,10 +524,12 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
}
}, getScoreCacheLists());
- if (mServiceConnection != null) {
- mServiceConnection.dump(fd, writer, args);
- } else {
- writer.println("ScoringServiceConnection: null");
+ synchronized (mServiceConnectionLock) {
+ if (mServiceConnection != null) {
+ mServiceConnection.dump(fd, writer, args);
+ } else {
+ writer.println("ScoringServiceConnection: null");
+ }
}
writer.flush();
}
@@ -535,10 +562,27 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
}
}
+ private void throwIfCalledOnMainThread() {
+ if (Thread.currentThread() == mContext.getMainLooper().getThread()) {
+ throw new RuntimeException("Cannot invoke on the main thread");
+ }
+ }
+
+ @Nullable
+ private INetworkRecommendationProvider getRecommendationProvider() {
+ synchronized (mServiceConnectionLock) {
+ if (mServiceConnection != null) {
+ return mServiceConnection.getRecommendationProvider();
+ }
+ }
+ return null;
+ }
+
private static class ScoringServiceConnection implements ServiceConnection {
private final ComponentName mComponentName;
- private boolean mBound = false;
- private boolean mConnected = false;
+ private volatile boolean mBound = false;
+ private volatile boolean mConnected = false;
+ private volatile INetworkRecommendationProvider mRecommendationProvider;
ScoringServiceConnection(ComponentName componentName) {
mComponentName = componentName;
@@ -569,12 +613,19 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
} catch (RuntimeException e) {
Log.e(TAG, "Unbind failed.", e);
}
+
+ mRecommendationProvider = null;
+ }
+
+ INetworkRecommendationProvider getRecommendationProvider() {
+ return mRecommendationProvider;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (DBG) Log.d(TAG, "ScoringServiceConnection: " + name.flattenToString());
mConnected = true;
+ mRecommendationProvider = INetworkRecommendationProvider.Stub.asInterface(service);
}
@Override
@@ -583,6 +634,7 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
Log.d(TAG, "ScoringServiceConnection, disconnected: " + name.flattenToString());
}
mConnected = false;
+ mRecommendationProvider = null;
}
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
@@ -590,4 +642,43 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
+ ", connected: " + mConnected);
}
}
+
+ /**
+ * Executes the async requestRecommendation() call with a timeout.
+ */
+ private static final class RequestRecommendationCaller
+ extends TimedRemoteCaller<RecommendationResult> {
+ private final IRemoteCallback mCallback;
+
+ RequestRecommendationCaller(long callTimeoutMillis) {
+ super(callTimeoutMillis);
+ mCallback = new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ final RecommendationResult result =
+ data.getParcelable(EXTRA_RECOMMENDATION_RESULT);
+ final int sequence = data.getInt(EXTRA_SEQUENCE, -1);
+ onRemoteMethodResult(result, sequence);
+ }
+ };
+ }
+
+ /**
+ * Runs the requestRecommendation() call on the given {@link INetworkRecommendationProvider}
+ * instance.
+ *
+ * @param target the {@link INetworkRecommendationProvider} to request a recommendation
+ * from
+ * @param request the {@link RecommendationRequest} from the calling client
+ * @return a {@link RecommendationResult} from the provider
+ * @throws RemoteException if the call failed
+ * @throws TimeoutException if the call took longer than the set timeout
+ */
+ RecommendationResult getRecommendationResult(INetworkRecommendationProvider target,
+ RecommendationRequest request) throws RemoteException, TimeoutException {
+ final int sequence = onBeforeRemoteCall();
+ target.requestRecommendation(request, mCallback, sequence);
+ return getResultTimed(sequence);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 31939749ee66..203f841bb305 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -105,11 +105,11 @@ public class Installer extends SystemService {
}
}
- public void createAppData(String uuid, String packageName, int userId, int flags, int appId,
+ public long createAppData(String uuid, String packageName, int userId, int flags, int appId,
String seInfo, int targetSdkVersion) throws InstallerException {
- if (!checkBeforeRemote()) return;
+ if (!checkBeforeRemote()) return -1;
try {
- mInstalld.createAppData(uuid, packageName, userId, flags, appId, seInfo,
+ return mInstalld.createAppData(uuid, packageName, userId, flags, appId, seInfo,
targetSdkVersion);
} catch (Exception e) {
throw InstallerException.from(e);
@@ -182,16 +182,6 @@ public class Installer extends SystemService {
}
}
- public long getAppDataInode(String uuid, String packageName, int userId, int flags)
- throws InstallerException {
- if (!checkBeforeRemote()) return -1;
- try {
- return mInstalld.getAppDataInode(uuid, packageName, userId, flags);
- } catch (Exception e) {
- throw InstallerException.from(e);
- }
- }
-
public void dexopt(String apkPath, int uid, @Nullable String pkgName, String instructionSet,
int dexoptNeeded, @Nullable String outputPath, int dexFlags,
String compilerFilter, @Nullable String volumeUuid, @Nullable String sharedLibraries)
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 5f469a148c0a..7ce5aa8ae39d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -19974,8 +19974,9 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
Preconditions.checkNotNull(app.seinfo);
+ long ceDataInode = -1;
try {
- mInstaller.createAppData(volumeUuid, packageName, userId, flags,
+ ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, flags,
appId, app.seinfo, app.targetSdkVersion);
} catch (InstallerException e) {
if (app.isSystemApp()) {
@@ -19983,7 +19984,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
+ ", but trying to recover: " + e);
destroyAppDataLeafLIF(pkg, userId, flags);
try {
- mInstaller.createAppData(volumeUuid, packageName, userId, flags,
+ ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, flags,
appId, app.seinfo, app.targetSdkVersion);
logCriticalInfo(Log.DEBUG, "Recovery succeeded!");
} catch (InstallerException e2) {
@@ -19994,21 +19995,13 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
}
}
- if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
- try {
- // CE storage is unlocked right now, so read out the inode and
- // remember for use later when it's locked
- // TODO: mark this structure as dirty so we persist it!
- final long ceDataInode = mInstaller.getAppDataInode(volumeUuid, packageName, userId,
- StorageManager.FLAG_STORAGE_CE);
- synchronized (mPackages) {
- final PackageSetting ps = mSettings.mPackages.get(packageName);
- if (ps != null) {
- ps.setCeDataInode(ceDataInode, userId);
- }
+ if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) {
+ // TODO: mark this structure as dirty so we persist it!
+ synchronized (mPackages) {
+ final PackageSetting ps = mSettings.mPackages.get(packageName);
+ if (ps != null) {
+ ps.setCeDataInode(ceDataInode, userId);
}
- } catch (InstallerException e) {
- Slog.e(TAG, "Failed to find inode for " + packageName + ": " + e);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
index 50911cb32ddf..c653b8eecffa 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
@@ -16,10 +16,13 @@
package com.android.server;
+import static android.net.NetworkRecommendationProvider.EXTRA_RECOMMENDATION_RESULT;
+import static android.net.NetworkRecommendationProvider.EXTRA_SEQUENCE;
import static android.net.NetworkScoreManager.CACHE_FILTER_NONE;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
@@ -28,12 +31,15 @@ import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyListOf;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.Manifest.permission;
@@ -44,37 +50,42 @@ import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.net.INetworkRecommendationProvider;
import android.net.INetworkScoreCache;
import android.net.NetworkKey;
-import android.net.NetworkScoreManager;
import android.net.NetworkScorerAppManager;
import android.net.NetworkScorerAppManager.NetworkScorerAppData;
+import android.net.RecommendationRequest;
+import android.net.RecommendationResult;
import android.net.ScoredNetwork;
import android.net.WifiKey;
+import android.net.wifi.WifiConfiguration;
+import android.os.Bundle;
import android.os.IBinder;
+import android.os.IRemoteCallback;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.provider.Settings;
-import android.provider.Settings.Global;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4;
-import com.android.internal.R;
import com.android.server.devicepolicy.MockUtils;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
-import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.List;
/**
* Tests for {@link NetworkScoreService}.
@@ -85,12 +96,8 @@ public class NetworkScoreServiceTest {
private static final ScoredNetwork SCORED_NETWORK =
new ScoredNetwork(new NetworkKey(new WifiKey("\"ssid\"", "00:00:00:00:00:00")),
null /* rssiCurve*/);
- private static final NetworkScorerAppData PREV_SCORER = new NetworkScorerAppData(
- "prevPackageName", 0, "prevScorerName", null /* configurationActivityClassName */,
- "prevScoringServiceClass");
- private static final NetworkScorerAppData NEW_SCORER = new NetworkScorerAppData(
- "newPackageName", 1, "newScorerName", null /* configurationActivityClassName */,
- "newScoringServiceClass");
+ private static final NetworkScorerAppData NEW_SCORER =
+ new NetworkScorerAppData("newPackageName", 1, "newScoringServiceClass");
@Mock private PackageManager mPackageManager;
@Mock private NetworkScorerAppManager mNetworkScorerAppManager;
@@ -98,10 +105,12 @@ public class NetworkScoreServiceTest {
@Mock private Resources mResources;
@Mock private INetworkScoreCache.Stub mNetworkScoreCache, mNetworkScoreCache2;
@Mock private IBinder mIBinder, mIBinder2;
+ @Mock private INetworkRecommendationProvider mRecommendationProvider;
@Captor private ArgumentCaptor<List<ScoredNetwork>> mScoredNetworkCaptor;
private ContentResolver mContentResolver;
private NetworkScoreService mNetworkScoreService;
+ private RecommendationRequest mRecommendationRequest;
@Before
public void setUp() throws Exception {
@@ -112,57 +121,135 @@ public class NetworkScoreServiceTest {
when(mContext.getContentResolver()).thenReturn(mContentResolver);
when(mContext.getResources()).thenReturn(mResources);
mNetworkScoreService = new NetworkScoreService(mContext, mNetworkScorerAppManager);
+ WifiConfiguration configuration = new WifiConfiguration();
+ mRecommendationRequest = new RecommendationRequest.Builder()
+ .setCurrentRecommendedWifiConfig(configuration).build();
}
@Test
- public void testSystemReady_networkScorerProvisioned() throws Exception {
- Settings.Global.putInt(mContentResolver, Global.NETWORK_SCORING_PROVISIONED, 1);
+ public void testSystemRunning() {
+ when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER);
- mNetworkScoreService.systemReady();
+ mNetworkScoreService.systemRunning();
- verify(mNetworkScorerAppManager, never()).setActiveScorer(anyString());
+ verify(mContext).bindServiceAsUser(MockUtils.checkIntent(new Intent().setComponent(
+ new ComponentName(NEW_SCORER.packageName,
+ NEW_SCORER.recommendationServiceClassName))),
+ any(ServiceConnection.class),
+ eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
+ eq(UserHandle.SYSTEM));
}
@Test
- public void testSystemReady_networkScorerNotProvisioned_defaultScorer() throws Exception {
- Settings.Global.putInt(mContentResolver, Global.NETWORK_SCORING_PROVISIONED, 0);
-
- when(mResources.getString(R.string.config_defaultNetworkScorerPackageName))
- .thenReturn(NEW_SCORER.mPackageName);
+ public void testRequestScores_noPermission() throws Exception {
+ doThrow(new SecurityException()).when(mContext)
+ .enforceCallingOrSelfPermission(eq(permission.BROADCAST_NETWORK_PRIVILEGED),
+ anyString());
+ try {
+ mNetworkScoreService.requestScores(null);
+ fail("BROADCAST_NETWORK_PRIVILEGED not enforced.");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
- mNetworkScoreService.systemReady();
+ @Test
+ public void testRequestScores_providerNotConnected() throws Exception {
+ assertFalse(mNetworkScoreService.requestScores(new NetworkKey[0]));
+ verifyZeroInteractions(mRecommendationProvider);
+ }
- verify(mNetworkScorerAppManager).setActiveScorer(NEW_SCORER.mPackageName);
- assertEquals(1,
- Settings.Global.getInt(mContentResolver, Global.NETWORK_SCORING_PROVISIONED));
+ @Test
+ public void testRequestScores_providerThrowsRemoteException() throws Exception {
+ injectProvider();
+ doThrow(new RemoteException()).when(mRecommendationProvider)
+ .requestScores(any(NetworkKey[].class));
+ assertFalse(mNetworkScoreService.requestScores(new NetworkKey[0]));
}
@Test
- public void testSystemReady_networkScorerNotProvisioned_noDefaultScorer() throws Exception {
- Settings.Global.putInt(mContentResolver, Global.NETWORK_SCORING_PROVISIONED, 0);
+ public void testRequestScores_providerAvailable() throws Exception {
+ injectProvider();
- when(mResources.getString(R.string.config_defaultNetworkScorerPackageName))
- .thenReturn(null);
+ final NetworkKey[] networks = new NetworkKey[0];
+ assertTrue(mNetworkScoreService.requestScores(networks));
+ verify(mRecommendationProvider).requestScores(networks);
+ }
- mNetworkScoreService.systemReady();
+ @Test
+ public void testRequestRecommendation_noPermission() throws Exception {
+ doThrow(new SecurityException()).when(mContext)
+ .enforceCallingOrSelfPermission(eq(permission.BROADCAST_NETWORK_PRIVILEGED),
+ anyString());
+ try {
+ mNetworkScoreService.requestRecommendation(mRecommendationRequest);
+ fail("BROADCAST_NETWORK_PRIVILEGED not enforced.");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
- verify(mNetworkScorerAppManager, never()).setActiveScorer(anyString());
- assertEquals(1,
- Settings.Global.getInt(mContentResolver, Global.NETWORK_SCORING_PROVISIONED));
+ @Test
+ public void testRequestRecommendation_mainThread() throws Exception {
+ when(mContext.getMainLooper()).thenReturn(Looper.myLooper());
+ try {
+ mNetworkScoreService.requestRecommendation(mRecommendationRequest);
+ fail("requestRecommendation run on main thread.");
+ } catch (RuntimeException e) {
+ // expected
+ }
}
@Test
- public void testSystemRunning() {
- when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER);
+ public void testRequestRecommendation_providerNotConnected() throws Exception {
+ when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
+
+ final RecommendationResult result =
+ mNetworkScoreService.requestRecommendation(mRecommendationRequest);
+ assertNotNull(result);
+ assertEquals(mRecommendationRequest.getCurrentSelectedConfig(),
+ result.getWifiConfiguration());
+ }
- mNetworkScoreService.systemRunning();
+ @Test
+ public void testRequestRecommendation_providerThrowsRemoteException() throws Exception {
+ injectProvider();
+ when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
+ doThrow(new RemoteException()).when(mRecommendationProvider)
+ .requestRecommendation(eq(mRecommendationRequest), isA(IRemoteCallback.class),
+ anyInt());
+
+ final RecommendationResult result =
+ mNetworkScoreService.requestRecommendation(mRecommendationRequest);
+ assertNotNull(result);
+ assertEquals(mRecommendationRequest.getCurrentSelectedConfig(),
+ result.getWifiConfiguration());
+ }
- verify(mContext).bindServiceAsUser(MockUtils.checkIntent(new Intent().setComponent(
- new ComponentName(NEW_SCORER.mPackageName, NEW_SCORER.mScoringServiceClassName))),
- any(ServiceConnection.class),
- eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
- eq(UserHandle.SYSTEM));
+ @Test
+ public void testRequestRecommendation_resultReturned() throws Exception {
+ injectProvider();
+ when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
+ final WifiConfiguration wifiConfiguration = new WifiConfiguration();
+ wifiConfiguration.SSID = "testRequestRecommendation_resultReturned";
+ final RecommendationResult providerResult =
+ new RecommendationResult(wifiConfiguration);
+ final Bundle bundle = new Bundle();
+ bundle.putParcelable(EXTRA_RECOMMENDATION_RESULT, providerResult);
+ doAnswer(invocation -> {
+ bundle.putInt(EXTRA_SEQUENCE, invocation.getArgumentAt(2, int.class));
+ invocation.getArgumentAt(1, IRemoteCallback.class).sendResult(bundle);
+ return null;
+ }).when(mRecommendationProvider)
+ .requestRecommendation(eq(mRecommendationRequest), isA(IRemoteCallback.class),
+ anyInt());
+
+ final RecommendationResult result =
+ mNetworkScoreService.requestRecommendation(mRecommendationRequest);
+ assertNotNull(result);
+ assertEquals(providerResult.getWifiConfiguration().SSID,
+ result.getWifiConfiguration().SSID);
}
@Test
@@ -288,45 +375,6 @@ public class NetworkScoreServiceTest {
}
@Test
- public void testSetActiveScorer_failure() throws RemoteException {
- when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(PREV_SCORER);
- when(mNetworkScorerAppManager.setActiveScorer(NEW_SCORER.mPackageName)).thenReturn(false);
- mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache,
- CACHE_FILTER_NONE);
-
- boolean success = mNetworkScoreService.setActiveScorer(NEW_SCORER.mPackageName);
-
- assertFalse(success);
- verify(mNetworkScoreCache).clearScores();
- verify(mContext).bindServiceAsUser(MockUtils.checkIntent(new Intent().setComponent(
- new ComponentName(PREV_SCORER.mPackageName, PREV_SCORER.mScoringServiceClassName))),
- any(ServiceConnection.class),
- eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
- eq(UserHandle.SYSTEM));
- }
-
- @Test
- public void testSetActiveScorer_success() throws RemoteException {
- when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(PREV_SCORER, NEW_SCORER);
- when(mNetworkScorerAppManager.setActiveScorer(NEW_SCORER.mPackageName)).thenReturn(true);
- mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache,
- CACHE_FILTER_NONE);
-
- boolean success = mNetworkScoreService.setActiveScorer(NEW_SCORER.mPackageName);
-
- assertTrue(success);
- verify(mNetworkScoreCache).clearScores();
- verify(mContext).bindServiceAsUser(MockUtils.checkIntent(new Intent().setComponent(
- new ComponentName(NEW_SCORER.mPackageName, NEW_SCORER.mScoringServiceClassName))),
- any(ServiceConnection.class),
- eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
- eq(UserHandle.SYSTEM));
- verify(mContext, times(2)).sendBroadcastAsUser(
- MockUtils.checkIntentAction(NetworkScoreManager.ACTION_SCORER_CHANGED),
- eq(UserHandle.SYSTEM));
- }
-
- @Test
public void testDisableScoring_notActiveScorer_noBroadcastNetworkPermission() {
when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
@@ -338,48 +386,6 @@ public class NetworkScoreServiceTest {
} catch (SecurityException e) {
// expected
}
-
- }
-
- @Test
- public void testDisableScoring_activeScorer() throws RemoteException {
- when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true);
- when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(PREV_SCORER, null);
- when(mNetworkScorerAppManager.setActiveScorer(null)).thenReturn(true);
- mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache,
- CACHE_FILTER_NONE);
-
- mNetworkScoreService.disableScoring();
-
- verify(mNetworkScoreCache).clearScores();
- verify(mContext).sendBroadcastAsUser(
- MockUtils.checkIntent(new Intent(NetworkScoreManager.ACTION_SCORER_CHANGED)
- .setPackage(PREV_SCORER.mPackageName)),
- eq(UserHandle.SYSTEM));
- verify(mContext, never()).bindServiceAsUser(any(Intent.class),
- any(ServiceConnection.class), anyInt(), any(UserHandle.class));
- }
-
- @Test
- public void testDisableScoring_notActiveScorer_hasBroadcastNetworkPermission()
- throws RemoteException {
- when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
- when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
- .thenReturn(PackageManager.PERMISSION_GRANTED);
- when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(PREV_SCORER, null);
- when(mNetworkScorerAppManager.setActiveScorer(null)).thenReturn(true);
- mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache,
- CACHE_FILTER_NONE);
-
- mNetworkScoreService.disableScoring();
-
- verify(mNetworkScoreCache).clearScores();
- verify(mContext).sendBroadcastAsUser(
- MockUtils.checkIntent(new Intent(NetworkScoreManager.ACTION_SCORER_CHANGED)
- .setPackage(PREV_SCORER.mPackageName)),
- eq(UserHandle.SYSTEM));
- verify(mContext, never()).bindServiceAsUser(any(Intent.class),
- any(ServiceConnection.class), anyInt(), any(UserHandle.class));
}
@Test
@@ -434,4 +440,24 @@ public class NetworkScoreServiceTest {
assertFalse(stringWriter.toString().isEmpty());
}
+
+ // "injects" the mock INetworkRecommendationProvider into the NetworkScoreService.
+ private void injectProvider() {
+ final ComponentName componentName = new ComponentName(NEW_SCORER.packageName,
+ NEW_SCORER.recommendationServiceClassName);
+ when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER);
+ when(mContext.bindServiceAsUser(isA(Intent.class), isA(ServiceConnection.class), anyInt(),
+ isA(UserHandle.class))).thenAnswer(new Answer<Boolean>() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ IBinder mockBinder = mock(IBinder.class);
+ when(mockBinder.queryLocalInterface(anyString()))
+ .thenReturn(mRecommendationProvider);
+ invocation.getArgumentAt(1, ServiceConnection.class)
+ .onServiceConnected(componentName, mockBinder);
+ return true;
+ }
+ });
+ mNetworkScoreService.systemRunning();
+ }
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 8225110f11b1..051f1bd67f90 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -626,6 +626,15 @@ public class CarrierConfigManager {
public static final String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
/**
+ * Determines whether High Definition audio property is displayed in the dialer UI.
+ * If {@code false}, remove the HD audio property from the connection so that HD audio related
+ * UI is not displayed. If {@code true}, keep HD audio property as it is configured.
+ * @hide
+ */
+ public static final String KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL =
+ "display_hd_audio_property_bool";
+
+ /**
* Determines whether video conference calls are supported by a carrier. When {@code true},
* video calls can be merged into conference calls, {@code false} otherwiwse.
* <p>
@@ -1155,6 +1164,7 @@ public class CarrierConfigManager {
sDefaults.putInt(KEY_CDMA_3WAYCALL_FLASH_DELAY_INT , 0);
sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true);
sDefaults.putBoolean(KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL, false);
+ sDefaults.putBoolean(KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL, true);
sDefaults.putBoolean(KEY_EDITABLE_ENHANCED_4G_LTE_BOOL, true);
sDefaults.putBoolean(KEY_HIDE_IMS_APN_BOOL, false);
sDefaults.putBoolean(KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL, false);
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 43e624667644..3b7f7216c147 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -701,6 +701,7 @@ public class WifiConfiguration implements Parcelable {
* {@link com.android.server.wifi.WifiStateMachine}, or via a network score in
* {@link com.android.server.wifi.ExternalScoreEvaluator}.
*/
+ @SystemApi
public boolean meteredHint;
/**
diff --git a/wifi/java/android/net/wifi/WifiNetworkScoreCache.java b/wifi/java/android/net/wifi/WifiNetworkScoreCache.java
index c3287481aec9..9dd118bdbc55 100755
--- a/wifi/java/android/net/wifi/WifiNetworkScoreCache.java
+++ b/wifi/java/android/net/wifi/WifiNetworkScoreCache.java
@@ -17,13 +17,19 @@
package android.net.wifi;
import android.Manifest.permission;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.Context;
+import android.os.Handler;
import android.net.INetworkScoreCache;
import android.net.NetworkKey;
import android.net.ScoredNetwork;
import android.util.Log;
+import com.android.internal.util.Preconditions;
+import com.android.internal.annotations.GuardedBy;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.HashMap;
@@ -43,30 +49,55 @@ public class WifiNetworkScoreCache extends INetworkScoreCache.Stub {
// We treat the lowest possible score as though there were no score, effectively allowing the
// scorer to provide an RSSI threshold below which a network should not be used.
public static final int INVALID_NETWORK_SCORE = Byte.MIN_VALUE;
+
+ // See {@link #CacheListener}.
+ @Nullable
+ @GuardedBy("mCacheLock")
+ private CacheListener mListener;
+
private final Context mContext;
+ private final Object mCacheLock = new Object();
// The key is of the form "<ssid>"<bssid>
// TODO: What about SSIDs that can't be encoded as UTF-8?
private final Map<String, ScoredNetwork> mNetworkCache;
+
public WifiNetworkScoreCache(Context context) {
- mContext = context;
+ this(context, null /* listener */);
+ }
+
+ /**
+ * Instantiates a WifiNetworkScoreCache.
+ *
+ * @param context Application context
+ * @param listener CacheListener for cache updates
+ */
+ public WifiNetworkScoreCache(Context context, @Nullable CacheListener listener) {
+ mContext = context.getApplicationContext();
+ mListener = listener;
mNetworkCache = new HashMap<String, ScoredNetwork>();
}
@Override public final void updateScores(List<ScoredNetwork> networks) {
- if (networks == null) {
+ if (networks == null || networks.isEmpty()) {
return;
- }
- Log.e(TAG, "updateScores list size=" + networks.size());
+ }
+ Log.d(TAG, "updateScores list size=" + networks.size());
- synchronized(mNetworkCache) {
- for (ScoredNetwork network : networks) {
- String networkKey = buildNetworkKey(network);
- if (networkKey == null) continue;
- mNetworkCache.put(networkKey, network);
- }
- }
+ synchronized(mNetworkCache) {
+ for (ScoredNetwork network : networks) {
+ String networkKey = buildNetworkKey(network);
+ if (networkKey == null) continue;
+ mNetworkCache.put(networkKey, network);
+ }
+ }
+
+ synchronized (mCacheLock) {
+ if (mListener != null) {
+ mListener.post(networks);
+ }
+ }
}
@Override public final void clearScores() {
@@ -193,4 +224,53 @@ public class WifiNetworkScoreCache extends INetworkScoreCache.Stub {
}
}
+ /** Registers a CacheListener instance, replacing the previous listener if it existed. */
+ public void registerListener(CacheListener listener) {
+ synchronized (mCacheLock) {
+ mListener = listener;
+ }
+ }
+
+ /** Removes the registered CacheListener. */
+ public void unregisterListener() {
+ synchronized (mCacheLock) {
+ mListener = null;
+ }
+ }
+
+ /** Listener for updates to the cache inside WifiNetworkScoreCache. */
+ public abstract static class CacheListener {
+
+ private Handler mHandler;
+
+ /**
+ * Constructor for CacheListener.
+ *
+ * @param handler the Handler on which to invoke the {@link #networkCacheUpdated} method.
+ * This cannot be null.
+ */
+ public CacheListener(@NonNull Handler handler) {
+ Preconditions.checkNotNull(handler);
+ mHandler = handler;
+ }
+
+ /** Invokes the {@link #networkCacheUpdated(List<ScoredNetwork>)} method on the handler. */
+ void post(List<ScoredNetwork> updatedNetworks) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ networkCacheUpdated(updatedNetworks);
+ }
+ });
+ }
+
+ /**
+ * Invoked whenever the cache is updated.
+ *
+ * <p>Clearing the cache does not invoke this method.
+ *
+ * @param updatedNetworks the networks that were updated
+ */
+ public abstract void networkCacheUpdated(List<ScoredNetwork> updatedNetworks);
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
index f8549b9cb715..12d499570ace 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
@@ -17,6 +17,8 @@
package android.net.wifi;
import static org.junit.Assert.*;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -24,6 +26,9 @@ import android.net.NetworkKey;
import android.net.RssiCurve;
import android.net.ScoredNetwork;
import android.net.WifiKey;
+import android.net.wifi.WifiNetworkScoreCache.CacheListener;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -33,124 +38,167 @@ import org.junit.Rule;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+
/** Unit tests for {@link WifiNetworkScoreCache}. */
@RunWith(AndroidJUnit4.class)
@SmallTest
public class WifiNetworkScoreCacheTest {
- @Mock public Context mockContext; // isn't used, can be null
- @Mock private RssiCurve mockRssiCurve;
-
- public static final String SSID = "ssid";
- public static final String FORMATTED_SSID = "\"" + SSID + "\"";
- public static final String BSSID = "AA:AA:AA:AA:AA:AA";
-
- public static final WifiKey VALID_KEY = new WifiKey(FORMATTED_SSID, BSSID);
-
- public static final ScanResult VALID_SCAN_RESULT = buildScanResult(SSID, BSSID);
-
- private ScoredNetwork mValidScoredNetwork;
- private WifiNetworkScoreCache mScoreCache =
- new WifiNetworkScoreCache(mockContext);
-
- private static ScanResult buildScanResult(String ssid, String bssid) {
- return new ScanResult(
- WifiSsid.createFromAsciiEncoded(ssid),
- bssid,
- "" /* caps */,
- 0 /* level */,
- 0 /* frequency */,
- 0 /* tsf */,
- 0 /* distCm */,
- 0 /* distSdCm*/);
- }
-
- private static ScoredNetwork buildScoredNetwork(WifiKey key, RssiCurve curve) {
- return new ScoredNetwork(new NetworkKey(key), curve);
- }
-
- // Called from setup
- private void initializeCacheWithValidScoredNetwork() {
- mScoreCache.updateScores(ImmutableList.of(mValidScoredNetwork));
- }
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mValidScoredNetwork = buildScoredNetwork(VALID_KEY, mockRssiCurve);
- mScoreCache = new WifiNetworkScoreCache(mockContext);
- initializeCacheWithValidScoredNetwork();
- }
-
-
- @Test
- public void isScoredNetworkShouldReturnTrueAfterUpdateScoresIsCalled() {
- assertTrue(mScoreCache.isScoredNetwork(VALID_SCAN_RESULT));
- }
-
- @Test
- public void isScoredNetworkShouldReturnFalseAfterClearScoresIsCalled() {
- mScoreCache.clearScores();
- assertFalse(mScoreCache.isScoredNetwork(VALID_SCAN_RESULT));
- }
-
- @Test
- public void updateScoresShouldAddNewNetwork() {
- WifiKey key2 = new WifiKey("\"ssid2\"", BSSID);
- ScoredNetwork network2 = buildScoredNetwork(key2, mockRssiCurve);
- ScanResult result2 = buildScanResult("ssid2", BSSID);
-
- mScoreCache.updateScores(ImmutableList.of(network2));
-
- assertTrue(mScoreCache.isScoredNetwork(VALID_SCAN_RESULT));
- assertTrue(mScoreCache.isScoredNetwork(result2));
- }
-
- @Test
- public void hasScoreCurveShouldReturnTrue() {
- assertTrue(mScoreCache.hasScoreCurve(VALID_SCAN_RESULT));
- }
-
- @Test
- public void hasScoreCurveShouldReturnFalseWhenNoCachedNetwork() {
- ScanResult unscored = buildScanResult("fake", BSSID);
- assertFalse(mScoreCache.hasScoreCurve(unscored));
- }
-
- @Test
- public void hasScoreCurveShouldReturnFalseWhenScoredNetworkHasNoCurve() {
- ScoredNetwork noCurve = buildScoredNetwork(VALID_KEY, null /* rssiCurve */);
- mScoreCache.updateScores(ImmutableList.of(noCurve));
-
- assertFalse(mScoreCache.hasScoreCurve(VALID_SCAN_RESULT));
- }
-
- @Test
- public void getNetworkScoreShouldReturnScore() {
- final byte score = 50;
- final int rssi = -70;
- ScanResult result = new ScanResult(VALID_SCAN_RESULT);
- result.level = rssi;
-
- when(mockRssiCurve.lookupScore(rssi)).thenReturn(score);
-
- assertEquals(score, mScoreCache.getNetworkScore(result));
- }
-
- @Test
- public void getMeteredHintShouldReturnFalse() {
- assertFalse(mScoreCache.getMeteredHint(VALID_SCAN_RESULT));
- }
-
- @Test
- public void getMeteredHintShouldReturnTrue() {
- ScoredNetwork network =
- new ScoredNetwork(new NetworkKey(VALID_KEY), mockRssiCurve, true /* metered Hint */);
- mScoreCache.updateScores(ImmutableList.of(network));
-
- assertTrue(mScoreCache.getMeteredHint(VALID_SCAN_RESULT));
- }
+ public static final String SSID = "ssid";
+ public static final String FORMATTED_SSID = "\"" + SSID + "\"";
+ public static final String BSSID = "AA:AA:AA:AA:AA:AA";
+
+ public static final WifiKey VALID_KEY = new WifiKey(FORMATTED_SSID, BSSID);
+
+ public static final ScanResult VALID_SCAN_RESULT = buildScanResult(SSID, BSSID);
+
+ @Mock private Context mockApplicationContext;
+ @Mock private Context mockContext; // isn't used, can be null
+ @Mock private RssiCurve mockRssiCurve;
+
+
+ private CacheListener mCacheListener;
+ private CountDownLatch mLatch;
+ private Handler mHandler;
+ private List<ScoredNetwork> mUpdatedNetworksCaptor;
+ private ScoredNetwork mValidScoredNetwork;
+ private WifiNetworkScoreCache mScoreCache =
+ new WifiNetworkScoreCache(mockContext);
+
+ private static ScanResult buildScanResult(String ssid, String bssid) {
+ return new ScanResult(
+ WifiSsid.createFromAsciiEncoded(ssid),
+ bssid,
+ "" /* caps */,
+ 0 /* level */,
+ 0 /* frequency */,
+ 0 /* tsf */,
+ 0 /* distCm */,
+ 0 /* distSdCm*/);
+ }
+
+ private static ScoredNetwork buildScoredNetwork(WifiKey key, RssiCurve curve) {
+ return new ScoredNetwork(new NetworkKey(key), curve);
+ }
+
+ // Called from setup
+ private void initializeCacheWithValidScoredNetwork() {
+ mScoreCache.updateScores(ImmutableList.of(mValidScoredNetwork));
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mockContext.getApplicationContext()).thenReturn(mockApplicationContext);
+
+ mValidScoredNetwork = buildScoredNetwork(VALID_KEY, mockRssiCurve);
+ mScoreCache = new WifiNetworkScoreCache(mockContext);
+ initializeCacheWithValidScoredNetwork();
+
+ HandlerThread thread = new HandlerThread("WifiNetworkScoreCacheTest Handler Thread");
+ thread.start();
+ mHandler = new Handler(thread.getLooper());
+ mLatch = new CountDownLatch(1);
+ mCacheListener = new CacheListener(mHandler) {
+ @Override
+ public void networkCacheUpdated(List<ScoredNetwork> updatedNetworks) {
+ mUpdatedNetworksCaptor = updatedNetworks;
+ mLatch.countDown();
+ }
+ };
+ }
+
+
+ @Test
+ public void isScoredNetworkShouldReturnTrueAfterUpdateScoresIsCalled() {
+ assertTrue(mScoreCache.isScoredNetwork(VALID_SCAN_RESULT));
+ }
+
+ @Test
+ public void isScoredNetworkShouldReturnFalseAfterClearScoresIsCalled() {
+ mScoreCache.clearScores();
+ assertFalse(mScoreCache.isScoredNetwork(VALID_SCAN_RESULT));
+ }
+
+ @Test
+ public void updateScoresShouldAddNewNetwork() {
+ WifiKey key2 = new WifiKey("\"ssid2\"", BSSID);
+ ScoredNetwork network2 = buildScoredNetwork(key2, mockRssiCurve);
+ ScanResult result2 = buildScanResult("ssid2", BSSID);
+
+ mScoreCache.updateScores(ImmutableList.of(network2));
+
+ assertTrue(mScoreCache.isScoredNetwork(VALID_SCAN_RESULT));
+ assertTrue(mScoreCache.isScoredNetwork(result2));
+ }
+
+ @Test
+ public void hasScoreCurveShouldReturnTrue() {
+ assertTrue(mScoreCache.hasScoreCurve(VALID_SCAN_RESULT));
+ }
+
+ @Test
+ public void hasScoreCurveShouldReturnFalseWhenNoCachedNetwork() {
+ ScanResult unscored = buildScanResult("fake", BSSID);
+ assertFalse(mScoreCache.hasScoreCurve(unscored));
+ }
+
+ @Test
+ public void hasScoreCurveShouldReturnFalseWhenScoredNetworkHasNoCurve() {
+ ScoredNetwork noCurve = buildScoredNetwork(VALID_KEY, null /* rssiCurve */);
+ mScoreCache.updateScores(ImmutableList.of(noCurve));
+
+ assertFalse(mScoreCache.hasScoreCurve(VALID_SCAN_RESULT));
+ }
+
+ @Test
+ public void getNetworkScoreShouldReturnScore() {
+ final byte score = 50;
+ final int rssi = -70;
+ ScanResult result = new ScanResult(VALID_SCAN_RESULT);
+ result.level = rssi;
+
+ when(mockRssiCurve.lookupScore(rssi)).thenReturn(score);
+
+ assertEquals(score, mScoreCache.getNetworkScore(result));
+ }
+
+ @Test
+ public void getMeteredHintShouldReturnFalse() {
+ assertFalse(mScoreCache.getMeteredHint(VALID_SCAN_RESULT));
+ }
+
+ @Test
+ public void getMeteredHintShouldReturnTrue() {
+ ScoredNetwork network =
+ new ScoredNetwork(
+ new NetworkKey(VALID_KEY), mockRssiCurve, true /* metered Hint */);
+ mScoreCache.updateScores(ImmutableList.of(network));
+
+ assertTrue(mScoreCache.getMeteredHint(VALID_SCAN_RESULT));
+ }
+
+ @Test
+ public void updateScoresShouldInvokeCacheListener_networkCacheUpdated() {
+ mScoreCache = new WifiNetworkScoreCache(mockContext, mCacheListener);
+ initializeCacheWithValidScoredNetwork();
+
+ try {
+ mLatch.await(1, TimeUnit.SECONDS); // wait for listener to be executed
+ } catch (InterruptedException e) {
+ fail("Interrupted Exception while waiting for listener to be invoked.");
+ }
+ assertEquals("One network should be updated", 1, mUpdatedNetworksCaptor.size());
+ assertEquals(mValidScoredNetwork, mUpdatedNetworksCaptor.get(0));
+ }
}