From 1e8c37a0dceed2dfbb5a03409de6e97911f316fe Mon Sep 17 00:00:00 2001 From: Winson Date: Wed, 7 Oct 2020 10:26:45 -0700 Subject: [PATCH] Refactor legacy domain verification code Moves everything to com.android.server.pm.intent.verify.legacy, in preparation for replacement with new classes. No functional changes were made, although the code may be slightly slower since lambdas are now passed around to do locking. Eventually the entire legacy package will be deleted. Any attempts at backwards compatbility will involve a brand new wrapper of the v1 APIs which delegate into the v2 methods. Exempt-From-Owner-Approval: Already approved by owners on main branch Bug: 163565078 Test: atest IntentFilterVerificationTest Test: manual, verify with `dumpsys package d` that an app auto verifies Change-Id: Id7d428b939cab6dd887567abcc7ba0e8f3fb7638 --- .../com/android/internal/util/ArrayUtils.java | 15 + .../server/pm/PackageManagerService.java | 887 ++++-------------- .../android/server/pm/PackageSettingBase.java | 10 +- .../java/com/android/server/pm/Settings.java | 302 +----- .../legacy/IntentFilterVerificationKey.java | 64 ++ .../IntentFilterVerificationManager.java | 584 ++++++++++++ .../IntentFilterVerificationParams.java | 42 + .../IntentFilterVerificationResponse.java | 43 + .../IntentFilterVerificationSettings.java | 393 ++++++++ .../legacy/IntentFilterVerificationState.java | 130 +++ .../verify/legacy/IntentVerifierProxy.java | 203 ++++ .../verify/legacy/IntentVerifyUtils.java | 49 + .../pm/PackageManagerSettingsTests.java | 75 +- 13 files changed, 1782 insertions(+), 1015 deletions(-) create mode 100644 services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationKey.java create mode 100644 services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationManager.java create mode 100644 services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationParams.java create mode 100644 services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationResponse.java create mode 100644 services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationSettings.java create mode 100644 services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationState.java create mode 100644 services/core/java/com/android/server/pm/intent/verify/legacy/IntentVerifierProxy.java create mode 100644 services/core/java/com/android/server/pm/intent/verify/legacy/IntentVerifyUtils.java diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java index 3cf00aed3880d..c6fd6eec2f915 100644 --- a/core/java/com/android/internal/util/ArrayUtils.java +++ b/core/java/com/android/internal/util/ArrayUtils.java @@ -35,6 +35,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.function.IntFunction; /** @@ -599,6 +600,20 @@ public class ArrayUtils { return cur; } + /** + * Similar to {@link Set#addAll(Collection)}}, but with support for set values of {@code null}. + */ + public static @NonNull ArraySet addAll(@Nullable ArraySet cur, + @Nullable Collection val) { + if (cur == null) { + cur = new ArraySet<>(); + } + if (val != null) { + cur.addAll(val); + } + return cur; + } + public static @Nullable ArraySet remove(@Nullable ArraySet cur, T val) { if (cur == null) { return null; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 6e5bd947c456e..32d699f9875fa 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -377,6 +377,10 @@ import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.DexoptOptions; import com.android.server.pm.dex.PackageDexUsage; import com.android.server.pm.dex.ViewCompiler; +import com.android.server.pm.intent.verify.legacy.IntentFilterVerificationManager; +import com.android.server.pm.intent.verify.legacy.IntentFilterVerificationParams; +import com.android.server.pm.intent.verify.legacy.IntentVerifierProxy; +import com.android.server.pm.intent.verify.legacy.IntentVerifyUtils; import com.android.server.pm.parsing.PackageCacher; import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.PackageParser2; @@ -399,6 +403,7 @@ import com.android.server.utils.Watchable; import com.android.server.utils.Watched; import com.android.server.utils.WatchedArrayMap; import com.android.server.utils.WatchedSparseBooleanArray; +import com.android.server.utils.WatchedSparseIntArray; import com.android.server.utils.Watcher; import com.android.server.wm.ActivityTaskManagerInternal; @@ -460,6 +465,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Predicate; +import java.util.function.Supplier; /** * Keep track of all those APKs everywhere. @@ -1063,6 +1069,9 @@ public class PackageManagerService extends IPackageManager.Stub private final ServiceProducer mGetLocalServiceProducer; private final ServiceProducer mGetSystemServiceProducer; private final Singleton mModuleInfoProviderProducer; + private final Singleton + mIntentFilterVerificationManagerProducer; + private final Singleton mHandlerProducer; Injector(Context context, Object lock, Installer installer, Object installLock, PackageAbiHelper abiHelper, @@ -1091,6 +1100,8 @@ public class PackageManagerService extends IPackageManager.Stub instantAppResolverConnectionProducer, Producer moduleInfoProviderProducer, Producer legacyPermissionManagerInternalProducer, + Producer intentFilterVerificationManagerProducer, + Producer handlerProducer, SystemWrapper systemWrapper, ServiceProducer getLocalServiceProducer, ServiceProducer getSystemServiceProducer) { @@ -1128,6 +1139,9 @@ public class PackageManagerService extends IPackageManager.Stub mSystemWrapper = systemWrapper; mGetLocalServiceProducer = getLocalServiceProducer; mGetSystemServiceProducer = getSystemServiceProducer; + mIntentFilterVerificationManagerProducer = + new Singleton<>(intentFilterVerificationManagerProducer); + mHandlerProducer = new Singleton<>(handlerProducer); } /** @@ -1273,6 +1287,14 @@ public class PackageManagerService extends IPackageManager.Stub public LegacyPermissionManagerInternal getLegacyPermissionManagerInternal() { return mLegacyPermissionManagerInternalProducer.get(this, mPackageManager); } + + public IntentFilterVerificationManager getIntentFilterVerificationManager() { + return mIntentFilterVerificationManagerProducer.get(this, mPackageManager); + } + + public Handler getHandler() { + return mHandlerProducer.get(this, mPackageManager); + } } /** Provides an abstraction to static access to system state. */ @@ -1327,8 +1349,6 @@ public class PackageManagerService extends IPackageManager.Stub public InstantAppRegistry instantAppRegistry; public InstantAppResolverConnection instantAppResolverConnection; public ComponentName instantAppResolverSettingsComponent; - public @Nullable IntentFilterVerifier intentFilterVerifier; - public @Nullable ComponentName intentFilterVerifierComponent; public boolean isPreNmr1Upgrade; public boolean isPreNupgrade; public boolean isPreQupgrade; @@ -1451,10 +1471,7 @@ public class PackageManagerService extends IPackageManager.Stub boolean mResolverReplaced = false; - private final @Nullable ComponentName mIntentFilterVerifierComponent; - private final @Nullable IntentFilterVerifier mIntentFilterVerifier; - - private int mIntentFilterVerificationToken = 0; + private final @NonNull IntentFilterVerificationManager mIntentFilterVerificationManager; /** The service connection to the ephemeral resolver */ final InstantAppResolverConnection mInstantAppResolverConnection; @@ -1470,9 +1487,6 @@ public class PackageManagerService extends IPackageManager.Stub private final Map> mNoKillInstallObservers = Collections.synchronizedMap(new HashMap<>()); - final SparseArray mIntentFilterVerificationStates - = new SparseArray<>(); - // Internal interface for permission manager private final PermissionManagerServiceInternal mPermissionManager; @@ -1492,262 +1506,6 @@ public class PackageManagerService extends IPackageManager.Stub private final PackageProperty mPackageProperty = new PackageProperty(); - private static class IFVerificationParams { - String packageName; - boolean hasDomainUrls; - List activities; - boolean replacing; - int userId; - int verifierUid; - - public IFVerificationParams(String packageName, boolean hasDomainUrls, - List activities, boolean _replacing, - int _userId, int _verifierUid) { - this.packageName = packageName; - this.hasDomainUrls = hasDomainUrls; - this.activities = activities; - replacing = _replacing; - userId = _userId; - verifierUid = _verifierUid; - } - } - - private interface IntentFilterVerifier { - 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 { - private Context mContext; - private ComponentName mIntentFilterVerifierComponent; - private ArrayList mCurrentIntentFilterVerifications = new ArrayList<>(); - - public IntentVerifierProxy(Context context, ComponentName verifierComponent) { - mContext = context; - mIntentFilterVerifierComponent = verifierComponent; - } - - private String getDefaultScheme() { - return IntentFilter.SCHEME_HTTPS; - } - - @Override - public void startVerifications(int userId) { - // Launch verifications requests - int count = mCurrentIntentFilterVerifications.size(); - for (int n=0; n filters = ivs.getFilters(); - final int filterCount = filters.size(); - ArraySet domainsSet = new ArraySet<>(); - for (int m=0; m filters = ivs.getFilters(); - final int count = filters.size(); - if (DEBUG_DOMAIN_VERIFICATION) { - Slog.i(TAG, "Received verification response " + verificationId - + " for " + count + " filters, verified=" + verified); - } - for (int n=0; n 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 - public boolean addOneIntentFilterVerification(int verifierUid, int userId, int verificationId, - ParsedIntentInfo filter, String packageName) { - if (!hasValidDomains(filter)) { - return false; - } - IntentFilterVerificationState ivs = mIntentFilterVerificationStates.get(verificationId); - if (ivs == null) { - ivs = createDomainVerificationState(verifierUid, userId, verificationId, - packageName); - } - if (DEBUG_DOMAIN_VERIFICATION) { - Slog.d(TAG, "Adding verification filter for " + packageName + ": " + filter); - } - ivs.addFilter(filter); - return true; - } - - private IntentFilterVerificationState createDomainVerificationState(int verifierUid, - int userId, int verificationId, String packageName) { - IntentFilterVerificationState ivs = new IntentFilterVerificationState( - verifierUid, userId, packageName); - ivs.setPendingState(); - synchronized (mLock) { - mIntentFilterVerificationStates.append(verificationId, ivs); - mCurrentIntentFilterVerifications.add(verificationId); - } - return ivs; - } - } - - private static boolean hasValidDomains(ParsedIntentInfo filter) { - return filter.hasCategory(Intent.CATEGORY_BROWSABLE) - && (filter.hasDataScheme(IntentFilter.SCHEME_HTTP) || - filter.hasDataScheme(IntentFilter.SCHEME_HTTPS)); - } - // Set of pending broadcasts for aggregating enable/disable of components. @VisibleForTesting(visibility = Visibility.PACKAGE) public static class PendingPackageBroadcasts { @@ -1822,8 +1580,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; + public static final int START_INTENT_FILTER_VERIFICATIONS = 17; + public static final int INTENT_FILTER_VERIFIED = 18; static final int WRITE_PACKAGE_LIST = 19; static final int INSTANT_APP_RESOLUTION_PHASE_TWO = 20; static final int ENABLE_ROLLBACK_STATUS = 21; @@ -1925,6 +1683,124 @@ public class PackageManagerService extends IPackageManager.Stub private final PackageUsage mPackageUsage = new PackageUsage(); private final CompilerStats mCompilerStats = new CompilerStats(); + // TODO(b/171251883): STOPSHIP Remove + private final IntentVerifierProxy.PackageManagerServiceConnection + mIntentFilterVerificationConnection = + new IntentVerifierProxy.PackageManagerServiceConnection() { + @Override + public void lock(Runnable block) { + synchronized (mLock) { + block.run(); + } + } + + @Override + public T lockReturn(Supplier block) { + synchronized (mLock) { + return block.get(); + } + } + + @Override + public void debugLog(String message) { + if (DEBUG_DOMAIN_VERIFICATION) { + Slog.d(TAG + "IntentFilterVerify", message); + } + } + + @Override + public void verboseLog(String message) { + if (DEBUG_DOMAIN_VERIFICATION) { + Slog.v(TAG + "IntentFilterVerify", message); + } + } + + @Override + public void warnLog(String message) { + Slog.w(TAG + "IntentFilterVerify", message); + } + + @Override + public void infoLog(String message) { + Slog.i(TAG + "IntentFilterVerify", message); + } + + @Override + public void writeSettings(String packageName, ArraySet domainsSet) { + synchronized (mLock) { + PackageSetting ps = mSettings.mPackages.get(packageName); + if (ps == null) { + if (DEBUG_DOMAIN_VERIFICATION) { + warnLog("No package known: " + packageName); + } + } else { + mIntentFilterVerificationManager.updatePackageSetting(ps, + domainsSet); + PackageManagerService.this.scheduleWriteSettingsLocked(); + } + } + } + + @Override + public void scheduleWriteSettingsLocked() { + PackageManagerService.this.scheduleWriteSettingsLocked(); + } + + @Override + public void scheduleWritePackageRestrictionsLocked(int userId) { + PackageManagerService.this.scheduleWritePackageRestrictionsLocked( + userId); + } + + @Override + public long getVerificationTimeout() { + return PackageManagerService.this.getVerificationTimeout(); + } + + @Override + public String getInstantAppPackageName(int callingUid) { + return PackageManagerService.this.getInstantAppPackageName(callingUid); + } + + @Nullable + @Override + public PackageSetting getPackageSettingLPr(@NonNull String packageName) { + return mSettings.getPackageLPr(packageName); + } + + @NonNull + @Override + public Map getPackageSettingsLPr() { + return mSettings.mPackages; + } + + @Override + public boolean shouldFilterApplicationLocked(PackageSetting ps, + int callingUid, @UserIdInt int userId) { + return PackageManagerService.this.shouldFilterApplicationLocked(ps, callingUid, + userId); + } + + @Override + public int getPackageUid(String packageName, int flags, + @UserIdInt int userId) { + return PackageManagerService.this.getPackageUid(packageName, flags, + userId); + } + + @NonNull + @Override + public WatchedSparseIntArray getNextAppLinkGeneration() { + return mSettings.mNextAppLinkGeneration; + } + + @NonNull + @Override + public DeviceIdleInternal getDeviceIdleInternal() { + return mInjector.getLocalService(DeviceIdleInternal.class); + } + }; + /** * Invalidate the package info cache, which includes updating the cached computer. * @hide @@ -2145,7 +2021,6 @@ public class PackageManagerService extends IPackageManager.Stub boolean isImplicitImageCaptureIntentAndNotSetByDpc); int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps, boolean onlyExposedExplicitly, boolean isImplicitImageCaptureIntentAndNotSetByDpc); - long getDomainVerificationStatusLPr(PackageSetting ps, int userId); void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId, boolean requireFullPermission, boolean checkShell, String message); void enforceCrossUserPermission(int callingUid, @UserIdInt int userId, @@ -2773,7 +2648,7 @@ public class PackageManagerService extends IPackageManager.Stub continue; } // Try to get the status from User settings first - long packedStatus = getDomainVerificationStatusLPr(ps, userId); + long packedStatus = IntentVerifyUtils.getDomainVerificationStatus(ps, userId); int status = (int)(packedStatus >> 32); int linkGeneration = (int)(packedStatus & 0xFFFFFFFF); if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) { @@ -2985,7 +2860,8 @@ public class PackageManagerService extends IPackageManager.Stub if (ps == null) { continue; } - long verificationState = getDomainVerificationStatusLPr(ps, parentUserId); + long verificationState = + IntentVerifyUtils.getDomainVerificationStatus(ps, parentUserId); int status = (int)(verificationState >> 32); if (result == null) { result = new CrossProfileDomainInfo(); @@ -3242,7 +3118,8 @@ public class PackageManagerService extends IPackageManager.Stub final String packageName = info.activityInfo.packageName; final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps.getInstantApp(userId)) { - final long packedStatus = getDomainVerificationStatusLPr(ps, userId); + final long packedStatus = + IntentVerifyUtils.getDomainVerificationStatus(ps, userId); final int status = (int)(packedStatus >> 32); if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { // there's a local instant application installed, but, the user has @@ -4176,7 +4053,8 @@ public class PackageManagerService extends IPackageManager.Stub // only check domain verification status if the app is not a browser if (!info.handleAllWebDataURI) { // Try to get the status from User settings first - final long packedStatus = getDomainVerificationStatusLPr(ps, userId); + final long packedStatus = + IntentVerifyUtils.getDomainVerificationStatus(ps, userId); final int status = (int) (packedStatus >> 32); if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS || status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) { @@ -4475,21 +4353,6 @@ public class PackageManagerService extends IPackageManager.Stub return updateFlagsForComponent(flags, userId); } - // Returns a packed value as a long: - // - // high 'int'-sized word: link status: undefined/ask/never/always. - // low 'int'-sized word: relative priority among 'always' results. - public long getDomainVerificationStatusLPr(PackageSetting ps, int userId) { - long result = ps.getDomainVerificationStatusForUser(userId); - // if none available, get the status - if (result >> 32 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) { - if (ps.getIntentFilterVerificationInfo() != null) { - result = ((long)ps.getIntentFilterVerificationInfo().getStatus()) << 32; - } - } - return result; - } - /** * Checks if the request is from the system or an app that has the appropriate cross-user * permissions defined as follows: @@ -5340,52 +5203,12 @@ public class PackageManagerService extends IPackageManager.Stub break; } case START_INTENT_FILTER_VERIFICATIONS: { - IFVerificationParams params = (IFVerificationParams) msg.obj; - verifyIntentFiltersIfNeeded(params.userId, params.verifierUid, params.replacing, - params.packageName, params.hasDomainUrls, params.activities); + mIntentFilterVerificationManager.verifyIntentFiltersIfNeeded( + (IntentFilterVerificationParams) msg.obj); 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(); - - if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, - "Processing IntentFilter verification with token:" - + verificationId + " and userId:" + userId); - - final IntentFilterVerificationResponse response = - (IntentFilterVerificationResponse) msg.obj; - - state.setVerifierResponse(response.callerUid, response.code); - - if (DEBUG_DOMAIN_VERIFICATION) 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) { - if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Domains failing verification: " - + response.getFailedDomainsString()); - } - - if (state.isVerificationComplete()) { - mIntentFilterVerifier.receiveVerificationResponse(verificationId); - } else { - if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, - "IntentFilter verification with token:" + verificationId - + " was not said to be complete"); - } - + mIntentFilterVerificationManager.onFilterVerified(msg); break; } case INSTANT_APP_RESOLUTION_PHASE_TWO: { @@ -6037,7 +5860,8 @@ public class PackageManagerService extends IPackageManager.Stub lock), (i, pm) -> new Settings(Environment.getDataDirectory(), RuntimePermissionsPersistence.createInstance(), - i.getPermissionManagerServiceInternal(), lock), + i.getPermissionManagerServiceInternal(), + i.getIntentFilterVerificationManager(), lock), (i, pm) -> AppsFilter.create(pm.mPmInternal, i), (i, pm) -> (PlatformCompat) ServiceManager.getService("platform_compat"), (i, pm) -> SystemConfig.getInstance(), @@ -6070,6 +5894,15 @@ public class PackageManagerService extends IPackageManager.Stub i.getContext(), cn, Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE), (i, pm) -> new ModuleInfoProvider(i.getContext(), pm), (i, pm) -> LegacyPermissionManagerService.create(i.getContext()), + (i, pm) -> new IntentFilterVerificationManager(pm.mContext, i.getHandler(), + pm.mIntentFilterVerificationConnection, SystemConfig.getInstance(), + i.getUserManagerService()), + (i, pm) -> { + HandlerThread thread = new ServiceThread(TAG, + Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/); + thread.start(); + return pm.new PackageHandler(thread.getLooper()); + }, new DefaultSystemWrapper(), LocalServices::getService, context::getSystemService); @@ -6241,6 +6074,9 @@ public class PackageManagerService extends IPackageManager.Stub mPermissionManager = injector.getPermissionManagerServiceInternal(); mSettings = injector.getSettings(); mUserManager = injector.getUserManagerService(); + mIntentFilterVerificationManager = injector.getIntentFilterVerificationManager(); + mHandler = injector.getHandler(); + mApexManager = testParams.apexManager; mArtManagerService = testParams.artManagerService; mAvailableFeatures = testParams.availableFeatures; @@ -6250,14 +6086,11 @@ public class PackageManagerService extends IPackageManager.Stub mDexManager = testParams.dexManager; mDirsToScanAsSystem = testParams.dirsToScanAsSystem; mFactoryTest = testParams.factoryTest; - mHandler = testParams.handler; mIncrementalManager = testParams.incrementalManager; mInstallerService = testParams.installerService; mInstantAppRegistry = testParams.instantAppRegistry; mInstantAppResolverConnection = testParams.instantAppResolverConnection; mInstantAppResolverSettingsComponent = testParams.instantAppResolverSettingsComponent; - mIntentFilterVerifier = testParams.intentFilterVerifier; - mIntentFilterVerifierComponent = testParams.intentFilterVerifierComponent; mIsPreNMR1Upgrade = testParams.isPreNmr1Upgrade; mIsPreNUpgrade = testParams.isPreNupgrade; mIsPreQUpgrade = testParams.isPreQupgrade; @@ -6485,12 +6318,10 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mInstallLock) { // writer synchronized (mLock) { - HandlerThread handlerThread = new ServiceThread(TAG, - Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/); - handlerThread.start(); - mHandler = new PackageHandler(handlerThread.getLooper()); + mHandler = injector.getHandler(); mProcessLoggingHandler = new ProcessLoggingHandler(); Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT); + mIntentFilterVerificationManager = injector.getIntentFilterVerificationManager(); ArrayMap libConfig = systemConfig.getSharedLibraries(); @@ -7080,13 +6911,8 @@ public class PackageManagerService extends IPackageManager.Stub mRequiredVerifierPackage = getRequiredButNotReallyRequiredVerifierLPr(); mRequiredInstallerPackage = getRequiredInstallerLPr(); mRequiredUninstallerPackage = getRequiredUninstallerLPr(); - mIntentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr(); - if (mIntentFilterVerifierComponent != null) { - mIntentFilterVerifier = new IntentVerifierProxy(mContext, - mIntentFilterVerifierComponent); - } else { - mIntentFilterVerifier = null; - } + mIntentFilterVerificationManager.setVerifierComponent( + getIntentFilterVerifierComponentNameLPr()); mServicesExtensionPackageName = getRequiredServicesExtensionPackageLPr(); mSharedSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr( PackageManager.SYSTEM_SHARED_LIBRARY_SHARED, @@ -7095,8 +6921,6 @@ public class PackageManagerService extends IPackageManager.Stub mRequiredVerifierPackage = null; mRequiredInstallerPackage = null; mRequiredUninstallerPackage = null; - mIntentFilterVerifierComponent = null; - mIntentFilterVerifier = null; mServicesExtensionPackageName = null; mSharedSystemSharedLibraryPackageName = null; } @@ -7766,56 +7590,7 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mLock") private void primeDomainVerificationsLPw(int userId) { - if (DEBUG_DOMAIN_VERIFICATION) { - Slog.d(TAG, "Priming domain verifications in user " + userId); - } - - SystemConfig systemConfig = mInjector.getSystemConfig(); - ArraySet packages = systemConfig.getLinkedApps(); - - for (String packageName : packages) { - AndroidPackage pkg = mPackages.get(packageName); - if (pkg != null) { - if (!pkg.isSystem()) { - Slog.w(TAG, "Non-system app '" + packageName + "' in sysconfig "); - continue; - } - - ArraySet domains = null; - for (ParsedActivity a : pkg.getActivities()) { - for (ParsedIntentInfo filter : a.getIntents()) { - if (hasValidDomains(filter)) { - if (domains == null) { - domains = new ArraySet<>(); - } - domains.addAll(filter.getHostsList()); - } - } - } - - if (domains != null && domains.size() > 0) { - if (DEBUG_DOMAIN_VERIFICATION) { - Slog.v(TAG, " + " + packageName); - } - // 'Undefined' in the global IntentFilterVerificationInfo, i.e. the usual - // state w.r.t. the formal app-linkage "no verification attempted" state; - // and then 'always' in the per-user state actually used for intent resolution. - final IntentFilterVerificationInfo ivi; - ivi = mSettings.createIntentFilterVerificationIfNeededLPw(packageName, domains); - ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED); - mSettings.updateIntentFilterVerificationStatusLPw(packageName, - INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS, userId); - } else { - Slog.w(TAG, "Sysconfig package '" + packageName - + "' does not handle web links"); - } - } else { - Slog.w(TAG, "Unknown package " + packageName + " in sysconfig "); - } - } - - scheduleWritePackageRestrictionsLocked(userId); - scheduleWriteSettingsLocked(); + mIntentFilterVerificationManager.primeDomainVerificationsLPw(userId, mPackages); } private boolean packageIsBrowser(String packageName, int userId) { @@ -9663,7 +9438,8 @@ public class PackageManagerService extends IPackageManager.Stub if (ri.activityInfo.applicationInfo.isInstantApp()) { final String packageName = ri.activityInfo.packageName; final PackageSetting ps = mSettings.getPackageLPr(packageName); - final long packedStatus = getDomainVerificationStatusLPr(ps, userId); + final long packedStatus = + IntentVerifyUtils.getDomainVerificationStatus(ps, userId); final int status = (int)(packedStatus >> 32); if (status != INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) { return ri; @@ -10244,13 +10020,6 @@ public class PackageManagerService extends IPackageManager.Stub xpDomainInfo, userId, debug); } - // Returns a packed value as a long: - // - // high 'int'-sized word: link status: undefined/ask/never/always. - // low 'int'-sized word: relative priority among 'always' results. - private long getDomainVerificationStatusLPr(PackageSetting ps, int userId) { - return liveComputer().getDomainVerificationStatusLPr(ps, userId); - } private ResolveInfo querySkipCurrentProfileIntents( List matchingFilters, Intent intent, String resolvedType, @@ -16441,76 +16210,25 @@ public class PackageManagerService extends IPackageManager.Stub } @Override - public void verifyIntentFilter(int id, int verificationCode, List failedDomains) - 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, failedDomains); - msg.arg1 = id; - msg.obj = response; - mHandler.sendMessage(msg); + public void verifyIntentFilter(int id, int verificationCode, List failedDomains) { + mIntentFilterVerificationManager.queueVerifyResult(id, verificationCode, failedDomains); } @Override public int getIntentVerificationStatus(String packageName, int userId) { - final int callingUid = Binder.getCallingUid(); - if (UserHandle.getUserId(callingUid) != userId) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, - "getIntentVerificationStatus" + userId); - } - if (getInstantAppPackageName(callingUid) != null) { - return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; - } - synchronized (mLock) { - final PackageSetting ps = mSettings.getPackageLPr(packageName); - if (ps == null - || shouldFilterApplicationLocked( - ps, callingUid, UserHandle.getUserId(callingUid))) { - return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; - } - return mSettings.getIntentFilterVerificationStatusLPr(packageName, userId); - } + return mIntentFilterVerificationManager.getIntentVerificationStatus(packageName, userId); } @Override public boolean updateIntentVerificationStatus(String packageName, int status, int userId) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); - - boolean result = false; - synchronized (mLock) { - final PackageSetting ps = mSettings.getPackageLPr(packageName); - if (shouldFilterApplicationLocked( - ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) { - return false; - } - result = mSettings.updateIntentFilterVerificationStatusLPw(packageName, status, userId); - } - if (result) { - scheduleWritePackageRestrictionsLocked(userId); - } - return result; + return mIntentFilterVerificationManager.updateIntentVerificationStatus(packageName, status, + userId); } @Override public @NonNull ParceledListSlice getIntentFilterVerifications( String packageName) { - final int callingUid = Binder.getCallingUid(); - if (getInstantAppPackageName(callingUid) != null) { - return ParceledListSlice.emptyList(); - } - synchronized (mLock) { - final PackageSetting ps = mSettings.getPackageLPr(packageName); - if (shouldFilterApplicationLocked(ps, callingUid, UserHandle.getUserId(callingUid))) { - return ParceledListSlice.emptyList(); - } - return new ParceledListSlice<>(mSettings.getIntentFilterVerificationsLPr(packageName)); - } + return mIntentFilterVerificationManager.getIntentFilterVerifications(packageName); } @Override @@ -20194,7 +19912,8 @@ public class PackageManagerService extends IPackageManager.Stub } if (!instantApp) { - startIntentFilterVerifications(args.user.getIdentifier(), replace, parsedPackage); + mIntentFilterVerificationManager.startIntentFilterVerifications( + args.user.getIdentifier(), replace, parsedPackage); } else { if (DEBUG_DOMAIN_VERIFICATION) { Slog.d(TAG, "Not verifying instant app install for app links: " + pkgName); @@ -20533,190 +20252,6 @@ public class PackageManagerService extends IPackageManager.Stub } } - private void startIntentFilterVerifications(int userId, boolean replacing, AndroidPackage pkg) { - if (mIntentFilterVerifierComponent == null) { - Slog.w(TAG, "No IntentFilter verification will not be done as " - + "there is no IntentFilterVerifier available!"); - return; - } - - final int verifierUid = getPackageUid( - mIntentFilterVerifierComponent.getPackageName(), - MATCH_DEBUG_TRIAGED_MISSING, - (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId); - - Message msg = mHandler.obtainMessage(START_INTENT_FILTER_VERIFICATIONS); - msg.obj = new IFVerificationParams( - pkg.getPackageName(), - pkg.isHasDomainUrls(), - pkg.getActivities(), - replacing, - userId, - verifierUid - ); - mHandler.sendMessage(msg); - } - - private void verifyIntentFiltersIfNeeded(int userId, int verifierUid, boolean replacing, - String packageName, - boolean hasDomainUrls, - List activities) { - int size = activities.size(); - if (size == 0) { - if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, - "No activity, so no need to verify any IntentFilter!"); - return; - } - - if (!hasDomainUrls) { - if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, - "No domain URLs, so no need to verify any IntentFilter!"); - return; - } - - if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Checking for userId:" + userId - + " if any IntentFilter from the " + size - + " Activities needs verification ..."); - - int count = 0; - boolean handlesWebUris = false; - ArraySet domains = new ArraySet<>(); - final boolean previouslyVerified; - boolean hostSetExpanded = false; - boolean needToRunVerify = false; - synchronized (mLock) { - // 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. - 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 (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 (ParsedActivity a : activities) { - for (ParsedIntentInfo filter : a.getIntents()) { - if (filter.handlesWebUris(true)) { - handlesWebUris = true; - } - if (needsVerification && filter.needsVerification()) { - if (DEBUG_DOMAIN_VERIFICATION) { - Slog.d(TAG, "autoVerify requested, processing all filters"); - } - 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; - } - } - } - - // 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 (ParsedActivity a : activities) { - for (ParsedIntentInfo filter : a.getIntents()) { - // 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 /*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 (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 web filters or no new host policy for " + packageName); - } - } - } - - @GuardedBy("mLock") - private boolean needsNetworkVerificationLPr(String packageName) { - 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_ALWAYS: - case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK: - return true; - - default: - // Nothing to do - return false; - } - } - private static boolean isExternal(PackageSetting ps) { return (ps.pkgFlags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0; } @@ -21386,7 +20921,8 @@ public class PackageManagerService extends IPackageManager.Stub if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) { final SparseBooleanArray changedUsers = new SparseBooleanArray(); synchronized (mLock) { - clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL, true); + mIntentFilterVerificationManager.clearIntentFilterVerificationsLocked( + deletedPs.name, UserHandle.USER_ALL, true); clearDefaultBrowserIfNeeded(packageName); mSettings.getKeySetManagerService().removeAppKeySetDataLPw(packageName); mAppsFilter.removePackage(getPackageSetting(packageName)); @@ -22464,40 +22000,6 @@ public class PackageManagerService extends IPackageManager.Stub mSettings.clearPackagePreferredActivities(packageName, outUserChanged, userId); } - /** This method takes a specific user id as well as UserHandle.USER_ALL. */ - @GuardedBy("mLock") - private void clearIntentFilterVerificationsLPw(int userId) { - final int packageCount = mPackages.size(); - for (int i = 0; i < packageCount; i++) { - AndroidPackage pkg = mPackages.valueAt(i); - clearIntentFilterVerificationsLPw(pkg.getPackageName(), userId, true); - } - } - - /** This method takes a specific user id as well as UserHandle.USER_ALL. */ - @GuardedBy("mLock") - void clearIntentFilterVerificationsLPw(String packageName, int userId, - boolean alsoResetStatus) { - if (SystemConfig.getInstance().getLinkedApps().contains(packageName)) { - // Nope, need to preserve the system configuration approval for this app - return; - } - - if (userId == UserHandle.USER_ALL) { - if (mSettings.removeIntentFilterVerificationLPw(packageName, - mUserManager.getUserIds())) { - for (int oneUserId : mUserManager.getUserIds()) { - scheduleWritePackageRestrictionsLocked(oneUserId); - } - } - } else { - if (mSettings.removeIntentFilterVerificationLPw(packageName, userId, - alsoResetStatus)) { - scheduleWritePackageRestrictionsLocked(userId); - } - } - } - /** Clears state for all users, and touches intent filter verification policy */ void clearDefaultBrowserIfNeeded(String packageName) { for (int oneUserId : mUserManager.getUserIds()) { @@ -22553,7 +22055,8 @@ public class PackageManagerService extends IPackageManager.Stub } synchronized (mLock) { mSettings.applyDefaultPreferredAppsLPw(userId); - clearIntentFilterVerificationsLPw(userId); + mIntentFilterVerificationManager.clearIntentFilterVerificationsLocked(userId, + mPackages); primeDomainVerificationsLPw(userId); final int numPackages = mPackages.size(); for (int i = 0; i < numPackages; i++) { @@ -22824,7 +22327,8 @@ public class PackageManagerService extends IPackageManager.Stub serializer.startTag(null, TAG_INTENT_FILTER_VERIFICATION); synchronized (mLock) { - mSettings.writeAllDomainVerificationsLPr(serializer, userId); + mIntentFilterVerificationManager.writeAllDomainVerificationsLPr(serializer, userId, + mSettings.mPackages); } serializer.endTag(null, TAG_INTENT_FILTER_VERIFICATION); @@ -22852,7 +22356,8 @@ public class PackageManagerService extends IPackageManager.Stub restoreFromXml(parser, userId, TAG_INTENT_FILTER_VERIFICATION, (parser1, userId1) -> { synchronized (mLock) { - mSettings.readAllDomainVerificationsLPr(parser1, userId1); + mIntentFilterVerificationManager.readAllDomainVerificationsLPr(parser1, + userId1); writeSettingsLPrTEMP(); } }); @@ -24394,8 +23899,10 @@ public class PackageManagerService extends IPackageManager.Stub if (dumpState.isDumping(DumpState.DUMP_INTENT_FILTER_VERIFIERS) && packageName == null) { - if (mIntentFilterVerifierComponent != null) { - String verifierPackageName = mIntentFilterVerifierComponent.getPackageName(); + ComponentName verifierComponent = + mIntentFilterVerificationManager.getVerifierComponent(); + if (verifierComponent != null) { + String verifierPackageName = verifierComponent.getPackageName(); if (!checkin) { if (dumpState.onTitlePrinted()) pw.println(); @@ -24776,8 +24283,10 @@ public class PackageManagerService extends IPackageManager.Stub UserHandle.USER_SYSTEM)); proto.end(requiredVerifierPackageToken); - if (mIntentFilterVerifierComponent != null) { - String verifierPackageName = mIntentFilterVerifierComponent.getPackageName(); + ComponentName verifierComponent = + mIntentFilterVerificationManager.getVerifierComponent(); + if (verifierComponent != null) { + String verifierPackageName = verifierComponent.getPackageName(); final long verifierPackageToken = proto.start(PackageServiceDumpProto.VERIFIER_PACKAGE); proto.write(PackageServiceDumpProto.PackageShortProto.NAME, verifierPackageName); diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index 67bd82b429831..d123c7770cfe2 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -644,11 +644,11 @@ public abstract class PackageSettingBase extends SettingBase { return excludedUserIds; } - IntentFilterVerificationInfo getIntentFilterVerificationInfo() { + public IntentFilterVerificationInfo getIntentFilterVerificationInfo() { return verificationInfo; } - void setIntentFilterVerificationInfo(IntentFilterVerificationInfo info) { + public void setIntentFilterVerificationInfo(IntentFilterVerificationInfo info) { verificationInfo = info; onChanged(); } @@ -657,14 +657,14 @@ public abstract class PackageSettingBase extends SettingBase { // // high 'int'-sized word: link status: undefined/ask/never/always. // low 'int'-sized word: relative priority among 'always' results. - long getDomainVerificationStatusForUser(int userId) { + public long getDomainVerificationStatusForUser(int userId) { PackageUserState state = readUserState(userId); long result = (long) state.appLinkGeneration; result |= ((long) state.domainVerificationStatus) << 32; return result; } - void setDomainVerificationStatusForUser(final int status, int generation, int userId) { + public void setDomainVerificationStatusForUser(final int status, int generation, int userId) { PackageUserState state = modifyUserState(userId); state.domainVerificationStatus = status; if (status == PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) { @@ -673,7 +673,7 @@ public abstract class PackageSettingBase extends SettingBase { } } - void clearDomainVerificationStatusForUser(int userId) { + 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 349d556eac874..89b6bfbbfecb5 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -21,7 +21,6 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE; -import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS; import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY; import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN; @@ -29,7 +28,6 @@ import static android.content.pm.PackageManager.UNINSTALL_REASON_USER_TYPE; import static android.os.Process.PACKAGE_INFO_GID; import static android.os.Process.SYSTEM_UID; -import static com.android.server.pm.PackageManagerService.DEBUG_DOMAIN_VERIFICATION; import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import android.annotation.NonNull; @@ -108,6 +106,7 @@ import com.android.permission.persistence.RuntimePermissionsState; import com.android.server.LocalServices; import com.android.server.backup.PreferredActivityBackupHelper; import com.android.server.pm.Installer.InstallerException; +import com.android.server.pm.intent.verify.legacy.IntentFilterVerificationManager; import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; @@ -131,6 +130,7 @@ import libcore.io.IoUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; import java.io.BufferedWriter; import java.io.File; @@ -147,7 +147,6 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.Date; import java.util.Iterator; import java.util.List; @@ -283,9 +282,9 @@ public final class Settings implements Watchable, Snappable { "persistent-preferred-activities"; static final String TAG_CROSS_PROFILE_INTENT_FILTERS = "crossProfile-intent-filters"; - private static final String TAG_DOMAIN_VERIFICATION = "domain-verification"; + public static final String TAG_DOMAIN_VERIFICATION = "domain-verification"; private static final String TAG_DEFAULT_APPS = "default-apps"; - private static final String TAG_ALL_INTENT_FILTER_VERIFICATION = + public static final String TAG_ALL_INTENT_FILTER_VERIFICATION = "all-intent-filter-verifications"; private static final String TAG_DEFAULT_BROWSER = "default-browser"; private static final String TAG_DEFAULT_DIALER = "default-dialer"; @@ -390,12 +389,6 @@ public final class Settings implements Watchable, Snappable { private final WatchedSparseArray> mBlockUninstallPackages = new WatchedSparseArray<>(); - // Set of restored intent-filter verification states - @Watched - private final WatchedArrayMap - mRestoredIntentFilterVerifications = - new WatchedArrayMap(); - private static final class KernelPackageState { int appId; int[] excludedUserIds; @@ -487,7 +480,10 @@ public final class Settings implements Watchable, Snappable { @Watched final WatchedSparseArray mDefaultBrowserApp = new WatchedSparseArray(); + // TODO(b/161161364): This seems unused, and is probably not relevant in the new API, but should + // verify. // App-link priority tracking, per-user + @NonNull @Watched final WatchedSparseIntArray mNextAppLinkGeneration = new WatchedSparseIntArray(); @@ -512,6 +508,8 @@ public final class Settings implements Watchable, Snappable { private final LegacyPermissionDataProvider mPermissionDataProvider; + private final IntentFilterVerificationManager mIntentFilterVerificationManager; + /** * The observer that watches for changes from array members */ @@ -538,13 +536,12 @@ public final class Settings implements Watchable, Snappable { mStoppedPackagesFilename = null; mBackupStoppedPackagesFilename = null; mKernelMappingFilename = null; - + mIntentFilterVerificationManager = null; mPackages.registerObserver(mObserver); mInstallerPackages.registerObserver(mObserver); mKernelMapping.registerObserver(mObserver); mDisabledSysPackages.registerObserver(mObserver); mBlockUninstallPackages.registerObserver(mObserver); - mRestoredIntentFilterVerifications.registerObserver(mObserver); mVersion.registerObserver(mObserver); mPreferredActivities.registerObserver(mObserver); mPersistentPreferredActivities.registerObserver(mObserver); @@ -560,7 +557,8 @@ public final class Settings implements Watchable, Snappable { } Settings(File dataDir, RuntimePermissionsPersistence runtimePermissionsPersistence, - LegacyPermissionDataProvider permissionDataProvider, Object lock) { + LegacyPermissionDataProvider permissionDataProvider, + IntentFilterVerificationManager intentFilterVerificationManager, Object lock) { mLock = lock; mAppIds = new WatchedArrayList<>(); mOtherAppIds = new WatchedSparseArray<>(); @@ -587,12 +585,13 @@ public final class Settings implements Watchable, Snappable { mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml"); mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml"); + mIntentFilterVerificationManager = intentFilterVerificationManager; + mPackages.registerObserver(mObserver); mInstallerPackages.registerObserver(mObserver); mKernelMapping.registerObserver(mObserver); mDisabledSysPackages.registerObserver(mObserver); mBlockUninstallPackages.registerObserver(mObserver); - mRestoredIntentFilterVerifications.registerObserver(mObserver); mVersion.registerObserver(mObserver); mPreferredActivities.registerObserver(mObserver); mPersistentPreferredActivities.registerObserver(mObserver); @@ -629,11 +628,12 @@ public final class Settings implements Watchable, Snappable { mBackupStoppedPackagesFilename = null; mKernelMappingFilename = null; + mIntentFilterVerificationManager = r.mIntentFilterVerificationManager; + mInstallerPackages.addAll(r.mInstallerPackages); mKernelMapping.putAll(r.mKernelMapping); mDisabledSysPackages.putAll(r.mDisabledSysPackages); mBlockUninstallPackages.snapshot(r.mBlockUninstallPackages); - mRestoredIntentFilterVerifications.putAll(r.mRestoredIntentFilterVerifications); mVersion.putAll(r.mVersion); mVerifierDeviceIdentity = r.mVerifierDeviceIdentity; WatchedSparseArray.snapshot( @@ -1169,13 +1169,10 @@ public final class Settings implements Watchable, Snappable { } } - IntentFilterVerificationInfo ivi = mRestoredIntentFilterVerifications.get(p.name); - if (ivi != null) { - if (DEBUG_DOMAIN_VERIFICATION) { - Slog.i(TAG, "Applying restored IVI for " + p.name + " : " + ivi.getStatusString()); - } - mRestoredIntentFilterVerifications.remove(p.name); - p.setIntentFilterVerificationInfo(ivi); + IntentFilterVerificationInfo info = + mIntentFilterVerificationManager.getRestoredIntentFilterVerificationInfo(p.name); + if (info != null) { + p.setIntentFilterVerificationInfo(info); } } @@ -1307,129 +1304,6 @@ public final class Settings implements Watchable, Snappable { 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) { - if (DEBUG_DOMAIN_VERIFICATION) { - Slog.w(PackageManagerService.TAG, "No package known: " + packageName); - } - return null; - } - return ps.getIntentFilterVerificationInfo(); - } - - /* package protected */ - IntentFilterVerificationInfo createIntentFilterVerificationIfNeededLPw(String packageName, - ArraySet domains) { - PackageSetting ps = mPackages.get(packageName); - if (ps == null) { - if (DEBUG_DOMAIN_VERIFICATION) { - Slog.w(PackageManagerService.TAG, "No package known: " + packageName); - } - return null; - } - IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo(); - if (ivi == null) { - ivi = new IntentFilterVerificationInfo(packageName, domains); - ps.setIntentFilterVerificationInfo(ivi); - if (DEBUG_DOMAIN_VERIFICATION) { - Slog.d(PackageManagerService.TAG, - "Creating new IntentFilterVerificationInfo for pkg: " + packageName); - } - } else { - ivi.setDomains(domains); - if (DEBUG_DOMAIN_VERIFICATION) { - Slog.d(PackageManagerService.TAG, - "Setting domains to existing IntentFilterVerificationInfo for pkg: " + - packageName + " and with domains: " + ivi.getDomainsString()); - } - } - return ivi; - } - - int getIntentFilterVerificationStatusLPr(String packageName, int userId) { - PackageSetting ps = mPackages.get(packageName); - if (ps == null) { - if (DEBUG_DOMAIN_VERIFICATION) { - Slog.w(PackageManagerService.TAG, "No package known: " + packageName); - } - return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; - } - return (int)(ps.getDomainVerificationStatusForUser(userId) >> 32); - } - - boolean updateIntentFilterVerificationStatusLPw(String packageName, final int status, int userId) { - // Update the status for the current package - PackageSetting current = mPackages.get(packageName); - if (current == null) { - if (DEBUG_DOMAIN_VERIFICATION) { - Slog.w(PackageManagerService.TAG, "No package known: " + packageName); - } - return false; - } - - final int alwaysGeneration; - if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) { - alwaysGeneration = mNextAppLinkGeneration.get(userId) + 1; - mNextAppLinkGeneration.put(userId, alwaysGeneration); - } else { - alwaysGeneration = 0; - } - - current.setDomainVerificationStatusForUser(status, alwaysGeneration, userId); - return true; - } - - /** - * Used for Settings App and PackageManagerService dump. Should be read only. - */ - List getIntentFilterVerificationsLPr( - String packageName) { - if (packageName == null) { - return Collections.emptyList(); - } - ArrayList 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; - } - - boolean removeIntentFilterVerificationLPw(String packageName, int userId, - boolean alsoResetStatus) { - PackageSetting ps = mPackages.get(packageName); - if (ps == null) { - if (DEBUG_DOMAIN_VERIFICATION) { - Slog.w(PackageManagerService.TAG, "No package known: " + packageName); - } - return false; - } - 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, true); - } - return result; - } - String removeDefaultBrowserPackageNameLPw(int userId) { return (userId == UserHandle.USER_ALL) ? null : mDefaultBrowserApp.removeReturnOld(userId); } @@ -1591,40 +1465,7 @@ public final class Settings implements Watchable, Snappable { } } - private void readDomainVerificationLPw(TypedXmlPullParser parser, - PackageSettingBase packageSetting) throws XmlPullParserException, IOException { - IntentFilterVerificationInfo ivi = new IntentFilterVerificationInfo(parser); - packageSetting.setIntentFilterVerificationInfo(ivi); - if (DEBUG_PARSER) { - Log.d(TAG, "Read domain verification for package: " + ivi.getPackageName()); - } - } - - private void readRestoredIntentFilterVerifications(TypedXmlPullParser parser) - throws XmlPullParserException, IOException { - int outerDepth = parser.getDepth(); - int type; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { - if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { - continue; - } - final String tagName = parser.getName(); - if (tagName.equals(TAG_DOMAIN_VERIFICATION)) { - IntentFilterVerificationInfo ivi = new IntentFilterVerificationInfo(parser); - if (DEBUG_DOMAIN_VERIFICATION) { - Slog.i(TAG, "Restored IVI for " + ivi.getPackageName() - + " status=" + ivi.getStatusString()); - } - mRestoredIntentFilterVerifications.put(ivi.getPackageName(), ivi); - } else { - Slog.w(TAG, "Unknown element: " + tagName); - XmlUtils.skipCurrentTag(parser); - } - } - } - - void readDefaultAppsLPw(TypedXmlPullParser parser, int userId) + void readDefaultAppsLPw(XmlPullParser parser, int userId) throws XmlPullParserException, IOException { int outerDepth = parser.getDepth(); int type; @@ -2038,77 +1879,7 @@ public final class Settings implements Watchable, Snappable { serializer.endTag(null, TAG_CROSS_PROFILE_INTENT_FILTERS); } - void writeDomainVerificationsLPr(TypedXmlSerializer serializer, - IntentFilterVerificationInfo verificationInfo) - throws IllegalArgumentException, IllegalStateException, IOException { - if (verificationInfo != null && verificationInfo.getPackageName() != null) { - serializer.startTag(null, TAG_DOMAIN_VERIFICATION); - verificationInfo.writeToXml(serializer); - if (DEBUG_DOMAIN_VERIFICATION) { - Slog.d(TAG, "Wrote domain verification for package: " - + verificationInfo.getPackageName()); - } - serializer.endTag(null, TAG_DOMAIN_VERIFICATION); - } - } - - // Specifically for backup/restore - void writeAllDomainVerificationsLPr(TypedXmlSerializer serializer, int userId) - throws IllegalArgumentException, IllegalStateException, IOException { - serializer.startTag(null, TAG_ALL_INTENT_FILTER_VERIFICATION); - final int N = mPackages.size(); - for (int i = 0; i < N; i++) { - PackageSetting ps = mPackages.valueAt(i); - IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo(); - if (ivi != null) { - writeDomainVerificationsLPr(serializer, ivi); - } - } - serializer.endTag(null, TAG_ALL_INTENT_FILTER_VERIFICATION); - } - - // Specifically for backup/restore - void readAllDomainVerificationsLPr(TypedXmlPullParser parser, int userId) - throws XmlPullParserException, IOException { - mRestoredIntentFilterVerifications.clear(); - - int outerDepth = parser.getDepth(); - int type; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { - if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { - continue; - } - - String tagName = parser.getName(); - if (tagName.equals(TAG_DOMAIN_VERIFICATION)) { - IntentFilterVerificationInfo ivi = new IntentFilterVerificationInfo(parser); - final String pkgName = ivi.getPackageName(); - final PackageSetting ps = mPackages.get(pkgName); - if (ps != null) { - // known/existing package; update in place - ps.setIntentFilterVerificationInfo(ivi); - if (DEBUG_DOMAIN_VERIFICATION) { - Slog.d(TAG, "Restored IVI for existing app " + pkgName - + " status=" + ivi.getStatusString()); - } - } else { - mRestoredIntentFilterVerifications.put(pkgName, ivi); - if (DEBUG_DOMAIN_VERIFICATION) { - Slog.d(TAG, "Restored IVI for pending app " + pkgName - + " status=" + ivi.getStatusString()); - } - } - } else { - PackageManagerService.reportSettingsProblem(Log.WARN, - "Unknown element under : " - + parser.getName()); - XmlUtils.skipCurrentTag(parser); - } - } - } - - void writeDefaultAppsLPr(TypedXmlSerializer serializer, int userId) + void writeDefaultAppsLPr(XmlSerializer serializer, int userId) throws IllegalArgumentException, IllegalStateException, IOException { serializer.startTag(null, TAG_DEFAULT_APPS); String defaultBrowser = mDefaultBrowserApp.get(userId); @@ -2569,22 +2340,7 @@ public final class Settings implements Watchable, Snappable { } } - final int numIVIs = mRestoredIntentFilterVerifications.size(); - if (numIVIs > 0) { - if (DEBUG_DOMAIN_VERIFICATION) { - Slog.i(TAG, "Writing restored-ivi entries to packages.xml"); - } - serializer.startTag(null, "restored-ivi"); - for (int i = 0; i < numIVIs; i++) { - IntentFilterVerificationInfo ivi = mRestoredIntentFilterVerifications.valueAt(i); - writeDomainVerificationsLPr(serializer, ivi); - } - serializer.endTag(null, "restored-ivi"); - } else { - if (DEBUG_DOMAIN_VERIFICATION) { - Slog.i(TAG, " no restored IVI entries to write"); - } - } + mIntentFilterVerificationManager.writeRestoredIntentFilterVerifications(serializer); mKeySetManagerService.writeKeySetManagerServiceLPr(serializer); @@ -2973,7 +2729,8 @@ public final class Settings implements Watchable, Snappable { writeSigningKeySetLPr(serializer, pkg.keySetData); writeUpgradeKeySetsLPr(serializer, pkg.keySetData); writeKeySetAliasesLPr(serializer, pkg.keySetData); - writeDomainVerificationsLPr(serializer, pkg.verificationInfo); + mIntentFilterVerificationManager.writeDomainVerificationsLPr(serializer, + pkg.verificationInfo); writeMimeGroupLPr(serializer, pkg.mimeGroups); serializer.endTag(null, "package"); @@ -3105,7 +2862,7 @@ public final class Settings implements Watchable, Snappable { mRenamedPackages.put(nname, oname); } } else if (tagName.equals("restored-ivi")) { - readRestoredIntentFilterVerifications(parser); + mIntentFilterVerificationManager.readRestoredIntentFilterVerifications(parser); } else if (tagName.equals("last-platform-version")) { // Upgrade from older XML schema final VersionInfo internal = findOrCreateVersion( @@ -3946,7 +3703,12 @@ public final class Settings implements Watchable, Snappable { packageSetting.installSource = packageSetting.installSource.setInitiatingPackageSignatures(signatures); } else if (tagName.equals(TAG_DOMAIN_VERIFICATION)) { - readDomainVerificationLPw(parser, packageSetting); + IntentFilterVerificationInfo ivi = + mIntentFilterVerificationManager.readDomainVerificationLPw(parser); + packageSetting.setIntentFilterVerificationInfo(ivi); + if (DEBUG_PARSER) { + Log.d(TAG, "Read domain verification for package: " + ivi.getPackageName()); + } } else if (tagName.equals(TAG_MIME_GROUP)) { packageSetting.mimeGroups = readMimeGroupLPw(parser, packageSetting.mimeGroups); } else if (tagName.equals(TAG_USES_STATIC_LIB)) { diff --git a/services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationKey.java b/services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationKey.java new file mode 100644 index 0000000000000..1a15d8a75fc0e --- /dev/null +++ b/services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationKey.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2020 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.intent.verify.legacy; + +/** + * 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/intent/verify/legacy/IntentFilterVerificationManager.java b/services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationManager.java new file mode 100644 index 0000000000000..ee4081028cc04 --- /dev/null +++ b/services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationManager.java @@ -0,0 +1,584 @@ +/* + * Copyright (C) 2020 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.intent.verify.legacy; + +import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS; +import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK; +import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; +import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; + +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.IntentFilterVerificationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ParceledListSlice; +import android.content.pm.parsing.component.ParsedActivity; +import android.content.pm.parsing.component.ParsedIntentInfo; +import android.os.Binder; +import android.os.Handler; +import android.os.Message; +import android.os.UserHandle; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.SparseArray; +import android.util.StringBuilderPrinter; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.CollectionUtils; +import com.android.server.SystemConfig; +import com.android.server.pm.PackageManagerService; +import com.android.server.pm.PackageSetting; +import com.android.server.pm.UserManagerService; +import com.android.server.pm.parsing.pkg.AndroidPackage; +import com.android.server.utils.WatchedArrayMap; + +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class IntentFilterVerificationManager { + + private final Context mContext; + private final Handler mHandler; + private final IntentVerifierProxy.PackageManagerServiceConnection mConnection; + private final SystemConfig mSystemConfig; + private final IntentFilterVerificationSettings mSettings; + + private final IntentVerifierProxy mVerifier; + + private int mIntentFilterVerificationToken = 0; + private boolean mHasVerifier; + + private final SparseArray mStates = new SparseArray<>(); + + public IntentFilterVerificationManager(Context context, Handler handler, + IntentVerifierProxy.PackageManagerServiceConnection connection, + SystemConfig systemConfig, UserManagerService userManager) { + mContext = context; + mHandler = handler; + mConnection = connection; + mSystemConfig = systemConfig; + mSettings = new IntentFilterVerificationSettings(mContext, userManager, connection); + mVerifier = new IntentVerifierProxy(mContext, connection); + } + + public void setVerifierComponent(@Nullable ComponentName componentName) { + mVerifier.setComponent(componentName); + mHasVerifier = componentName != null; + } + + @Nullable + public ComponentName getVerifierComponent() { + return mVerifier.getComponent(); + } + + public void startIntentFilterVerifications(int userId, boolean replacing, AndroidPackage pkg) { + if (!mHasVerifier) { + mConnection.warnLog("No IntentFilter verification will not be done as " + + "there is no IntentFilterVerifier available!"); + return; + } + + final int verifierUid = mConnection.getPackageUid( + mVerifier.getComponent().getPackageName(), + MATCH_DEBUG_TRIAGED_MISSING, + (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId); + + Message msg = mHandler.obtainMessage( + PackageManagerService.START_INTENT_FILTER_VERIFICATIONS); + msg.obj = new IntentFilterVerificationParams( + pkg.getPackageName(), + pkg.isHasDomainUrls(), + pkg.getActivities(), + replacing, + userId, + verifierUid + ); + mHandler.sendMessage(msg); + } + + public void verifyIntentFiltersIfNeeded(IntentFilterVerificationParams params) { + if (!mHasVerifier) { + return; + } + + int userId = params.userId; + int verifierUid = params.verifierUid; + boolean replacing = params.replacing; + String packageName = params.packageName; + boolean hasDomainUrls = params.hasDomainUrls; + List activities = params.activities; + + + int size = activities.size(); + if (size == 0) { + mConnection.debugLog("No activity, so no need to verify any IntentFilter!"); + return; + } + + if (!hasDomainUrls) { + mConnection.debugLog("No domain URLs, so no need to verify any IntentFilter!"); + return; + } + + mConnection.debugLog("Checking for userId:" + userId + + " if any IntentFilter from the " + size + + " Activities needs verification ..."); + + boolean runVerify = mConnection.lockReturn(() -> { + int count = 0; + boolean handlesWebUris = false; + ArraySet domains = new ArraySet<>(); + final boolean previouslyVerified; + boolean hostSetExpanded = false; + boolean needToRunVerify = false; + + // 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. + IntentFilterVerificationInfo ivi = + mSettings.getIntentFilterVerificationLPr(packageName); + previouslyVerified = (ivi != null); + if (!replacing && previouslyVerified) { + mConnection.infoLog("Package " + packageName + " already verified: status=" + + ivi.getStatusString()); + return false; + } + + mConnection.infoLog(" 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); + + mConnection.infoLog(" needsVerification = " + needsVerification); + StringBuilder builder = new StringBuilder(); + StringBuilderPrinter printer = new StringBuilderPrinter(builder); + for (ParsedActivity a : activities) { + mConnection.infoLog(" activity = " + a.getClassName()); + for (ParsedIntentInfo filter : a.getIntents()) { + builder.setLength(0); + filter.dump(printer, ""); + mConnection.infoLog(" filter = " + builder.toString()); + mConnection.infoLog(" handlesWebUris = " + filter.handlesWebUris(true)); + mConnection.infoLog(" needsVerification = " + filter.needsVerification()); + if (filter.handlesWebUris(true)) { + handlesWebUris = true; + } + if (needsVerification && filter.needsVerification()) { + mConnection.debugLog("autoVerify requested, processing all filters"); + 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; + } + } + } + + mConnection.infoLog(" needToRunVerify = " + needToRunVerify); + mConnection.infoLog(" previouslyVerified = " + previouslyVerified); + // 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 (ParsedActivity a : activities) { + for (ParsedIntentInfo filter : a.getIntents()) { + // 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 /*onlyWebSchemes*/)) { + mConnection.debugLog("Verification needed for IntentFilter:" + + filter.toString()); + mVerifier.addOneIntentFilterVerification(verifierUid, userId, + verificationId, filter, packageName, mStates); + domains.addAll(filter.getHostsList()); + count++; + } + } + } + } + + mConnection.infoLog(" 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) { + mConnection.infoLog("Host set not expanding + ALWAYS -> no need to reverify"); + ivi.setDomains(domains); + mConnection.scheduleWriteSettingsLocked(); + return false; + } 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. + mSettings.clearIntentFilterVerificationsLocked(packageName, userId, !keepCurState); + return false; + } + + if (needToRunVerify && count > 0) { + // app requested autoVerify and has at least one matching intent filter + mConnection.debugLog("Starting " + count + + " IntentFilter verification" + (count > 1 ? "s" : "") + + " for userId:" + userId); + return true; + } else { + mConnection.debugLog("No web filters or no new host policy for " + packageName); + return false; + } + }); + + if (runVerify) { + mVerifier.startVerifications(userId, mStates); + } + } + + private boolean needsNetworkVerificationLPr(String packageName) { + 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_ALWAYS: + case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK: + return true; + + default: + // Nothing to do + return false; + } + } + + public void queueVerifyResult(int id, int verificationCode, List failedDomains) { + if (!mHasVerifier) { + return; + } + + mContext.enforceCallingOrSelfPermission( + Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT, + "Only intentfilter verification agents can verify applications"); + + final Message msg = mHandler.obtainMessage(PackageManagerService.INTENT_FILTER_VERIFIED); + final IntentFilterVerificationResponse + response = new IntentFilterVerificationResponse( + Binder.getCallingUid(), verificationCode, failedDomains); + msg.arg1 = id; + msg.obj = response; + mHandler.sendMessage(msg); + } + + public void onFilterVerified(Message msg) { + if (!mHasVerifier) { + return; + } + + final int verificationId = msg.arg1; + + final IntentFilterVerificationState state = mStates.get(verificationId); + if (state == null) { + mConnection.warnLog("Invalid IntentFilter verification token " + + verificationId + " received"); + return; + } + + final int userId = state.getUserId(); + + mConnection.debugLog("Processing IntentFilter verification with token:" + + verificationId + " and userId:" + userId); + + final IntentFilterVerificationResponse + response = + (IntentFilterVerificationResponse) msg.obj; + + state.setVerifierResponse(response.callerUid, response.code); + + mConnection.debugLog("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) { + mConnection.debugLog("Domains failing verification: " + + response.getFailedDomainsString()); + } + + if (state.isVerificationComplete()) { + receiveVerificationResponse(verificationId); + } else { + mConnection.debugLog("IntentFilter verification with token:" + verificationId + + " was not said to be complete"); + } + } + + public void receiveVerificationResponse(int verificationId) { + IntentFilterVerificationState ivs = mStates.get(verificationId); + + final boolean verified = ivs.isVerified(); + + ArrayList filters = ivs.getFilters(); + final int count = filters.size(); + mConnection.debugLog("Received verification response " + verificationId + + " for " + count + " filters, verified=" + verified); + + for (int n = 0; n < count; n++) { + ParsedIntentInfo filter = filters.get(n); + filter.setVerified(verified); + + mConnection.debugLog("IntentFilter " + filter.toString() + + " verified with result:" + verified + " and hosts:" + + ivs.getHostsString()); + } + + mStates.remove(verificationId); + + final String packageName = ivs.getPackageName(); + IntentFilterVerificationInfo ivi = mSettings.getIntentFilterVerificationLPr(packageName); + if (ivi == null) { + mConnection.warnLog("IntentFilterVerificationInfo not found for verificationId:" + + verificationId + " packageName:" + packageName); + return; + } + + mConnection.lock(() -> { + if (verified) { + ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS); + } else { + ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK); + } + mConnection.scheduleWriteSettingsLocked(); + + updateUser(packageName, ivs.getUserId(), verified); + }); + } + + private void updateUser(String packageName, @UserIdInt int userId, boolean verified) { + if (userId == UserHandle.USER_ALL) { + mConnection.infoLog("autoVerify ignored when installing for all users"); + return; + } + + final int userStatus = mSettings.getIntentFilterVerificationStatusLPr(packageName, userId); + + int updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; + boolean needUpdate = false; + + // 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 packages = systemConfig.getLinkedApps(); + if (!packages.contains(packageName)) { + // updatedStatus is already UNDEFINED + needUpdate = true; + + mConnection.debugLog( + "Formerly validated but now failing; demoting"); + } else { + mConnection.debugLog("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; + mConnection.debugLog("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; + + + // Nothing to do + } + + if (needUpdate) { + mSettings.updateIntentFilterVerificationStatusLPw(packageName, updatedStatus, userId); + mConnection.scheduleWritePackageRestrictionsLocked(userId); + } + } + + public void primeDomainVerificationsLPw(int userId, Map packages) { + if (!mHasVerifier) { + return; + } + + mConnection.debugLog("Priming domain verifications in user " + userId); + + ArraySet packageNames = mSystemConfig.getLinkedApps(); + + for (int pkgNameIndex = 0; pkgNameIndex < packageNames.size(); pkgNameIndex++) { + String packageName = packageNames.valueAt(pkgNameIndex); + AndroidPackage pkg = packages.get(packageName); + if (pkg == null) { + mConnection.warnLog("Unknown package " + packageName + " in sysconfig "); + continue; + } else if (!pkg.isSystem()) { + mConnection.warnLog("Non-system app '" + packageName + "' in sysconfig "); + continue; + } + ArraySet domains = null; + List activities = pkg.getActivities(); + for (int activityIndex = 0; activityIndex < activities.size(); activityIndex++) { + List intentInfos = activities.get(activityIndex).getIntents(); + for (int infoIndex = 0; infoIndex < intentInfos.size(); infoIndex++) { + ParsedIntentInfo intentInfo = intentInfos.get(infoIndex); + if (IntentVerifyUtils.hasValidDomains(intentInfo)) { + domains = ArrayUtils.addAll(domains, intentInfo.getHostsList()); + } + } + } + + if (CollectionUtils.isEmpty(domains)) { + mConnection.warnLog("Sysconfig package '" + packageName + + "' does not handle web links"); + continue; + } + + mConnection.verboseLog(" + " + packageName); + // 'Undefined' in the global IntentFilterVerificationInfo, i.e. the usual + // state w.r.t. the formal app-linkage "no verification attempted" state; + // and then 'always' in the per-user state actually used for intent resolution. + final IntentFilterVerificationInfo ivi; + ivi = mSettings.createIntentFilterVerificationIfNeededLPw(packageName, domains); + ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED); + mSettings.updateIntentFilterVerificationStatusLPw(packageName, + INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS, userId); + } + + mConnection.scheduleWritePackageRestrictionsLocked(userId); + mConnection.scheduleWriteSettingsLocked(); + } + + public IntentFilterVerificationInfo updatePackageSetting(@NonNull PackageSetting pkgSetting, + ArraySet domainSet) { + return mSettings.updatePackageSetting(pkgSetting, domainSet); + } + + @NonNull + public ParceledListSlice getIntentFilterVerifications( + @NonNull String packageName) { + return mSettings.getIntentFilterVerifications(packageName); + } + + public int getIntentVerificationStatus(@NonNull String packageName, int userId) { + return mSettings.getIntentVerificationStatus(packageName, userId); + } + + public boolean updateIntentVerificationStatus(@NonNull String packageName, int status, + int userId) { + return mSettings.updateIntentVerificationStatus(packageName, status, userId); + } + + public void clearIntentFilterVerificationsLocked(@NonNull String packageName, int userId, + boolean alsoResetStatus) { + mSettings.clearIntentFilterVerificationsLocked(packageName, userId, alsoResetStatus); + } + + public void clearIntentFilterVerificationsLocked(int userId, + WatchedArrayMap packages) { + mSettings.clearIntentFilterVerificationsLocked(userId, packages); + } + + public void writeAllDomainVerificationsLPr(TypedXmlSerializer serializer, int userId, + @NonNull Map pkgSettings) throws IOException { + mSettings.writeAllDomainVerificationsLPr(serializer, userId, pkgSettings); + } + + public void readAllDomainVerificationsLPr(TypedXmlPullParser parser, @UserIdInt int userId) + throws IOException, XmlPullParserException { + mSettings.readAllDomainVerificationsLPr(parser, userId); + } + + public void writeDomainVerificationsLPr(@NonNull TypedXmlSerializer serializer, + @NonNull IntentFilterVerificationInfo info) throws IOException { + mSettings.writeDomainVerificationsLPr(serializer, info); + } + + @Nullable + public IntentFilterVerificationInfo getRestoredIntentFilterVerificationInfo( + @NonNull String packageName) { + return mSettings.getRestoredIntentFilterVerificationInfo(packageName); + } + + public void readRestoredIntentFilterVerifications(@NonNull TypedXmlPullParser parser) + throws IOException, XmlPullParserException { + mSettings.readRestoredIntentFilterVerifications(parser); + } + + public void writeRestoredIntentFilterVerifications(@NonNull TypedXmlSerializer serializer) + throws IOException { + mSettings.writeRestoredIntentFilterVerifications(serializer); + } + + @NonNull + public IntentFilterVerificationInfo readDomainVerificationLPw( + @NonNull TypedXmlPullParser parser) + throws IOException, XmlPullParserException { + return mSettings.readDomainVerificationLPw(parser); + } +} diff --git a/services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationParams.java b/services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationParams.java new file mode 100644 index 0000000000000..699c3ef29d845 --- /dev/null +++ b/services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationParams.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2020 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.intent.verify.legacy; + +import android.content.pm.parsing.component.ParsedActivity; + +import java.util.List; + +public class IntentFilterVerificationParams { + + String packageName; + boolean hasDomainUrls; + List activities; + boolean replacing; + int userId; + int verifierUid; + + public IntentFilterVerificationParams(String packageName, boolean hasDomainUrls, + List activities, boolean _replacing, + int _userId, int _verifierUid) { + this.packageName = packageName; + this.hasDomainUrls = hasDomainUrls; + this.activities = activities; + replacing = _replacing; + userId = _userId; + verifierUid = _verifierUid; + } +} diff --git a/services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationResponse.java b/services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationResponse.java new file mode 100644 index 0000000000000..c513380b2e79b --- /dev/null +++ b/services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationResponse.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2020 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.intent.verify.legacy; + + +import java.util.List; + +public class IntentFilterVerificationResponse { + public final int callerUid; + public final int code; + public final List failedDomains; + + public IntentFilterVerificationResponse(int callerUid, int code, List 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/intent/verify/legacy/IntentFilterVerificationSettings.java b/services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationSettings.java new file mode 100644 index 0000000000000..2aac51402d102 --- /dev/null +++ b/services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationSettings.java @@ -0,0 +1,393 @@ +/* + * Copyright (C) 2020 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.intent.verify.legacy; + +import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS; +import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.content.Context; +import android.content.pm.IntentFilterVerificationInfo; +import android.content.pm.ParceledListSlice; +import android.os.Binder; +import android.os.UserHandle; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.Log; +import android.util.SparseIntArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.XmlUtils; +import com.android.server.SystemConfig; +import com.android.server.pm.PackageManagerService; +import com.android.server.pm.PackageSetting; +import com.android.server.pm.Settings; +import com.android.server.pm.UserManagerService; +import com.android.server.pm.parsing.pkg.AndroidPackage; +import com.android.server.utils.WatchedArrayMap; +import com.android.server.utils.WatchedSparseIntArray; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class IntentFilterVerificationSettings { + + private final Context mContext; + private final IntentVerifierProxy.PackageManagerServiceConnection mConnection; + private final UserManagerService mUserManagerService; + + // Set of restored intent-filter verification states + final ArrayMap mRestoredIntentFilterVerifications = + new ArrayMap<>(); + + public IntentFilterVerificationSettings(Context context, + UserManagerService userManagerService, + IntentVerifierProxy.PackageManagerServiceConnection connection) { + mContext = context; + mConnection = connection; + mUserManagerService = userManagerService; + } + + public int getIntentVerificationStatus(@NonNull String packageName, int userId) { + final int callingUid = Binder.getCallingUid(); + if (UserHandle.getUserId(callingUid) != userId) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + "getIntentVerificationStatus" + userId); + } + if (mConnection.getInstantAppPackageName(callingUid) != null) { + return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; + } + return mConnection.lockReturn(() -> { + final PackageSetting ps = mConnection.getPackageSettingLPr(packageName); + if (ps == null + || mConnection.shouldFilterApplicationLocked( + ps, callingUid, UserHandle.getUserId(callingUid))) { + return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; + } + return getIntentFilterVerificationStatusLPr(packageName, userId); + }); + } + + public boolean updateIntentVerificationStatus(@NonNull String packageName, int status, + int userId) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); + + boolean result = mConnection.lockReturn(() -> { + final PackageSetting ps = mConnection.getPackageSettingLPr(packageName); + if (mConnection.shouldFilterApplicationLocked( + ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) { + return false; + } + return updateIntentFilterVerificationStatusLPw(packageName, status, userId); + }); + if (result) { + mConnection.scheduleWritePackageRestrictionsLocked(userId); + } + return result; + } + + @NonNull + public ParceledListSlice getIntentFilterVerifications( + @NonNull String packageName) { + final int callingUid = Binder.getCallingUid(); + if (mConnection.getInstantAppPackageName(callingUid) != null) { + return ParceledListSlice.emptyList(); + } + return mConnection.lockReturn(() -> { + final PackageSetting ps = mConnection.getPackageSettingLPr(packageName); + if (mConnection.shouldFilterApplicationLocked(ps, callingUid, + UserHandle.getUserId(callingUid))) { + return ParceledListSlice.emptyList(); + } + return new ParceledListSlice<>(getIntentFilterVerificationsLPr(packageName)); + }); + } + + + /** This method takes a specific user id as well as UserHandle.USER_ALL. */ + public void clearIntentFilterVerificationsLocked(int userId, + WatchedArrayMap packages) { + final int packageCount = packages.size(); + for (int i = 0; i < packageCount; i++) { + AndroidPackage pkg = packages.valueAt(i); + clearIntentFilterVerificationsLocked(pkg.getPackageName(), userId, true); + } + } + + /** This method takes a specific user id as well as UserHandle.USER_ALL. */ + public void clearIntentFilterVerificationsLocked(String packageName, int userId, + boolean alsoResetStatus) { + if (SystemConfig.getInstance().getLinkedApps().contains(packageName)) { + // Nope, need to preserve the system configuration approval for this app + return; + } + + if (userId == UserHandle.USER_ALL) { + if (removeIntentFilterVerificationLPw(packageName, mUserManagerService.getUserIds())) { + for (int oneUserId : mUserManagerService.getUserIds()) { + mConnection.scheduleWritePackageRestrictionsLocked(oneUserId); + } + } + } else { + if (removeIntentFilterVerificationLPw(packageName, userId, alsoResetStatus)) { + mConnection.scheduleWritePackageRestrictionsLocked(userId); + } + } + } + + @Nullable + public IntentFilterVerificationInfo createIntentFilterVerificationIfNeededLPw( + String packageName, ArraySet domains) { + PackageSetting pkgSetting = mConnection.getPackageSettingLPr(packageName); + if (pkgSetting == null) { + mConnection.warnLog("No package known: " + packageName); + return null; + } + return updatePackageSetting(pkgSetting, domains); + } + + public IntentFilterVerificationInfo updatePackageSetting(@NonNull PackageSetting pkgSetting, + ArraySet domains) { + String pkgName = pkgSetting.name; + IntentFilterVerificationInfo ivi = pkgSetting.getIntentFilterVerificationInfo(); + if (ivi == null) { + ivi = new IntentFilterVerificationInfo(pkgName, domains); + pkgSetting.setIntentFilterVerificationInfo(ivi); + mConnection.debugLog("Creating new IntentFilterVerificationInfo for pkg: " + pkgName); + } else { + ivi.setDomains(domains); + mConnection.debugLog( + "Setting domains to existing IntentFilterVerificationInfo for pkg: " + + pkgName + " and with domains: " + ivi.getDomainsString()); + } + return ivi; + } + + public int getIntentFilterVerificationStatusLPr(@NonNull String packageName, + @UserIdInt int userId) { + PackageSetting pkgSetting = mConnection.getPackageSettingLPr(packageName); + if (pkgSetting == null) { + mConnection.warnLog("No package known: " + packageName); + return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; + } + return (int) (pkgSetting.getDomainVerificationStatusForUser(userId) >> 32); + } + + @Nullable + public IntentFilterVerificationInfo getIntentFilterVerificationLPr( + @NonNull String packageName) { + PackageSetting ps = mConnection.getPackageSettingLPr(packageName); + if (ps == null) { + mConnection.warnLog("No package known: " + packageName); + return null; + } + return ps.getIntentFilterVerificationInfo(); + } + + boolean updateIntentFilterVerificationStatusLPw(String packageName, final int status, + int userId) { + // Update the status for the current package + PackageSetting current = mConnection.getPackageSettingLPr(packageName); + if (current == null) { + mConnection.warnLog("No package known: " + packageName); + return false; + } + + final int alwaysGeneration; + if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) { + WatchedSparseIntArray nextAppLinkGeneration = mConnection.getNextAppLinkGeneration(); + alwaysGeneration = nextAppLinkGeneration.get(userId) + 1; + nextAppLinkGeneration.put(userId, alwaysGeneration); + } else { + alwaysGeneration = 0; + } + + current.setDomainVerificationStatusForUser(status, alwaysGeneration, userId); + return true; + } + + private boolean removeIntentFilterVerificationLPw(String packageName, int userId, + boolean alsoResetStatus) { + PackageSetting ps = mConnection.getPackageSettingLPr(packageName); + if (ps == null) { + mConnection.warnLog("No package known: " + packageName); + return false; + } + if (alsoResetStatus) { + ps.clearDomainVerificationStatusForUser(userId); + } + ps.setIntentFilterVerificationInfo(null); + return true; + } + + private boolean removeIntentFilterVerificationLPw(String packageName, int[] userIds) { + boolean result = false; + for (int userId : userIds) { + result |= removeIntentFilterVerificationLPw(packageName, userId, true); + } + return result; + } + + private List getIntentFilterVerificationsLPr( + String packageName) { + if (packageName == null) { + return Collections.emptyList(); + } + ArrayList result = new ArrayList<>(); + for (PackageSetting ps : mConnection.getPackageSettingsLPr().values()) { + IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo(); + if (ivi == null || TextUtils.isEmpty(ivi.getPackageName()) || + !ivi.getPackageName().equalsIgnoreCase(packageName)) { + continue; + } + result.add(ivi); + } + return result; + } + + // Specifically for backup/restore + public void writeAllDomainVerificationsLPr(TypedXmlSerializer serializer, int userId, + @NonNull Map pkgSettings) + throws IllegalArgumentException, IllegalStateException, IOException { + serializer.startTag(null, Settings.TAG_ALL_INTENT_FILTER_VERIFICATION); + for (PackageSetting value : pkgSettings.values()) { + IntentFilterVerificationInfo ivi = value.getIntentFilterVerificationInfo(); + if (ivi != null) { + writeDomainVerificationsLPr(serializer, ivi); + } + } + serializer.endTag(null, Settings.TAG_ALL_INTENT_FILTER_VERIFICATION); + } + + public void writeDomainVerificationsLPr(TypedXmlSerializer serializer, + IntentFilterVerificationInfo verificationInfo) + throws IllegalArgumentException, IllegalStateException, IOException { + if (verificationInfo != null && verificationInfo.getPackageName() != null) { + serializer.startTag(null, Settings.TAG_DOMAIN_VERIFICATION); + verificationInfo.writeToXml(serializer); + mConnection.debugLog("Wrote domain verification for package: " + + verificationInfo.getPackageName()); + serializer.endTag(null, Settings.TAG_DOMAIN_VERIFICATION); + } + } + + // Specifically for backup/restore + public void readAllDomainVerificationsLPr(TypedXmlPullParser parser, int userId) + throws XmlPullParserException, IOException { + mRestoredIntentFilterVerifications.clear(); + + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals(Settings.TAG_DOMAIN_VERIFICATION)) { + IntentFilterVerificationInfo ivi = new IntentFilterVerificationInfo(parser); + final String pkgName = ivi.getPackageName(); + final PackageSetting ps = mConnection.getPackageSettingLPr(pkgName); + if (ps != null) { + // known/existing package; update in place + ps.setIntentFilterVerificationInfo(ivi); + mConnection.debugLog("Restored IVI for existing app " + pkgName + + " status=" + ivi.getStatusString()); + } else { + mRestoredIntentFilterVerifications.put(pkgName, ivi); + mConnection.debugLog("Restored IVI for pending app " + pkgName + + " status=" + ivi.getStatusString()); + } + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Unknown element under : " + + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + } + + public IntentFilterVerificationInfo getRestoredIntentFilterVerificationInfo( + @NonNull String packageName) { + IntentFilterVerificationInfo info = mRestoredIntentFilterVerifications.remove(packageName); + if (info != null) { + mConnection.infoLog( + "Applying restored IVI for " + packageName + " : " + info.getStatusString()); + } + + return info; + } + + public void readRestoredIntentFilterVerifications(@NonNull TypedXmlPullParser parser) + throws IOException, XmlPullParserException { + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + final String tagName = parser.getName(); + if (tagName.equals(Settings.TAG_DOMAIN_VERIFICATION)) { + IntentFilterVerificationInfo ivi = new IntentFilterVerificationInfo(parser); + mConnection.infoLog("Restored IVI for " + ivi.getPackageName() + + " status=" + ivi.getStatusString()); + mRestoredIntentFilterVerifications.put(ivi.getPackageName(), ivi); + } else { + mConnection.warnLog("Unknown element: " + tagName); + XmlUtils.skipCurrentTag(parser); + } + } + } + + public void writeRestoredIntentFilterVerifications(@NonNull TypedXmlSerializer serializer) + throws IOException { + final int numIVIs = mRestoredIntentFilterVerifications.size(); + if (numIVIs > 0) { + mConnection.infoLog("Writing restored-ivi entries to packages.xml"); + serializer.startTag(null, "restored-ivi"); + for (int i = 0; i < numIVIs; i++) { + IntentFilterVerificationInfo ivi = mRestoredIntentFilterVerifications.valueAt(i); + writeDomainVerificationsLPr(serializer, ivi); + } + serializer.endTag(null, "restored-ivi"); + } else { + mConnection.infoLog(" no restored IVI entries to write"); + } + } + + @NonNull + public IntentFilterVerificationInfo readDomainVerificationLPw( + @NonNull TypedXmlPullParser parser) + throws IOException, XmlPullParserException { + return new IntentFilterVerificationInfo(parser); + } +} diff --git a/services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationState.java b/services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationState.java new file mode 100644 index 0000000000000..7026fbda89cc2 --- /dev/null +++ b/services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationState.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2020 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.intent.verify.legacy; + +import android.content.pm.PackageManager; +import android.content.pm.parsing.component.ParsedIntentInfo; +import android.util.ArraySet; +import android.util.Slog; + +import java.util.ArrayList; + +public class IntentFilterVerificationState { + static final String TAG = IntentFilterVerificationState.class.getName(); + + public static final int STATE_UNDEFINED = 0; + public static final int STATE_VERIFICATION_PENDING = 1; + public static final int STATE_VERIFICATION_SUCCESS = 2; + public static final int STATE_VERIFICATION_FAILURE = 3; + + private int mRequiredVerifierUid = 0; + + private int mState; + + private ArrayList mFilters = new ArrayList<>(); + private ArraySet 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 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(" "); + } + String host = mHosts.valueAt(i); + // "*.example.tld" is validated via https://example.tld + if (host.startsWith("*.")) { + host = host.substring(2); + } + sb.append(host); + } + 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; + } + Slog.d(TAG, "Cannot set verifier response with callerUid:" + callerUid + " and code:" + + code + " as required verifierUid is:" + mRequiredVerifierUid); + return false; + } + + public void addFilter(ParsedIntentInfo filter) { + mFilters.add(filter); + mHosts.addAll(filter.getHostsList()); + } +} diff --git a/services/core/java/com/android/server/pm/intent/verify/legacy/IntentVerifierProxy.java b/services/core/java/com/android/server/pm/intent/verify/legacy/IntentVerifierProxy.java new file mode 100644 index 0000000000000..b2213dc917116 --- /dev/null +++ b/services/core/java/com/android/server/pm/intent/verify/legacy/IntentVerifierProxy.java @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2020 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.intent.verify.legacy; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.BroadcastOptions; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.content.pm.parsing.component.ParsedIntentInfo; +import android.os.Process; +import android.os.UserHandle; +import android.util.ArraySet; +import android.util.SparseArray; +import android.util.SparseIntArray; + +import com.android.server.DeviceIdleInternal; +import com.android.server.pm.PackageSetting; +import com.android.server.utils.WatchedSparseIntArray; + +import java.util.ArrayList; +import java.util.Map; +import java.util.function.Supplier; + +public class IntentVerifierProxy { + + private final Context mContext; + private final PackageManagerServiceConnection mConnection; + + private final ArrayList mCurrentIntentFilterVerifications = new ArrayList<>(); + + @Nullable + private ComponentName mIntentFilterVerifierComponent; + + public IntentVerifierProxy(Context context, PackageManagerServiceConnection connection) { + mConnection = connection; + mContext = context; + } + + private String getDefaultScheme() { + return IntentFilter.SCHEME_HTTPS; + } + + public void setComponent(@Nullable ComponentName componentName) { + this.mIntentFilterVerifierComponent = componentName; + } + + @Nullable + public ComponentName getComponent() { + return mIntentFilterVerifierComponent; + } + + public void startVerifications(int userId, SparseArray states) { + if (mIntentFilterVerifierComponent == null) { + return; + } + + // Launch verifications requests + int count = mCurrentIntentFilterVerifications.size(); + for (int n = 0; n < count; n++) { + int verificationId = mCurrentIntentFilterVerifications.get(n); + final IntentFilterVerificationState ivs = states.get(verificationId); + + String packageName = ivs.getPackageName(); + + ArrayList filters = ivs.getFilters(); + final int filterCount = filters.size(); + ArraySet domainsSet = new ArraySet<>(); + for (int m = 0; m < filterCount; m++) { + ParsedIntentInfo filter = filters.get(m); + domainsSet.addAll(filter.getHostsList()); + } + mConnection.writeSettings(packageName, domainsSet); + sendVerificationRequest(verificationId, ivs); + } + mCurrentIntentFilterVerifications.clear(); + } + + private void sendVerificationRequest(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); + + final long allowListTimeout = mConnection.getVerificationTimeout(); + final BroadcastOptions options = BroadcastOptions.makeBasic(); + options.setTemporaryAppWhitelistDuration(allowListTimeout); + + mConnection.getDeviceIdleInternal().addPowerSaveTempWhitelistApp(Process.myUid(), + mIntentFilterVerifierComponent.getPackageName(), allowListTimeout, + UserHandle.USER_SYSTEM, true, "intent filter verifier"); + + mContext.sendBroadcastAsUser(verificationIntent, UserHandle.SYSTEM, + null, options.toBundle()); + mConnection.debugLog("Sending IntentFilter verification broadcast"); + } + + public boolean addOneIntentFilterVerification(int verifierUid, int userId, int verificationId, + ParsedIntentInfo filter, String packageName, + SparseArray states) { + if (!IntentVerifyUtils.hasValidDomains(filter)) { + return false; + } + IntentFilterVerificationState ivs = states.get(verificationId); + if (ivs == null) { + ivs = createDomainVerificationState(verifierUid, userId, verificationId, + packageName, states); + } + mConnection.debugLog("Adding verification filter for " + packageName + ": " + filter); + ivs.addFilter(filter); + return true; + } + + private IntentFilterVerificationState createDomainVerificationState(int verifierUid, + int userId, int verificationId, String packageName, + SparseArray states) { + IntentFilterVerificationState + ivs = new IntentFilterVerificationState( + verifierUid, userId, packageName); + ivs.setPendingState(); + mConnection.lock(() -> { + states.append(verificationId, ivs); + mCurrentIntentFilterVerifications.add(verificationId); + }); + return ivs; + } + + public interface PackageManagerServiceConnection { + void lock(Runnable block); + + T lockReturn(Supplier block); + + void debugLog(String message); + + void verboseLog(String message); + + void warnLog(String message); + + void infoLog(String message); + + void writeSettings(String packageName, ArraySet domainsSet); + + // Seems this is used when an IFVI object is mutated, and it's assumed that the same object + // ends up written to disk. + void scheduleWriteSettingsLocked(); + + long getVerificationTimeout(); + + void scheduleWritePackageRestrictionsLocked(@UserIdInt int userId); + + String getInstantAppPackageName(int callingUid); + + @Nullable + PackageSetting getPackageSettingLPr(@NonNull String packageName); + + @NonNull + Map getPackageSettingsLPr(); + + boolean shouldFilterApplicationLocked(PackageSetting ps, int callingUid, + @UserIdInt int userId); + + int getPackageUid(String packageName, int flags, @UserIdInt int userId); + + @NonNull + WatchedSparseIntArray getNextAppLinkGeneration(); + + /** + * DeviceIdleInternal has a dependency on PackageManager, so it can't be passed in at + * initialization. It has to be accessed at use time. + */ + @NonNull + DeviceIdleInternal getDeviceIdleInternal(); + } +} diff --git a/services/core/java/com/android/server/pm/intent/verify/legacy/IntentVerifyUtils.java b/services/core/java/com/android/server/pm/intent/verify/legacy/IntentVerifyUtils.java new file mode 100644 index 0000000000000..389aa20e9ff04 --- /dev/null +++ b/services/core/java/com/android/server/pm/intent/verify/legacy/IntentVerifyUtils.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2020 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.intent.verify.legacy; + +import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; + +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.parsing.component.ParsedIntentInfo; + +import com.android.server.pm.PackageSetting; + +public class IntentVerifyUtils { + + public static boolean hasValidDomains(ParsedIntentInfo filter) { + return filter.hasCategory(Intent.CATEGORY_BROWSABLE) + && (filter.hasDataScheme(IntentFilter.SCHEME_HTTP) || + filter.hasDataScheme(IntentFilter.SCHEME_HTTPS)); + } + + // Returns a packed value as a long: + // + // high 'int'-sized word: link status: undefined/ask/never/always. + // low 'int'-sized word: relative priority among 'always' results. + public static long getDomainVerificationStatus(PackageSetting ps, int userId) { + long result = ps.getDomainVerificationStatusForUser(userId); + // if none available, get the status + if (result >> 32 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) { + if (ps.getIntentFilterVerificationInfo() != null) { + result = ((long) ps.getIntentFilterVerificationInfo().getStatus()) << 32; + } + } + return result; + } +} diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java index 333ec9295b93f..75c69872bb524 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -36,7 +36,6 @@ import static org.junit.Assert.fail; import android.annotation.NonNull; import android.app.PropertyInvalidatedCache; -import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageParser; @@ -59,6 +58,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.permission.persistence.RuntimePermissionsPersistence; import com.android.server.LocalServices; +import com.android.server.pm.intent.verify.legacy.IntentFilterVerificationManager; import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.parsing.pkg.ParsedPackage; import com.android.server.pm.permission.LegacyPermissionDataProvider; @@ -94,6 +94,8 @@ public class PackageManagerSettingsTests { RuntimePermissionsPersistence mRuntimePermissionsPersistence; @Mock LegacyPermissionDataProvider mPermissionDataProvider; + @Mock + IntentFilterVerificationManager mIntentFilterVerificationManager; @Before public void initializeMocks() { @@ -112,10 +114,7 @@ public class PackageManagerSettingsTests { throws ReflectiveOperationException, IllegalAccessException { /* write out files and read */ writeOldFiles(); - final Context context = InstrumentationRegistry.getContext(); - final Object lock = new Object(); - Settings settings = new Settings(context.getFilesDir(), - mRuntimePermissionsPersistence, mPermissionDataProvider, lock); + Settings settings = makeSettings(); assertThat(settings.readLPw(createFakeUsers()), is(true)); verifyKeySetMetaData(settings); } @@ -126,10 +125,7 @@ public class PackageManagerSettingsTests { throws ReflectiveOperationException, IllegalAccessException { // write out files and read writeOldFiles(); - final Context context = InstrumentationRegistry.getContext(); - final Object lock = new Object(); - Settings settings = new Settings(context.getFilesDir(), - mRuntimePermissionsPersistence, mPermissionDataProvider, lock); + Settings settings = makeSettings(); assertThat(settings.readLPw(createFakeUsers()), is(true)); // write out, read back in and verify the same @@ -142,10 +138,7 @@ public class PackageManagerSettingsTests { public void testSettingsReadOld() { // Write delegateshellthe package files and make sure they're parsed properly the first time writeOldFiles(); - final Context context = InstrumentationRegistry.getContext(); - final Object lock = new Object(); - Settings settings = new Settings(context.getFilesDir(), - mRuntimePermissionsPersistence, mPermissionDataProvider, lock); + Settings settings = makeSettings(); assertThat(settings.readLPw(createFakeUsers()), is(true)); assertThat(settings.getPackageLPr(PACKAGE_NAME_3), is(notNullValue())); assertThat(settings.getPackageLPr(PACKAGE_NAME_1), is(notNullValue())); @@ -164,16 +157,12 @@ public class PackageManagerSettingsTests { public void testNewPackageRestrictionsFile() throws ReflectiveOperationException { // Write the package files and make sure they're parsed properly the first time writeOldFiles(); - final Context context = InstrumentationRegistry.getContext(); - final Object lock = new Object(); - Settings settings = new Settings(context.getFilesDir(), - mRuntimePermissionsPersistence, mPermissionDataProvider, lock); + Settings settings = makeSettings(); assertThat(settings.readLPw(createFakeUsers()), is(true)); settings.writeLPr(); // Create Settings again to make it read from the new files - settings = new Settings(context.getFilesDir(), - mRuntimePermissionsPersistence, mPermissionDataProvider, lock); + settings = makeSettings(); assertThat(settings.readLPw(createFakeUsers()), is(true)); PackageSetting ps = settings.getPackageLPr(PACKAGE_NAME_2); @@ -200,10 +189,7 @@ public class PackageManagerSettingsTests { @Test public void testReadPackageRestrictions_noSuspendingPackage() { writePackageRestrictions_noSuspendingPackageXml(0); - final Object lock = new Object(); - final Context context = InstrumentationRegistry.getTargetContext(); - final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, - lock); + Settings settingsUnderTest = makeSettings(); final WatchableTester watcher = new WatchableTester(settingsUnderTest, "noSuspendingPackage"); watcher.register(); @@ -244,10 +230,7 @@ public class PackageManagerSettingsTests { @Test public void testReadPackageRestrictions_noSuspendParamsMap() { writePackageRestrictions_noSuspendParamsMapXml(0); - final Object lock = new Object(); - final Context context = InstrumentationRegistry.getTargetContext(); - final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, - lock); + final Settings settingsUnderTest = makeSettings(); final WatchableTester watcher = new WatchableTester(settingsUnderTest, "noSuspendParamsMap"); watcher.register(); @@ -281,9 +264,7 @@ public class PackageManagerSettingsTests { @Test public void testReadWritePackageRestrictions_suspendInfo() { - final Context context = InstrumentationRegistry.getTargetContext(); - final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, - new Object()); + final Settings settingsUnderTest = makeSettings(); final WatchableTester watcher = new WatchableTester(settingsUnderTest, "suspendInfo"); watcher.register(); final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1); @@ -397,9 +378,7 @@ public class PackageManagerSettingsTests { @Test public void testReadWritePackageRestrictions_distractionFlags() { - final Context context = InstrumentationRegistry.getTargetContext(); - final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, - new Object()); + final Settings settingsUnderTest = makeSettings(); final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1); final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2); final PackageSetting ps3 = createPackageSetting(PACKAGE_NAME_3); @@ -440,10 +419,7 @@ public class PackageManagerSettingsTests { @Test public void testWriteReadUsesStaticLibraries() { - final Context context = InstrumentationRegistry.getTargetContext(); - final Object lock = new Object(); - final Settings settingsUnderTest = new Settings(context.getFilesDir(), - mRuntimePermissionsPersistence, mPermissionDataProvider, lock); + final Settings settingsUnderTest = makeSettings(); final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1); ps1.appId = Process.FIRST_APPLICATION_UID; ps1.pkg = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME_1).hideAsParsed()) @@ -516,10 +492,7 @@ public class PackageManagerSettingsTests { public void testEnableDisable() { // Write the package files and make sure they're parsed properly the first time writeOldFiles(); - final Context context = InstrumentationRegistry.getContext(); - final Object lock = new Object(); - Settings settings = new Settings(context.getFilesDir(), - mRuntimePermissionsPersistence, mPermissionDataProvider, lock); + Settings settings = makeSettings(); final WatchableTester watcher = new WatchableTester(settings, "testEnableDisable"); watcher.register(); assertThat(settings.readLPw(createFakeUsers()), is(true)); @@ -698,12 +671,9 @@ public class PackageManagerSettingsTests { /** Update package; changing shared user throws exception */ @Test public void testUpdatePackageSetting03() { - final Context context = InstrumentationRegistry.getContext(); - final Object lock = new Object(); - final Settings testSettings01 = new Settings(context.getFilesDir(), - mRuntimePermissionsPersistence, mPermissionDataProvider, lock); + Settings settings = makeSettings(); final SharedUserSetting testUserSetting01 = createSharedUserSetting( - testSettings01, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/); + settings, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/); final PackageSetting testPkgSetting01 = createPackageSetting(0 /*sharedUserId*/, 0 /*pkgFlags*/); try { @@ -808,12 +778,9 @@ public class PackageManagerSettingsTests { /** Create PackageSetting for a shared user */ @Test public void testCreateNewSetting03() { - final Context context = InstrumentationRegistry.getContext(); - final Object lock = new Object(); - final Settings testSettings01 = new Settings(context.getFilesDir(), - mRuntimePermissionsPersistence, mPermissionDataProvider, lock); + Settings settings = makeSettings(); final SharedUserSetting testUserSetting01 = createSharedUserSetting( - testSettings01, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/); + settings, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/); final PackageSetting testPkgSetting01 = Settings.createNewSetting( PACKAGE_NAME, null /*originalPkg*/, @@ -1212,6 +1179,12 @@ public class PackageManagerSettingsTests { deleteFolder(InstrumentationRegistry.getTargetContext().getFilesDir()); } + private Settings makeSettings() { + return new Settings(InstrumentationRegistry.getContext().getFilesDir(), + mRuntimePermissionsPersistence, mPermissionDataProvider, + mIntentFilterVerificationManager, new Object()); + } + private void verifyKeySetMetaData(Settings settings) throws ReflectiveOperationException, IllegalAccessException { ArrayMap packages =