summaryrefslogtreecommitdiffstats
path: root/services/core/java
diff options
context:
space:
mode:
authorFabrice Di Meglio <fdimeglio@google.com>2014-11-19 17:12:32 -0800
committerFabrice Di Meglio <fdimeglio@google.com>2015-03-30 10:58:35 -0700
commit1c1b47125da018b44240739db75f8898e064a948 (patch)
treec2c4b241798ae9e5d61fd09955b8c0d2704bb133 /services/core/java
parent523fe91af4baf26cd26e46c1418a072574959b73 (diff)
downloadframeworks_base-1c1b47125da018b44240739db75f8898e064a948.zip
frameworks_base-1c1b47125da018b44240739db75f8898e064a948.tar.gz
frameworks_base-1c1b47125da018b44240739db75f8898e064a948.tar.bz2
Add IntentFilter auto verification
The purpose of this feature is to prompt the Disambiguation dialog to Users as less as possible. - add the new "autoVerify" property to the IntentFilter class - add new APIs to PackageManager: verifyIntentFilter(int, int, List<String>), getIntentVerificationStatus(String, int), updateIntentVerificationStatus(String, int, int), getIntentFilterVerifications(String) for supporting IntentFilter verification - add support for multi-user - update PackageManager for IntentFilter verification: basically when we are installing a new package, ask for verification of all domains from the IntentFilters that have the "autoVerify" to true. This means that the PackageManager will send a well defined protected broadcast (with a new INTENT_FILTER_NEEDS_VERIFICATION action) to an IntentFilter verifier to do the real job of verification. We are passing in the broadcast Intent all the necessary data for doing the verification. The PackageManager will receive as response the result code of the domain verifications and, if needed, the list of domains that have failed the verification. - add a new INTENT_FILTER_VERIFICATION_AGENT permission that needs to be set by an intent filter verifier to be considered as a trustable party by the PackageManager. - add also a new BIND_INTENT_FILTER_VERIFIER permission for securing the binding between the PackageManager and a service doing the intent filter verifications. - add ResolveInfo filterNeedsVerification which is a boolean to knows if the IntentFilter is of a type that needs a verification (action VIEW, category BROWABLE, HTTP/HTTPS data URI) - add new "domain-preferred-apps" / "d" dump command for listing the prefered Apps for all domains - add new "intent-filter-verifiers" / "ivf" command for listing the IntentFilterVerifier used - introduce the IntentVerificationService which is a basic service for verifying IntentFilters. This service will send HTTPS requests to the domain declared in the IntentFilter(s) for doing the verification. This service has a low priority level so that it can be replaced by a more sophisticated one if needed. This service is updating the PackageManager intent verification states thru the updateIntentVerificationStatus(...) API. - update MockPackageManager Change-Id: I0bfed193d0bf1f7c7ac79f6c1b160b7ab93b5fb5
Diffstat (limited to 'services/core/java')
-rw-r--r--services/core/java/com/android/server/IntentResolver.java26
-rw-r--r--services/core/java/com/android/server/pm/IntentFilterVerificationKey.java63
-rw-r--r--services/core/java/com/android/server/pm/IntentFilterVerificationResponse.java43
-rw-r--r--services/core/java/com/android/server/pm/IntentFilterVerificationState.java125
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java618
-rw-r--r--services/core/java/com/android/server/pm/PackageSettingBase.java30
-rw-r--r--services/core/java/com/android/server/pm/Settings.java153
7 files changed, 1037 insertions, 21 deletions
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index cea1ebe..744156b 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -47,6 +47,7 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
final private static String TAG = "IntentResolver";
final private static boolean DEBUG = false;
final private static boolean localLOGV = DEBUG || false;
+ final private static boolean localVerificationLOGV = DEBUG || false;
public void addFilter(F f) {
if (localLOGV) {
@@ -478,7 +479,7 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
/**
* Returns whether the object associated with the given filter is
- * "stopped," that is whether it should not be included in the result
+ * "stopped", that is whether it should not be included in the result
* if the intent requests to excluded stopped objects.
*/
protected boolean isFilterStopped(F filter, int userId) {
@@ -486,6 +487,22 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
}
/**
+ * Returns whether the given filter is "verified" that is whether it has been verified against
+ * its data URIs.
+ *
+ * The verification would happen only and only if the Intent action is
+ * {@link android.content.Intent#ACTION_VIEW} and the Intent category is
+ * {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent data scheme
+ * is "http" or "https".
+ *
+ * @see android.content.IntentFilter#setAutoVerify(boolean)
+ * @see android.content.IntentFilter#getAutoVerify()
+ */
+ protected boolean isFilterVerified(F filter) {
+ return filter.isVerified();
+ }
+
+ /**
* Returns whether this filter is owned by this package. This must be
* implemented to provide correct filtering of Intents that have
* specified a package name they are to be delivered to.
@@ -710,6 +727,13 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
continue;
}
+ // Are we verified ?
+ if (filter.getAutoVerify()) {
+ if (localVerificationLOGV || debug) {
+ Slog.v(TAG, " Filter verified: " + isFilterVerified(filter));
+ }
+ }
+
// Do we already have this one?
if (!allowFilterResult(filter, dest)) {
if (debug) {
diff --git a/services/core/java/com/android/server/pm/IntentFilterVerificationKey.java b/services/core/java/com/android/server/pm/IntentFilterVerificationKey.java
new file mode 100644
index 0000000..399b03c
--- /dev/null
+++ b/services/core/java/com/android/server/pm/IntentFilterVerificationKey.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.pm;
+
+import java.util.Arrays;
+
+/**
+ * This is the key for the map of {@link android.content.pm.IntentFilterVerificationInfo}s
+ * maintained by the {@link com.android.server.pm.PackageManagerService}
+ */
+class IntentFilterVerificationKey {
+ public String domains;
+ public String packageName;
+ public String className;
+
+ public IntentFilterVerificationKey(String[] domains, String packageName, String className) {
+ StringBuilder sb = new StringBuilder();
+ for (String host : domains) {
+ sb.append(host);
+ }
+ this.domains = sb.toString();
+ this.packageName = packageName;
+ this.className = className;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ IntentFilterVerificationKey that = (IntentFilterVerificationKey) o;
+
+ if (domains != null ? !domains.equals(that.domains) : that.domains != null) return false;
+ if (className != null ? !className.equals(that.className) : that.className != null)
+ return false;
+ if (packageName != null ? !packageName.equals(that.packageName) : that.packageName != null)
+ return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = domains != null ? domains.hashCode() : 0;
+ result = 31 * result + (packageName != null ? packageName.hashCode() : 0);
+ result = 31 * result + (className != null ? className.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/IntentFilterVerificationResponse.java b/services/core/java/com/android/server/pm/IntentFilterVerificationResponse.java
new file mode 100644
index 0000000..ead399b
--- /dev/null
+++ b/services/core/java/com/android/server/pm/IntentFilterVerificationResponse.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.pm;
+
+
+import java.util.List;
+
+/* package private */ class IntentFilterVerificationResponse {
+ public final int callerUid;
+ public final int code;
+ public final List<String> failedDomains;
+
+ public IntentFilterVerificationResponse(int callerUid, int code, List<String> failedDomains) {
+ this.callerUid = callerUid;
+ this.code = code;
+ this.failedDomains = failedDomains;
+ }
+
+ public String getFailedDomainsString() {
+ StringBuilder sb = new StringBuilder();
+ for (String domain : failedDomains) {
+ if (sb.length() > 0) {
+ sb.append(" ");
+ }
+ sb.append(domain);
+ }
+ return sb.toString();
+ }
+}
diff --git a/services/core/java/com/android/server/pm/IntentFilterVerificationState.java b/services/core/java/com/android/server/pm/IntentFilterVerificationState.java
new file mode 100644
index 0000000..c09d6ae
--- /dev/null
+++ b/services/core/java/com/android/server/pm/IntentFilterVerificationState.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.pm;
+
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.util.ArraySet;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+public class IntentFilterVerificationState {
+ static final String TAG = IntentFilterVerificationState.class.getName();
+
+ public final static int STATE_UNDEFINED = 0;
+ public final static int STATE_VERIFICATION_PENDING = 1;
+ public final static int STATE_VERIFICATION_SUCCESS = 2;
+ public final static int STATE_VERIFICATION_FAILURE = 3;
+
+ private int mRequiredVerifierUid = 0;
+
+ private int mState;
+
+ private ArrayList<PackageParser.ActivityIntentInfo> mFilters = new ArrayList<>();
+ private ArraySet<String> mHosts = new ArraySet<>();
+ private int mUserId;
+
+ private String mPackageName;
+ private boolean mVerificationComplete;
+
+ public IntentFilterVerificationState(int verifierUid, int userId, String packageName) {
+ mRequiredVerifierUid = verifierUid;
+ mUserId = userId;
+ mPackageName = packageName;
+ mState = STATE_UNDEFINED;
+ mVerificationComplete = false;
+ }
+
+ public void setState(int state) {
+ if (state > STATE_VERIFICATION_FAILURE || state < STATE_UNDEFINED) {
+ mState = STATE_UNDEFINED;
+ } else {
+ mState = state;
+ }
+ }
+
+ public int getState() {
+ return mState;
+ }
+
+ public void setPendingState() {
+ setState(STATE_VERIFICATION_PENDING);
+ }
+
+ public ArrayList<PackageParser.ActivityIntentInfo> getFilters() {
+ return mFilters;
+ }
+
+ public boolean isVerificationComplete() {
+ return mVerificationComplete;
+ }
+
+ public boolean isVerified() {
+ if (mVerificationComplete) {
+ return (mState == STATE_VERIFICATION_SUCCESS);
+ }
+ return false;
+ }
+
+ public int getUserId() {
+ return mUserId;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public String getHostsString() {
+ StringBuilder sb = new StringBuilder();
+ final int count = mHosts.size();
+ for (int i=0; i<count; i++) {
+ if (i > 0) {
+ sb.append(" ");
+ }
+ sb.append(mHosts.valueAt(i));
+ }
+ return sb.toString();
+ }
+
+ public boolean setVerifierResponse(int callerUid, int code) {
+ if (mRequiredVerifierUid == callerUid) {
+ int state = STATE_UNDEFINED;
+ if (code == PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS) {
+ state = STATE_VERIFICATION_SUCCESS;
+ } else if (code == PackageManager.INTENT_FILTER_VERIFICATION_FAILURE) {
+ state = STATE_VERIFICATION_FAILURE;
+ }
+ mVerificationComplete = true;
+ setState(state);
+ return true;
+ }
+ Log.d(TAG, "Cannot set verifier response with callerUid:" + callerUid + " and code:" +
+ code + " as required verifierUid is:" + mRequiredVerifierUid);
+ return false;
+ }
+
+ public void addFilter(PackageParser.ActivityIntentInfo filter) {
+ mFilters.add(filter);
+ mHosts.addAll(filter.getHostsList());
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c7dc74f..b9dfc21 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -44,6 +44,10 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_USER_RESTRICTED;
import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
import static android.content.pm.PackageManager.INSTALL_FORWARD_LOCK;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
import static android.content.pm.PackageParser.isApkFile;
import static android.os.Process.PACKAGE_INFO_GID;
import static android.os.Process.SYSTEM_UID;
@@ -59,6 +63,8 @@ import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
+import android.Manifest;
+import android.content.pm.IntentFilterVerificationInfo;
import android.util.ArrayMap;
import com.android.internal.R;
@@ -504,6 +510,231 @@ public class PackageManagerService extends IPackageManager.Stub {
boolean mResolverReplaced = false;
+ private final ComponentName mIntentFilterVerifierComponent;
+ private int mIntentFilterVerificationToken = 0;
+
+ final SparseArray<IntentFilterVerificationState> mIntentFilterVerificationStates
+ = new SparseArray<IntentFilterVerificationState>();
+
+ private interface IntentFilterVerifier<T extends IntentFilter> {
+ boolean addOneIntentFilterVerification(int verifierId, int userId, int verificationId,
+ T filter, String packageName);
+ void startVerifications(int userId);
+ void receiveVerificationResponse(int verificationId);
+ }
+
+ private class IntentVerifierProxy implements IntentFilterVerifier<ActivityIntentInfo> {
+ private Context mContext;
+ private ComponentName mIntentFilterVerifierComponent;
+ private ArrayList<Integer> mCurrentIntentFilterVerifications = new ArrayList<Integer>();
+
+ public IntentVerifierProxy(Context context, ComponentName verifierComponent) {
+ mContext = context;
+ mIntentFilterVerifierComponent = verifierComponent;
+ }
+
+ private String getDefaultScheme() {
+ // TODO: replace SCHEME_HTTP with SCHEME_HTTPS
+ return IntentFilter.SCHEME_HTTP;
+ }
+
+ @Override
+ public void startVerifications(int userId) {
+ // Launch verifications requests
+ int count = mCurrentIntentFilterVerifications.size();
+ for (int n=0; n<count; n++) {
+ int verificationId = mCurrentIntentFilterVerifications.get(n);
+ final IntentFilterVerificationState ivs =
+ mIntentFilterVerificationStates.get(verificationId);
+
+ String packageName = ivs.getPackageName();
+ boolean modified = false;
+
+ ArrayList<PackageParser.ActivityIntentInfo> filters = ivs.getFilters();
+ final int filterCount = filters.size();
+ for (int m=0; m<filterCount; m++) {
+ PackageParser.ActivityIntentInfo filter = filters.get(m);
+ synchronized (mPackages) {
+ modified = mSettings.createIntentFilterVerificationIfNeededLPw(
+ packageName, filter.getHosts());
+ }
+ }
+ synchronized (mPackages) {
+ if (modified) {
+ scheduleWriteSettingsLocked();
+ }
+ }
+ sendVerificationRequest(userId, verificationId, ivs);
+ }
+ mCurrentIntentFilterVerifications.clear();
+ }
+
+ private void sendVerificationRequest(int userId, int verificationId,
+ IntentFilterVerificationState ivs) {
+
+ Intent verificationIntent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);
+ verificationIntent.putExtra(
+ PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID,
+ verificationId);
+ verificationIntent.putExtra(
+ PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME,
+ getDefaultScheme());
+ verificationIntent.putExtra(
+ PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS,
+ ivs.getHostsString());
+ verificationIntent.putExtra(
+ PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME,
+ ivs.getPackageName());
+ verificationIntent.setComponent(mIntentFilterVerifierComponent);
+ verificationIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+
+ UserHandle user = new UserHandle(userId);
+ mContext.sendBroadcastAsUser(verificationIntent, user);
+ Slog.d(TAG, "Sending IntenFilter verification broadcast");
+ }
+
+ public void receiveVerificationResponse(int verificationId) {
+ IntentFilterVerificationState ivs = mIntentFilterVerificationStates.get(verificationId);
+
+ final boolean verified = ivs.isVerified();
+
+ ArrayList<PackageParser.ActivityIntentInfo> filters = ivs.getFilters();
+ final int count = filters.size();
+ for (int n=0; n<count; n++) {
+ PackageParser.ActivityIntentInfo filter = filters.get(n);
+ filter.setVerified(verified);
+
+ Slog.d(TAG, "IntentFilter " + filter.toString() + " verified with result:"
+ + verified + " and hosts:" + ivs.getHostsString());
+ }
+
+ mIntentFilterVerificationStates.remove(verificationId);
+
+ final String packageName = ivs.getPackageName();
+ IntentFilterVerificationInfo ivi = null;
+
+ synchronized (mPackages) {
+ ivi = mSettings.getIntentFilterVerificationLPr(packageName);
+ }
+ if (ivi == null) {
+ Slog.w(TAG, "IntentFilterVerificationInfo not found for verificationId:"
+ + verificationId + " packageName:" + packageName);
+ return;
+ }
+ Slog.d(TAG, "Updating IntentFilterVerificationInfo for verificationId: "
+ + verificationId);
+
+ synchronized (mPackages) {
+ if (verified) {
+ ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS);
+ } else {
+ ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK);
+ }
+ scheduleWriteSettingsLocked();
+
+ final int userId = ivs.getUserId();
+ if (userId != UserHandle.USER_ALL) {
+ final int userStatus =
+ mSettings.getIntentFilterVerificationStatusLPr(packageName, userId);
+
+ int updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+ boolean needUpdate = false;
+
+ // We cannot override the STATUS_ALWAYS / STATUS_NEVER states if they have
+ // already been set by the User thru the Disambiguation dialog
+ switch (userStatus) {
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED:
+ if (verified) {
+ updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+ } else {
+ updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
+ }
+ needUpdate = true;
+ break;
+
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK:
+ if (verified) {
+ updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+ needUpdate = true;
+ }
+ break;
+
+ default:
+ // Nothing to do
+ }
+
+ if (needUpdate) {
+ mSettings.updateIntentFilterVerificationStatusLPw(
+ packageName, updatedStatus, userId);
+ scheduleWritePackageRestrictionsLocked(userId);
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean addOneIntentFilterVerification(int verifierId, int userId, int verificationId,
+ ActivityIntentInfo filter, String packageName) {
+ if (!(filter.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
+ filter.hasDataScheme(IntentFilter.SCHEME_HTTPS))) {
+ Slog.d(TAG, "IntentFilter does not contain HTTP nor HTTPS data scheme");
+ return false;
+ }
+ IntentFilterVerificationState ivs = mIntentFilterVerificationStates.get(verificationId);
+ if (ivs == null) {
+ ivs = createDomainVerificationState(verifierId, userId, verificationId,
+ packageName);
+ }
+ ArrayList<String> hosts = filter.getHostsList();
+ if (!hasValidHosts(hosts)) {
+ return false;
+ }
+ ivs.addFilter(filter);
+ return true;
+ }
+
+ private IntentFilterVerificationState createDomainVerificationState(int verifierId,
+ int userId, int verificationId, String packageName) {
+ IntentFilterVerificationState ivs = new IntentFilterVerificationState(
+ verifierId, userId, packageName);
+ ivs.setPendingState();
+ synchronized (mPackages) {
+ mIntentFilterVerificationStates.append(verificationId, ivs);
+ mCurrentIntentFilterVerifications.add(verificationId);
+ }
+ return ivs;
+ }
+
+ private boolean hasValidHosts(ArrayList<String> hosts) {
+ if (hosts.size() == 0) {
+ Slog.d(TAG, "IntentFilter does not contain any data hosts");
+ return false;
+ }
+ String hostEndBase = null;
+ for (String host : hosts) {
+ String[] hostParts = host.split("\\.");
+ // Should be at minimum a host like "example.com"
+ if (hostParts.length < 2) {
+ Slog.d(TAG, "IntentFilter does not contain a valid data host name: " + host);
+ return false;
+ }
+ // Verify that we have the same ending domain
+ int length = hostParts.length;
+ String hostEnd = hostParts[length - 1] + hostParts[length - 2];
+ if (hostEndBase == null) {
+ hostEndBase = hostEnd;
+ }
+ if (!hostEnd.equalsIgnoreCase(hostEndBase)) {
+ Slog.d(TAG, "IntentFilter does not contain the same data domains");
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ private IntentFilterVerifier mIntentFilterVerifier;
+
// Set of pending broadcasts for aggregating enable/disable of components.
static class PendingPackageBroadcasts {
// for each user id, a map of <package name -> components within that package>
@@ -590,6 +821,8 @@ public class PackageManagerService extends IPackageManager.Stub {
static final int WRITE_PACKAGE_RESTRICTIONS = 14;
static final int PACKAGE_VERIFIED = 15;
static final int CHECK_PENDING_VERIFICATION = 16;
+ static final int START_INTENT_FILTER_VERIFICATIONS = 17;
+ static final int INTENT_FILTER_VERIFIED = 18;
static final int WRITE_SETTINGS_DELAY = 10*1000; // 10 seconds
@@ -1240,6 +1473,54 @@ public class PackageManagerService extends IPackageManager.Stub {
break;
}
+ case START_INTENT_FILTER_VERIFICATIONS: {
+ int userId = msg.arg1;
+ int verifierUid = msg.arg2;
+ PackageParser.Package pkg = (PackageParser.Package)msg.obj;
+
+ verifyIntentFiltersIfNeeded(userId, verifierUid, pkg);
+ break;
+ }
+ case INTENT_FILTER_VERIFIED: {
+ final int verificationId = msg.arg1;
+
+ final IntentFilterVerificationState state = mIntentFilterVerificationStates.get(
+ verificationId);
+ if (state == null) {
+ Slog.w(TAG, "Invalid IntentFilter verification token "
+ + verificationId + " received");
+ break;
+ }
+
+ final int userId = state.getUserId();
+
+ Slog.d(TAG, "Processing IntentFilter verification with token:"
+ + verificationId + " and userId:" + userId);
+
+ final IntentFilterVerificationResponse response =
+ (IntentFilterVerificationResponse) msg.obj;
+
+ state.setVerifierResponse(response.callerUid, response.code);
+
+ Slog.d(TAG, "IntentFilter verification with token:" + verificationId
+ + " and userId:" + userId
+ + " is settings verifier response with response code:"
+ + response.code);
+
+ if (response.code == PackageManager.INTENT_FILTER_VERIFICATION_FAILURE) {
+ Slog.d(TAG, "Domains failing verification: "
+ + response.getFailedDomainsString());
+ }
+
+ if (state.isVerificationComplete()) {
+ mIntentFilterVerifier.receiveVerificationResponse(verificationId);
+ } else {
+ Slog.d(TAG, "IntentFilter verification with token:" + verificationId
+ + " was not said to be complete");
+ }
+
+ break;
+ }
}
}
}
@@ -1851,11 +2132,16 @@ public class PackageManagerService extends IPackageManager.Stub {
SystemClock.uptimeMillis());
mRequiredVerifierPackage = getRequiredVerifierLPr();
+
+ mInstallerService = new PackageInstallerService(context, this, mAppInstallDir);
+
+ mIntentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr();
+ mIntentFilterVerifier = new IntentVerifierProxy(mContext,
+ mIntentFilterVerifierComponent);
+
} // synchronized (mPackages)
} // synchronized (mInstallLock)
- mInstallerService = new PackageInstallerService(context, this, mAppInstallDir);
-
// Now after opening every single application zip, make sure they
// are all flushed. Not really needed, but keeps things nice and
// tidy.
@@ -1909,6 +2195,46 @@ public class PackageManagerService extends IPackageManager.Stub {
return requiredVerifier;
}
+ private ComponentName getIntentFilterVerifierComponentNameLPr() {
+ final Intent verification = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);
+ final List<ResolveInfo> receivers = queryIntentReceivers(verification, PACKAGE_MIME_TYPE,
+ PackageManager.GET_DISABLED_COMPONENTS, 0 /* userId */);
+
+ ComponentName verifierComponentName = null;
+
+ int priority = -1000;
+ final int N = receivers.size();
+ for (int i = 0; i < N; i++) {
+ final ResolveInfo info = receivers.get(i);
+
+ if (info.activityInfo == null) {
+ continue;
+ }
+
+ final String packageName = info.activityInfo.packageName;
+
+ final PackageSetting ps = mSettings.mPackages.get(packageName);
+ if (ps == null) {
+ continue;
+ }
+
+ if (checkPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT,
+ packageName, UserHandle.USER_OWNER) != PackageManager.PERMISSION_GRANTED) {
+ continue;
+ }
+
+ // Select the IntentFilterVerifier with the highest priority
+ if (priority < info.priority) {
+ priority = info.priority;
+ verifierComponentName = new ComponentName(packageName, info.activityInfo.name);
+ Slog.d(TAG, "Selecting IntentFilterVerifier: " + verifierComponentName +
+ " with priority: " + info.priority);
+ }
+ }
+
+ return verifierComponentName;
+ }
+
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
@@ -3532,14 +3858,20 @@ public class PackageManagerService extends IPackageManager.Stub {
resolveInfo = queryCrossProfileIntents(
matchingFilters, intent, resolvedType, flags, userId);
- // Check for results in the current profile.
+ // Check for results in the current profile. Adding GET_RESOLVED_FILTER flags
+ // as we need it later
List<ResolveInfo> result = mActivities.queryIntent(
intent, resolvedType, flags, userId);
if (resolveInfo != null) {
result.add(resolveInfo);
Collections.sort(result, mResolvePrioritySorter);
}
- return filterIfNotPrimaryUser(result, userId);
+ result = filterIfNotPrimaryUser(result, userId);
+ if (result.size() > 1) {
+ return filterCandidatesWithDomainPreferedActivitiesLPw(result);
+ }
+
+ return result;
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
@@ -3570,6 +3902,49 @@ public class PackageManagerService extends IPackageManager.Stub {
return resolveInfos;
}
+ private List<ResolveInfo> filterCandidatesWithDomainPreferedActivitiesLPw(
+ List<ResolveInfo> candidates) {
+ if (DEBUG_PREFERRED) {
+ Slog.v("TAG", "Filtering results with prefered activities. Candidates count: " +
+ candidates.size());
+ }
+ final int userId = UserHandle.getCallingUserId();
+ ArrayList<ResolveInfo> result = new ArrayList<ResolveInfo>(candidates);
+ synchronized (mPackages) {
+ final int count = result.size();
+ for (int n = count-1; n >= 0; n--) {
+ ResolveInfo info = result.get(n);
+ if (!info.filterNeedsVerification) {
+ continue;
+ }
+ String packageName = info.activityInfo.packageName;
+ PackageSetting ps = mSettings.mPackages.get(packageName);
+ if (ps != null) {
+ // Try to get the status from User settings first
+ int status = ps.getDomainVerificationStatusForUser(userId);
+ // if none available, get the master status
+ if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
+ if (ps.getIntentFilterVerificationInfo() != null) {
+ status = ps.getIntentFilterVerificationInfo().getStatus();
+ }
+ }
+ if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
+ result.clear();
+ result.add(info);
+ // We break the for loop as we are good to go
+ break;
+ } else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
+ result.remove(n);
+ }
+ }
+ }
+ }
+ if (DEBUG_PREFERRED) {
+ Slog.v("TAG", "Filtered results with prefered activities. New candidates count: " +
+ result.size());
+ }
+ return result;
+ }
private ResolveInfo querySkipCurrentProfileIntents(
List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
@@ -7444,6 +7819,9 @@ public class PackageManagerService extends IPackageManager.Stub {
if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
res.filter = info;
}
+ if (info != null) {
+ res.filterNeedsVerification = info.needsVerification();
+ }
res.priority = info.getPriority();
res.preferredOrder = activity.owner.mPreferredOrder;
//System.out.println("Result: " + res.activityInfo.className +
@@ -7666,8 +8044,6 @@ public class PackageManagerService extends IPackageManager.Stub {
}
res.priority = info.getPriority();
res.preferredOrder = service.owner.mPreferredOrder;
- //System.out.println("Result: " + res.activityInfo.className +
- // " = " + res.priority);
res.match = match;
res.isDefault = info.hasDefault;
res.labelRes = info.labelRes;
@@ -8544,6 +8920,45 @@ public class PackageManagerService extends IPackageManager.Stub {
android.provider.Settings.Global.PACKAGE_VERIFIER_ENABLE, 1) == 1;
}
+ @Override
+ public void verifyIntentFilter(int id, int verificationCode, List<String> outFailedDomains)
+ throws RemoteException {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT,
+ "Only intentfilter verification agents can verify applications");
+
+ final Message msg = mHandler.obtainMessage(INTENT_FILTER_VERIFIED);
+ final IntentFilterVerificationResponse response = new IntentFilterVerificationResponse(
+ Binder.getCallingUid(), verificationCode, outFailedDomains);
+ msg.arg1 = id;
+ msg.obj = response;
+ mHandler.sendMessage(msg);
+ }
+
+ @Override
+ public int getIntentVerificationStatus(String packageName, int userId) {
+ synchronized (mPackages) {
+ return mSettings.getIntentFilterVerificationStatusLPr(packageName, userId);
+ }
+ }
+
+ @Override
+ public boolean updateIntentVerificationStatus(String packageName, int status, int userId) {
+ boolean result = false;
+ synchronized (mPackages) {
+ result = mSettings.updateIntentFilterVerificationStatusLPw(packageName, status, userId);
+ }
+ scheduleWritePackageRestrictionsLocked(userId);
+ return result;
+ }
+
+ @Override
+ public List<IntentFilterVerificationInfo> getIntentFilterVerifications(String packageName) {
+ synchronized (mPackages) {
+ return mSettings.getIntentFilterVerificationsLPr(packageName);
+ }
+ }
+
/**
* Get the "allow unknown sources" setting.
*
@@ -10708,6 +11123,8 @@ public class PackageManagerService extends IPackageManager.Stub {
return;
}
+ startIntentFilterVerifications(args.user.getIdentifier(), pkg);
+
if (replace) {
replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
installerPackageName, res);
@@ -10723,6 +11140,86 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
+ private void startIntentFilterVerifications(int userId, PackageParser.Package pkg) {
+ if (mIntentFilterVerifierComponent == null) {
+ Slog.d(TAG, "No IntentFilter verification will not be done as "
+ + "there is no IntentFilterVerifier available!");
+ return;
+ }
+
+ final int verifierUid = getPackageUid(
+ mIntentFilterVerifierComponent.getPackageName(),
+ (userId == UserHandle.USER_ALL) ? UserHandle.USER_OWNER : userId);
+
+ mHandler.removeMessages(START_INTENT_FILTER_VERIFICATIONS);
+ final Message msg = mHandler.obtainMessage(START_INTENT_FILTER_VERIFICATIONS);
+ msg.obj = pkg;
+ msg.arg1 = userId;
+ msg.arg2 = verifierUid;
+
+ mHandler.sendMessage(msg);
+ }
+
+ private void verifyIntentFiltersIfNeeded(int userId, int verifierUid,
+ PackageParser.Package pkg) {
+ int size = pkg.activities.size();
+ if (size == 0) {
+ Slog.d(TAG, "No activity, so no need to verify any IntentFilter!");
+ return;
+ }
+
+ Slog.d(TAG, "Checking for userId:" + userId + " if any IntentFilter from the " + size
+ + " Activities needs verification ...");
+
+ final int verificationId = mIntentFilterVerificationToken++;
+ int count = 0;
+ synchronized (mPackages) {
+ for (PackageParser.Activity a : pkg.activities) {
+ for (ActivityIntentInfo filter : a.intents) {
+ boolean needFilterVerification = filter.needsVerification() &&
+ !filter.isVerified();
+ if (needFilterVerification && needNetworkVerificationLPr(filter)) {
+ Slog.d(TAG, "Verification needed for IntentFilter:" + filter.toString());
+ mIntentFilterVerifier.addOneIntentFilterVerification(
+ verifierUid, userId, verificationId, filter, pkg.packageName);
+ count++;
+ } else {
+ Slog.d(TAG, "No verification needed for IntentFilter:" + filter.toString());
+ }
+ }
+ }
+ }
+
+ if (count > 0) {
+ mIntentFilterVerifier.startVerifications(userId);
+ Slog.d(TAG, "Started " + count + " IntentFilter verification"
+ + (count > 1 ? "s" : "") + " for userId:" + userId + "!");
+ } else {
+ Slog.d(TAG, "No need to start any IntentFilter verification!");
+ }
+ }
+
+ private boolean needNetworkVerificationLPr(ActivityIntentInfo filter) {
+ final ComponentName cn = filter.activity.getComponentName();
+ final String packageName = cn.getPackageName();
+
+ IntentFilterVerificationInfo ivi = mSettings.getIntentFilterVerificationLPr(
+ packageName);
+ if (ivi == null) {
+ return true;
+ }
+ int status = ivi.getStatus();
+ switch (status) {
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED:
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK:
+ return true;
+
+ default:
+ // Nothing to do
+ return false;
+ }
+ }
+
private static boolean isMultiArch(PackageSetting ps) {
return (ps.pkgFlags & ApplicationInfo.FLAG_MULTIARCH) != 0;
}
@@ -11066,6 +11563,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
clearPackagePreferredActivitiesLPw(deletedPs.name, UserHandle.USER_ALL);
+ clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL);
}
// make sure to preserve per-user disabled state if this removal was just
// a downgrade of a system app to the factory package
@@ -11294,8 +11792,8 @@ public class PackageManagerService extends IPackageManager.Stub {
true, //notLaunched
false, //hidden
null, null, null,
- false // blockUninstall
- );
+ false, // blockUninstall
+ INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
if (!isSystemApp(ps)) {
if (ps.isAnyInstalled(sUserManager.getUserIds())) {
// Other user still have this package installed, so all
@@ -11918,6 +12416,19 @@ public class PackageManagerService extends IPackageManager.Stub {
return changed;
}
+ /** This method takes a specific user id as well as UserHandle.USER_ALL. */
+ void clearIntentFilterVerificationsLPw(String packageName, int userId) {
+ if (userId == UserHandle.USER_ALL) {
+ mSettings.removeIntentFilterVerificationLPw(packageName, sUserManager.getUserIds());
+ for (int oneUserId : sUserManager.getUserIds()) {
+ scheduleWritePackageRestrictionsLocked(oneUserId);
+ }
+ } else {
+ mSettings.removeIntentFilterVerificationLPw(packageName, userId);
+ scheduleWritePackageRestrictionsLocked(userId);
+ }
+ }
+
@Override
public void resetPreferredActivities(int userId) {
/* TODO: Actually use userId. Why is it being passed in? */
@@ -12428,6 +12939,8 @@ public class PackageManagerService extends IPackageManager.Stub {
public static final int DUMP_KEYSETS = 1 << 11;
public static final int DUMP_VERSION = 1 << 12;
public static final int DUMP_INSTALLS = 1 << 13;
+ public static final int DUMP_INTENT_FILTER_VERIFIERS = 1 << 14;
+ public static final int DUMP_DOMAIN_PREFERRED = 1 << 15;
public static final int OPTION_SHOW_FILTERS = 1 << 0;
@@ -12533,6 +13046,8 @@ public class PackageManagerService extends IPackageManager.Stub {
pw.println(" write: write current settings now");
pw.println(" <package.name>: info about given package");
pw.println(" installs: details about install sessions");
+ pw.println(" d[omain-preferred-apps]: print domains preferred apps");
+ pw.println(" i[ntent-filter-verifiers]|ifv: print intent filter verifier info");
return;
} else if ("--checkin".equals(opt)) {
checkin = true;
@@ -12569,6 +13084,8 @@ public class PackageManagerService extends IPackageManager.Stub {
fullPreferred = true;
opti++;
}
+ } else if ("d".equals(cmd) || "domain-preferred-apps".equals(cmd)) {
+ dumpState.setDump(DumpState.DUMP_DOMAIN_PREFERRED);
} else if ("p".equals(cmd) || "packages".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_PACKAGES);
} else if ("s".equals(cmd) || "shared-users".equals(cmd)) {
@@ -12579,6 +13096,9 @@ public class PackageManagerService extends IPackageManager.Stub {
dumpState.setDump(DumpState.DUMP_MESSAGES);
} else if ("v".equals(cmd) || "verifiers".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_VERIFIERS);
+ } else if ("i".equals(cmd) || "ifv".equals(cmd)
+ || "intent-filter-verifiers".equals(cmd)) {
+ dumpState.setDump(DumpState.DUMP_INTENT_FILTER_VERIFIERS);
} else if ("version".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_VERSION);
} else if ("k".equals(cmd) || "keysets".equals(cmd)) {
@@ -12634,6 +13154,29 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
+ if (dumpState.isDumping(DumpState.DUMP_INTENT_FILTER_VERIFIERS) &&
+ packageName == null) {
+ if (mIntentFilterVerifierComponent != null) {
+ String verifierPackageName = mIntentFilterVerifierComponent.getPackageName();
+ if (!checkin) {
+ if (dumpState.onTitlePrinted())
+ pw.println();
+ pw.println("Intent Filter Verifier:");
+ pw.print(" Using: ");
+ pw.print(verifierPackageName);
+ pw.print(" (uid=");
+ pw.print(getPackageUid(verifierPackageName, 0));
+ pw.println(")");
+ } else if (verifierPackageName != null) {
+ pw.print("ifv,"); pw.print(verifierPackageName);
+ pw.print(","); pw.println(getPackageUid(verifierPackageName, 0));
+ }
+ } else {
+ pw.println();
+ pw.println("No Intent Filter Verifier available!");
+ }
+ }
+
if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) {
boolean printedHeader = false;
final Iterator<String> it = mSharedLibraries.keySet().iterator();
@@ -12753,6 +13296,65 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) {
+ pw.println();
+ int count = mSettings.mPackages.size();
+ if (count == 0) {
+ pw.println("No domain preferred apps!");
+ pw.println();
+ } else {
+ final String prefix = " ";
+ Collection<PackageSetting> allPackageSettings = mSettings.mPackages.values();
+ if (allPackageSettings.size() == 0) {
+ pw.println("No domain preferred apps!");
+ pw.println();
+ } else {
+ pw.println("Domain preferred apps status:");
+ pw.println();
+ count = 0;
+ for (PackageSetting ps : allPackageSettings) {
+ IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo();
+ if (ivi == null || ivi.getPackageName() == null) continue;
+ pw.println(prefix + "Package Name: " + ivi.getPackageName());
+ pw.println(prefix + "Domains: " + ivi.getDomainsString());
+ pw.println(prefix + "Status: " + ivi.getStatusString());
+ pw.println();
+ count++;
+ }
+ if (count == 0) {
+ pw.println(prefix + "No domain preferred app status!");
+ pw.println();
+ }
+ for (int userId : sUserManager.getUserIds()) {
+ pw.println("Domain preferred apps for User " + userId + ":");
+ pw.println();
+ count = 0;
+ for (PackageSetting ps : allPackageSettings) {
+ IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo();
+ if (ivi == null || ivi.getPackageName() == null) {
+ continue;
+ }
+ final int status = ps.getDomainVerificationStatusForUser(userId);
+ if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
+ continue;
+ }
+ pw.println(prefix + "Package Name: " + ivi.getPackageName());
+ pw.println(prefix + "Domains: " + ivi.getDomainsString());
+ String statusStr = IntentFilterVerificationInfo.
+ getStatusStringFromValue(status);
+ pw.println(prefix + "Status: " + statusStr);
+ pw.println();
+ count++;
+ }
+ if (count == 0) {
+ pw.println(prefix + "No domain preferred apps!");
+ pw.println();
+ }
+ }
+ }
+ }
+ }
+
if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
mSettings.dumpPermissionsLPr(pw, packageName, dumpState);
if (packageName == null) {
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 35df33b..20120de 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -20,6 +20,8 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import android.content.pm.IntentFilterVerificationInfo;
+import android.content.pm.PackageManager;
import android.content.pm.PackageUserState;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -108,6 +110,9 @@ abstract class PackageSettingBase extends SettingBase {
/* package name of the app that installed this package */
String installerPackageName;
+
+ IntentFilterVerificationInfo verificationInfo;
+
PackageSettingBase(String name, String realName, File codePath, File resourcePath,
String legacyNativeLibraryPathString, String primaryCpuAbiString,
String secondaryCpuAbiString, String cpuAbiOverrideString,
@@ -214,6 +219,7 @@ abstract class PackageSettingBase extends SettingBase {
}
installStatus = base.installStatus;
keySetData = base.keySetData;
+ verificationInfo = base.verificationInfo;
}
private PackageUserState modifyUserState(int userId) {
@@ -317,7 +323,7 @@ abstract class PackageSettingBase extends SettingBase {
void setUserState(int userId, int enabled, boolean installed, boolean stopped,
boolean notLaunched, boolean hidden,
String lastDisableAppCaller, ArraySet<String> enabledComponents,
- ArraySet<String> disabledComponents, boolean blockUninstall) {
+ ArraySet<String> disabledComponents, boolean blockUninstall, int domainVerifState) {
PackageUserState state = modifyUserState(userId);
state.enabled = enabled;
state.installed = installed;
@@ -328,6 +334,7 @@ abstract class PackageSettingBase extends SettingBase {
state.enabledComponents = enabledComponents;
state.disabledComponents = disabledComponents;
state.blockUninstall = blockUninstall;
+ state.domainVerificationStatus = domainVerifState;
}
ArraySet<String> getEnabledComponents(int userId) {
@@ -415,4 +422,25 @@ abstract class PackageSettingBase extends SettingBase {
void removeUser(int userId) {
userState.delete(userId);
}
+
+ public IntentFilterVerificationInfo getIntentFilterVerificationInfo() {
+ return verificationInfo;
+ }
+
+ public void setIntentFilterVerificationInfo(IntentFilterVerificationInfo info) {
+ verificationInfo = info;
+ }
+
+ public int getDomainVerificationStatusForUser(int userId) {
+ return readUserState(userId).domainVerificationStatus;
+ }
+
+ public void setDomainVerificationStatusForUser(int status, int userId) {
+ modifyUserState(userId).domainVerificationStatus = status;
+ }
+
+ public void clearDomainVerificationStatusForUser(int userId) {
+ modifyUserState(userId).domainVerificationStatus =
+ PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+ }
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 8f185ec..dd58813 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -22,11 +22,13 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.PACKAGE_INFO_GID;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
+import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Binder;
@@ -41,6 +43,7 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.AtomicFile;
+import android.text.TextUtils;
import android.util.LogPrinter;
import android.util.SparseBooleanArray;
@@ -88,6 +91,7 @@ import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
@@ -160,6 +164,7 @@ final class Settings {
"persistent-preferred-activities";
static final String TAG_CROSS_PROFILE_INTENT_FILTERS =
"crossProfile-intent-filters";
+ public static final String TAG_DOMAIN_VERIFICATION = "domain-verification";
private static final String ATTR_NAME = "name";
private static final String ATTR_USER = "user";
@@ -174,6 +179,7 @@ final class Settings {
private static final String ATTR_HIDDEN = "hidden";
private static final String ATTR_INSTALLED = "inst";
private static final String ATTR_BLOCK_UNINSTALL = "blockUninstall";
+ private static final String ATTR_DOMAIN_VERIFICATON_STATE = "domainVerificationStatus";
private final Object mLock;
@@ -590,8 +596,8 @@ final class Settings {
true, // notLaunched
false, // hidden
null, null, null,
- false // blockUninstall
- );
+ false, // blockUninstall
+ INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
writePackageRestrictionsLPr(user.id);
}
}
@@ -865,7 +871,7 @@ final class Settings {
if (mOtherUserIds.get(uid) != null) {
PackageManagerService.reportSettingsProblem(Log.ERROR,
"Adding duplicate shared id: " + uid
- + " name=" + name);
+ + " name=" + name);
return false;
}
mOtherUserIds.put(uid, obj);
@@ -931,6 +937,96 @@ final class Settings {
return cpir;
}
+ /**
+ * The following functions suppose that you have a lock for managing access to the
+ * mIntentFiltersVerifications map.
+ */
+
+ /* package protected */
+ IntentFilterVerificationInfo getIntentFilterVerificationLPr(String packageName) {
+ PackageSetting ps = mPackages.get(packageName);
+ if (ps == null) {
+ Slog.w(PackageManagerService.TAG, "No package known for name: " + packageName);
+ return null;
+ }
+ return ps.getIntentFilterVerificationInfo();
+ }
+
+ /* package protected */
+ boolean createIntentFilterVerificationIfNeededLPw(String packageName, String[] domains) {
+ PackageSetting ps = mPackages.get(packageName);
+ if (ps == null) {
+ Slog.w(PackageManagerService.TAG, "No package known for name: " + packageName);
+ return false;
+ }
+ if (ps.getIntentFilterVerificationInfo() == null) {
+ IntentFilterVerificationInfo ivi = new IntentFilterVerificationInfo(packageName, domains);
+ ps.setIntentFilterVerificationInfo(ivi);
+ return false;
+ }
+ return true;
+ }
+
+ int getIntentFilterVerificationStatusLPr(String packageName, int userId) {
+ PackageSetting ps = mPackages.get(packageName);
+ if (ps == null) {
+ Slog.w(PackageManagerService.TAG, "No package known for name: " + packageName);
+ return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+ }
+ int status = ps.getDomainVerificationStatusForUser(userId);
+ if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
+ if (ps.getIntentFilterVerificationInfo() != null) {
+ status = ps.getIntentFilterVerificationInfo().getStatus();
+ }
+ }
+ return status;
+ }
+
+ boolean updateIntentFilterVerificationStatusLPw(String packageName, int status, int userId) {
+ PackageSetting ps = mPackages.get(packageName);
+ if (ps == null) {
+ Slog.w(PackageManagerService.TAG, "No package known for name: " + packageName);
+ return false;
+ }
+ ps.setDomainVerificationStatusForUser(status, userId);
+ return true;
+ }
+
+ /**
+ * Used for dump. Should be read only.
+ */
+ List<IntentFilterVerificationInfo> getIntentFilterVerificationsLPr(
+ String packageName) {
+ if (packageName == null) {
+ return Collections.<IntentFilterVerificationInfo>emptyList();
+ }
+ ArrayList<IntentFilterVerificationInfo> result = new ArrayList<>();
+ for (PackageSetting ps : mPackages.values()) {
+ IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo();
+ if (ivi == null || TextUtils.isEmpty(ivi.getPackageName()) ||
+ !ivi.getPackageName().equalsIgnoreCase(packageName)) {
+ continue;
+ }
+ result.add(ivi);
+ }
+ return result;
+ }
+
+ void removeIntentFilterVerificationLPw(String packageName, int userId) {
+ PackageSetting ps = mPackages.get(packageName);
+ if (ps == null) {
+ Slog.w(PackageManagerService.TAG, "No package known for name: " + packageName);
+ return;
+ }
+ ps.clearDomainVerificationStatusForUser(userId);
+ }
+
+ void removeIntentFilterVerificationLPw(String packageName, int[] userIds) {
+ for (int userId : userIds) {
+ removeIntentFilterVerificationLPw(packageName, userId);
+ }
+ }
+
private File getUserPackagesStateFile(int userId) {
// TODO: Implement a cleaner solution when adding tests.
// This instead of Environment.getUserSystemDirectory(userId) to support testing.
@@ -1083,6 +1179,25 @@ final class Settings {
}
}
+ private void readDomainVerificationLPw(XmlPullParser parser, PackageSettingBase packageSetting)
+ throws XmlPullParserException, IOException {
+ IntentFilterVerificationInfo ivi = new IntentFilterVerificationInfo(parser);
+ packageSetting.setIntentFilterVerificationInfo(ivi);
+ Log.d(TAG, "Read domain verification for package:" + ivi.getPackageName());
+ }
+
+ void writeDomainVerificationsLPr(XmlSerializer serializer, String packageName,
+ IntentFilterVerificationInfo verificationInfo)
+ throws IllegalArgumentException, IllegalStateException, IOException {
+ if (verificationInfo != null && verificationInfo.getPackageName() != null) {
+ serializer.startTag(null, TAG_DOMAIN_VERIFICATION);
+ verificationInfo.writeToXml(serializer);
+ Log.d(TAG, "Wrote domain verification for package: "
+ + verificationInfo.getPackageName());
+ serializer.endTag(null, TAG_DOMAIN_VERIFICATION);
+ }
+ }
+
void readPackageRestrictionsLPr(int userId) {
if (DEBUG_MU) {
Log.i(TAG, "Reading package restrictions for user=" + userId);
@@ -1127,8 +1242,8 @@ final class Settings {
false, // notLaunched
false, // hidden
null, null, null,
- false // blockUninstall
- );
+ false, // blockUninstall
+ INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
}
return;
}
@@ -1197,6 +1312,12 @@ final class Settings {
final boolean blockUninstall = blockUninstallStr == null
? false : Boolean.parseBoolean(blockUninstallStr);
+ final String verifStateStr =
+ parser.getAttributeValue(null, ATTR_DOMAIN_VERIFICATON_STATE);
+ final int verifState = (verifStateStr == null) ?
+ PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED :
+ Integer.parseInt(verifStateStr);
+
ArraySet<String> enabledComponents = null;
ArraySet<String> disabledComponents = null;
@@ -1217,7 +1338,8 @@ final class Settings {
}
ps.setUserState(userId, enabled, installed, stopped, notLaunched, hidden,
- enabledCaller, enabledComponents, disabledComponents, blockUninstall);
+ enabledCaller, enabledComponents, disabledComponents, blockUninstall,
+ verifState);
} else if (tagName.equals("preferred-activities")) {
readPreferredActivitiesLPw(parser, userId);
} else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) {
@@ -1364,7 +1486,9 @@ final class Settings {
&& ustate.enabledComponents.size() > 0)
|| (ustate.disabledComponents != null
&& ustate.disabledComponents.size() > 0)
- || ustate.blockUninstall) {
+ || ustate.blockUninstall
+ || (ustate.domainVerificationStatus !=
+ PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED)) {
serializer.startTag(null, TAG_PACKAGE);
serializer.attribute(null, ATTR_NAME, pkg.name);
if (DEBUG_MU) Log.i(TAG, " pkg=" + pkg.name + ", state=" + ustate.enabled);
@@ -1392,6 +1516,11 @@ final class Settings {
ustate.lastDisableAppCaller);
}
}
+ if (ustate.domainVerificationStatus !=
+ PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
+ serializer.attribute(null, ATTR_DOMAIN_VERIFICATON_STATE,
+ Integer.toString(ustate.domainVerificationStatus));
+ }
if (ustate.enabledComponents != null
&& ustate.enabledComponents.size() > 0) {
serializer.startTag(null, TAG_ENABLED_COMPONENTS);
@@ -1418,9 +1547,7 @@ final class Settings {
}
writePreferredActivitiesLPr(serializer, userId, true);
-
writePersistentPreferredActivitiesLPr(serializer, userId);
-
writeCrossProfileIntentFiltersLPr(serializer, userId);
serializer.endTag(null, TAG_PACKAGE_RESTRICTIONS);
@@ -1933,6 +2060,7 @@ final class Settings {
writeSigningKeySetsLPr(serializer, pkg.keySetData);
writeUpgradeKeySetsLPr(serializer, pkg.keySetData);
writeKeySetAliasesLPr(serializer, pkg.keySetData);
+ writeDomainVerificationsLPr(serializer, pkg.name, pkg.verificationInfo);
serializer.endTag(null, "package");
}
@@ -2109,7 +2237,8 @@ final class Settings {
// TODO: check whether this is okay! as it is very
// similar to how preferred-activities are treated
readCrossProfileIntentFiltersLPw(parser, 0);
- } else if (tagName.equals("updated-package")) {
+ }
+ else if (tagName.equals("updated-package")) {
readDisabledSysPackageLPw(parser);
} else if (tagName.equals("cleaning-package")) {
String name = parser.getAttributeValue(null, ATTR_NAME);
@@ -3024,6 +3153,8 @@ final class Settings {
long id = Long.parseLong(parser.getAttributeValue(null, "identifier"));
String alias = parser.getAttributeValue(null, "alias");
packageSetting.keySetData.addDefinedKeySet(id, alias);
+ } else if (tagName.equals(TAG_DOMAIN_VERIFICATION)) {
+ readDomainVerificationLPw(parser, packageSetting);
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Unknown element under <package>: " + parser.getName());
@@ -3388,7 +3519,7 @@ final class Settings {
return false;
}
- private List<UserInfo> getAllUsers() {
+ List<UserInfo> getAllUsers() {
long id = Binder.clearCallingIdentity();
try {
return UserManagerService.getInstance().getUsers(false);