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 =