Merge "Moved urlBarResourceId from autofill service manifest to whitelist settings." into pi-dev

am: 9e56800b6e

Change-Id: I521d7b979b343cf89db14864b63a10b29396694e
This commit is contained in:
Felipe Leme
2018-03-12 20:19:10 +00:00
committed by android-build-merger
10 changed files with 261 additions and 72 deletions

View File

@@ -11510,7 +11510,14 @@ public final class Settings {
/**
* The packages whitelisted to be run in autofill compatibility mode. The list
* of packages is ":" colon delimited.
* of packages is {@code ":"} colon delimited, and each entry has the name of the
* package and an optional list of url bar resource ids (the list is delimited by
* brackets&mdash{@code [} and {@code ]}&mdash and is also comma delimited).
*
* <p>For example, a list with 3 packages {@code p1}, {@code p2}, and {@code p3}, where
* package {@code p1} have one id ({@code url_bar}, {@code p2} has none, and {@code p3 }
* have 2 ids {@code url_foo} and {@code url_bas}) would be
* {@code p1[url_bar]:p2:p3[url_foo,url_bas]}
*
* @hide
*/

View File

@@ -32,7 +32,6 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
import android.util.Xml;
import com.android.internal.R;
@@ -79,7 +78,7 @@ public final class AutofillServiceInfo {
private final String mSettingsActivity;
@Nullable
private final ArrayMap<String, Pair<Long, String>> mCompatibilityPackages;
private final ArrayMap<String, Long> mCompatibilityPackages;
public AutofillServiceInfo(Context context, ComponentName comp, int userHandle)
throws PackageManager.NameNotFoundException {
@@ -117,7 +116,7 @@ public final class AutofillServiceInfo {
}
String settingsActivity = null;
ArrayMap<String, Pair<Long, String>> compatibilityPackages = null;
ArrayMap<String, Long> compatibilityPackages = null;
try {
final Resources resources = context.getPackageManager().getResourcesForApplication(
@@ -153,10 +152,9 @@ public final class AutofillServiceInfo {
mCompatibilityPackages = compatibilityPackages;
}
private ArrayMap<String, Pair<Long, String>> parseCompatibilityPackages(XmlPullParser parser,
Resources resources)
throws IOException, XmlPullParserException {
ArrayMap<String, Pair<Long, String>> compatibilityPackages = null;
private ArrayMap<String, Long> parseCompatibilityPackages(XmlPullParser parser,
Resources resources) throws IOException, XmlPullParserException {
ArrayMap<String, Long> compatibilityPackages = null;
final int outerDepth = parser.getDepth();
int type;
@@ -200,13 +198,18 @@ public final class AutofillServiceInfo {
} else {
maxVersionCode = Long.MAX_VALUE;
}
final String urlBarResourceId = cpAttributes.getString(
R.styleable.AutofillService_CompatibilityPackage_urlBarResourceId);
if (true) { // TODO(b/74445943): remove block after P DP2 is branched
final String urlBarResourceId = cpAttributes.getString(
R.styleable.AutofillService_CompatibilityPackage_urlBarResourceId);
if (urlBarResourceId != null) {
Log.e(TAG, "Service is using deprecated attribute 'urlBarResourceId'");
}
}
if (compatibilityPackages == null) {
compatibilityPackages = new ArrayMap<>();
}
compatibilityPackages.put(name, new Pair<>(maxVersionCode, urlBarResourceId));
compatibilityPackages.put(name, maxVersionCode);
} finally {
XmlUtils.skipCurrentTag(parser);
if (cpAttributes != null) {
@@ -228,23 +231,10 @@ public final class AutofillServiceInfo {
return mSettingsActivity;
}
public ArrayMap<String, Pair<Long, String>> getCompatibilityPackages() {
public ArrayMap<String, Long> getCompatibilityPackages() {
return mCompatibilityPackages;
}
/**
* Gets the resource id of the URL bar for a package. Used in compat mode
*/
// TODO: return a list of strings instead
@Nullable
public String getUrlBarResourceId(String packageName) {
if (mCompatibilityPackages == null) {
return null;
}
final Pair<Long, String> pair = mCompatibilityPackages.get(packageName);
return pair == null ? null : pair.second;
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();

View File

@@ -7978,9 +7978,7 @@
android.content.pm.PackageInfo#getLongVersionCode()} for the target package.
-->
<attr name="maxLongVersionCode" format="string" />
<!-- The resource id of view that contains the URL bar of the HTML page being loaded.
Typically used when compatibility mode is used in a browser.
-->
<!-- TODO(b/74445943): STOPSHIP (urlBarResourceId should be removed after P DP2 is branched)-->
<attr name="urlBarResourceId" format="string" />
</declare-styleable>

View File

@@ -2869,6 +2869,7 @@
<public name="outlineSpotShadowColor" />
<public name="outlineAmbientShadowColor" />
<public name="maxLongVersionCode" />
<!-- TODO(b/74445943): STOPSHIP (urlBarResourceId should be removed after P DP2 is branched)-->
<public name="urlBarResourceId" />
<!-- @hide @SystemApi -->
<public name="userRestriction" />

View File

@@ -59,9 +59,7 @@ import android.service.autofill.UserData;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.LocalLog;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -73,6 +71,7 @@ import android.view.autofill.IAutoFillManager;
import android.view.autofill.IAutoFillManagerClient;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.IResultReceiver;
@@ -88,8 +87,8 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
* Entry point service for autofill management.
@@ -105,6 +104,9 @@ public final class AutofillManagerService extends SystemService {
static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions";
private static final char COMPAT_PACKAGE_DELIMITER = ':';
private static final char COMPAT_PACKAGE_URL_IDS_DELIMITER = ',';
private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_BEGIN = '[';
private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_END = ']';
private final Context mContext;
private final AutoFillUI mUi;
@@ -326,7 +328,7 @@ public final class AutofillManagerService extends SystemService {
if (service == null) {
service = new AutofillManagerServiceImpl(mContext, mLock, mRequestsHistory,
mUiLatencyHistory, mWtfHistory, resolvedUserId, mUi,
mDisabledUsers.get(resolvedUserId));
mAutofillCompatState, mDisabledUsers.get(resolvedUserId));
mServicesCache.put(userId, service);
addCompatibilityModeRequestsLocked(service, userId);
}
@@ -544,23 +546,24 @@ public final class AutofillManagerService extends SystemService {
private void addCompatibilityModeRequestsLocked(@NonNull AutofillManagerServiceImpl service
, int userId) {
mAutofillCompatState.reset();
final ArrayMap<String, Pair<Long, String>> compatPackages =
final ArrayMap<String, Long> compatPackages =
service.getCompatibilityPackagesLocked();
if (compatPackages == null || compatPackages.isEmpty()) {
return;
}
final Set<String> whiteListedPackages = getWhitelistedCompatModePackages();
final Map<String, String[]> whiteListedPackages = getWhitelistedCompatModePackages();
final int compatPackageCount = compatPackages.size();
for (int i = 0; i < compatPackageCount; i++) {
final String packageName = compatPackages.keyAt(i);
if (whiteListedPackages == null || !whiteListedPackages.contains(packageName)) {
if (whiteListedPackages == null || !whiteListedPackages.containsKey(packageName)) {
Slog.w(TAG, "Ignoring not whitelisted compat package " + packageName);
continue;
}
final Long maxVersionCode = compatPackages.valueAt(i).first;
final Long maxVersionCode = compatPackages.valueAt(i);
if (maxVersionCode != null) {
mAutofillCompatState.addCompatibilityModeRequest(packageName,
maxVersionCode, userId);
maxVersionCode, whiteListedPackages.get(packageName), userId);
}
}
}
@@ -571,16 +574,60 @@ public final class AutofillManagerService extends SystemService {
Settings.Global.AUTOFILL_COMPAT_ALLOWED_PACKAGES);
}
private @Nullable Set<String> getWhitelistedCompatModePackages() {
final String compatPackagesSetting = getWhitelistedCompatModePackagesFromSettings();
if (TextUtils.isEmpty(compatPackagesSetting)) {
@Nullable
private Map<String, String[]> getWhitelistedCompatModePackages() {
return getWhitelistedCompatModePackages(getWhitelistedCompatModePackagesFromSettings());
}
@Nullable
@VisibleForTesting
static Map<String, String[]> getWhitelistedCompatModePackages(String setting) {
if (TextUtils.isEmpty(setting)) {
return null;
}
final Set<String> compatPackages = new ArraySet<>();
final ArrayMap<String, String[]> compatPackages = new ArrayMap<>();
final SimpleStringSplitter splitter = new SimpleStringSplitter(COMPAT_PACKAGE_DELIMITER);
splitter.setString(compatPackagesSetting);
splitter.setString(setting);
while (splitter.hasNext()) {
compatPackages.add(splitter.next());
final String packageBlock = splitter.next();
final int urlBlockIndex = packageBlock.indexOf(COMPAT_PACKAGE_URL_IDS_BLOCK_BEGIN);
final String packageName;
final List<String> urlBarIds;
if (urlBlockIndex == -1) {
packageName = packageBlock;
urlBarIds = null;
} else {
if (packageBlock.charAt(packageBlock.length() - 1)
!= COMPAT_PACKAGE_URL_IDS_BLOCK_END) {
Slog.w(TAG, "Ignoring entry '" + packageBlock + "' on '" + setting
+ "'because it does not end on '" + COMPAT_PACKAGE_URL_IDS_BLOCK_END +
"'");
continue;
}
packageName = packageBlock.substring(0, urlBlockIndex);
urlBarIds = new ArrayList<>();
final String urlBarIdsBlock =
packageBlock.substring(urlBlockIndex + 1, packageBlock.length() - 1);
if (sVerbose) {
Slog.v(TAG, "pkg:" + packageName + ": block:" + packageBlock + ": urls:"
+ urlBarIds + ": block:" + urlBarIdsBlock + ":");
}
final SimpleStringSplitter splitter2 =
new SimpleStringSplitter(COMPAT_PACKAGE_URL_IDS_DELIMITER);
splitter2.setString(urlBarIdsBlock);
while (splitter2.hasNext()) {
final String urlBarId = splitter2.next();
urlBarIds.add(urlBarId);
}
}
if (urlBarIds == null) {
compatPackages.put(packageName, null);
} else {
final String[] urlBarIdsArray = new String[urlBarIds.size()];
urlBarIds.toArray(urlBarIdsArray);
compatPackages.put(packageName, urlBarIdsArray);
}
}
return compatPackages;
}
@@ -598,13 +645,41 @@ public final class AutofillManagerService extends SystemService {
return mAutofillCompatState.isCompatibilityModeRequested(
packageName, versionCode, userId);
}
}
private static final class AutofillCompatState {
/**
* Compatibility mode metadata per package.
*/
private static final class PackageCompatState {
private final long maxVersionCode;
private final String[] urlBarResourceIds;
PackageCompatState(long maxVersionCode, String[] urlBarResourceIds) {
this.maxVersionCode = maxVersionCode;
this.urlBarResourceIds = urlBarResourceIds;
}
@Override
public String toString() {
return "PackageCompatState: [maxVersionCode=" + maxVersionCode
+ ", urlBarResourceIds=" + Arrays.toString(urlBarResourceIds) + "]";
}
}
/**
* Compatibility mode metadata associated with all services.
*
* <p>This object is defined here instead of on each {@link AutofillManagerServiceImpl} because
* it cannot hold a lock on the main lock when
* {@link AutofillCompatState#isCompatibilityModeRequested(String, long, int)} is called by
* external services.
*/
static final class AutofillCompatState {
private final Object mLock = new Object();
@GuardedBy("mLock")
private SparseArray<ArrayMap<String, Long>> mUserSpecs;
private SparseArray<ArrayMap<String, PackageCompatState>> mUserSpecs;
boolean isCompatibilityModeRequested(@NonNull String packageName,
long versionCode, @UserIdInt int userId) {
@@ -612,30 +687,49 @@ public final class AutofillManagerService extends SystemService {
if (mUserSpecs == null) {
return false;
}
final ArrayMap<String, Long> userSpec = mUserSpecs.get(userId);
final ArrayMap<String, PackageCompatState> userSpec = mUserSpecs.get(userId);
if (userSpec == null) {
return false;
}
final Long maxVersionCode = userSpec.get(packageName);
if (maxVersionCode == null) {
final PackageCompatState metadata = userSpec.get(packageName);
if (metadata == null) {
return false;
}
return versionCode <= maxVersionCode;
return versionCode <= metadata.maxVersionCode;
}
}
@Nullable
String[] getUrlBarResourceIds(@NonNull String packageName, @UserIdInt int userId) {
synchronized (mLock) {
if (mUserSpecs == null) {
return null;
}
final ArrayMap<String, PackageCompatState> userSpec = mUserSpecs.get(userId);
if (userSpec == null) {
return null;
}
final PackageCompatState metadata = userSpec.get(packageName);
if (metadata == null) {
return null;
}
return metadata.urlBarResourceIds;
}
}
void addCompatibilityModeRequest(@NonNull String packageName,
long versionCode, @UserIdInt int userId) {
long versionCode, @Nullable String[] urlBarResourceIds, @UserIdInt int userId) {
synchronized (mLock) {
if (mUserSpecs == null) {
mUserSpecs = new SparseArray<>();
}
ArrayMap<String, Long> userSpec = mUserSpecs.get(userId);
ArrayMap<String, PackageCompatState> userSpec = mUserSpecs.get(userId);
if (userSpec == null) {
userSpec = new ArrayMap<>();
mUserSpecs.put(userId, userSpec);
}
userSpec.put(packageName, versionCode);
userSpec.put(packageName,
new PackageCompatState(versionCode, urlBarResourceIds));
}
}

View File

@@ -64,7 +64,6 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.LocalLog;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
@@ -78,12 +77,12 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.LocalServices;
import com.android.server.autofill.AutofillManagerService.AutofillCompatState;
import com.android.server.autofill.ui.AutoFillUI;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;
/**
@@ -164,12 +163,15 @@ final class AutofillManagerServiceImpl {
@GuardedBy("mLock")
private FillEventHistory mEventHistory;
/** Shared instance, doesn't need to be logged */
private final AutofillCompatState mAutofillCompatState;
/** When was {@link PruneTask} last executed? */
private long mLastPrune = 0;
AutofillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory,
LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui,
boolean disabled) {
AutofillCompatState autofillCompatState, boolean disabled) {
mContext = context;
mLock = lock;
mRequestsHistory = requestsHistory;
@@ -178,6 +180,7 @@ final class AutofillManagerServiceImpl {
mUserId = userId;
mUi = ui;
mFieldClassificationStrategy = new FieldClassificationStrategy(context, userId);
mAutofillCompatState = autofillCompatState;
updateLocked(disabled);
}
@@ -208,14 +211,9 @@ final class AutofillManagerServiceImpl {
}
@GuardedBy("mLock")
@Nullable
String getUrlBarResourceIdForCompatModeLocked(@NonNull String packageName) {
if (mInfo == null) {
Slog.w(TAG, "getUrlBarResourceIdForCompatModeLocked(): no mInfo");
return null;
}
return mInfo.getUrlBarResourceId(packageName);
String[] getUrlBarResourceIdsForCompatMode(@NonNull String packageName) {
return mAutofillCompatState.getUrlBarResourceIds(packageName, mUserId);
}
@Nullable
@@ -893,7 +891,7 @@ final class AutofillManagerServiceImpl {
pw.print(prefix); pw.print("Field classification enabled: ");
pw.println(isFieldClassificationEnabledLocked());
pw.print(prefix); pw.print("Compat pkgs: ");
final ArrayMap<String, Pair<Long, String>> compatPkgs = getCompatibilityPackagesLocked();
final ArrayMap<String, Long> compatPkgs = getCompatibilityPackagesLocked();
if (compatPkgs == null) {
pw.println("N/A");
} else {
@@ -1022,7 +1020,7 @@ final class AutofillManagerServiceImpl {
}
@GuardedBy("mLock")
@Nullable ArrayMap<String, Pair<Long, String>> getCompatibilityPackagesLocked() {
@Nullable ArrayMap<String, Long> getCompatibilityPackagesLocked() {
if (mInfo != null) {
return mInfo.getCompatibilityPackages();
}

View File

@@ -30,6 +30,7 @@ import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.ArrayUtils;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -168,21 +169,24 @@ public final class Helper {
/**
* Sanitize the {@code webDomain} property of the URL bar node on compat mode.
*
* @param structure Assist structure
* @param urlBarIds list of ids; only the first id found will be sanitized.
*/
public static void sanitizeUrlBar(@NonNull AssistStructure structure,
@NonNull String urlBarId) {
@NonNull String[] urlBarIds) {
final ViewNode urlBarNode = findViewNode(structure, (node) -> {
return urlBarId.equals(node.getIdEntry());
return ArrayUtils.contains(urlBarIds, node.getIdEntry());
});
if (urlBarNode != null) {
final String domain = urlBarNode.getText().toString();
if (domain.isEmpty()) {
if (sDebug) Slog.d(TAG, "sanitizeUrlBar(): empty on " + urlBarId);
if (sDebug) Slog.d(TAG, "sanitizeUrlBar(): empty on " + urlBarNode.getIdEntry());
return;
}
urlBarNode.setWebDomain(domain);
if (sDebug) {
Slog.d(TAG, "sanitizeUrlBar(): id=" + urlBarId + ", domain="
Slog.d(TAG, "sanitizeUrlBar(): id=" + urlBarNode.getIdEntry() + ", domain="
+ urlBarNode.getWebDomain());
}
}

View File

@@ -274,11 +274,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
if (mCompatMode) {
// Sanitize URL bar, if needed
final String urlBarId = mService.getUrlBarResourceIdForCompatModeLocked(
final String[] urlBarIds = mService.getUrlBarResourceIdsForCompatMode(
mComponentName.getPackageName());
if (sDebug) Slog.d(TAG, "url_bar in compat mode: " + urlBarId);
if (urlBarId != null) {
Helper.sanitizeUrlBar(structure, urlBarId);
if (sDebug) {
Slog.d(TAG, "url_bars in compat mode: " + Arrays.toString(urlBarIds));
}
if (urlBarIds != null) {
Helper.sanitizeUrlBar(structure, urlBarIds);
}
}
structure.sanitizeForParceling(true);

View File

@@ -15,6 +15,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
frameworks-base-testutils \
services.accessibility \
services.appwidget \
services.autofill \
services.backup \
services.core \
services.devicepolicy \

View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.autofill;
import static com.android.server.autofill.AutofillManagerService.getWhitelistedCompatModePackages;
import static com.google.common.truth.Truth.assertThat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.Map;
@RunWith(JUnit4.class)
public class AutofillManagerServiceTest {
@Test
public void testGetWhitelistedCompatModePackages_null() {
assertThat(getWhitelistedCompatModePackages(null)).isNull();
}
@Test
public void testGetWhitelistedCompatModePackages_empty() {
assertThat(getWhitelistedCompatModePackages("")).isNull();
}
@Test
public void testGetWhitelistedCompatModePackages_onePackageNoUrls() {
assertThat(getWhitelistedCompatModePackages("one_is_the_loniest_package"))
.containsExactly("one_is_the_loniest_package", null);
}
@Test
public void testGetWhitelistedCompatModePackages_onePackageMissingEndDelimiter() {
assertThat(getWhitelistedCompatModePackages("one_is_the_loniest_package[")).isEmpty();
}
@Test
public void testGetWhitelistedCompatModePackages_onePackageOneUrl() {
final Map<String, String[]> result =
getWhitelistedCompatModePackages("one_is_the_loniest_package[url]");
assertThat(result).hasSize(1);
assertThat(result.get("one_is_the_loniest_package")).asList().containsExactly("url");
}
@Test
public void testGetWhitelistedCompatModePackages_onePackageMultipleUrls() {
final Map<String, String[]> result =
getWhitelistedCompatModePackages("one_is_the_loniest_package[4,5,8,15,16,23,42]");
assertThat(result).hasSize(1);
assertThat(result.get("one_is_the_loniest_package")).asList()
.containsExactly("4", "5", "8", "15", "16", "23", "42");
}
@Test
public void testGetWhitelistedCompatModePackages_multiplePackagesOneInvalid() {
final Map<String, String[]> result = getWhitelistedCompatModePackages("one:two[");
assertThat(result).hasSize(1);
assertThat(result.get("one")).isNull();
}
@Test
public void testGetWhitelistedCompatModePackages_multiplePackagesMultipleUrls() {
final Map<String, String[]> result =
getWhitelistedCompatModePackages("p1[p1u1]:p2:p3[p3u1,p3u2]");
assertThat(result).hasSize(3);
assertThat(result.get("p1")).asList().containsExactly("p1u1");
assertThat(result.get("p2")).isNull();
assertThat(result.get("p3")).asList().containsExactly("p3u1", "p3u2");
}
@Test
public void testGetWhitelistedCompatModePackages_threePackagesOneInvalid() {
final Map<String, String[]> result =
getWhitelistedCompatModePackages("p1[p1u1]:p2[:p3[p3u1,p3u2]");
assertThat(result).hasSize(2);
assertThat(result.get("p1")).asList().containsExactly("p1u1");
assertThat(result.get("p3")).asList().containsExactly("p3u1", "p3u2");
}
}