Merge "resolve merge conflicts of 0c62aa5 to master"
diff --git a/api/current.txt b/api/current.txt
index 89573ac..395d7aa 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -12733,7 +12733,6 @@
field public static final int RGBA_8888 = 1; // 0x1
field public static final int RGBA_F16 = 22; // 0x16
field public static final int RGBX_8888 = 2; // 0x2
- field public static final int RGBX_F16 = 23; // 0x17
field public static final deprecated int RGB_332 = 11; // 0xb
field public static final int RGB_565 = 4; // 0x4
field public static final int RGB_888 = 3; // 0x3
diff --git a/api/system-current.txt b/api/system-current.txt
index 870070b..8bedf98 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -13226,7 +13226,6 @@
field public static final int RGBA_8888 = 1; // 0x1
field public static final int RGBA_F16 = 22; // 0x16
field public static final int RGBX_8888 = 2; // 0x2
- field public static final int RGBX_F16 = 23; // 0x17
field public static final deprecated int RGB_332 = 11; // 0xb
field public static final int RGB_565 = 4; // 0x4
field public static final int RGB_888 = 3; // 0x3
diff --git a/api/test-current.txt b/api/test-current.txt
index 10554ff..e43d1b5 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -12764,7 +12764,6 @@
field public static final int RGBA_8888 = 1; // 0x1
field public static final int RGBA_F16 = 22; // 0x16
field public static final int RGBX_8888 = 2; // 0x2
- field public static final int RGBX_F16 = 23; // 0x17
field public static final deprecated int RGB_332 = 11; // 0xb
field public static final int RGB_565 = 4; // 0x4
field public static final int RGB_888 = 3; // 0x3
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 74aded6..32bf66a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3789,6 +3789,8 @@
ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE;
}
+ final boolean hasVisibleToEphemeral =
+ sa.hasValue(R.styleable.AndroidManifestActivity_visibleToEphemeral);
final boolean isEphemeral = ((flags & PARSE_IS_EPHEMERAL) != 0);
final boolean visibleToEphemeral = isEphemeral
|| sa.getBoolean(R.styleable.AndroidManifestActivity_visibleToEphemeral, false);
@@ -3827,7 +3829,7 @@
return null;
}
intent.setEphemeral(isEphemeral);
- intent.setVisibleToEphemeral(visibleToEphemeral);
+ intent.setVisibleToEphemeral(visibleToEphemeral || isWebBrowsableIntent(intent));
if (intent.countActions() == 0) {
Slog.w(TAG, "No actions in intent filter at "
+ mArchiveSourcePath + " "
@@ -3835,6 +3837,10 @@
} else {
a.intents.add(intent);
}
+ // adjust activity flags when we implicitly expose it via a browsable filter
+ if (!hasVisibleToEphemeral && intent.isVisibleToEphemeral()) {
+ a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+ }
} else if (!receiver && parser.getName().equals("preferred")) {
ActivityIntentInfo intent = new ActivityIntentInfo(a);
if (!parseIntent(res, parser, false /*allowGlobs*/, false /*allowAutoVerify*/,
@@ -3842,7 +3848,7 @@
return null;
}
intent.setEphemeral(isEphemeral);
- intent.setVisibleToEphemeral(visibleToEphemeral);
+ intent.setVisibleToEphemeral(visibleToEphemeral || isWebBrowsableIntent(intent));
if (intent.countActions() == 0) {
Slog.w(TAG, "No actions in preferred at "
+ mArchiveSourcePath + " "
@@ -3853,6 +3859,10 @@
}
owner.preferredActivityFilters.add(intent);
}
+ // adjust activity flags when we implicitly expose it via a browsable filter
+ if (!hasVisibleToEphemeral && intent.isVisibleToEphemeral()) {
+ a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+ }
} else if (parser.getName().equals("meta-data")) {
if ((a.metaData = parseMetaData(res, parser, a.metaData,
outError)) == null) {
@@ -4093,6 +4103,7 @@
}
}
+ // TODO add visibleToInstantApp attribute to activity alias
final boolean isEphemeral = ((flags & PARSE_IS_EPHEMERAL) != 0);
final boolean visibleToEphemeral = isEphemeral
|| ((a.info.flags & ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL) != 0);
@@ -4124,9 +4135,14 @@
+ parser.getPositionDescription());
} else {
intent.setEphemeral(isEphemeral);
- intent.setVisibleToEphemeral(visibleToEphemeral);
+ intent.setVisibleToEphemeral(visibleToEphemeral
+ || isWebBrowsableIntent(intent));
a.intents.add(intent);
}
+ // adjust activity flags when we implicitly expose it via a browsable filter
+ if (intent.isVisibleToEphemeral()) {
+ a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+ }
} else if (parser.getName().equals("meta-data")) {
if ((a.metaData=parseMetaData(res, parser, a.metaData,
outError)) == null) {
@@ -4262,6 +4278,8 @@
ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE;
}
+ final boolean hasVisibleToEphemeral =
+ sa.hasValue(R.styleable.AndroidManifestProvider_visibleToEphemeral);
final boolean isEphemeral = ((flags & PARSE_IS_EPHEMERAL) != 0);
final boolean visibleToEphemeral = isEphemeral
|| sa.getBoolean(R.styleable.AndroidManifestProvider_visibleToEphemeral, false);
@@ -4291,7 +4309,8 @@
}
p.info.authority = cpname.intern();
- if (!parseProviderTags(res, parser, isEphemeral, visibleToEphemeral, p, outError)) {
+ if (!parseProviderTags(
+ res, parser, isEphemeral, hasVisibleToEphemeral, visibleToEphemeral, p, outError)) {
return null;
}
@@ -4299,8 +4318,9 @@
}
private boolean parseProviderTags(Resources res, XmlResourceParser parser,
- boolean isEphemeral, boolean visibleToEphemeral, Provider outInfo, String[] outError)
- throws XmlPullParserException, IOException {
+ boolean isEphemeral, boolean hasVisibleToEphemeral, boolean visibleToEphemeral,
+ Provider outInfo, String[] outError)
+ throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -4317,8 +4337,12 @@
return false;
}
intent.setEphemeral(isEphemeral);
- intent.setVisibleToEphemeral(visibleToEphemeral);
+ intent.setVisibleToEphemeral(visibleToEphemeral || isWebBrowsableIntent(intent));
outInfo.intents.add(intent);
+ // adjust provider flags when we implicitly expose it via a browsable filter
+ if (!hasVisibleToEphemeral && intent.isVisibleToEphemeral()) {
+ outInfo.info.flags |= ProviderInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+ }
} else if (parser.getName().equals("meta-data")) {
if ((outInfo.metaData=parseMetaData(res, parser,
@@ -4565,6 +4589,8 @@
ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE;
}
+ final boolean hasVisibleToEphemeral =
+ sa.hasValue(R.styleable.AndroidManifestService_visibleToEphemeral);
final boolean isEphemeral = ((flags & PARSE_IS_EPHEMERAL) != 0);
final boolean visibleToEphemeral = isEphemeral
|| sa.getBoolean(R.styleable.AndroidManifestService_visibleToEphemeral, false);
@@ -4600,8 +4626,11 @@
return null;
}
intent.setEphemeral(isEphemeral);
- intent.setVisibleToEphemeral(visibleToEphemeral);
-
+ intent.setVisibleToEphemeral(visibleToEphemeral || isWebBrowsableIntent(intent));
+ // adjust activity flags when we implicitly expose it via a browsable filter
+ if (!hasVisibleToEphemeral && intent.isVisibleToEphemeral()) {
+ s.info.flags |= ServiceInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+ }
s.intents.add(intent);
} else if (parser.getName().equals("meta-data")) {
if ((s.metaData=parseMetaData(res, parser, s.metaData,
@@ -4629,6 +4658,12 @@
return s;
}
+ private boolean isWebBrowsableIntent(IntentInfo intent) {
+ return intent.hasAction(Intent.ACTION_VIEW)
+ && intent.hasCategory(Intent.CATEGORY_BROWSABLE)
+ && (intent.hasDataScheme("http") || intent.hasDataScheme("https"));
+ }
+
private boolean parseAllMetaData(Resources res, XmlResourceParser parser, String tag,
Component<?> outInfo, String[] outError) throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index 865b8dd..4e606ef 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -183,7 +183,7 @@
if (app == null) {
return null;
}
- return app.mPackageName;
+ return app.packageName;
}
/**
@@ -272,19 +272,11 @@
* @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 @@
/**
* 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 ebb31c9..4282ca7 100644
--- a/core/java/android/net/NetworkScorerAppManager.java
+++ b/core/java/android/net/NetworkScorerAppManager.java
@@ -19,160 +19,176 @@
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 null;
+ }
+
+ final List<String> potentialPkgs = getPotentialRecommendationProviderPackages();
+ if (potentialPkgs.isEmpty()) {
+ if (DEBUG) {
+ Log.d(TAG, "No Network Recommendation Providers specified.");
+ }
+ 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.");
+ }
+ }
+ }
+
+ // 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();
}
- 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;
- }
- 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;
- }
- 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;
- }
-
- // 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;
- }
- }
-
- // 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;
- }
-
- // 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));
+ if (VERBOSE) {
+ Log.d(TAG, "Configured packages: " + TextUtils.join(", ", packageArray));
}
- return scorers;
+ 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.");
+ }
+ }
+ }
+
+ return packages;
+ }
+
+ 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;
+ }
+
+ if (VERBOSE) {
+ Log.v(TAG, packageName + " does not have a service for " + serviceIntent);
+ }
+ return null;
}
/**
@@ -182,10 +198,15 @@
* 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 @@
*
* @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 @@
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 @@
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 05ca1aa..a96f90d 100644
--- a/core/java/android/net/RecommendationRequest.java
+++ b/core/java/android/net/RecommendationRequest.java
@@ -105,7 +105,16 @@
}
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 @@
@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/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index b656bb0..80711aa 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -1190,8 +1190,7 @@
}
}
-static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle,
- jlong bm1Handle) {
+static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle, jlong bm1Handle) {
SkBitmap bm0;
SkBitmap bm1;
@@ -1207,11 +1206,11 @@
bitmap0->bitmap().getSkBitmap(&bm0);
bitmap1->bitmap().getSkBitmap(&bm1);
- if (bm0.width() != bm1.width() ||
- bm0.height() != bm1.height() ||
- bm0.colorType() != bm1.colorType() ||
- bm0.alphaType() != bm1.alphaType() ||
- bm0.colorSpace() != bm1.colorSpace()) {
+ if (bm0.width() != bm1.width()
+ || bm0.height() != bm1.height()
+ || bm0.colorType() != bm1.colorType()
+ || bm0.alphaType() != bm1.alphaType()
+ || !SkColorSpace::Equals(bm0.colorSpace(), bm1.colorSpace())) {
return JNI_FALSE;
}
diff --git a/core/jni/android_view_GraphicBuffer.cpp b/core/jni/android_view_GraphicBuffer.cpp
index bb69e27..f18837f 100644
--- a/core/jni/android_view_GraphicBuffer.cpp
+++ b/core/jni/android_view_GraphicBuffer.cpp
@@ -144,8 +144,6 @@
return kN32_SkColorType;
case PIXEL_FORMAT_RGBA_FP16:
return kRGBA_F16_SkColorType;
- case PIXEL_FORMAT_RGBX_FP16:
- return kRGBA_F16_SkColorType;
case PIXEL_FORMAT_RGB_565:
return kRGB_565_SkColorType;
default:
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index a5b7671..96e6f81 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -169,7 +169,6 @@
case HAL_PIXEL_FORMAT_RGBA_8888:
case HAL_PIXEL_FORMAT_RGBX_8888:
case HAL_PIXEL_FORMAT_RGBA_FP16:
- case HAL_PIXEL_FORMAT_RGBX_FP16:
case HAL_PIXEL_FORMAT_RGB_888:
case HAL_PIXEL_FORMAT_RGB_565:
case HAL_PIXEL_FORMAT_Y8:
@@ -285,7 +284,6 @@
switch (format) {
case PIXEL_FORMAT_RGBX_8888: return kN32_SkColorType;
case PIXEL_FORMAT_RGBA_8888: return kN32_SkColorType;
- case PIXEL_FORMAT_RGBX_FP16: return kRGBA_F16_SkColorType;
case PIXEL_FORMAT_RGBA_FP16: return kRGBA_F16_SkColorType;
case PIXEL_FORMAT_RGB_565: return kRGB_565_SkColorType;
default: return kUnknown_SkColorType;
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 62c3d04..ed071cd 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -164,11 +164,6 @@
alphaType = kPremul_SkAlphaType;
break;
}
- case PIXEL_FORMAT_RGBX_FP16: {
- colorType = kRGBA_F16_SkColorType;
- alphaType = kOpaque_SkAlphaType;
- break;
- }
case PIXEL_FORMAT_RGBA_FP16: {
colorType = kRGBA_F16_SkColorType;
alphaType = kPremul_SkAlphaType;
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index d3c0202..613e040 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -87,10 +87,6 @@
colorType = kRGBA_F16_SkColorType;
alphaType = kPremul_SkAlphaType;
break;
- case WINDOW_FORMAT_RGBX_FP16:
- colorType = kRGBA_F16_SkColorType;
- alphaType = kOpaque_SkAlphaType;
- break;
case WINDOW_FORMAT_RGB_565:
colorType = kRGB_565_SkColorType;
alphaType = kOpaque_SkAlphaType;
diff --git a/core/jni/include/android_runtime/android_view_Surface.h b/core/jni/include/android_runtime/android_view_Surface.h
index b1e552a..1bc8521 100644
--- a/core/jni/include/android_runtime/android_view_Surface.h
+++ b/core/jni/include/android_runtime/android_view_Surface.h
@@ -42,6 +42,7 @@
NV16 = 0x10,
NV21 = 0x11,
YUY2 = 0x14,
+ RGBA_FP16 = 0x16,
RAW_SENSOR = 0x20,
PRIVATE = 0x22,
YUV_420_888 = 0x23,
diff --git a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
index 02c2517..5bfff26 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 @@
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());
+ public void testGetPotentialRecommendationProviderPackages_emptyConfig() throws Exception {
+ setNetworkRecommendationPackageNames(/*no configured packages*/);
+ assertTrue(mNetworkScorerAppManager.getPotentialRecommendationProviderPackages().isEmpty());
}
- 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));
- }
+ public void testGetPotentialRecommendationProviderPackages_permissionNotGranted()
+ throws Exception {
+ setNetworkRecommendationPackageNames("package1");
+ mockScoreNetworksDenied("package1");
- 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);
- }
+ assertTrue(mNetworkScorerAppManager.getPotentialRecommendationProviderPackages().isEmpty());
+ }
+
+ 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");
+
+ assertNull(mNetworkScorerAppManager.getNetworkRecommendationProviderData());
+ }
+
+ public void testGetNetworkRecommendationProviderData_scoreNetworksNotGranted()
+ throws Exception {
+ setNetworkRecommendationPackageNames("package1");
+ mockScoreNetworksDenied("package1");
+ mockRecommendationServiceAvailable("package1", 924 /* packageUid */);
+
+ 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);
+ }
- Mockito.when(mMockPm.queryBroadcastReceiversAsUser(
+ 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_SCORE_NETWORKS.equals(intent.getAction());
+ return NetworkScoreManager.ACTION_RECOMMEND_NETWORKS
+ .equals(intent.getAction())
+ && packageName.equals(intent.getPackage());
}
- }), 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;
- }
-
- ResolveInfo configActivityInfo = null;
- if (hasConfigActivity) {
- configActivityInfo = new ResolveInfo();
- configActivityInfo.activityInfo = new ActivityInfo();
- configActivityInfo.activityInfo.name = ".ConfigActivity";
- }
-
- ResolveInfo serviceInfo = null;
- if (hasServiceInfo) {
- serviceInfo = new ResolveInfo();
- serviceInfo.serviceInfo = new ServiceInfo();
- serviceInfo.serviceInfo.name = ".ScoringService";
- }
-
- return new ResolveInfoHolder(resolveInfo, configActivityInfo, serviceInfo);
- }
-
- private static class ResolveInfoHolder {
- final ResolveInfo scorerResolveInfo;
- final ResolveInfo configActivityResolveInfo;
- final ResolveInfo serviceResolveInfo;
-
- public ResolveInfoHolder(ResolveInfo scorerResolveInfo,
- ResolveInfo configActivityResolveInfo, ResolveInfo serviceResolveInfo) {
- this.scorerResolveInfo = scorerResolveInfo;
- this.configActivityResolveInfo = configActivityResolveInfo;
- this.serviceResolveInfo = serviceResolveInfo;
- }
+ }), 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 0000000..31560b0
--- /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/core/tests/coretests/src/android/view/ViewCaptureTest.java b/core/tests/coretests/src/android/view/ViewCaptureTest.java
index 15cfe23..1b4d4a2 100644
--- a/core/tests/coretests/src/android/view/ViewCaptureTest.java
+++ b/core/tests/coretests/src/android/view/ViewCaptureTest.java
@@ -16,6 +16,8 @@
package android.view;
+import static org.junit.Assert.assertTrue;
+
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -32,8 +34,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import static org.junit.Assert.assertTrue;
-
@RunWith(AndroidJUnit4.class)
public class ViewCaptureTest {
@@ -70,6 +70,7 @@
private void testCreateSnapshot(boolean skipChildren, int goldenResId) {
Bitmap result = mViewToCapture.createSnapshot(Bitmap.Config.ARGB_8888, 0, skipChildren);
+ result.setHasAlpha(false); // resource will have no alpha, since content is opaque
Bitmap golden = BitmapFactory.decodeResource(mActivity.getResources(), goldenResId);
assertTrue(golden.sameAs(result));
}
diff --git a/graphics/java/android/graphics/PixelFormat.java b/graphics/java/android/graphics/PixelFormat.java
index 0fa52f8..c3a9443 100644
--- a/graphics/java/android/graphics/PixelFormat.java
+++ b/graphics/java/android/graphics/PixelFormat.java
@@ -29,7 +29,7 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef({RGBA_8888, RGBX_8888, RGBA_F16, RGBX_F16, RGB_888, RGB_565})
+ @IntDef({RGBA_8888, RGBX_8888, RGBA_F16, RGB_888, RGB_565})
public @interface Format { };
// NOTE: these constants must match the values from graphics/common/x.x/types.hal
@@ -88,7 +88,6 @@
public static final int YCbCr_422_I = 0x14;
public static final int RGBA_F16 = 0x16;
- public static final int RGBX_F16 = 0x17;
/**
* @deprecated use {@link android.graphics.ImageFormat#JPEG
@@ -134,7 +133,6 @@
info.bytesPerPixel = 1;
break;
case RGBA_F16:
- case RGBX_F16:
info.bitsPerPixel = 64;
info.bytesPerPixel = 8;
break;
@@ -178,7 +176,6 @@
case RGB_888:
case RGB_565:
case RGBA_F16:
- case RGBX_F16:
return true;
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index d232e00..5c2787b 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -287,22 +287,6 @@
</intent-filter>
</activity>
- <activity android:name=".recents.grid.RecentsGridActivity"
- android:label="@string/accessibility_desc_recent_apps"
- android:exported="false"
- android:launchMode="singleInstance"
- android:excludeFromRecents="true"
- android:stateNotNeeded="true"
- android:resumeWhilePausing="true"
- android:screenOrientation="behind"
- android:resizeableActivity="true"
- android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
- android:theme="@style/RecentsTheme.Grid">
- <intent-filter>
- <action android:name="com.android.systemui.recents.TOGGLE_RECENTS" />
- </intent-filter>
- </activity>
-
<activity android:name=".recents.tv.RecentsTvActivity"
android:label="@string/accessibility_desc_recent_apps"
android:exported="false"
diff --git a/packages/SystemUI/res/drawable/header_dot.xml b/packages/SystemUI/res/drawable/header_dot.xml
index 568a9c2..dbc6b37 100644
--- a/packages/SystemUI/res/drawable/header_dot.xml
+++ b/packages/SystemUI/res/drawable/header_dot.xml
@@ -2,26 +2,27 @@
<!--
** Copyright 2015, 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
+** 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
+** 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
+** 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.
-->
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="oval">
+ android:shape="oval"
+ android:tint="?android:attr/colorControlNormal">
- <solid
+ <solid
android:color="#FFFFFF"/>
- <size
+ <size
android:width="3dp"
android:height="3dp"/>
</shape>
diff --git a/packages/SystemUI/res/layout-sw600dp/recents_grid.xml b/packages/SystemUI/res/layout-sw600dp/recents_grid.xml
deleted file mode 100644
index 1a054a5..0000000
--- a/packages/SystemUI/res/layout-sw600dp/recents_grid.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:id="@+id/recents_view"
- android:gravity="center"
- android:background="#99000000">
- <include layout="@layout/recents_stack_action_button" />
-</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index fe5606e..5f9ade5 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -720,18 +720,4 @@
<!-- The size of a PIP menu action icon. -->
<dimen name="pip_menu_action_icon_size">32dp</dimen>
- <!-- Values specific to grid-based recents. -->
- <!-- Margins around recent tasks. -->
- <dimen name="recents_grid_margin_left">15dp</dimen>
- <dimen name="recents_grid_margin_top">70dp</dimen>
- <dimen name="recents_grid_margin_right">15dp</dimen>
- <dimen name="recents_grid_margin_bottom">90dp</dimen>
- <!-- Margins around the "Clear all" button. -->
- <dimen name="recents_grid_clear_all_margin_left">0dp</dimen>
- <dimen name="recents_grid_clear_all_margin_top">30dp</dimen>
- <dimen name="recents_grid_clear_all_margin_right">15dp</dimen>
- <dimen name="recents_grid_clear_all_margin_bottom">0dp</dimen>
- <!-- Padding in between task views. -->
- <dimen name="recents_grid_inter_task_padding">15dp</dimen>
-
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 08b8988..93b6965 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -44,22 +44,6 @@
<item name="android:layout_marginBottom">0dp</item>
</style>
- <!-- Grid-based Recents theme. -->
- <style name="RecentsTheme.Grid">
- <item name="android:windowBackground">@color/transparent</item>
- <item name="android:colorBackgroundCacheHint">@null</item>
- <item name="android:windowShowWallpaper">true</item>
- <item name="android:windowDisablePreview">true</item>
- <item name="clearAllStyle">@style/ClearAllButtonLargeMargins</item>
- </style>
-
- <style name="ClearAllButtonLargeMargins">
- <item name="android:layout_marginStart">@dimen/recents_grid_clear_all_margin_left</item>
- <item name="android:layout_marginTop">@dimen/recents_grid_clear_all_margin_top</item>
- <item name="android:layout_marginEnd">@dimen/recents_grid_clear_all_margin_right</item>
- <item name="android:layout_marginBottom">@dimen/recents_grid_clear_all_margin_bottom</item>
- </style>
-
<!-- Performance optimized Recents theme (no wallpaper) -->
<style name="RecentsTheme.NoWallpaper">
<item name="android:windowBackground">@android:color/black</item>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index d9fc2fc..0265c9e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -57,7 +57,6 @@
import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.RecentsTaskLoader;
-import com.android.systemui.recents.grid.RecentsGridImpl;
import com.android.systemui.recents.tv.RecentsTvImpl;
import com.android.systemui.stackdivider.Divider;
@@ -84,7 +83,6 @@
static {
RECENTS_ACTIVITIES.add(RecentsImpl.RECENTS_ACTIVITY);
RECENTS_ACTIVITIES.add(RecentsTvImpl.RECENTS_TV_ACTIVITY);
- RECENTS_ACTIVITIES.add(RecentsGridImpl.RECENTS_MOSAIC_ACTIVITY);
}
// Purely for experimentation
diff --git a/packages/SystemUI/src/com/android/systemui/recents/grid/RecentsGridActivity.java b/packages/SystemUI/src/com/android/systemui/recents/grid/RecentsGridActivity.java
deleted file mode 100644
index 71c2148..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/grid/RecentsGridActivity.java
+++ /dev/null
@@ -1,504 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.recents.grid;
-
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.util.DisplayMetrics;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.animation.Animation;
-import android.view.animation.AnimationSet;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.ScaleAnimation;
-import android.view.animation.TranslateAnimation;
-import android.widget.FrameLayout;
-import android.widget.TextView;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.R;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsActivity;
-import com.android.systemui.recents.RecentsActivityLaunchState;
-import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.RecentsImpl;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
-import com.android.systemui.recents.events.activity.HideRecentsEvent;
-import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent;
-import com.android.systemui.recents.events.activity.LaunchTaskEvent;
-import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
-import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
-import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
-import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
-import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
-import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
-import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.misc.Utilities;
-import com.android.systemui.recents.model.RecentsTaskLoadPlan;
-import com.android.systemui.recents.model.RecentsTaskLoader;
-import com.android.systemui.recents.model.Task;
-import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.recents.views.TaskView;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-
-/**
- * The main grid recents activity started by the RecentsImpl.
- */
-public class RecentsGridActivity extends Activity {
- public final static int MAX_VISIBLE_TASKS = 9;
-
- private final static String TAG = "RecentsGridActivity";
- private final static int TITLE_BAR_HEIGHT_DP = 64;
-
- private ArrayList<Integer> mMargins = new ArrayList<>();
-
- private TaskStack mTaskStack;
- private ArrayList<Task> mTasks = new ArrayList<>();
- private ArrayList<TaskView> mTaskViews = new ArrayList<>();
- private ArrayList<Rect> mTaskViewRects;
- private FrameLayout mRecentsView;
- private TextView mEmptyView;
- private View mClearAllButton;
- private int mLastDisplayOrientation = Configuration.ORIENTATION_UNDEFINED;
- private int mLastDisplayDensity;
- private Rect mDisplayRect = new Rect();
- private LayoutInflater mInflater;
- private boolean mTouchExplorationEnabled;
- private Point mScreenSize;
- private int mTitleBarHeightPx;
- private int mStatusBarHeightPx;
- private int mNavigationBarHeightPx;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.recents_grid);
- SystemServicesProxy ssp = Recents.getSystemServices();
-
- Resources res = getResources();
- Integer[] margins = {
- res.getDimensionPixelSize(R.dimen.recents_grid_margin_left),
- res.getDimensionPixelSize(R.dimen.recents_grid_margin_top),
- res.getDimensionPixelSize(R.dimen.recents_grid_margin_right),
- res.getDimensionPixelSize(R.dimen.recents_grid_margin_bottom),
- };
- mMargins.addAll(Arrays.asList(margins));
-
- mInflater = LayoutInflater.from(this);
- Configuration appConfiguration = Utilities.getAppConfiguration(this);
- mDisplayRect = ssp.getDisplayRect();
- mLastDisplayOrientation = appConfiguration.orientation;
- mLastDisplayDensity = appConfiguration.densityDpi;
- mTouchExplorationEnabled = ssp.isTouchExplorationEnabled();
- mScreenSize = new Point();
- getWindowManager().getDefaultDisplay().getRealSize(mScreenSize);
- DisplayMetrics metrics = res.getDisplayMetrics();
- mTitleBarHeightPx = (int) (TITLE_BAR_HEIGHT_DP *
- ((float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT));
- mStatusBarHeightPx = res.getDimensionPixelSize(R.dimen.status_bar_height);
- mNavigationBarHeightPx = res.getDimensionPixelSize(R.dimen.navigation_bar_height);
-
- mRecentsView = (FrameLayout) findViewById(R.id.recents_view);
- mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
- View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
- View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
- getWindow().getAttributes().privateFlags |=
- WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
- mEmptyView = (TextView) mInflater.inflate(R.layout.recents_empty, mRecentsView, false);
- mClearAllButton = findViewById(R.id.button);
-
- FrameLayout.LayoutParams emptyViewLayoutParams = new FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT);
- emptyViewLayoutParams.gravity = Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL;
- mEmptyView.setLayoutParams(emptyViewLayoutParams);
- mRecentsView.addView(mEmptyView);
-
- mClearAllButton.setVisibility(View.VISIBLE);
- FrameLayout.LayoutParams lp =
- (FrameLayout.LayoutParams) mClearAllButton.getLayoutParams();
- lp.gravity = Gravity.END;
-
- mClearAllButton.setOnClickListener(v -> {
- EventBus.getDefault().send(new DismissAllTaskViewsEvent());
- });
-
- mRecentsView.setOnClickListener(v -> {
- EventBus.getDefault().send(new HideRecentsEvent(
- false /* triggeredFromAltTab */, false /* triggeredFromHomeKey */));
- });
-
- EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY);
- }
-
- private TaskView createView() {
- return (TaskView) mInflater.inflate(R.layout.recents_task_view, mRecentsView, false);
- }
-
- private void removeTaskViews() {
- for (View taskView : mTaskViews) {
- ViewGroup parent = (ViewGroup) taskView.getParent();
- if (parent != null) {
- parent.removeView(taskView);
- }
- }
- }
-
- private void clearTaskViews() {
- removeTaskViews();
- mTaskViews.clear();
- }
-
- private TaskView getChildViewForTask(Task task) {
- for (TaskView tv : mTaskViews) {
- if (tv.getTask() == task) {
- return tv;
- }
- }
- return null;
- }
-
- /**
- * Starts animations for each task view to either enlarge it to the size of the screen (when
- * launching a task), or (if {@code reverse} is true, to reduce it from the size of the screen
- * back to its place in the recents layout (when opening recents).
- * @param animationListener An animation listener for executing code before or after the
- * animations run.
- * @param reverse Whether the blow-up animations should be run in reverse.
- */
- private void startBlowUpAnimations(Animation.AnimationListener animationListener,
- boolean reverse) {
- if (mTaskViews.size() == 0) {
- return;
- }
- int screenWidth = mLastDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE
- ? mScreenSize.x : mScreenSize.y;
- int screenHeight = mLastDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE
- ? mScreenSize.y : mScreenSize.x;
- screenHeight -= mStatusBarHeightPx + mNavigationBarHeightPx;
- for (int i = 0; i < mTaskViews.size(); i++) {
- View tv = mTaskViews.get(i);
- AnimationSet animations = new AnimationSet(true /* shareInterpolator */);
- animations.setInterpolator(new DecelerateInterpolator());
- if (i == 0 && animationListener != null) {
- animations.setAnimationListener(animationListener);
- }
- animations.setFillBefore(reverse);
- animations.setFillAfter(!reverse);
- Rect initialRect = mTaskViewRects.get(mTaskViewRects.size() - 1 - i);
- int xDelta = - initialRect.left;
- int yDelta = - initialRect.top - mTitleBarHeightPx + mStatusBarHeightPx;
- TranslateAnimation translate = new TranslateAnimation(
- reverse ? xDelta : 0, reverse ? 0 : xDelta,
- reverse ? yDelta : 0, reverse ? 0 : yDelta);
- translate.setDuration(250);
- animations.addAnimation(translate);
-
-
- float xScale = (float) screenWidth / (float) initialRect.width();
- float yScale = (float) screenHeight /
- ((float) initialRect.height() - mTitleBarHeightPx);
- ScaleAnimation scale = new ScaleAnimation(
- reverse ? xScale : 1, reverse ? 1 : xScale,
- reverse ? yScale : 1, reverse ? 1 : yScale,
- Animation.ABSOLUTE, 0, Animation.ABSOLUTE, mStatusBarHeightPx);
- scale.setDuration(300);
- animations.addAnimation(scale);
-
- tv.startAnimation(animations);
- }
- }
-
- private void updateControlVisibility() {
- boolean empty = (mTasks.size() == 0);
- mClearAllButton.setVisibility(empty ? View.INVISIBLE : View.VISIBLE);
- mEmptyView.setVisibility(empty ? View.VISIBLE : View.INVISIBLE);
- if (empty) {
- mEmptyView.bringToFront();
- }
- }
-
- private void updateModel() {
- RecentsTaskLoader loader = Recents.getTaskLoader();
- RecentsTaskLoadPlan plan = RecentsImpl.consumeInstanceLoadPlan();
- if (plan == null) {
- plan = loader.createLoadPlan(this);
- }
- RecentsConfiguration config = Recents.getConfiguration();
- RecentsActivityLaunchState launchState = config.getLaunchState();
- if (!plan.hasTasks()) {
- loader.preloadTasks(plan, -1, !launchState.launchedFromHome);
- }
- mTaskStack = plan.getTaskStack();
- RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
- loadOpts.runningTaskId = launchState.launchedToTaskId;
- loadOpts.numVisibleTasks = MAX_VISIBLE_TASKS;
- loadOpts.numVisibleTaskThumbnails = MAX_VISIBLE_TASKS;
- loader.loadTasks(this, plan, loadOpts);
-
- mTasks = mTaskStack.getStackTasks();
- }
-
- private void updateViews() {
- int screenWidth = mLastDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE
- ? mScreenSize.x : mScreenSize.y;
- int screenHeight = mLastDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE
- ? mScreenSize.y : mScreenSize.x;
- int paddingPixels = getResources().getDimensionPixelSize(
- R.dimen.recents_grid_inter_task_padding);
- mTaskViewRects = TaskGridLayoutAlgorithm.getRectsForTaskCount(
- mTasks.size(), screenWidth, screenHeight, getAppRectRatio(), paddingPixels,
- mMargins, mTitleBarHeightPx);
- boolean recycleViews = (mTaskViews.size() == mTasks.size());
- if (!recycleViews) {
- clearTaskViews();
- }
- for (int i = 0; i < mTasks.size(); i++) {
- Task task = mTasks.get(i);
- // We keep the same ordering in the model as other Recents flavors (older tasks are
- // first in the stack) so that the logic can be similar, but we reverse the order
- // when placing views on the screen so that most recent tasks are displayed first.
- Rect rect = mTaskViewRects.get(mTaskViewRects.size() - 1 - i);
- TaskView taskView;
- if (recycleViews) {
- taskView = mTaskViews.get(i);
- } else {
- taskView = createView();
- }
- taskView.onTaskBound(task, mTouchExplorationEnabled, mLastDisplayOrientation,
- mDisplayRect);
- Recents.getTaskLoader().loadTaskData(task);
- taskView.setTouchEnabled(true);
- // Show dismiss button right away.
- taskView.startNoUserInteractionAnimation();
- taskView.setLayoutParams(new FrameLayout.LayoutParams(rect.width(), rect.height()));
- taskView.setTranslationX(rect.left);
- taskView.setTranslationY(rect.top);
- if (!recycleViews) {
- mRecentsView.addView(taskView);
- mTaskViews.add(taskView);
- }
- }
- updateControlVisibility();
- }
-
- private float getAppRectRatio() {
- if (mLastDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE) {
- return (float) mScreenSize.x /
- (float) (mScreenSize.y - mStatusBarHeightPx - mNavigationBarHeightPx);
- } else {
- return (float) mScreenSize.y /
- (float) (mScreenSize.x - mStatusBarHeightPx - mNavigationBarHeightPx);
- }
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, true));
- updateModel();
- updateViews();
- if (mTaskViews.size() > 0) {
- mTaskViews.get(mTaskViews.size() - 1).bringToFront();
- }
- startBlowUpAnimations(new Animation.AnimationListener() {
- @Override
- public void onAnimationStart(Animation animation) { }
-
- @Override
- public void onAnimationEnd(Animation animation) {
- updateViews();
- }
-
- @Override
- public void onAnimationRepeat(Animation animation) { }
- }, true /* reverse */);
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, false));
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- EventBus.getDefault().unregister(this);
- }
-
- @Override
- public void onBackPressed() {
- // Back behaves like the recents button so just trigger a toggle event.
- EventBus.getDefault().send(new ToggleRecentsEvent());
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- // Notify of the config change.
- Configuration newDeviceConfiguration = Utilities.getAppConfiguration(this);
- mDisplayRect = Recents.getSystemServices().getDisplayRect();
- int numStackTasks = mTaskStack.getStackTaskCount();
- EventBus.getDefault().send(new ConfigurationChangedEvent(false /* fromMultiWindow */,
- mLastDisplayOrientation != newDeviceConfiguration.orientation,
- mLastDisplayDensity != newDeviceConfiguration.densityDpi, numStackTasks > 0));
- mLastDisplayOrientation = newDeviceConfiguration.orientation;
- mLastDisplayDensity = newDeviceConfiguration.densityDpi;
- updateViews();
- }
-
- void dismissRecentsToHome() {
- Intent startMain = new Intent(Intent.ACTION_MAIN);
- startMain.addCategory(Intent.CATEGORY_HOME);
- startActivity(startMain);
- }
-
- /** Launches the task that recents was launched from if possible. */
- boolean launchPreviousTask() {
- if (mRecentsView != null) {
- Task task = mTaskStack.getLaunchTarget();
- if (task != null) {
- TaskView taskView = getChildViewForTask(task);
- EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null,
- INVALID_STACK_ID, false));
- return true;
- }
- }
- return false;
- }
-
- /** Dismisses recents back to the launch target task. */
- boolean dismissRecentsToLaunchTargetTaskOrHome() {
- SystemServicesProxy ssp = Recents.getSystemServices();
- if (ssp.isRecentsActivityVisible()) {
- // If we can launch the task that Recents was launched from, do that, otherwise go home.
- if (launchPreviousTask()) return true;
- dismissRecentsToHome();
- }
- return false;
- }
-
- /**** EventBus events ****/
-
- public final void onBusEvent(HideRecentsEvent event) {
- if (event.triggeredFromAltTab) {
- dismissRecentsToLaunchTargetTaskOrHome();
- } else if (event.triggeredFromHomeKey) {
- dismissRecentsToHome();
- } else {
- // Fall through tap on the background view but not on any of the tasks.
- dismissRecentsToHome();
- }
- }
-
- public final void onBusEvent(ToggleRecentsEvent event) {
- dismissRecentsToLaunchTargetTaskOrHome();
- }
-
- public final void onBusEvent(DismissTaskViewEvent event) {
- int taskIndex = mTaskViews.indexOf(event.taskView);
- if (taskIndex != -1) {
- mTasks.remove(taskIndex);
- ((ViewGroup) event.taskView.getParent()).removeView(event.taskView);
- mTaskViews.remove(taskIndex);
- EventBus.getDefault().send(
- new TaskViewDismissedEvent(event.taskView.getTask(), event.taskView, null));
- }
- }
-
- public final void onBusEvent(TaskViewDismissedEvent event) {
- mRecentsView.announceForAccessibility(this.getString(
- R.string.accessibility_recents_item_dismissed, event.task.title));
- updateControlVisibility();
-
- EventBus.getDefault().send(new DeleteTaskDataEvent(event.task));
-
- MetricsLogger.action(this, MetricsEvent.OVERVIEW_DISMISS,
- event.task.key.getComponent().toString());
- }
-
- public final void onBusEvent(DeleteTaskDataEvent event) {
- // Remove any stored data from the loader.
- RecentsTaskLoader loader = Recents.getTaskLoader();
- loader.deleteTaskData(event.task, false);
-
- // Remove the task from activity manager.
- SystemServicesProxy ssp = Recents.getSystemServices();
- ssp.removeTask(event.task.key.id);
- }
-
- public final void onBusEvent(final DismissAllTaskViewsEvent event) {
- // Keep track of the tasks which will have their data removed.
- ArrayList<Task> tasks = new ArrayList<>(mTaskStack.getStackTasks());
- mRecentsView.announceForAccessibility(this.getString(
- R.string.accessibility_recents_all_items_dismissed));
- mTaskStack.removeAllTasks();
- for (int i = tasks.size() - 1; i >= 0; i--) {
- EventBus.getDefault().send(new DeleteTaskDataEvent(tasks.get(i)));
- }
- mTasks = new ArrayList<>();
- updateModel();
- updateViews();
-
- MetricsLogger.action(this, MetricsEvent.OVERVIEW_DISMISS_ALL);
- }
-
- public final void onBusEvent(AllTaskViewsDismissedEvent event) {
- SystemServicesProxy ssp = Recents.getSystemServices();
- if (!ssp.hasDockedTask()) {
- dismissRecentsToHome();
- }
- }
-
- public final void onBusEvent(LaunchNextTaskRequestEvent event) {
- if (mTaskStack.getTaskCount() > 0) {
- Task launchTask = mTaskStack.getNextLaunchTarget();
- TaskView launchTaskView = getChildViewForTask(launchTask);
- if (launchTaskView != null) {
- EventBus.getDefault().send(new LaunchTaskEvent(launchTaskView,
- launchTask, null, INVALID_STACK_ID, false /* screenPinningRequested */));
- MetricsLogger.action(this, MetricsEvent.OVERVIEW_LAUNCH_PREVIOUS_TASK,
- launchTask.key.getComponent().toString());
- return;
- }
- }
- // We couldn't find a matching task view, or there are no tasks. Just hide recents back
- // to home.
- EventBus.getDefault().send(new HideRecentsEvent(false, true));
- }
-
- public final void onBusEvent(LaunchTaskEvent event) {
- event.taskView.bringToFront();
- startActivity(event.task.key.baseIntent);
- // Eventually we should start blow-up animations here, but we need to make sure it's done
- // in parallel with starting the activity so that we don't introduce unneeded latency.
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/grid/RecentsGridImpl.java b/packages/SystemUI/src/com/android/systemui/recents/grid/RecentsGridImpl.java
deleted file mode 100644
index 41acaa0..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/grid/RecentsGridImpl.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.recents.grid;
-
-import android.app.ActivityManager;
-import android.content.Context;
-import android.content.Intent;
-import android.os.UserHandle;
-
-import com.android.systemui.recents.RecentsImpl;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
-
-public class RecentsGridImpl extends RecentsImpl {
- public static final String RECENTS_MOSAIC_ACTIVITY =
- "com.android.systemui.recents.grid.RecentsGridActivity";
-
- public RecentsGridImpl(Context context) {
- super(context);
- }
-
- @Override
- protected void startRecentsActivity(ActivityManager.RunningTaskInfo runningTask,
- boolean isHomeStackVisible, boolean animate, int growTarget) {
- Intent intent = new Intent();
- intent.setClassName(RECENTS_PACKAGE, RECENTS_MOSAIC_ACTIVITY);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
- | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
-
- mContext.startActivityAsUser(intent, UserHandle.CURRENT);
- EventBus.getDefault().send(new RecentsActivityStartingEvent());
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/grid/TaskGridLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/grid/TaskGridLayoutAlgorithm.java
deleted file mode 100644
index 648f2f0..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/grid/TaskGridLayoutAlgorithm.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.recents.grid;
-
-import android.graphics.Rect;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-class TaskGridLayoutAlgorithm {
-
- public enum VerticalGravity {
- START, END, CENTER
- }
-
- public static final List<Integer> ZERO_MARGIN = new ArrayList<>();
- static {
- Integer[] zero = {0, 0, 0, 0};
- ZERO_MARGIN.addAll(Arrays.asList(zero));
- }
- private static final String TAG = "TaskGridLayoutAlgorithm";
-
- /**
- * Calculates the adequate rectangles for the specified number of tasks to be layed out on
- * the screen.
- * @param count The number of task views to layout.
- * @param containerWidth The width of the whole area containing those tasks.
- * @param containerHeight The height of the whole area containing those tasks.
- * @param screenRatio The ratio of the device's screen, so that tasks have the same aspect
- * ratio (ignoring the title bar).
- * @param padding The amount of padding, in pixels, in between task views.
- * @param margins The amount of space to be left blank around the area on the left, top, right
- * and bottom.
- * @param titleBarHeight The height, in pixels, of the task views title bar.
- * @return A list of rectangles to be used for layout.
- */
- static ArrayList<Rect> getRectsForTaskCount(int count, int containerWidth, int containerHeight,
- float screenRatio, int padding, List<Integer> margins, int titleBarHeight) {
- return getRectsForTaskCount(count, containerWidth, containerHeight, screenRatio, padding,
- margins, titleBarHeight, null, VerticalGravity.CENTER);
- }
-
- private static ArrayList<Rect> getRectsForTaskCount(int count, int containerWidth,
- int containerHeight, float screenRatio, int padding, List<Integer> margins,
- int titleBarHeight, Rect preCalculatedTile, VerticalGravity gravity) {
- ArrayList<Rect> rects = new ArrayList<>(count);
- boolean landscape = (containerWidth > containerHeight);
- containerWidth -= margins.get(0) + margins.get(2);
- containerHeight -= margins.get(1) + margins.get(3);
-
- // We support at most 9 tasks in this layout.
- count = Math.min(count, RecentsGridActivity.MAX_VISIBLE_TASKS);
-
- if (count == 0) {
- return rects;
- }
- if (count <= 3) {
- // Base case: single line.
- int taskWidth, taskHeight;
- if (preCalculatedTile != null) {
- taskWidth = preCalculatedTile.width();
- taskHeight = preCalculatedTile.height();
- } else {
- // Divide available width in equal parts.
- int maxTaskWidth = (containerWidth - (count - 1) * padding) / count;
- int maxTaskHeight = containerHeight;
- if (maxTaskHeight >= maxTaskWidth / screenRatio + titleBarHeight) {
- // Width bound.
- taskWidth = maxTaskWidth;
- taskHeight = (int) (maxTaskWidth / screenRatio + titleBarHeight);
- } else {
- // Height bound.
- taskHeight = maxTaskHeight;
- taskWidth = (int) ((taskHeight - titleBarHeight) * screenRatio);
- }
- }
- int emptySpaceX = containerWidth - (count * taskWidth) - (count - 1) * padding;
- int emptySpaceY = containerHeight - taskHeight;
- for (int i = 0; i < count; i++) {
- int left = emptySpaceX / 2 + i * taskWidth + i * padding;
- int top;
- switch (gravity) {
- case CENTER:
- top = emptySpaceY / 2;
- break;
- case END:
- top = emptySpaceY;
- break;
- case START:
- default:
- top = 0;
- break;
- }
- Rect rect = new Rect(left, top, left + taskWidth, top + taskHeight);
- rect.offset(margins.get(0), margins.get(1));
- rects.add(rect);
- }
- } else if (count < 7) {
- // Two lines.
- int lineHeight = (containerHeight - padding) / 2;
- int lineTaskCount = (int) Math.ceil((double) count / 2);
- List<Rect> rectsA = getRectsForTaskCount(lineTaskCount, containerWidth, lineHeight,
- screenRatio, padding, ZERO_MARGIN, titleBarHeight, null, VerticalGravity.END);
- List<Rect> rectsB = getRectsForTaskCount(count - lineTaskCount, containerWidth,
- lineHeight, screenRatio, padding, ZERO_MARGIN, titleBarHeight, rectsA.get(0),
- VerticalGravity.START);
- for (int i = 0; i < rectsA.size(); i++) {
- rectsA.get(i).offset(margins.get(0), margins.get(1));
- }
- for (int i = 0; i < rectsB.size(); i++) {
- rectsB.get(i).offset(margins.get(0), margins.get(1) + lineHeight + padding);
- }
- rects.addAll(rectsA);
- rects.addAll(rectsB);
- } else {
- // Three lines.
- int lineHeight = (containerHeight - 2 * padding) / 3;
- int lineTaskCount = (int) Math.ceil((double) count / 3);
- List<Rect> rectsA = getRectsForTaskCount(lineTaskCount, containerWidth, lineHeight,
- screenRatio, padding, ZERO_MARGIN, titleBarHeight, null, VerticalGravity.END);
- List<Rect> rectsB = getRectsForTaskCount(lineTaskCount, containerWidth, lineHeight,
- screenRatio, padding, ZERO_MARGIN, titleBarHeight, rectsA.get(0),
- VerticalGravity.END);
- List<Rect> rectsC = getRectsForTaskCount(count - (2 * lineTaskCount), containerWidth,
- lineHeight, screenRatio, padding, ZERO_MARGIN, titleBarHeight, rectsA.get(0),
- VerticalGravity.START);
- for (int i = 0; i < rectsA.size(); i++) {
- rectsA.get(i).offset(margins.get(0), margins.get(1));
- }
- for (int i = 0; i < rectsB.size(); i++) {
- rectsB.get(i).offset(margins.get(0), margins.get(1) + lineHeight + padding);
- }
- for (int i = 0; i < rectsC.size(); i++) {
- rectsC.get(i).offset(margins.get(0), margins.get(1) + 2 * (lineHeight + padding));
- }
- rects.addAll(rectsA);
- rects.addAll(rectsB);
- rects.addAll(rectsC);
- }
- return rects;
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/grid/TaskGridLayoutAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/recents/grid/TaskGridLayoutAlgorithmTest.java
deleted file mode 100644
index a9f811b..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/grid/TaskGridLayoutAlgorithmTest.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-package com.android.systemui.recents.grid;
-
-import android.graphics.Rect;
-import android.test.suitebuilder.annotation.SmallTest;
-import com.android.systemui.SysuiTestCase;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertTrue;
-
-import org.junit.Test;
-
-@SmallTest
-public class TaskGridLayoutAlgorithmTest extends SysuiTestCase {
-
- private static final List<Integer> ZERO_MARGIN = TaskGridLayoutAlgorithm.ZERO_MARGIN;
-
- @Test
- public void testOneTile() {
- List<Rect> rects = TaskGridLayoutAlgorithm.getRectsForTaskCount(
- 1, 1000, 1000, 1 /* screenRatio */, 0 /* padding */, ZERO_MARGIN, 0);
- assertEquals(1, rects.size());
- Rect singleRect = rects.get(0);
- assertEquals(1000, singleRect.width());
- }
-
- @Test
- public void testTwoTilesLandscape() {
- List<Rect> rects = TaskGridLayoutAlgorithm.getRectsForTaskCount(
- 2, 1200, 500, 1.2f /* screenRatio */, 0 /* padding */, ZERO_MARGIN, 0);
- assertEquals(2, rects.size());
- for (Rect rect : rects) {
- assertEquals(600, rect.width());
- assertEquals(499, rect.height());
- }
- }
-
- @Test
- public void testTwoTilesLandscapeWithPadding() {
- List<Rect> rects = TaskGridLayoutAlgorithm.getRectsForTaskCount(
- 2, 1200, 500, 1.19f /* screenRatio */, 10 /* padding */, ZERO_MARGIN, 0);
- assertEquals(2, rects.size());
- Rect rectA = rects.get(0);
- Rect rectB = rects.get(1);
- assertEquals(595, rectA.width());
- assertEquals(595, rectB.width());
- assertEquals(605, rectB.left);
- }
-
- @Test
- public void testTwoTilesPortrait() {
- List<Rect> rects = TaskGridLayoutAlgorithm.getRectsForTaskCount(
- 2, 500, 1200, 1 /* screenRatio */, 0 /* padding */, ZERO_MARGIN, 0);
- assertEquals(2, rects.size());
- for (Rect rect : rects) {
- assertEquals(250, rect.width());
- assertEquals(250, rect.height());
- }
- }
-
- @Test
- public void testThreeTiles() {
- List<Rect> rects = TaskGridLayoutAlgorithm.getRectsForTaskCount(
- 3, 1200, 500, 2 /* screenRatio */, 0 /* padding */, ZERO_MARGIN, 0);
- assertEquals(3, rects.size());
- for (Rect rect : rects) {
- assertEquals(400, rect.width());
- assertEquals(200, rect.height());
- }
- }
-
- @Test
- public void testFourTiles() {
- List<Rect> rects = TaskGridLayoutAlgorithm.getRectsForTaskCount(
- 4, 1200, 500, 2.4f /* screenRatio */, 0 /* padding */, ZERO_MARGIN, 0);
- assertEquals(4, rects.size());
- for (Rect rect : rects) {
- assertEquals(600, rect.width());
- assertEquals(249, rect.height());
- }
- Rect rectD = rects.get(3);
- assertEquals(600, rectD.left);
- assertEquals(250, rectD.top);
- }
-
- @Test
- public void testNineTiles() {
- List<Rect> rects = TaskGridLayoutAlgorithm.getRectsForTaskCount(
- 9, 1200, 600, 2 /* screenRatio */, 0 /* padding */, ZERO_MARGIN, 0);
- assertEquals(9, rects.size());
- for (Rect rect : rects) {
- assertEquals(400, rect.width());
- assertEquals(200, rect.height());
- }
- Rect rectE = rects.get(4);
- assertEquals(400, rectE.left);
- assertEquals(200, rectE.top);
- Rect rectI = rects.get(8);
- assertEquals(800, rectI.left);
- assertEquals(400, rectI.top);
- }}
-
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index a1c3564..f712f12 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.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.Collections;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
/**
@@ -67,17 +74,20 @@
*/
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 @@
* 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 @@
}
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 @@
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 @@
}
}
+ /**
+ * 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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
@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 @@
@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 @@
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 @@
}
}, 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 @@
}
}
+ 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 @@
} 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 @@
Log.d(TAG, "ScoringServiceConnection, disconnected: " + name.flattenToString());
}
mConnected = false;
+ mRecommendationProvider = null;
}
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
@@ -590,4 +642,43 @@
+ ", 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/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9b67bd2..eae4905 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -369,6 +369,7 @@
import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS;
import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
import static com.android.server.am.ActivityStackSupervisor.RESTORE_FROM_RECENTS;
import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
@@ -539,8 +540,6 @@
private static final String INTENT_REMOTE_BUGREPORT_FINISHED =
"android.intent.action.REMOTE_BUGREPORT_FINISHED";
- // Used to indicate that a task is removed it should also be removed from recents.
- private static final boolean REMOVE_FROM_RECENTS = true;
// Used to indicate that an app transition should be animated.
static final boolean ANIMATE = true;
@@ -4826,7 +4825,7 @@
// because we don't support returning them across task boundaries. Also, to
// keep backwards compatibility we remove the task from recents when finishing
// task with root activity.
- res = removeTaskByIdLocked(tr.taskId, false, finishWithRootActivity);
+ res = mStackSupervisor.removeTaskByIdLocked(tr.taskId, false, finishWithRootActivity);
if (!res) {
Slog.i(TAG, "Removing task failed to finish activity");
}
@@ -5471,7 +5470,7 @@
tr.getBaseIntent().getComponent().getPackageName();
if (tr.userId != userId) continue;
if (!taskPackageName.equals(packageName)) continue;
- removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);
+ mStackSupervisor.removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);
}
}
@@ -9585,79 +9584,6 @@
mWindowManager.executeAppTransition();
}
- private void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess,
- boolean removeFromRecents) {
- if (removeFromRecents) {
- mRecentTasks.remove(tr);
- tr.removedFromRecents();
- }
- ComponentName component = tr.getBaseIntent().getComponent();
- if (component == null) {
- Slog.w(TAG, "No component for base intent of task: " + tr);
- return;
- }
-
- // Find any running services associated with this app and stop if needed.
- mServices.cleanUpRemovedTaskLocked(tr, component, new Intent(tr.getBaseIntent()));
-
- if (!killProcess) {
- return;
- }
-
- // Determine if the process(es) for this task should be killed.
- final String pkg = component.getPackageName();
- ArrayList<ProcessRecord> procsToKill = new ArrayList<>();
- ArrayMap<String, SparseArray<ProcessRecord>> pmap = mProcessNames.getMap();
- for (int i = 0; i < pmap.size(); i++) {
-
- SparseArray<ProcessRecord> uids = pmap.valueAt(i);
- for (int j = 0; j < uids.size(); j++) {
- ProcessRecord proc = uids.valueAt(j);
- if (proc.userId != tr.userId) {
- // Don't kill process for a different user.
- continue;
- }
- if (proc == mHomeProcess) {
- // Don't kill the home process along with tasks from the same package.
- continue;
- }
- if (!proc.pkgList.containsKey(pkg)) {
- // Don't kill process that is not associated with this task.
- continue;
- }
-
- for (int k = 0; k < proc.activities.size(); k++) {
- TaskRecord otherTask = proc.activities.get(k).task;
- if (tr.taskId != otherTask.taskId && otherTask.inRecents) {
- // Don't kill process(es) that has an activity in a different task that is
- // also in recents.
- return;
- }
- }
-
- if (proc.foregroundServices) {
- // Don't kill process(es) with foreground service.
- return;
- }
-
- // Add process to kill list.
- procsToKill.add(proc);
- }
- }
-
- // Kill the running processes.
- for (int i = 0; i < procsToKill.size(); i++) {
- ProcessRecord pr = procsToKill.get(i);
- if (pr.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND
- && pr.curReceivers.isEmpty()) {
- pr.kill("remove task", true);
- } else {
- // We delay killing processes that are not in the background or running a receiver.
- pr.waitingToKill = "remove task";
- }
- }
- }
-
private void removeTasksByPackageNameLocked(String packageName, int userId) {
// Remove all tasks with activities in the specified package from the list of recent tasks
for (int i = mRecentTasks.size() - 1; i >= 0; i--) {
@@ -9667,7 +9593,7 @@
ComponentName cn = tr.intent.getComponent();
if (cn != null && cn.getPackageName().equals(packageName)) {
// If the package name matches, remove the task.
- removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS);
+ mStackSupervisor.removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS);
}
}
}
@@ -9685,35 +9611,11 @@
final boolean sameComponent = cn != null && cn.getPackageName().equals(packageName)
&& (filterByClasses == null || filterByClasses.contains(cn.getClassName()));
if (sameComponent) {
- removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);
+ mStackSupervisor.removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);
}
}
}
- /**
- * Removes the task with the specified task id.
- *
- * @param taskId Identifier of the task to be removed.
- * @param killProcess Kill any process associated with the task if possible.
- * @param removeFromRecents Whether to also remove the task from recents.
- * @return Returns true if the given task was found and removed.
- */
- private boolean removeTaskByIdLocked(int taskId, boolean killProcess,
- boolean removeFromRecents) {
- final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(
- taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
- if (tr != null) {
- tr.removeTaskActivitiesLocked();
- cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
- if (tr.isPersistable) {
- notifyTaskPersisterLocked(null, true);
- }
- return true;
- }
- Slog.w(TAG, "Request to remove task ignored for non-existent task " + taskId);
- return false;
- }
-
@Override
public void removeStack(int stackId) {
enforceCallingPermission(Manifest.permission.MANAGE_ACTIVITY_STACKS, "removeStack()");
@@ -9724,15 +9626,7 @@
synchronized (this) {
final long ident = Binder.clearCallingIdentity();
try {
- final ActivityStack stack = mStackSupervisor.getStack(stackId);
- if (stack == null) {
- return;
- }
- final ArrayList<TaskRecord> tasks = stack.getAllTasks();
- for (int i = tasks.size() - 1; i >= 0; i--) {
- removeTaskByIdLocked(
- tasks.get(i).taskId, true /* killProcess */, REMOVE_FROM_RECENTS);
- }
+ mStackSupervisor.removeStackLocked(stackId);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -9761,7 +9655,7 @@
synchronized (this) {
final long ident = Binder.clearCallingIdentity();
try {
- return removeTaskByIdLocked(taskId, true, REMOVE_FROM_RECENTS);
+ return mStackSupervisor.removeTaskByIdLocked(taskId, true, REMOVE_FROM_RECENTS);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -22520,7 +22414,8 @@
long origId = Binder.clearCallingIdentity();
try {
// We remove the task from recents to preserve backwards
- if (!removeTaskByIdLocked(mTaskId, false, REMOVE_FROM_RECENTS)) {
+ if (!mStackSupervisor.removeTaskByIdLocked(mTaskId, false,
+ REMOVE_FROM_RECENTS)) {
throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
}
} finally {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index fe261f1..b4b3465 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -249,6 +249,9 @@
// Don't execute any calls to resume.
static final boolean DEFER_RESUME = true;
+ // Used to indicate that a task is removed it should also be removed from recents.
+ static final boolean REMOVE_FROM_RECENTS = true;
+
// Activity actions an app cannot start if it uses a permission which is not granted.
private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION =
new ArrayMap<>();
@@ -2183,7 +2186,8 @@
// Update the return-to to reflect where the pinned stack task was moved
// from so that we retain the stack that was previously visible if the
// pinned stack is recreated. See moveActivityToPinnedStackLocked().
- task.setTaskToReturnTo(getFocusedStack().getStackId() == HOME_STACK_ID
+ final int focusedStackId = getFocusedStack().getStackId();
+ task.setTaskToReturnTo(focusedStackId == HOME_STACK_ID || !onTop
? HOME_ACTIVITY_TYPE : APPLICATION_ACTIVITY_TYPE);
}
moveTaskToStackLocked(tasks.get(i).taskId,
@@ -2374,6 +2378,141 @@
return activityContainer.mStack;
}
+ /**
+ * Removes the stack associed with the given {@param stackId}. If the {@param stackId} is the
+ * pinned stack, then its tasks are not explicitly removed when the stack is destroyed, but
+ * instead moved back onto the fullscreen stack.
+ */
+ void removeStackLocked(int stackId) {
+ final ActivityStack stack = getStack(stackId);
+ if (stack == null) {
+ return;
+ }
+
+ final ArrayList<TaskRecord> tasks = stack.getAllTasks();
+ if (stack.getStackId() == PINNED_STACK_ID) {
+ final ActivityStack fullscreenStack = getStack(FULLSCREEN_WORKSPACE_STACK_ID);
+ if (fullscreenStack != null) {
+ final boolean isFullscreenStackVisible =
+ fullscreenStack.getStackVisibilityLocked(null) == STACK_VISIBLE;
+ for (int i = 0; i < tasks.size(); i++) {
+ // Insert the task either at the top of the fullscreen stack if it is hidden,
+ // or just under the top task if it is currently visible
+ final int insertPosition = isFullscreenStackVisible
+ ? Math.max(0, fullscreenStack.getChildCount() - 1)
+ : fullscreenStack.getChildCount();
+ positionTaskInStackLocked(tasks.get(i).taskId, FULLSCREEN_WORKSPACE_STACK_ID,
+ insertPosition);
+ }
+ ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ resumeFocusedStackTopActivityLocked();
+ } else {
+ // If there is no fullscreen stack, then create the stack and move all the tasks
+ // onto the stack
+ moveTasksToFullscreenStackLocked(PINNED_STACK_ID, false /* onTop */);
+ }
+ } else {
+ for (int i = tasks.size() - 1; i >= 0; i--) {
+ removeTaskByIdLocked(tasks.get(i).taskId, true /* killProcess */,
+ REMOVE_FROM_RECENTS);
+ }
+ }
+ }
+
+ /**
+ * Removes the task with the specified task id.
+ *
+ * @param taskId Identifier of the task to be removed.
+ * @param killProcess Kill any process associated with the task if possible.
+ * @param removeFromRecents Whether to also remove the task from recents.
+ * @return Returns true if the given task was found and removed.
+ */
+ boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents) {
+ final TaskRecord tr = anyTaskForIdLocked(taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
+ if (tr != null) {
+ tr.removeTaskActivitiesLocked();
+ cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
+ if (tr.isPersistable) {
+ mService.notifyTaskPersisterLocked(null, true);
+ }
+ return true;
+ }
+ Slog.w(TAG, "Request to remove task ignored for non-existent task " + taskId);
+ return false;
+ }
+
+ void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess, boolean removeFromRecents) {
+ if (removeFromRecents) {
+ mRecentTasks.remove(tr);
+ tr.removedFromRecents();
+ }
+ ComponentName component = tr.getBaseIntent().getComponent();
+ if (component == null) {
+ Slog.w(TAG, "No component for base intent of task: " + tr);
+ return;
+ }
+
+ // Find any running services associated with this app and stop if needed.
+ mService.mServices.cleanUpRemovedTaskLocked(tr, component, new Intent(tr.getBaseIntent()));
+
+ if (!killProcess) {
+ return;
+ }
+
+ // Determine if the process(es) for this task should be killed.
+ final String pkg = component.getPackageName();
+ ArrayList<ProcessRecord> procsToKill = new ArrayList<>();
+ ArrayMap<String, SparseArray<ProcessRecord>> pmap = mService.mProcessNames.getMap();
+ for (int i = 0; i < pmap.size(); i++) {
+
+ SparseArray<ProcessRecord> uids = pmap.valueAt(i);
+ for (int j = 0; j < uids.size(); j++) {
+ ProcessRecord proc = uids.valueAt(j);
+ if (proc.userId != tr.userId) {
+ // Don't kill process for a different user.
+ continue;
+ }
+ if (proc == mService.mHomeProcess) {
+ // Don't kill the home process along with tasks from the same package.
+ continue;
+ }
+ if (!proc.pkgList.containsKey(pkg)) {
+ // Don't kill process that is not associated with this task.
+ continue;
+ }
+
+ for (int k = 0; k < proc.activities.size(); k++) {
+ TaskRecord otherTask = proc.activities.get(k).task;
+ if (tr.taskId != otherTask.taskId && otherTask.inRecents) {
+ // Don't kill process(es) that has an activity in a different task that is
+ // also in recents.
+ return;
+ }
+ }
+
+ if (proc.foregroundServices) {
+ // Don't kill process(es) with foreground service.
+ return;
+ }
+
+ // Add process to kill list.
+ procsToKill.add(proc);
+ }
+ }
+
+ // Kill the running processes.
+ for (int i = 0; i < procsToKill.size(); i++) {
+ ProcessRecord pr = procsToKill.get(i);
+ if (pr.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND
+ && pr.curReceivers.isEmpty()) {
+ pr.kill("remove task", true);
+ } else {
+ // We delay killing processes that are not in the background or running a receiver.
+ pr.waitingToKill = "remove task";
+ }
+ }
+ }
+
int getNextStackId() {
while (true) {
if (mNextFreeStackId >= FIRST_DYNAMIC_STACK_ID
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
index 50911cb..c653b8e 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.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.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 @@
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 @@
@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,44 +121,9 @@
when(mContext.getContentResolver()).thenReturn(mContentResolver);
when(mContext.getResources()).thenReturn(mResources);
mNetworkScoreService = new NetworkScoreService(mContext, mNetworkScorerAppManager);
- }
-
- @Test
- public void testSystemReady_networkScorerProvisioned() throws Exception {
- Settings.Global.putInt(mContentResolver, Global.NETWORK_SCORING_PROVISIONED, 1);
-
- mNetworkScoreService.systemReady();
-
- verify(mNetworkScorerAppManager, never()).setActiveScorer(anyString());
- }
-
- @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);
-
- mNetworkScoreService.systemReady();
-
- verify(mNetworkScorerAppManager).setActiveScorer(NEW_SCORER.mPackageName);
- assertEquals(1,
- Settings.Global.getInt(mContentResolver, Global.NETWORK_SCORING_PROVISIONED));
-
- }
-
- @Test
- public void testSystemReady_networkScorerNotProvisioned_noDefaultScorer() throws Exception {
- Settings.Global.putInt(mContentResolver, Global.NETWORK_SCORING_PROVISIONED, 0);
-
- when(mResources.getString(R.string.config_defaultNetworkScorerPackageName))
- .thenReturn(null);
-
- mNetworkScoreService.systemReady();
-
- verify(mNetworkScorerAppManager, never()).setActiveScorer(anyString());
- assertEquals(1,
- Settings.Global.getInt(mContentResolver, Global.NETWORK_SCORING_PROVISIONED));
+ WifiConfiguration configuration = new WifiConfiguration();
+ mRecommendationRequest = new RecommendationRequest.Builder()
+ .setCurrentRecommendedWifiConfig(configuration).build();
}
@Test
@@ -159,13 +133,126 @@
mNetworkScoreService.systemRunning();
verify(mContext).bindServiceAsUser(MockUtils.checkIntent(new Intent().setComponent(
- new ComponentName(NEW_SCORER.mPackageName, NEW_SCORER.mScoringServiceClassName))),
+ 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 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
+ }
+ }
+
+ @Test
+ public void testRequestScores_providerNotConnected() throws Exception {
+ assertFalse(mNetworkScoreService.requestScores(new NetworkKey[0]));
+ verifyZeroInteractions(mRecommendationProvider);
+ }
+
+ @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 testRequestScores_providerAvailable() throws Exception {
+ injectProvider();
+
+ final NetworkKey[] networks = new NetworkKey[0];
+ assertTrue(mNetworkScoreService.requestScores(networks));
+ verify(mRecommendationProvider).requestScores(networks);
+ }
+
+ @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
+ }
+ }
+
+ @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 testRequestRecommendation_providerNotConnected() throws Exception {
+ when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
+
+ final RecommendationResult result =
+ mNetworkScoreService.requestRecommendation(mRecommendationRequest);
+ assertNotNull(result);
+ assertEquals(mRecommendationRequest.getCurrentSelectedConfig(),
+ result.getWifiConfiguration());
+ }
+
+ @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());
+ }
+
+ @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
public void testUpdateScores_notActiveScorer() {
when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
@@ -288,45 +375,6 @@
}
@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 @@
} 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 @@
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();
+ }
}