diff options
author | Christopher Tate <ctate@google.com> | 2020-05-28 17:49:53 -0700 |
---|---|---|
committer | Anis Assi <anisassi@google.com> | 2020-06-30 16:10:15 -0700 |
commit | 2b71d2b7243810bee5505bd62c30c42a5cb6a1bb (patch) | |
tree | 50027ea4843541500af8956d1447f2e18d9962ed | |
parent | 2f0f72046970cfea594466aab506566aac672749 (diff) | |
download | base-android-8.1.0_r80.tar.gz |
Only autoVerify at install for new hostsandroid-8.1.0_r80
Re-run app link verification at update time only when the set of hosts
has expanded. Intentionally revoke verify history when an app stops
using autoVerify, as a one-time measure to place it back into the
non-autoverify model for tracking the user's launch preferences. If the
app starts using autoVerify again later, it behaves identically to an
app that has never done so before.
Bug: 151475497
Bug: 146204120
Test: described on master CL
Merged-In: I200d85085ce79842a3ed39377d1f75ec381c8991
Change-Id: Ibaf087946966ad82d60c7b255e3ee75990716b63
(cherry picked from commit 153de338c182dbdbcbc3b32186cf783805fb7757)
-rw-r--r-- | services/core/java/com/android/server/pm/PackageManagerService.java | 204 | ||||
-rw-r--r-- | services/core/java/com/android/server/pm/Settings.java | 10 |
2 files changed, 156 insertions, 58 deletions
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 6e4bfec135b4..edb1dd7cc22c 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -114,6 +114,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.AppOpsManager; +import android.app.BroadcastOptions; import android.app.IActivityManager; import android.app.ResourcesManager; import android.app.admin.IDevicePolicyManager; @@ -1103,9 +1104,13 @@ public class PackageManagerService extends IPackageManager.Stub verificationIntent.setComponent(mIntentFilterVerifierComponent); verificationIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + final long whitelistTimeout = getVerificationTimeout(); + final BroadcastOptions options = BroadcastOptions.makeBasic(); + options.setTemporaryAppWhitelistDuration(whitelistTimeout); + DeviceIdleController.LocalService idleController = getDeviceIdleController(); idleController.addPowerSaveTempWhitelistApp(Process.myUid(), - mIntentFilterVerifierComponent.getPackageName(), getVerificationTimeout(), + mIntentFilterVerifierComponent.getPackageName(), whitelistTimeout, UserHandle.USER_SYSTEM, true, "intent filter verifier"); mContext.sendBroadcastAsUser(verificationIntent, UserHandle.SYSTEM); @@ -1146,9 +1151,6 @@ public class PackageManagerService extends IPackageManager.Stub + verificationId + " packageName:" + packageName); return; } - if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, - "Updating IntentFilterVerificationInfo for package " + packageName - +" verificationId:" + verificationId); synchronized (mPackages) { if (verified) { @@ -1166,36 +1168,70 @@ public class PackageManagerService extends IPackageManager.Stub 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 - } + // In a success case, we promote from undefined or ASK to ALWAYS. This + // supports a flow where the app fails validation but then ships an updated + // APK that passes, and therefore deserves to be in ALWAYS. + // + // If validation failed, the undefined state winds up in the basic ASK behavior, + // but apps that previously passed and became ALWAYS are *demoted* out of + // that state, since they would not deserve the ALWAYS behavior in case of a + // clean install. + switch (userStatus) { + case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS: + if (!verified) { + // Don't demote if sysconfig says 'always' + SystemConfig systemConfig = SystemConfig.getInstance(); + ArraySet<String> packages = systemConfig.getLinkedApps(); + if (!packages.contains(packageName)) { + // updatedStatus is already UNDEFINED + needUpdate = true; + + if (DEBUG_DOMAIN_VERIFICATION) { + Slog.d(TAG, "Formerly validated but now failing; demoting"); + } + } else { + if (DEBUG_DOMAIN_VERIFICATION) { + Slog.d(TAG, "Updating bundled package " + packageName + + " failed autoVerify, but sysconfig supersedes"); + } + // leave needUpdate == false here intentionally + } + } + break; + + case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED: + // Stay in 'undefined' on verification failure + if (verified) { + updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS; + } + needUpdate = true; + if (DEBUG_DOMAIN_VERIFICATION) { + Slog.d(TAG, "Applying update; old=" + userStatus + + " new=" + updatedStatus); + } + break; + + case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK: + // Keep in 'ask' on failure + 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); } + } else { + Slog.i(TAG, "autoVerify ignored when installing for all users"); } - } + } } @Override @@ -19035,70 +19071,125 @@ public class PackageManagerService extends IPackageManager.Stub int count = 0; final String packageName = pkg.packageName; - + boolean handlesWebUris = false; + ArraySet<String> domains = new ArraySet<>(); + final boolean previouslyVerified; + boolean hostSetExpanded = false; + boolean needToRunVerify = false; synchronized (mPackages) { // If this is a new install and we see that we've already run verification for this // package, we have nothing to do: it means the state was restored from backup. - if (!replacing) { - IntentFilterVerificationInfo ivi = - mSettings.getIntentFilterVerificationLPr(packageName); - if (ivi != null) { - if (DEBUG_DOMAIN_VERIFICATION) { - Slog.i(TAG, "Package " + packageName+ " already verified: status=" - + ivi.getStatusString()); - } - return; + IntentFilterVerificationInfo ivi = + mSettings.getIntentFilterVerificationLPr(packageName); + previouslyVerified = (ivi != null); + if (!replacing && previouslyVerified) { + if (DEBUG_DOMAIN_VERIFICATION) { + Slog.i(TAG, "Package " + packageName + " already verified: status=" + + ivi.getStatusString()); } + return; } - // If any filters need to be verified, then all need to be. - boolean needToVerify = false; + if (DEBUG_DOMAIN_VERIFICATION) { + Slog.i(TAG, " Previous verified hosts: " + + (ivi == null ? "[none]" : ivi.getDomainsString())); + } + + // If any filters need to be verified, then all need to be. In addition, we need to + // know whether an updating app has any web navigation intent filters, to re- + // examine handling policy even if not re-verifying. + final boolean needsVerification = needsNetworkVerificationLPr(packageName); for (PackageParser.Activity a : pkg.activities) { for (ActivityIntentInfo filter : a.intents) { - if (filter.needsVerification() && needsNetworkVerificationLPr(filter)) { + if (filter.handlesWebUris(true)) { + handlesWebUris = true; + } + if (needsVerification && filter.needsVerification()) { if (DEBUG_DOMAIN_VERIFICATION) { - Slog.d(TAG, "Intent filter needs verification, so processing all filters"); + Slog.d(TAG, "autoVerify requested, processing all filters"); } - needToVerify = true; + needToRunVerify = true; + // It's safe to break out here because filter.needsVerification() + // can only be true if filter.handlesWebUris(true) returned true, so + // we've already noted that. break; } } } - if (needToVerify) { + // Compare the new set of recognized hosts if the app is either requesting + // autoVerify or has previously used autoVerify but no longer does. + if (needToRunVerify || previouslyVerified) { final int verificationId = mIntentFilterVerificationToken++; for (PackageParser.Activity a : pkg.activities) { for (ActivityIntentInfo filter : a.intents) { // Run verification against hosts mentioned in any web-nav intent filter, // even if the filter matches non-web schemes as well - if (filter.handlesWebUris(false) && needsNetworkVerificationLPr(filter)) { + if (filter.handlesWebUris(false /*onlyWebSchemes*/)) { if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Verification needed for IntentFilter:" + filter.toString()); mIntentFilterVerifier.addOneIntentFilterVerification( verifierUid, userId, verificationId, filter, packageName); + domains.addAll(filter.getHostsList()); count++; } } } } + + if (DEBUG_DOMAIN_VERIFICATION) { + Slog.i(TAG, " Update published hosts: " + domains.toString()); + } + + // If we've previously verified this same host set (or a subset), we can trust that + // a current ALWAYS policy is still applicable. If this is the case, we're done. + // (If we aren't in ALWAYS, we want to reverify to allow for apps that had failing + // hosts in their intent filters, then pushed a new apk that removed them and now + // passes.) + // + // Cases: + // + still autoVerify (needToRunVerify): + // - preserve current state if all of: unexpanded, in always + // - otherwise rerun as usual (fall through) + // + no longer autoVerify (alreadyVerified && !needToRunVerify) + // - wipe verification history always + // - preserve current state if all of: unexpanded, in always + hostSetExpanded = !previouslyVerified + || (ivi != null && !ivi.getDomains().containsAll(domains)); + final int currentPolicy = + mSettings.getIntentFilterVerificationStatusLPr(packageName, userId); + final boolean keepCurState = !hostSetExpanded + && currentPolicy == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS; + + if (needToRunVerify && keepCurState) { + if (DEBUG_DOMAIN_VERIFICATION) { + Slog.i(TAG, "Host set not expanding + ALWAYS -> no need to reverify"); + } + ivi.setDomains(domains); + scheduleWriteSettingsLocked(); + return; + } else if (previouslyVerified && !needToRunVerify) { + // Prior autoVerify state but not requesting it now. Clear autoVerify history, + // and preserve the always policy iff the host set is not expanding. + clearIntentFilterVerificationsLPw(packageName, userId, !keepCurState); + return; + } } - if (count > 0) { + if (needToRunVerify && count > 0) { + // app requested autoVerify and has at least one matching intent filter if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Starting " + count + " IntentFilter verification" + (count > 1 ? "s" : "") + " for userId:" + userId); mIntentFilterVerifier.startVerifications(userId); } else { if (DEBUG_DOMAIN_VERIFICATION) { - Slog.d(TAG, "No filters or not all autoVerify for " + packageName); + Slog.d(TAG, "No web filters or no new host policy for " + packageName); } } - } - - private boolean needsNetworkVerificationLPr(ActivityIntentInfo filter) { - final ComponentName cn = filter.activity.getComponentName(); - final String packageName = cn.getPackageName(); + } + private boolean needsNetworkVerificationLPr(String packageName) { IntentFilterVerificationInfo ivi = mSettings.getIntentFilterVerificationLPr( packageName); if (ivi == null) { @@ -19107,6 +19198,7 @@ public class PackageManagerService extends IPackageManager.Stub int status = ivi.getStatus(); switch (status) { case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED: + case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS: case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK: return true; @@ -19816,7 +19908,7 @@ public class PackageManagerService extends IPackageManager.Stub boolean installedStateChanged = false; if (deletedPs != null) { if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) { - clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL); + clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL, true); clearDefaultBrowserIfNeeded(packageName); mSettings.mKeySetManagerService.removeAppKeySetDataLPw(packageName); removedAppId = mSettings.removePackageLPw(packageName); @@ -21122,12 +21214,13 @@ public class PackageManagerService extends IPackageManager.Stub final int packageCount = mPackages.size(); for (int i = 0; i < packageCount; i++) { PackageParser.Package pkg = mPackages.valueAt(i); - clearIntentFilterVerificationsLPw(pkg.packageName, userId); + clearIntentFilterVerificationsLPw(pkg.packageName, userId, true); } } /** This method takes a specific user id as well as UserHandle.USER_ALL. */ - void clearIntentFilterVerificationsLPw(String packageName, int userId) { + void clearIntentFilterVerificationsLPw(String packageName, int userId, + boolean alsoResetStatus) { if (userId == UserHandle.USER_ALL) { if (mSettings.removeIntentFilterVerificationLPw(packageName, sUserManager.getUserIds())) { @@ -21136,7 +21229,8 @@ public class PackageManagerService extends IPackageManager.Stub } } } else { - if (mSettings.removeIntentFilterVerificationLPw(packageName, userId)) { + if (mSettings.removeIntentFilterVerificationLPw(packageName, userId, + alsoResetStatus)) { scheduleWritePackageRestrictionsLocked(userId); } } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 56835f69a3c7..39ed808dd599 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -1371,7 +1371,8 @@ final class Settings { return result; } - boolean removeIntentFilterVerificationLPw(String packageName, int userId) { + boolean removeIntentFilterVerificationLPw(String packageName, int userId, + boolean alsoResetStatus) { PackageSetting ps = mPackages.get(packageName); if (ps == null) { if (DEBUG_DOMAIN_VERIFICATION) { @@ -1379,14 +1380,17 @@ final class Settings { } return false; } - ps.clearDomainVerificationStatusForUser(userId); + if (alsoResetStatus) { + ps.clearDomainVerificationStatusForUser(userId); + } + ps.setIntentFilterVerificationInfo(null); return true; } boolean removeIntentFilterVerificationLPw(String packageName, int[] userIds) { boolean result = false; for (int userId : userIds) { - result |= removeIntentFilterVerificationLPw(packageName, userId); + result |= removeIntentFilterVerificationLPw(packageName, userId, true); } return result; } |