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

+ * This contains the set of packages which have been invalidated and will require + * re-verification. The exact domains can be retrieved with + * {@link DomainVerificationManager#getDomainVerificationSet(String)} + * + * @hide + */ +@SuppressWarnings("DefaultAnnotationParam") +@DataClass(genHiddenConstructor = true, genAidl = false, genEqualsHashCode = true) +@SystemApi +public final class DomainVerificationRequest implements Parcelable { + + /** + * The package names of the apps that need to be verified. The receiver should call + * {@link DomainVerificationManager#getDomainVerificationSet(String)} with each of + * these values to get the actual set of domains that need to be acted on. + */ + @NonNull + @DataClass.ParcelWith(Parcelling.BuiltIn.ForStringSet.class) + private final Set mPackageNames; + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/domain/verify/DomainVerificationRequest.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** + * Creates a new DomainVerificationRequest. + * + * @param packageNames + * The package names of the apps that need to be verified. The receiver should call + * {@link DomainVerificationManager#getDomainVerificationSet(String)} with each of + * these values to get the actual set of domains that need to be acted on. + * @hide + */ + @DataClass.Generated.Member + public DomainVerificationRequest( + @NonNull Set packageNames) { + this.mPackageNames = packageNames; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mPackageNames); + + // onConstructed(); // You can define this method to get a callback + } + + /** + * The package names of the apps that need to be verified. The receiver should call + * {@link DomainVerificationManager#getDomainVerificationSet(String)} with each of + * these values to get the actual set of domains that need to be acted on. + */ + @DataClass.Generated.Member + public @NonNull Set getPackageNames() { + return mPackageNames; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@android.annotation.Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(DomainVerificationRequest other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + DomainVerificationRequest that = (DomainVerificationRequest) o; + //noinspection PointlessBooleanExpression + return true + && java.util.Objects.equals(mPackageNames, that.mPackageNames); + } + + @Override + @DataClass.Generated.Member + public int hashCode() { + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + java.util.Objects.hashCode(mPackageNames); + return _hash; + } + + @DataClass.Generated.Member + static Parcelling> sParcellingForPackageNames = + Parcelling.Cache.get( + Parcelling.BuiltIn.ForStringSet.class); + static { + if (sParcellingForPackageNames == null) { + sParcellingForPackageNames = Parcelling.Cache.put( + new Parcelling.BuiltIn.ForStringSet()); + } + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + sParcellingForPackageNames.parcel(mPackageNames, dest, flags); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ DomainVerificationRequest(@NonNull android.os.Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + Set packageNames = sParcellingForPackageNames.unparcel(in); + + this.mPackageNames = packageNames; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mPackageNames); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public DomainVerificationRequest[] newArray(int size) { + return new DomainVerificationRequest[size]; + } + + @Override + public DomainVerificationRequest createFromParcel(@NonNull android.os.Parcel in) { + return new DomainVerificationRequest(in); + } + }; + + @DataClass.Generated( + time = 1611795646938L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/content/pm/domain/verify/DomainVerificationRequest.java", + inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForStringSet.class) java.util.Set mPackageNames\nclass DomainVerificationRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genAidl=false, genEqualsHashCode=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/content/pm/domain/verify/DomainVerificationSet.aidl b/core/java/android/content/pm/domain/verify/DomainVerificationSet.aidl new file mode 100644 index 0000000000000..0208907224e6c --- /dev/null +++ b/core/java/android/content/pm/domain/verify/DomainVerificationSet.aidl @@ -0,0 +1,19 @@ +/* + * 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 android.content.pm.domain.verify; + +parcelable DomainVerificationSet; diff --git a/core/java/android/content/pm/domain/verify/DomainVerificationSet.java b/core/java/android/content/pm/domain/verify/DomainVerificationSet.java new file mode 100644 index 0000000000000..bc076505bae23 --- /dev/null +++ b/core/java/android/content/pm/domain/verify/DomainVerificationSet.java @@ -0,0 +1,336 @@ +/* + * 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 android.content.pm.domain.verify; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.content.pm.PackageManager; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; +import com.android.internal.util.Parcelling; + +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +/** + * Contains the state of all domains for a given package on device. Used by the domain verification + * agent to determine the domains declared by a package that need to be verified by comparing + * against the digital asset links response from the server hosting that domain. + *

+ * These values for each domain can be modified through + * {@link DomainVerificationManager#setDomainVerificationStatus(UUID, Set, int)}. + * + * @hide + */ +@SystemApi +@SuppressWarnings("DefaultAnnotationParam") +@DataClass(genAidl = true, genHiddenConstructor = true, genParcelable = true, genToString = true, + genEqualsHashCode = true) +public final class DomainVerificationSet implements Parcelable { + + /** + * A domain verification ID for use in later API calls. This represents the snapshot + * of the domains for a package on device, and will be invalidated whenever the + * package changes. + *

+ * An exception will be thrown at the next API call that receives the ID if it is no + * longer valid. + *

+ * The caller may also be notified with a broadcast whenever a package and ID is + * invalidated, at which point it can use the package name to evict existing + * requests with an invalid set ID. If the caller wants to manually check if any + * IDs have been invalidate, the {@link PackageManager#getChangedPackages(int)} + * API will allow tracking the packages changed since the last query of this + * method, prompting the caller to re-query. + *

+ * This allows the caller to arbitrarily grant or revoke domain verification + * status, through + * {@link DomainVerificationManager#setDomainVerificationStatus(UUID, Set, int)}. + */ + @NonNull + @DataClass.ParcelWith(Parcelling.BuiltIn.ForUUID.class) + private final UUID mIdentifier; + + /** + * The package name that this data corresponds to. + */ + @NonNull + private final String mPackageName; + + /** + * Map of host names to their current state. State is an integer, which defaults to + * {@link DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the + * domain verification agent (the intended consumer of this API), which can be equal + * to {@link DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or + * greater than {@link DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for + * any unsuccessful response. + *

+ * Any value non-inclusive between those 2 values are reserved for use by the system. + * The domain verification agent may be able to act on these reserved values, and this + * ability can be queried using {@link DomainVerificationManager#isStateModifiable(int)}. + * It is expected that the agent attempt to verify all domains that it can modify the + * state of, even if it does not understand the meaning of those values. + */ + @NonNull + private final Map mHostToStateMap; + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/domain/verify/DomainVerificationSet.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** + * Creates a new DomainVerificationSet. + * + * @param identifier + * A domain verification ID for use in later API calls. This represents the snapshot + * of the domains for a package on device, and will be invalidated whenever the + * package changes. + *

+ * An exception will be thrown at the next API call that receives the ID if it is no + * longer valid. + *

+ * The caller may also be notified with a broadcast whenever a package and ID is + * invalidated, at which point it can use the package name to evict existing + * requests with an invalid set ID. If the caller wants to manually check if any + * IDs have been invalidate, the {@link PackageManager#getChangedPackages(int)} + * API will allow tracking the packages changed since the last query of this + * method, prompting the caller to re-query. + *

+ * This allows the caller to arbitrarily grant or revoke domain verification + * status, through + * {@link DomainVerificationManager#setDomainVerificationStatus(UUID, Set, int)}. + * @param packageName + * The package name that this data corresponds to. + * @param hostToStateMap + * Map of host names to their current state. State is an integer, which defaults to + * {@link DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the + * domain verification agent (the intended consumer of this API), which can be equal + * to {@link DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or + * greater than {@link DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for + * any unsuccessful response. + *

+ * Any value non-inclusive between those 2 values are reserved for use by the system. + * The domain verification agent may be able to act on these reserved values, and this + * ability can be queried using {@link DomainVerificationManager#isStateModifiable(int)}. + * It is expected that the agent attempt to verify all domains that it can modify the + * state of, even if it does not understand the meaning of those values. + * @hide + */ + @DataClass.Generated.Member + public DomainVerificationSet( + @NonNull UUID identifier, + @NonNull String packageName, + @NonNull Map hostToStateMap) { + this.mIdentifier = identifier; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mIdentifier); + this.mPackageName = packageName; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mPackageName); + this.mHostToStateMap = hostToStateMap; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mHostToStateMap); + + // onConstructed(); // You can define this method to get a callback + } + + /** + * A domain verification ID for use in later API calls. This represents the snapshot + * of the domains for a package on device, and will be invalidated whenever the + * package changes. + *

+ * An exception will be thrown at the next API call that receives the ID if it is no + * longer valid. + *

+ * The caller may also be notified with a broadcast whenever a package and ID is + * invalidated, at which point it can use the package name to evict existing + * requests with an invalid set ID. If the caller wants to manually check if any + * IDs have been invalidate, the {@link PackageManager#getChangedPackages(int)} + * API will allow tracking the packages changed since the last query of this + * method, prompting the caller to re-query. + *

+ * This allows the caller to arbitrarily grant or revoke domain verification + * status, through + * {@link DomainVerificationManager#setDomainVerificationStatus(UUID, Set, int)}. + */ + @DataClass.Generated.Member + public @NonNull UUID getIdentifier() { + return mIdentifier; + } + + /** + * The package name that this data corresponds to. + */ + @DataClass.Generated.Member + public @NonNull String getPackageName() { + return mPackageName; + } + + /** + * Map of host names to their current state. State is an integer, which defaults to + * {@link DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the + * domain verification agent (the intended consumer of this API), which can be equal + * to {@link DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or + * greater than {@link DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for + * any unsuccessful response. + *

+ * Any value non-inclusive between those 2 values are reserved for use by the system. + * The domain verification agent may be able to act on these reserved values, and this + * ability can be queried using {@link DomainVerificationManager#isStateModifiable(int)}. + * It is expected that the agent attempt to verify all domains that it can modify the + * state of, even if it does not understand the meaning of those values. + */ + @DataClass.Generated.Member + public @NonNull Map getHostToStateMap() { + return mHostToStateMap; + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "DomainVerificationSet { " + + "identifier = " + mIdentifier + ", " + + "packageName = " + mPackageName + ", " + + "hostToStateMap = " + mHostToStateMap + + " }"; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@android.annotation.Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(DomainVerificationSet other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + DomainVerificationSet that = (DomainVerificationSet) o; + //noinspection PointlessBooleanExpression + return true + && java.util.Objects.equals(mIdentifier, that.mIdentifier) + && java.util.Objects.equals(mPackageName, that.mPackageName) + && java.util.Objects.equals(mHostToStateMap, that.mHostToStateMap); + } + + @Override + @DataClass.Generated.Member + public int hashCode() { + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + java.util.Objects.hashCode(mIdentifier); + _hash = 31 * _hash + java.util.Objects.hashCode(mPackageName); + _hash = 31 * _hash + java.util.Objects.hashCode(mHostToStateMap); + return _hash; + } + + @DataClass.Generated.Member + static Parcelling sParcellingForIdentifier = + Parcelling.Cache.get( + Parcelling.BuiltIn.ForUUID.class); + static { + if (sParcellingForIdentifier == null) { + sParcellingForIdentifier = Parcelling.Cache.put( + new Parcelling.BuiltIn.ForUUID()); + } + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + sParcellingForIdentifier.parcel(mIdentifier, dest, flags); + dest.writeString(mPackageName); + dest.writeMap(mHostToStateMap); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ DomainVerificationSet(@NonNull android.os.Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + UUID identifier = sParcellingForIdentifier.unparcel(in); + String packageName = in.readString(); + Map hostToStateMap = new java.util.LinkedHashMap<>(); + in.readMap(hostToStateMap, Integer.class.getClassLoader()); + + this.mIdentifier = identifier; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mIdentifier); + this.mPackageName = packageName; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mPackageName); + this.mHostToStateMap = hostToStateMap; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mHostToStateMap); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public DomainVerificationSet[] newArray(int size) { + return new DomainVerificationSet[size]; + } + + @Override + public DomainVerificationSet createFromParcel(@NonNull android.os.Parcel in) { + return new DomainVerificationSet(in); + } + }; + + @DataClass.Generated( + time = 1611795504275L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/content/pm/domain/verify/DomainVerificationSet.java", + inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.util.Map mHostToStateMap\nclass DomainVerificationSet extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.aidl b/core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.aidl new file mode 100644 index 0000000000000..edcdb76813e7c --- /dev/null +++ b/core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.aidl @@ -0,0 +1,19 @@ +/* + * 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 android.content.pm.domain.verify; + +parcelable DomainVerificationUserSelection; diff --git a/core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.java b/core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.java new file mode 100644 index 0000000000000..2e26ef3c97dad --- /dev/null +++ b/core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.java @@ -0,0 +1,342 @@ +/* + * 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 android.content.pm.domain.verify; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.content.Context; +import android.os.Parcelable; +import android.os.UserHandle; + +import com.android.internal.util.DataClass; +import com.android.internal.util.Parcelling; + +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +/** + * Contains the user selection state for a package. This means all web HTTP(S) domains + * declared by a package in its manifest, whether or not they were marked for auto + * verification. + *

+ * By default, all apps are allowed to automatically open links with domains that they've + * successfully verified against. This is reflected by {@link #isLinkHandlingAllowed()}. + * The user can decide to disable this, disallowing the application from opening these + * links. + *

+ * Separately, independent of this toggle, the user can choose specific domains to allow + * an app to open, which is reflected as part of {@link #getHostToUserSelectionMap()}, + * which maps the domain name to the true/false state of whether it was enabled by the user. + *

+ * These values can be changed through the + * {@link DomainVerificationManager#setDomainVerificationLinkHandlingAllowed(String, + * boolean)} and + * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, + * boolean)} APIs. + *

+ * Note that because state is per user, if a different user needs to be changed, one will + * need to use {@link Context#createContextAsUser(UserHandle, int)} and hold the + * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission. + * + * @hide + */ +@SystemApi +@SuppressWarnings("DefaultAnnotationParam") +@DataClass(genAidl = true, genHiddenConstructor = true, genParcelable = true, genToString = true, + genEqualsHashCode = true) +public final class DomainVerificationUserSelection implements Parcelable { + + /** + * @see DomainVerificationSet#getIdentifier + */ + @NonNull + @DataClass.ParcelWith(Parcelling.BuiltIn.ForUUID.class) + private final UUID mIdentifier; + + /** + * The package name that this data corresponds to. + */ + @NonNull + private final String mPackageName; + + /** + * The user that this data corresponds to. + */ + @NonNull + private final UserHandle mUser; + + /** + * Whether or not this package is allowed to open links. + */ + @NonNull + private final boolean mLinkHandlingAllowed; + + /** + * Retrieve the existing user selection state for the matching + * {@link #getPackageName()}, as was previously set by + * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, + * boolean)}. + * + * @return Map of hosts to enabled state for the given package and user. + */ + @NonNull + private final Map mHostToUserSelectionMap; + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** + * Creates a new DomainVerificationUserSelection. + * + * @param packageName + * The package name that this data corresponds to. + * @param user + * The user that this data corresponds to. + * @param linkHandlingAllowed + * Whether or not this package is allowed to open links. + * @param hostToUserSelectionMap + * Retrieve the existing user selection state for the matching + * {@link #getPackageName()}, as was previously set by + * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, + * boolean)}. + * @hide + */ + @DataClass.Generated.Member + public DomainVerificationUserSelection( + @NonNull UUID identifier, + @NonNull String packageName, + @NonNull UserHandle user, + @NonNull boolean linkHandlingAllowed, + @NonNull Map hostToUserSelectionMap) { + this.mIdentifier = identifier; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mIdentifier); + this.mPackageName = packageName; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mPackageName); + this.mUser = user; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mUser); + this.mLinkHandlingAllowed = linkHandlingAllowed; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mLinkHandlingAllowed); + this.mHostToUserSelectionMap = hostToUserSelectionMap; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mHostToUserSelectionMap); + + // onConstructed(); // You can define this method to get a callback + } + + /** + * @see DomainVerificationSet#getIdentifier + */ + @DataClass.Generated.Member + public @NonNull UUID getIdentifier() { + return mIdentifier; + } + + /** + * The package name that this data corresponds to. + */ + @DataClass.Generated.Member + public @NonNull String getPackageName() { + return mPackageName; + } + + /** + * The user that this data corresponds to. + */ + @DataClass.Generated.Member + public @NonNull UserHandle getUser() { + return mUser; + } + + /** + * Whether or not this package is allowed to open links. + */ + @DataClass.Generated.Member + public @NonNull boolean isLinkHandlingAllowed() { + return mLinkHandlingAllowed; + } + + /** + * Retrieve the existing user selection state for the matching + * {@link #getPackageName()}, as was previously set by + * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, + * boolean)}. + * + * @return Map of hosts to enabled state for the given package and user. + */ + @DataClass.Generated.Member + public @NonNull Map getHostToUserSelectionMap() { + return mHostToUserSelectionMap; + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "DomainVerificationUserSelection { " + + "identifier = " + mIdentifier + ", " + + "packageName = " + mPackageName + ", " + + "user = " + mUser + ", " + + "linkHandlingAllowed = " + mLinkHandlingAllowed + ", " + + "hostToUserSelectionMap = " + mHostToUserSelectionMap + + " }"; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@android.annotation.Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(DomainVerificationUserSelection other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + DomainVerificationUserSelection that = (DomainVerificationUserSelection) o; + //noinspection PointlessBooleanExpression + return true + && java.util.Objects.equals(mIdentifier, that.mIdentifier) + && java.util.Objects.equals(mPackageName, that.mPackageName) + && java.util.Objects.equals(mUser, that.mUser) + && mLinkHandlingAllowed == that.mLinkHandlingAllowed + && java.util.Objects.equals(mHostToUserSelectionMap, that.mHostToUserSelectionMap); + } + + @Override + @DataClass.Generated.Member + public int hashCode() { + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + java.util.Objects.hashCode(mIdentifier); + _hash = 31 * _hash + java.util.Objects.hashCode(mPackageName); + _hash = 31 * _hash + java.util.Objects.hashCode(mUser); + _hash = 31 * _hash + Boolean.hashCode(mLinkHandlingAllowed); + _hash = 31 * _hash + java.util.Objects.hashCode(mHostToUserSelectionMap); + return _hash; + } + + @DataClass.Generated.Member + static Parcelling sParcellingForIdentifier = + Parcelling.Cache.get( + Parcelling.BuiltIn.ForUUID.class); + static { + if (sParcellingForIdentifier == null) { + sParcellingForIdentifier = Parcelling.Cache.put( + new Parcelling.BuiltIn.ForUUID()); + } + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + byte flg = 0; + if (mLinkHandlingAllowed) flg |= 0x8; + dest.writeByte(flg); + sParcellingForIdentifier.parcel(mIdentifier, dest, flags); + dest.writeString(mPackageName); + dest.writeTypedObject(mUser, flags); + dest.writeMap(mHostToUserSelectionMap); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ DomainVerificationUserSelection(@NonNull android.os.Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + byte flg = in.readByte(); + boolean linkHandlingAllowed = (flg & 0x8) != 0; + UUID identifier = sParcellingForIdentifier.unparcel(in); + String packageName = in.readString(); + UserHandle user = (UserHandle) in.readTypedObject(UserHandle.CREATOR); + Map hostToUserSelectionMap = new java.util.LinkedHashMap<>(); + in.readMap(hostToUserSelectionMap, Boolean.class.getClassLoader()); + + this.mIdentifier = identifier; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mIdentifier); + this.mPackageName = packageName; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mPackageName); + this.mUser = user; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mUser); + this.mLinkHandlingAllowed = linkHandlingAllowed; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mLinkHandlingAllowed); + this.mHostToUserSelectionMap = hostToUserSelectionMap; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mHostToUserSelectionMap); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public DomainVerificationUserSelection[] newArray(int size) { + return new DomainVerificationUserSelection[size]; + } + + @Override + public DomainVerificationUserSelection createFromParcel(@NonNull android.os.Parcel in) { + return new DomainVerificationUserSelection(in); + } + }; + + @DataClass.Generated( + time = 1611799495498L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.java", + inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull android.os.UserHandle mUser\nprivate final @android.annotation.NonNull boolean mLinkHandlingAllowed\nprivate final @android.annotation.NonNull java.util.Map mHostToUserSelectionMap\nclass DomainVerificationUserSelection extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/content/pm/domain/verify/TEST_MAPPING b/core/java/android/content/pm/domain/verify/TEST_MAPPING new file mode 100644 index 0000000000000..ffb1d9a600846 --- /dev/null +++ b/core/java/android/content/pm/domain/verify/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "presubmit": [ + { + "name": "PackageManagerServiceUnitTests", + "options": [ + { + "include-filter": "com.android.server.pm.test.domain.verify" + } + ] + } + ] +} diff --git a/core/java/com/android/internal/util/Parcelling.java b/core/java/com/android/internal/util/Parcelling.java index dd64c402fdbf4..1ab316d07e42d 100644 --- a/core/java/com/android/internal/util/Parcelling.java +++ b/core/java/com/android/internal/util/Parcelling.java @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.regex.Pattern; /** @@ -291,5 +292,19 @@ public interface Parcelling { return s == null ? null : Pattern.compile(s); } } + + class ForUUID implements Parcelling { + + @Override + public void parcel(UUID item, Parcel dest, int parcelFlags) { + dest.writeString(item == null ? null : item.toString()); + } + + @Override + public UUID unparcel(Parcel source) { + String string = source.readString(); + return string == null ? null : UUID.fromString(string); + } + } } } diff --git a/services/tests/PackageManagerServiceTests/unit/Android.bp b/services/tests/PackageManagerServiceTests/unit/Android.bp new file mode 100644 index 0000000000000..fed5f45e6ffd3 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/Android.bp @@ -0,0 +1,27 @@ +// 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. + +android_test { + name: "PackageManagerServiceUnitTests", + srcs: ["src/**/*.kt"], + static_libs: [ + "androidx.test.rules", + "androidx.test.runner", + "junit", + "services.core", + "truth-prebuilt", + ], + platform_apis: true, + test_suites: ["device-tests"], +} diff --git a/services/tests/PackageManagerServiceTests/unit/AndroidManifest.xml b/services/tests/PackageManagerServiceTests/unit/AndroidManifest.xml new file mode 100644 index 0000000000000..2ef7a1f56e765 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + diff --git a/services/tests/PackageManagerServiceTests/unit/AndroidTest.xml b/services/tests/PackageManagerServiceTests/unit/AndroidTest.xml new file mode 100644 index 0000000000000..78dd1c5f69901 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/AndroidTest.xml @@ -0,0 +1,30 @@ + + + + + diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationCoreApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationCoreApiTest.kt new file mode 100644 index 0000000000000..2ff7baf1f5708 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationCoreApiTest.kt @@ -0,0 +1,174 @@ +/* + * 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.test.domain.verify + +import android.content.pm.domain.verify.DomainVerificationRequest +import android.content.pm.domain.verify.DomainVerificationSet +import android.content.pm.domain.verify.DomainVerificationUserSelection +import android.os.Parcel +import android.os.Parcelable +import android.os.UserHandle +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import java.util.UUID + +@RunWith(Parameterized::class) +class DomainVerificationCoreApiTest { + + companion object { + private val IS_EQUAL_TO: (value: Any, other: Any) -> Unit = { value, other -> + assertThat(value).isEqualTo(other) + } + private val IS_MAP_EQUAL_TO: (value: Map<*, *>, other: Map<*, *>) -> Unit = { value, + other -> + assertThat(value).containsExactlyEntriesIn(other) + } + + @JvmStatic + @Parameterized.Parameters + fun parameters() = arrayOf( + Parameter( + initial = { + DomainVerificationRequest( + setOf( + "com.test.pkg.one", + "com.test.pkg.two" + ) + ) + }, + unparcel = { DomainVerificationRequest.CREATOR.createFromParcel(it) }, + assertion = { first, second -> + assertAll>(first, second, + { it.packageNames }, { it.component1() }) { value, other -> + assertThat(value).containsExactlyElementsIn(other) + } + } + ), + Parameter( + initial = { + DomainVerificationSet( + UUID.fromString("703f6d34-6241-4cfd-8176-2e1d23355811"), + "com.test.pkg", + mapOf( + "example.com" to 0, + "example.org" to 1, + "example.new" to 1000 + ) + ) + }, + unparcel = { DomainVerificationSet.CREATOR.createFromParcel(it) }, + assertion = { first, second -> + assertAll(first, second, + { it.identifier }, { it.component1() }, IS_EQUAL_TO + ) + assertAll(first, second, + { it.packageName }, { it.component2() }, IS_EQUAL_TO + ) + assertAll>(first, second, + { it.hostToStateMap }, { it.component3() }, IS_MAP_EQUAL_TO + ) + } + ), + Parameter( + initial = { + DomainVerificationUserSelection( + UUID.fromString("703f6d34-6241-4cfd-8176-2e1d23355811"), + "com.test.pkg", + UserHandle.of(10), + true, + mapOf( + "example.com" to true, + "example.org" to false, + "example.new" to true + ) + ) + }, + unparcel = { DomainVerificationUserSelection.CREATOR.createFromParcel(it) }, + assertion = { first, second -> + assertAll(first, second, + { it.identifier }, { it.component1() }, IS_EQUAL_TO + ) + assertAll(first, second, + { it.packageName }, { it.component2() }, IS_EQUAL_TO + ) + assertAll(first, second, + { it.user }, { it.component3() }, IS_EQUAL_TO + ) + assertAll( + first, second, { it.isLinkHandlingAllowed }, + { it.component4() }, IS_EQUAL_TO + ) + assertAll>( + first, second, { it.hostToUserSelectionMap }, + { it.component5() }, IS_MAP_EQUAL_TO + ) + } + ) + ) + + class Parameter( + val initial: () -> T, + val unparcel: (Parcel) -> T, + private val assertion: (first: T, second: T) -> Unit + ) { + @Suppress("UNCHECKED_CAST") + fun assert(first: Any, second: Any) = assertion(first as T, second as T) + } + + private fun assertAll(vararg values: T, block: (value: T, other: T) -> Unit) { + values.indices.drop(1).forEach { + block(values[0], values[it]) + } + } + + private fun assertAll( + first: T, + second: T, + fieldValue: (T) -> V, + componentValue: (T) -> V, + assertion: (value: V, other: V) -> Unit + ) { + val values = arrayOf(fieldValue(first), fieldValue(second), + componentValue(first), componentValue(second)) + values.indices.drop(1).forEach { + @Suppress("UNCHECKED_CAST") + assertion(values[0] as V, values[it] as V) + } + } + } + + @Parameterized.Parameter(0) + lateinit var parameter: Parameter<*> + + @Test + fun parcel() { + val parcel = Parcel.obtain() + val initial = parameter.initial() + initial.writeToParcel(parcel, 0) + parcel.setDataPosition(0) + + val newInitial = parameter.initial() + val unparceled = parameter.unparcel(parcel) + parameter.assert(newInitial, unparceled) + + assertAll(initial, newInitial, unparceled) { value: Any, other: Any -> + assertThat(value).isEqualTo(other) + } + } +} diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationModelExtensions.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationModelExtensions.kt new file mode 100644 index 0000000000000..6997c78199cec --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationModelExtensions.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2021 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.test.domain.verify + +import android.content.pm.domain.verify.DomainVerificationRequest +import android.content.pm.domain.verify.DomainVerificationSet +import android.content.pm.domain.verify.DomainVerificationUserSelection + +operator fun DomainVerificationRequest.component1() = packageNames + +operator fun DomainVerificationSet.component1() = identifier +operator fun DomainVerificationSet.component2() = packageName +operator fun DomainVerificationSet.component3() = hostToStateMap + +operator fun DomainVerificationUserSelection.component1() = identifier +operator fun DomainVerificationUserSelection.component2() = packageName +operator fun DomainVerificationUserSelection.component3() = user +operator fun DomainVerificationUserSelection.component4() = isLinkHandlingAllowed +operator fun DomainVerificationUserSelection.component5() = hostToUserSelectionMap From e803a1bd33a866fff132c2dfdb39e35d609a611e Mon Sep 17 00:00:00 2001 From: Winson Date: Tue, 15 Dec 2020 14:02:11 -0800 Subject: [PATCH 03/23] Add internal domain verification data classes Eventually will be used to store state that's included as part of com.android.server.pm.Settings. Also adds equality and Kotlin index operator mutation support to SparseArray, to improve ease of use. Exempt-From-Owner-Approval: Already approved by owners on main branch Bug: 163565712 CTS-Coverage-Bug: 179382047 Test: none, will be tested as part of follow up change Change-Id: Ie4eca3a99633465337758ee165e07f35c8db87c8 --- core/api/current.txt | 2 + core/java/android/util/SparseArray.java | 43 +++ .../models/DomainVerificationPkgState.java | 251 ++++++++++++++++++ .../models/DomainVerificationStateMap.java | 122 +++++++++ .../models/DomainVerificationUserState.java | 185 +++++++++++++ 5 files changed, 603 insertions(+) create mode 100644 services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationPkgState.java create mode 100644 services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationStateMap.java create mode 100644 services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationUserState.java diff --git a/core/api/current.txt b/core/api/current.txt index 731d1a91610c2..4b67ec2138e69 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -45795,6 +45795,8 @@ package android.util { method public void clear(); method public android.util.SparseArray clone(); method public boolean contains(int); + method public boolean contentEquals(@Nullable android.util.SparseArray); + method public int contentHashCode(); method public void delete(int); method public E get(int); method public E get(int, E); diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java index 86120d1e650ce..6718e93f908c4 100644 --- a/core/java/android/util/SparseArray.java +++ b/core/java/android/util/SparseArray.java @@ -16,6 +16,7 @@ package android.util; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import com.android.internal.util.ArrayUtils; @@ -23,6 +24,8 @@ import com.android.internal.util.GrowingArrayUtils; import libcore.util.EmptyArray; +import java.util.Objects; + /** * SparseArray maps integers to Objects and, unlike a normal array of Objects, * its indices can contain gaps. SparseArray is intended to be more memory-efficient @@ -505,4 +508,44 @@ public class SparseArray implements Cloneable { buffer.append('}'); return buffer.toString(); } + + /** + * For backwards compatibility reasons, {@link Object#equals(Object)} cannot be implemented, + * so this serves as a manually invoked alternative. + */ + public boolean contentEquals(@Nullable SparseArray other) { + if (other == null) { + return false; + } + + int size = size(); + if (size != other.size()) { + return false; + } + + for (int index = 0; index < size; index++) { + int key = keyAt(index); + if (!Objects.equals(valueAt(index), other.get(key))) { + return false; + } + } + + return true; + } + + /** + * For backwards compatibility, {@link Object#hashCode()} cannot be implemented, so this serves + * as a manually invoked alternative. + */ + public int contentHashCode() { + int hash = 0; + int size = size(); + for (int index = 0; index < size; index++) { + int key = keyAt(index); + E value = valueAt(index); + hash = 31 * hash + Objects.hashCode(key); + hash = 31 * hash + Objects.hashCode(value); + } + return hash; + } } diff --git a/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationPkgState.java b/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationPkgState.java new file mode 100644 index 0000000000000..1dc55494cae5a --- /dev/null +++ b/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationPkgState.java @@ -0,0 +1,251 @@ +/* + * 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.domain.verify.models; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.content.pm.domain.verify.DomainVerificationManager; +import android.content.pm.domain.verify.DomainVerificationState; +import android.util.ArrayMap; +import android.util.SparseArray; + +import com.android.internal.util.DataClass; + +import java.util.Objects; +import java.util.UUID; + +/** + * State for a single package for the domain verification APIs. Stores the state of each individual + * domain declared by the package, including its verification state and user selection state. + */ +@DataClass(genToString = true, genEqualsHashCode = true) +public class DomainVerificationPkgState { + + @NonNull + private final String mPackageName; + + @NonNull + private UUID mId; + + /** + * Whether or not the package declares any autoVerify domains. This is separate from an empty + * check on the map itself, because an empty map means no response recorded, not necessarily no + * domains declared. When this is false, {@link #mStateMap} will be empty, but + * {@link #mUserSelectionStates} may contain any domains the user has explicitly chosen to + * allow this package to open, which may or may not be marked autoVerify. + */ + private final boolean mHasAutoVerifyDomains; + + /** + * Map of domains to state integers. Only domains that are not set to the default value of + * {@link DomainVerificationState#STATE_NO_RESPONSE} are included. + * + * TODO(b/159952358): Hide the state map entirely from the caller, to allow optimizations, + * such as storing no state when the package is marked as a linked app in SystemConfig. + */ + @NonNull + private final ArrayMap mStateMap; + + @NonNull + private final SparseArray mUserSelectionStates; + + public DomainVerificationPkgState(@NonNull String packageName, @NonNull UUID id, + boolean hasAutoVerifyDomains) { + this(packageName, id, hasAutoVerifyDomains, new ArrayMap<>(0), new SparseArray<>(0)); + } + + @Nullable + public DomainVerificationUserState getUserSelectionState(@UserIdInt int userId) { + return mUserSelectionStates.get(userId); + } + + @Nullable + public DomainVerificationUserState getOrCreateUserSelectionState(@UserIdInt int userId) { + DomainVerificationUserState userState = mUserSelectionStates.get(userId); + if (userState == null) { + userState = new DomainVerificationUserState(userId); + mUserSelectionStates.put(userId, userState); + } + return userState; + } + + public void setId(@NonNull UUID id) { + mId = id; + } + + public void removeUser(@UserIdInt int userId) { + mUserSelectionStates.remove(userId); + } + + public void removeAllUsers() { + mUserSelectionStates.clear(); + } + + private int userSelectionStatesHashCode() { + return mUserSelectionStates.contentHashCode(); + } + + private boolean userSelectionStatesEquals( + @NonNull SparseArray other) { + return mUserSelectionStates.contentEquals(other); + } + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationPkgState.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** + * Creates a new DomainVerificationPkgState. + * + * @param stateMap + * Map of domains to state integers. Only domains that are not set to the default value of + * {@link DomainVerificationManager#STATE_NO_RESPONSE} are included. + * + * TODO(b/159952358): Hide the state map entirely from the caller, to allow optimizations, + * such as storing no state when the package is marked as a linked app in SystemConfig. + */ + @DataClass.Generated.Member + public DomainVerificationPkgState( + @NonNull String packageName, + @NonNull UUID id, + boolean hasAutoVerifyDomains, + @NonNull ArrayMap stateMap, + @NonNull SparseArray userSelectionStates) { + this.mPackageName = packageName; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mPackageName); + this.mId = id; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mId); + this.mHasAutoVerifyDomains = hasAutoVerifyDomains; + this.mStateMap = stateMap; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mStateMap); + this.mUserSelectionStates = userSelectionStates; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mUserSelectionStates); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public @NonNull String getPackageName() { + return mPackageName; + } + + @DataClass.Generated.Member + public @NonNull UUID getId() { + return mId; + } + + @DataClass.Generated.Member + public boolean isHasAutoVerifyDomains() { + return mHasAutoVerifyDomains; + } + + /** + * Map of domains to state integers. Only domains that are not set to the default value of + * {@link DomainVerificationManager#STATE_NO_RESPONSE} are included. + * + * TODO(b/159952358): Hide the state map entirely from the caller, to allow optimizations, + * such as storing no state when the package is marked as a linked app in SystemConfig. + */ + @DataClass.Generated.Member + public @NonNull ArrayMap getStateMap() { + return mStateMap; + } + + @DataClass.Generated.Member + public @NonNull SparseArray getUserSelectionStates() { + return mUserSelectionStates; + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "DomainVerificationPkgState { " + + "packageName = " + mPackageName + ", " + + "id = " + mId + ", " + + "hasAutoVerifyDomains = " + mHasAutoVerifyDomains + ", " + + "stateMap = " + mStateMap + ", " + + "userSelectionStates = " + mUserSelectionStates + + " }"; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(DomainVerificationPkgState other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + DomainVerificationPkgState that = (DomainVerificationPkgState) o; + //noinspection PointlessBooleanExpression + return true + && Objects.equals(mPackageName, that.mPackageName) + && Objects.equals(mId, that.mId) + && mHasAutoVerifyDomains == that.mHasAutoVerifyDomains + && Objects.equals(mStateMap, that.mStateMap) + && userSelectionStatesEquals(that.mUserSelectionStates); + } + + @Override + @DataClass.Generated.Member + public int hashCode() { + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + Objects.hashCode(mPackageName); + _hash = 31 * _hash + Objects.hashCode(mId); + _hash = 31 * _hash + Boolean.hashCode(mHasAutoVerifyDomains); + _hash = 31 * _hash + Objects.hashCode(mStateMap); + _hash = 31 * _hash + userSelectionStatesHashCode(); + return _hash; + } + + @DataClass.Generated( + time = 1608234185474L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationPkgState.java", + inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate @android.annotation.NonNull java.util.UUID mId\nprivate final boolean mHasAutoVerifyDomains\nprivate final @android.annotation.NonNull android.util.ArrayMap mStateMap\nprivate final @android.annotation.NonNull android.util.SparseArray mUserSelectionStates\npublic @android.annotation.Nullable com.android.server.pm.domain.verify.models.DomainVerificationUserState getUserSelectionState(int)\npublic @android.annotation.Nullable com.android.server.pm.domain.verify.models.DomainVerificationUserState getOrCreateUserSelectionState(int)\npublic void setId(java.util.UUID)\npublic void removeUser(int)\npublic void removeAllUsers()\nprivate int userSelectionStatesHashCode()\nprivate boolean userSelectionStatesEquals(android.util.SparseArray)\nclass DomainVerificationPkgState extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationStateMap.java b/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationStateMap.java new file mode 100644 index 0000000000000..ece1ce8281ac5 --- /dev/null +++ b/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationStateMap.java @@ -0,0 +1,122 @@ +/* + * 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.domain.verify.models; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.ArrayMap; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.UUID; + +/** + * A feature specific implementation of a multi-key map, since lookups by both a {@link String} + * package name and {@link UUID} domain set ID should be supported. + * + * @param stored object type + */ +public class DomainVerificationStateMap { + + private static final String TAG = "DomainVerificationStateMap"; + + @NonNull + private final ArrayMap mPackageNameMap = new ArrayMap<>(); + + @NonNull + private final ArrayMap mDomainSetIdMap = new ArrayMap<>(); + + public int size() { + return mPackageNameMap.size(); + } + + @NonNull + public ValueType valueAt(@IntRange(from = 0) int index) { + return mPackageNameMap.valueAt(index); + } + + @Nullable + public ValueType get(@NonNull String packageName) { + return mPackageNameMap.get(packageName); + } + + @Nullable + public ValueType get(@NonNull UUID domainSetId) { + return mDomainSetIdMap.get(domainSetId); + } + + public void put(@NonNull String packageName, @NonNull UUID domainSetId, + @NonNull ValueType valueType) { + if (mPackageNameMap.containsKey(packageName)) { + remove(packageName); + } + + mPackageNameMap.put(packageName, valueType); + mDomainSetIdMap.put(domainSetId, valueType); + } + + @Nullable + public ValueType remove(@NonNull String packageName) { + ValueType valueRemoved = mPackageNameMap.remove(packageName); + if (valueRemoved != null) { + int index = mDomainSetIdMap.indexOfValue(valueRemoved); + if (index >= 0) { + mDomainSetIdMap.removeAt(index); + } + } + return valueRemoved; + } + + @Nullable + public ValueType remove(@NonNull UUID domainSetId) { + ValueType valueRemoved = mDomainSetIdMap.remove(domainSetId); + if (valueRemoved != null) { + int index = mPackageNameMap.indexOfValue(valueRemoved); + if (index >= 0) { + mPackageNameMap.removeAt(index); + } + } + return valueRemoved; + } + + @NonNull + public List getPackageNames() { + return new ArrayList<>(mPackageNameMap.keySet()); + } + + /** + * Exposes the backing values collection of the one of the internal maps. Should only be used + * for test assertions. + */ + @VisibleForTesting + public Collection values() { + return new ArrayList<>(mPackageNameMap.values()); + } + + @Override + public String toString() { + return "DomainVerificationStateMap{" + + "packageNameMap=" + mPackageNameMap + + ", domainSetIdMap=" + mDomainSetIdMap + + '}'; + } +} diff --git a/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationUserState.java b/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationUserState.java new file mode 100644 index 0000000000000..43595408d17b0 --- /dev/null +++ b/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationUserState.java @@ -0,0 +1,185 @@ +/* + * 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.domain.verify.models; + +import android.annotation.NonNull; +import android.annotation.UserIdInt; +import android.util.ArraySet; + +import com.android.internal.util.DataClass; + +import java.util.Set; + +/** + * Tracks which domains have been explicitly enabled by the user, allowing it to automatically open + * that domain when a web URL Intent is sent ft. + */ +@DataClass(genSetters = true, genEqualsHashCode = true, genToString = true) +public class DomainVerificationUserState { + + @UserIdInt + private final int mUserId; + + /** List of domains which have been enabled by the user. **/ + @NonNull + private final ArraySet mEnabledHosts; + + /** Whether to disallow this package from automatically opening links by auto verification. */ + private boolean mDisallowLinkHandling; + + public DomainVerificationUserState(@UserIdInt int userId) { + mUserId = userId; + mEnabledHosts = new ArraySet<>(); + } + + public DomainVerificationUserState addHosts(@NonNull ArraySet newHosts) { + mEnabledHosts.addAll(newHosts); + return this; + } + + public DomainVerificationUserState addHosts(@NonNull Set newHosts) { + mEnabledHosts.addAll(newHosts); + return this; + } + + public DomainVerificationUserState removeHosts(@NonNull ArraySet newHosts) { + mEnabledHosts.removeAll(newHosts); + return this; + } + + public DomainVerificationUserState removeHosts(@NonNull Set newHosts) { + mEnabledHosts.removeAll(newHosts); + return this; + } + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationUserState.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** + * Creates a new DomainVerificationUserState. + * + * @param enabledHosts + * List of domains which have been enabled by the user. * + */ + @DataClass.Generated.Member + public DomainVerificationUserState( + @UserIdInt int userId, + @NonNull ArraySet enabledHosts, + boolean disallowLinkHandling) { + this.mUserId = userId; + com.android.internal.util.AnnotationValidations.validate( + UserIdInt.class, null, mUserId); + this.mEnabledHosts = enabledHosts; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mEnabledHosts); + this.mDisallowLinkHandling = disallowLinkHandling; + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public @UserIdInt int getUserId() { + return mUserId; + } + + /** + * List of domains which have been enabled by the user. * + */ + @DataClass.Generated.Member + public @NonNull ArraySet getEnabledHosts() { + return mEnabledHosts; + } + + @DataClass.Generated.Member + public boolean isDisallowLinkHandling() { + return mDisallowLinkHandling; + } + + @DataClass.Generated.Member + public @NonNull DomainVerificationUserState setDisallowLinkHandling( boolean value) { + mDisallowLinkHandling = value; + return this; + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "DomainVerificationUserState { " + + "userId = " + mUserId + ", " + + "enabledHosts = " + mEnabledHosts + ", " + + "disallowLinkHandling = " + mDisallowLinkHandling + + " }"; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@android.annotation.Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(DomainVerificationUserState other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + DomainVerificationUserState that = (DomainVerificationUserState) o; + //noinspection PointlessBooleanExpression + return true + && mUserId == that.mUserId + && java.util.Objects.equals(mEnabledHosts, that.mEnabledHosts) + && mDisallowLinkHandling == that.mDisallowLinkHandling; + } + + @Override + @DataClass.Generated.Member + public int hashCode() { + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + mUserId; + _hash = 31 * _hash + java.util.Objects.hashCode(mEnabledHosts); + _hash = 31 * _hash + Boolean.hashCode(mDisallowLinkHandling); + return _hash; + } + + @DataClass.Generated( + time = 1608234273324L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationUserState.java", + inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull android.util.ArraySet mEnabledHosts\nprivate boolean mDisallowLinkHandling\npublic com.android.server.pm.domain.verify.models.DomainVerificationUserState addHosts(android.util.ArraySet)\npublic com.android.server.pm.domain.verify.models.DomainVerificationUserState addHosts(java.util.Set)\npublic com.android.server.pm.domain.verify.models.DomainVerificationUserState removeHosts(android.util.ArraySet)\npublic com.android.server.pm.domain.verify.models.DomainVerificationUserState removeHosts(java.util.Set)\nclass DomainVerificationUserState extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genSetters=true, genEqualsHashCode=true, genToString=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} From dea682ad805314dd6dc9b175e8d172e3211c7fe0 Mon Sep 17 00:00:00 2001 From: Winson Date: Tue, 15 Dec 2020 15:05:52 -0800 Subject: [PATCH 04/23] Add new domain verification permissions One for the domain verification agent package, one for the agent's broadcast receiver, and one for Settings to mutate the user state. Exempt-From-Owner-Approval: Already approved by owners on main branch Bug: 163565712 Test: none, will be tested as part of implementation/enforcement Change-Id: I680a495103ae1bc0c22695c38b3e9ad74ca47d8f --- core/api/system-current.txt | 3 +++ core/res/AndroidManifest.xml | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 9a95dacef7ecd..8cae855455127 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -34,6 +34,7 @@ package android { field public static final String BIND_CONTENT_CAPTURE_SERVICE = "android.permission.BIND_CONTENT_CAPTURE_SERVICE"; field public static final String BIND_CONTENT_SUGGESTIONS_SERVICE = "android.permission.BIND_CONTENT_SUGGESTIONS_SERVICE"; field public static final String BIND_DIRECTORY_SEARCH = "android.permission.BIND_DIRECTORY_SEARCH"; + field public static final String BIND_DOMAIN_VERIFICATION_AGENT = "android.permission.BIND_DOMAIN_VERIFICATION_AGENT"; field public static final String BIND_EUICC_SERVICE = "android.permission.BIND_EUICC_SERVICE"; field public static final String BIND_EXTERNAL_STORAGE_SERVICE = "android.permission.BIND_EXTERNAL_STORAGE_SERVICE"; field public static final String BIND_GBA_SERVICE = "android.permission.BIND_GBA_SERVICE"; @@ -86,6 +87,7 @@ package android { field public static final String CRYPT_KEEPER = "android.permission.CRYPT_KEEPER"; field public static final String DEVICE_POWER = "android.permission.DEVICE_POWER"; field public static final String DISPATCH_PROVISIONING_MESSAGE = "android.permission.DISPATCH_PROVISIONING_MESSAGE"; + field public static final String DOMAIN_VERIFICATION_AGENT = "android.permission.DOMAIN_VERIFICATION_AGENT"; field public static final String ENTER_CAR_MODE_PRIORITIZED = "android.permission.ENTER_CAR_MODE_PRIORITIZED"; field public static final String EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS = "android.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS"; field public static final String FORCE_BACK = "android.permission.FORCE_BACK"; @@ -258,6 +260,7 @@ package android { field public static final String TV_VIRTUAL_REMOTE_CONTROLLER = "android.permission.TV_VIRTUAL_REMOTE_CONTROLLER"; field public static final String UNLIMITED_SHORTCUTS_API_CALLS = "android.permission.UNLIMITED_SHORTCUTS_API_CALLS"; field public static final String UPDATE_APP_OPS_STATS = "android.permission.UPDATE_APP_OPS_STATS"; + field public static final String UPDATE_DOMAIN_VERIFICATION_USER_SELECTION = "android.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION"; field public static final String UPDATE_FONTS = "android.permission.UPDATE_FONTS"; field public static final String UPDATE_LOCK = "android.permission.UPDATE_LOCK"; field public static final String UPDATE_TIME_ZONE_RULES = "android.permission.UPDATE_TIME_ZONE_RULES"; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 5442c03555903..6a9d7861cd987 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -4702,6 +4702,26 @@ + + + + + + + + + Date: Tue, 15 Dec 2020 14:50:45 -0800 Subject: [PATCH 05/23] Add DomainVerificationManager Contains the core SDK @SystemApi portion of the domain verification APIs, including the state codes and get/set methods for package/user state. Set methods are gated by corresponding permissions, but get methods were left available as general @SystemApi in case someone finds a use case for them. Adds a new exception which allows the manager to be more specific in what caused a failure. Exempt-From-Owner-Approval: Already approved by owners on main branch Bug: 163565712 Test: none, just API interfaces, will be tested with implementation Change-Id: I86802fe4b88a8a0f63a1798944c5d8c3a3bab09b --- core/api/system-current.txt | 27 ++ core/java/android/content/Context.java | 9 + core/java/android/content/Intent.java | 15 + .../verify/DomainVerificationManager.java | 363 ++++++++++++++++++ .../verify/DomainVerificationManagerImpl.java | 193 ++++++++++ .../verify/DomainVerificationState.java | 93 +++++ .../verify/IDomainVerificationManager.aidl | 44 +++ core/res/AndroidManifest.xml | 1 + 8 files changed, 745 insertions(+) create mode 100644 core/java/android/content/pm/domain/verify/DomainVerificationManager.java create mode 100644 core/java/android/content/pm/domain/verify/DomainVerificationManagerImpl.java create mode 100644 core/java/android/content/pm/domain/verify/DomainVerificationState.java create mode 100644 core/java/android/content/pm/domain/verify/IDomainVerificationManager.aidl diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 8cae855455127..1c19918f7add5 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -2103,6 +2103,7 @@ package android.content { field public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 262144; // 0x40000 field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions"; field public static final String CONTEXTHUB_SERVICE = "contexthub"; + field public static final String DOMAIN_VERIFICATION_SERVICE = "domain_verification"; field public static final String ETHERNET_SERVICE = "ethernet"; field public static final String EUICC_CARD_SERVICE = "euicc_card"; field public static final String FONT_SERVICE = "font"; @@ -2146,6 +2147,7 @@ package android.content { field public static final String ACTION_CALL_PRIVILEGED = "android.intent.action.CALL_PRIVILEGED"; field public static final String ACTION_DEVICE_CUSTOMIZATION_READY = "android.intent.action.DEVICE_CUSTOMIZATION_READY"; field public static final String ACTION_DIAL_EMERGENCY = "android.intent.action.DIAL_EMERGENCY"; + field public static final String ACTION_DOMAINS_NEED_VERIFICATION = "android.intent.action.DOMAINS_NEED_VERIFICATION"; field public static final String ACTION_FACTORY_RESET = "android.intent.action.FACTORY_RESET"; field public static final String ACTION_GLOBAL_BUTTON = "android.intent.action.GLOBAL_BUTTON"; field public static final String ACTION_INCIDENT_REPORT_READY = "android.intent.action.INCIDENT_REPORT_READY"; @@ -2701,6 +2703,31 @@ package android.content.pm.dex { package android.content.pm.domain.verify { + public interface DomainVerificationManager { + method @Nullable @RequiresPermission(allOf={android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, android.Manifest.permission.QUERY_ALL_PACKAGES, android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION}, conditional=true) public android.content.pm.domain.verify.DomainVerificationSet getDomainVerificationSet(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; + method @Nullable @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public android.content.pm.domain.verify.DomainVerificationUserSelection getDomainVerificationUserSelection(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; + method @NonNull @RequiresPermission(allOf={android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, android.Manifest.permission.QUERY_ALL_PACKAGES}) public java.util.List getValidVerificationPackageNames(); + method public static boolean isStateModifiable(int); + method public static boolean isStateVerified(int); + method @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public void setDomainVerificationLinkHandlingAllowed(@NonNull String, boolean) throws android.content.pm.PackageManager.NameNotFoundException; + method @RequiresPermission(allOf={android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, android.Manifest.permission.QUERY_ALL_PACKAGES}) public void setDomainVerificationStatus(@NonNull java.util.UUID, @NonNull java.util.Set, int) throws android.content.pm.domain.verify.DomainVerificationManager.InvalidDomainSetException, android.content.pm.PackageManager.NameNotFoundException; + method @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public void setDomainVerificationUserSelection(@NonNull java.util.UUID, @NonNull java.util.Set, boolean) throws android.content.pm.domain.verify.DomainVerificationManager.InvalidDomainSetException, android.content.pm.PackageManager.NameNotFoundException; + field public static final String EXTRA_VERIFICATION_REQUEST = "android.content.pm.domain.verify.extra.VERIFICATION_REQUEST"; + field public static final int STATE_FIRST_VERIFIER_DEFINED = 1024; // 0x400 + field public static final int STATE_NO_RESPONSE = 0; // 0x0 + field public static final int STATE_SUCCESS = 1; // 0x1 + } + + public static class DomainVerificationManager.InvalidDomainSetException extends android.util.AndroidException { + method @Nullable public java.util.UUID getDomainSetId(); + method @Nullable public String getPackageName(); + method public int getReason(); + field public static final int REASON_ID_INVALID = 2; // 0x2 + field public static final int REASON_ID_NULL = 1; // 0x1 + field public static final int REASON_SET_NULL_OR_EMPTY = 3; // 0x3 + field public static final int REASON_UNKNOWN_DOMAIN = 4; // 0x4 + } + public final class DomainVerificationRequest implements android.os.Parcelable { method public int describeContents(); method @NonNull public java.util.Set getPackageNames(); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 4dc41b2d51144..987de3fca6b1b 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -5450,6 +5450,15 @@ public abstract class Context { */ public static final String GAME_SERVICE = "game"; + /** + * Use with {@link #getSystemService(String)} to access domain verification service. + * + * @see #getSystemService(String) + * @hide + */ + @SystemApi + public static final String DOMAIN_VERIFICATION_SERVICE = "domain_verification"; + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 1752b480c06b5..e6753427b908d 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2846,6 +2846,21 @@ public class Intent implements Parcelable, Cloneable { @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION"; + + /** + * Broadcast Action: Sent to the system domain verification agent when an app's domains need + * to be verified. The data contains the domains hosts to be verified against. + *

+ * This is a protected intent that can only be sent by the system. + *

+ * + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_DOMAINS_NEED_VERIFICATION = + "android.intent.action.DOMAINS_NEED_VERIFICATION"; + /** * Broadcast Action: Resources for a set of packages (which were * previously unavailable) are currently diff --git a/core/java/android/content/pm/domain/verify/DomainVerificationManager.java b/core/java/android/content/pm/domain/verify/DomainVerificationManager.java new file mode 100644 index 0000000000000..91dcdd562a057 --- /dev/null +++ b/core/java/android/content/pm/domain/verify/DomainVerificationManager.java @@ -0,0 +1,363 @@ +/* + * 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 android.content.pm.domain.verify; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.annotation.SystemService; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.UserHandle; +import android.util.AndroidException; + +import java.util.List; +import java.util.Set; +import java.util.UUID; + +/** + * System service to access the domain verification APIs. + * + * Allows the approved domain verification + * agent on the device (the sole holder of + * {@link android.Manifest.permission#DOMAIN_VERIFICATION_AGENT}) to update the approval status + * of domains declared by applications in their AndroidManifest.xml, to allow them to open those + * links inside the app when selected by the user. This is done through querying + * {@link #getDomainVerificationSet(String)} and calling + * {@link #setDomainVerificationStatus(UUID, Set, int)}. + * + * Also allows the domain preference settings (holder of + * {@link android.Manifest.permission#UPDATE_DOMAIN_VERIFICATION_USER_SELECTION}) to update the + * preferences of the user, when they have chosen to explicitly allow an application to open links. + * This is done through querying {@link #getDomainVerificationUserSelection(String)} and calling + * {@link #setDomainVerificationUserSelection(UUID, Set, boolean)} and + * {@link #setDomainVerificationLinkHandlingAllowed(String, boolean)}. + * + * @hide + */ +@SystemApi +@SystemService(Context.DOMAIN_VERIFICATION_SERVICE) +public interface DomainVerificationManager { + + /** + * Extra field name for a {@link DomainVerificationRequest} for the requested packages. + * Passed to an the domain verification agent that handles + * {@link Intent#ACTION_DOMAINS_NEED_VERIFICATION}. + */ + String EXTRA_VERIFICATION_REQUEST = + "android.content.pm.domain.verify.extra.VERIFICATION_REQUEST"; + + /** + * No response has been recorded by either the system or any verification agent. + */ + int STATE_NO_RESPONSE = DomainVerificationState.STATE_NO_RESPONSE; + + /** The verification agent has explicitly verified the domain at some point. */ + int STATE_SUCCESS = DomainVerificationState.STATE_SUCCESS; + + /** + * The first available custom response code. This and any greater integer, along with + * {@link #STATE_SUCCESS} are the only values settable by the verification agent. All values + * will be treated as if the domain is unverified. + */ + int STATE_FIRST_VERIFIER_DEFINED = DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED; + + /** @hide */ + @NonNull + static String stateToDebugString(@DomainVerificationState.State int state) { + switch (state) { + case DomainVerificationState.STATE_NO_RESPONSE: + return "none"; + case DomainVerificationState.STATE_SUCCESS: + return "verified"; + case DomainVerificationState.STATE_APPROVED: + return "approved"; + case DomainVerificationState.STATE_DENIED: + return "denied"; + case DomainVerificationState.STATE_MIGRATED: + return "migrated"; + case DomainVerificationState.STATE_RESTORED: + return "restored"; + case DomainVerificationState.STATE_LEGACY_FAILURE: + return "legacy_failure"; + default: + return String.valueOf(state); + } + } + + /** + * Checks if a state considers the corresponding domain to be successfully verified. The + * domain verification agent may use this to determine whether or not to re-verify a domain. + */ + static boolean isStateVerified(@DomainVerificationState.State int state) { + switch (state) { + case DomainVerificationState.STATE_SUCCESS: + case DomainVerificationState.STATE_APPROVED: + case DomainVerificationState.STATE_MIGRATED: + case DomainVerificationState.STATE_RESTORED: + return true; + case DomainVerificationState.STATE_NO_RESPONSE: + case DomainVerificationState.STATE_DENIED: + case DomainVerificationState.STATE_LEGACY_FAILURE: + default: + return false; + } + } + + /** + * Checks if a state is modifiable by the domain verification agent. This is useful as the + * platform may add new state codes in newer versions, and older verification agents can use + * this method to determine if a state can be changed without having to be aware of what the + * new state means. + */ + static boolean isStateModifiable(@DomainVerificationState.State int state) { + switch (state) { + case DomainVerificationState.STATE_NO_RESPONSE: + case DomainVerificationState.STATE_SUCCESS: + case DomainVerificationState.STATE_MIGRATED: + case DomainVerificationState.STATE_RESTORED: + case DomainVerificationState.STATE_LEGACY_FAILURE: + return true; + case DomainVerificationState.STATE_APPROVED: + case DomainVerificationState.STATE_DENIED: + return false; + default: + return state >= DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED; + } + } + + /** + * For determine re-verify policy. This is hidden from the domain verification agent so that + * no behavior is made based on the result. + * @hide + */ + static boolean isStateDefault(@DomainVerificationState.State int state) { + switch (state) { + case DomainVerificationState.STATE_NO_RESPONSE: + case DomainVerificationState.STATE_MIGRATED: + case DomainVerificationState.STATE_RESTORED: + return true; + case DomainVerificationState.STATE_SUCCESS: + case DomainVerificationState.STATE_APPROVED: + case DomainVerificationState.STATE_DENIED: + case DomainVerificationState.STATE_LEGACY_FAILURE: + default: + return false; + } + } + + /** + * Used to iterate all {@link DomainVerificationSet} values to do cleanup or retries. This is + * usually a heavy workload and should be done infrequently. + * + * @return the current snapshot of package names with valid autoVerify URLs. + */ + @NonNull + @RequiresPermission(allOf = { + android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, + android.Manifest.permission.QUERY_ALL_PACKAGES + }) + List getValidVerificationPackageNames(); + + /** + * Retrieves the domain verification state for a given package. The caller must be the domain + * verification agent for the device with + * {@link android.Manifest.permission#DOMAIN_VERIFICATION_AGENT}, or hold + * {@link android.Manifest.permission#UPDATE_DOMAIN_VERIFICATION_USER_SELECTION}. + * Also requires that the caller have the + * {@link android.Manifest.permission#QUERY_ALL_PACKAGES} permission in addition to either of + * the requirements above. + * + * @return the data for the package, or null if it does not declare any autoVerify domains + * @throws NameNotFoundException If the package is unavailable. This is an unrecoverable error + * and should not be re-tried except on a time scheduled basis. + */ + @Nullable + @RequiresPermission(allOf = { + android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, + android.Manifest.permission.QUERY_ALL_PACKAGES, + android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION + }, conditional = true) + DomainVerificationSet getDomainVerificationSet(@NonNull String packageName) + throws NameNotFoundException; + + /** + * Change the verification status of the {@param domains} of the package associated with + * {@param domainSetId}. + * + * @param domainSetId See {@link DomainVerificationSet#getIdentifier()}. + * @param domains List of host names to change the state of. + * @param state See {@link DomainVerificationSet#getHostToStateMap()}. + * @throws InvalidDomainSetException If the ID is invalidated or the {@param domains} are + * invalid. This usually means the work being processed by the + * verification agent is outdated and a new request should + * be scheduled, if one has not already been done as part of + * the {@link Intent#ACTION_DOMAINS_NEED_VERIFICATION} + * broadcast. + * @throws NameNotFoundException If the ID is known to be good, but the package is + * unavailable. This may be because the package is + * installed on a volume that is no longer mounted. This + * error is unrecoverable until the package is available + * again, and should not be re-tried except on a time + * scheduled basis. + */ + @RequiresPermission(allOf = { + android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, + android.Manifest.permission.QUERY_ALL_PACKAGES + }) + void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set domains, + @DomainVerificationState.State int state) + throws InvalidDomainSetException, NameNotFoundException; + + /** + * TODO(b/178525735): This documentation is incorrect in the context of UX changes. + * Change whether the given {@param packageName} is allowed to automatically open verified + * HTTP/HTTPS domains. The final state is determined along with the verification status for the + * specific domain being opened and other system state. An app with this enabled is not + * guaranteed to be the sole link handler for its domains. + * + * By default, all apps are allowed to open verified links. Users must disable them explicitly. + */ + @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) + void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName, boolean allowed) + throws NameNotFoundException; + + /** + * Update the recorded user selection for the given {@param domains} for the given {@param + * domainSetId}. This state is recorded for the lifetime of a domain for a package on device, + * and will never be reset by the system short of an app data clear. + * + * This state is stored per device user. If another user needs to be changed, the appropriate + * permissions must be acquired and + * {@link Context#createPackageContextAsUser(String, int, UserHandle)} should be used. + * + * This will be combined with the verification status and other system state to determine which + * application is launched to handle an app link. + * + * @param domainSetId See {@link DomainVerificationSet#getIdentifier()}. + * @param domains The domains to toggle the state of. + * @param enabled Whether or not the app should automatically open the domains specified. + * @throws InvalidDomainSetException If the ID is invalidated or the {@param domains} are + * invalid. + * @throws NameNotFoundException If the ID is known to be good, but the package is + * unavailable. This may be because the package is + * installed on a volume that is no longer mounted. This + * error is unrecoverable until the package is available + * again, and should not be re-tried except on a time + * scheduled basis. + */ + @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) + void setDomainVerificationUserSelection(@NonNull UUID domainSetId, + @NonNull Set domains, boolean enabled) + throws InvalidDomainSetException, NameNotFoundException; + + /** + * Retrieve the user selection data for the given {@param packageName} and the current user. + * It is the responsibility of the caller to ensure that the + * {@link DomainVerificationUserSelection#getIdentifier()} matches any prior API calls. + * + * This state is stored per device user. If another user needs to be accessed, the appropriate + * permissions must be acquired and + * {@link Context#createPackageContextAsUser(String, int, UserHandle)} should be used. + * + * @param packageName The app to query state for. + * @return the user selection verification data for the given package for the current user, + * or null if the package does not declare any HTTP/HTTPS domains. + */ + @Nullable + @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) + DomainVerificationUserSelection getDomainVerificationUserSelection(@NonNull String packageName) + throws NameNotFoundException; + + /** + * Thrown if a {@link DomainVerificationSet#getIdentifier()}} or an associated set of domains + * provided by the caller is no longer valid. This may be recoverable, and the caller should + * re-query the package name associated with the ID using + * {@link #getDomainVerificationSet(String)} in order to check. If that also fails, then the + * package is no longer known to the device and thus all pending work for it should be dropped. + */ + class InvalidDomainSetException extends AndroidException { + + public static final int REASON_ID_NULL = 1; + public static final int REASON_ID_INVALID = 2; + public static final int REASON_SET_NULL_OR_EMPTY = 3; + public static final int REASON_UNKNOWN_DOMAIN = 4; + + /** @hide */ + @IntDef({ + REASON_ID_NULL, + REASON_ID_INVALID, + REASON_SET_NULL_OR_EMPTY, + REASON_UNKNOWN_DOMAIN + }) + public @interface Reason { + } + + private static String buildMessage(@Nullable UUID domainSetId, @Nullable String packageName, + @Reason int reason) { + switch (reason) { + case REASON_ID_NULL: + return "Domain set ID cannot be null"; + case REASON_ID_INVALID: + return "Domain set ID " + domainSetId + " has been invalidated"; + case REASON_SET_NULL_OR_EMPTY: + return "Domain set cannot be null or empty"; + case REASON_UNKNOWN_DOMAIN: + return "Domain set contains value that was not declared by the target package " + + packageName; + default: + return "Unknown failure"; + } + } + + @Reason + private final int mReason; + + @Nullable + private final UUID mDomainSetId; + + @Nullable + private final String mPackageName; + + /** @hide */ + public InvalidDomainSetException(@Nullable UUID domainSetId, @Nullable String packageName, + @Reason int reason) { + super(buildMessage(domainSetId, packageName, reason)); + mDomainSetId = domainSetId; + mPackageName = packageName; + mReason = reason; + } + + @Nullable + public UUID getDomainSetId() { + return mDomainSetId; + } + + @Nullable + public String getPackageName() { + return mPackageName; + } + + @Reason + public int getReason() { + return mReason; + } + } +} diff --git a/core/java/android/content/pm/domain/verify/DomainVerificationManagerImpl.java b/core/java/android/content/pm/domain/verify/DomainVerificationManagerImpl.java new file mode 100644 index 0000000000000..8ce0e9a79da99 --- /dev/null +++ b/core/java/android/content/pm/domain/verify/DomainVerificationManagerImpl.java @@ -0,0 +1,193 @@ +/* + * 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 android.content.pm.domain.verify; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.RemoteException; +import android.os.ServiceSpecificException; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.UUID; + +/** + * @hide + */ +@SuppressWarnings("RedundantThrows") +public class DomainVerificationManagerImpl implements DomainVerificationManager { + + public static final int ERROR_INVALID_DOMAIN_SET = 1; + public static final int ERROR_NAME_NOT_FOUND = 2; + + @IntDef(prefix = { "ERROR_" }, value = { + ERROR_INVALID_DOMAIN_SET, + ERROR_NAME_NOT_FOUND, + }) + private @interface Error { + } + + private final Context mContext; + + private final IDomainVerificationManager mDomainVerificationManager; + + public DomainVerificationManagerImpl(Context context, + IDomainVerificationManager domainVerificationManager) { + mContext = context; + mDomainVerificationManager = domainVerificationManager; + } + + @NonNull + @Override + public List getValidVerificationPackageNames() { + try { + return mDomainVerificationManager.getValidVerificationPackageNames(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Nullable + @Override + public DomainVerificationSet getDomainVerificationSet(@NonNull String packageName) + throws NameNotFoundException { + try { + return mDomainVerificationManager.getDomainVerificationSet(packageName); + } catch (Exception e) { + Exception converted = rethrow(e, packageName); + if (converted instanceof NameNotFoundException) { + throw (NameNotFoundException) converted; + } else if (converted instanceof RuntimeException) { + throw (RuntimeException) converted; + } else { + throw new RuntimeException(converted); + } + } + } + + @Override + public void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set domains, + int state) throws InvalidDomainSetException, NameNotFoundException { + try { + mDomainVerificationManager.setDomainVerificationStatus(domainSetId.toString(), + new ArrayList<>(domains), state); + } catch (Exception e) { + Exception converted = rethrow(e, domainSetId); + if (converted instanceof NameNotFoundException) { + throw (NameNotFoundException) converted; + } else if (converted instanceof RuntimeException) { + throw (RuntimeException) converted; + } else { + throw new RuntimeException(converted); + } + } + } + + @Override + public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName, + boolean allowed) throws NameNotFoundException { + try { + mDomainVerificationManager.setDomainVerificationLinkHandlingAllowed(packageName, + allowed, mContext.getUserId()); + } catch (Exception e) { + Exception converted = rethrow(e, packageName); + if (converted instanceof NameNotFoundException) { + throw (NameNotFoundException) converted; + } else if (converted instanceof RuntimeException) { + throw (RuntimeException) converted; + } else { + throw new RuntimeException(converted); + } + } + } + + @Override + public void setDomainVerificationUserSelection(@NonNull UUID domainSetId, + @NonNull Set domains, boolean enabled) + throws InvalidDomainSetException, NameNotFoundException { + try { + mDomainVerificationManager.setDomainVerificationUserSelection(domainSetId.toString(), + new ArrayList<>(domains), enabled, mContext.getUserId()); + } catch (Exception e) { + Exception converted = rethrow(e, domainSetId); + if (converted instanceof NameNotFoundException) { + throw (NameNotFoundException) converted; + } else if (converted instanceof RuntimeException) { + throw (RuntimeException) converted; + } else { + throw new RuntimeException(converted); + } + } + } + + @Nullable + @Override + public DomainVerificationUserSelection getDomainVerificationUserSelection( + @NonNull String packageName) throws NameNotFoundException { + try { + return mDomainVerificationManager.getDomainVerificationUserSelection(packageName, + mContext.getUserId()); + } catch (Exception e) { + Exception converted = rethrow(e, packageName); + if (converted instanceof NameNotFoundException) { + throw (NameNotFoundException) converted; + } else if (converted instanceof RuntimeException) { + throw (RuntimeException) converted; + } else { + throw new RuntimeException(converted); + } + } + } + + private Exception rethrow(Exception exception, @Nullable UUID domainSetId) { + return rethrow(exception, domainSetId, null); + } + + private Exception rethrow(Exception exception, @Nullable String packageName) { + return rethrow(exception, null, packageName); + } + + private Exception rethrow(Exception exception, @Nullable UUID domainSetId, + @Nullable String packageName) { + if (exception instanceof ServiceSpecificException) { + int packedErrorCode = ((ServiceSpecificException) exception).errorCode; + if (packageName == null) { + packageName = exception.getMessage(); + } + + @Error int managerErrorCode = packedErrorCode & 0xFFFF; + switch (managerErrorCode) { + case ERROR_INVALID_DOMAIN_SET: + int errorSpecificCode = packedErrorCode >> 16; + return new InvalidDomainSetException(domainSetId, packageName, + errorSpecificCode); + case ERROR_NAME_NOT_FOUND: + return new NameNotFoundException(packageName); + default: + return exception; + } + } else if (exception instanceof RemoteException) { + return ((RemoteException) exception).rethrowFromSystemServer(); + } else { + return exception; + } + } +} diff --git a/core/java/android/content/pm/domain/verify/DomainVerificationState.java b/core/java/android/content/pm/domain/verify/DomainVerificationState.java new file mode 100644 index 0000000000000..a9adab91056eb --- /dev/null +++ b/core/java/android/content/pm/domain/verify/DomainVerificationState.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2021 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 android.content.pm.domain.verify; + +import android.annotation.IntDef; + +/** + * @hide + */ +public interface DomainVerificationState { + + /** + * @hide + */ + @IntDef({ + STATE_NO_RESPONSE, + STATE_SUCCESS, + STATE_MIGRATED, + STATE_RESTORED, + STATE_APPROVED, + STATE_DENIED, + STATE_LEGACY_FAILURE, + STATE_FIRST_VERIFIER_DEFINED + }) + @interface State { + } + + // TODO(b/159952358): Document all the places that states need to be updated when one is added + /** + * @see DomainVerificationManager#STATE_NO_RESPONSE + */ + int STATE_NO_RESPONSE = 0; + + /** + * @see DomainVerificationManager#STATE_SUCCESS + */ + int STATE_SUCCESS = 1; + + /** + * The system has chosen to ignore the verification agent's opinion on whether the domain should + * be verified. This will treat the domain as verified. + *

+ * TODO: This currently combines SysConfig and instant app. Is it worth separating those? + */ + int STATE_APPROVED = 2; + + /** + * The system has chosen to ignore the verification agent's opinion on whether the domain should + * be verified. This will treat the domain as unverified. + */ + int STATE_DENIED = 3; + + /** + * The state was migrated from the previous intent filter verification API. This will treat the + * domain as verified, but it should be updated by the verification agent. The older API's + * collection and handling of verifying domains may lead to improperly migrated state. + */ + int STATE_MIGRATED = 4; + + /** + * The state was restored from a user backup or by the system. This is treated as if the domain + * was verified, but the verification agent may choose to re-verify this domain to be certain + * nothing has changed since the snapshot. + */ + int STATE_RESTORED = 5; + + /** + * The domain was failed by a legacy intent filter verification agent from v1 of the API. This + * is made distinct from {@link #STATE_FIRST_VERIFIER_DEFINED} to prevent any v2 verification + * agent from misinterpreting the result, since {@link #STATE_FIRST_VERIFIER_DEFINED} is agent + * specific and can be defined as a special error code. + */ + int STATE_LEGACY_FAILURE = 6; + + /** + * @see DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED + */ + int STATE_FIRST_VERIFIER_DEFINED = 0b10000000000; +} diff --git a/core/java/android/content/pm/domain/verify/IDomainVerificationManager.aidl b/core/java/android/content/pm/domain/verify/IDomainVerificationManager.aidl new file mode 100644 index 0000000000000..3726480bec0d4 --- /dev/null +++ b/core/java/android/content/pm/domain/verify/IDomainVerificationManager.aidl @@ -0,0 +1,44 @@ +/* + * Copyright 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 android.content.pm.domain.verify; + +import android.content.pm.domain.verify.DomainVerificationSet; +import android.content.pm.domain.verify.DomainVerificationUserSelection; +import java.util.List; + +/** + * @see DomainVerificationManager + * @hide + */ +interface IDomainVerificationManager { + + List getValidVerificationPackageNames(); + + @nullable + DomainVerificationSet getDomainVerificationSet(String packageName); + + @nullable + DomainVerificationUserSelection getDomainVerificationUserSelection(String packageName, + int userId); + + void setDomainVerificationStatus(String domainSetId, in List domains, int state); + + void setDomainVerificationLinkHandlingAllowed(String packageName, boolean allowed, int userId); + + void setDomainVerificationUserSelection(String domainSetId, in List domains, + boolean enabled, int userId); +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 6a9d7861cd987..3bff755c63c63 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -93,6 +93,7 @@ + From 018a0099204830de3b36ac34a4435aec9c5f3c5d Mon Sep 17 00:00:00 2001 From: Winson Date: Wed, 16 Dec 2020 12:54:36 -0800 Subject: [PATCH 06/23] Add DomainVerificationService skeleton Just the basic SystemService so that future CLs can add methods. Starts the service in SystemServer, but effectively does nothing. Exempt-From-Owner-Approval: Already approved by owners on main branch Bug: 163565712 Test: none, just a skeleton Change-Id: I1388423894e1e96511ab0f7de8ac5a7c2eea6de0 --- .../DomainVerificationManagerInternal.java | 25 ++++ .../verify/DomainVerificationManagerStub.java | 121 ++++++++++++++++++ .../verify/DomainVerificationService.java | 116 +++++++++++++++++ .../java/com/android/server/SystemServer.java | 14 ++ 4 files changed, 276 insertions(+) create mode 100644 services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java create mode 100644 services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerStub.java create mode 100644 services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java new file mode 100644 index 0000000000000..1927e9fffcc87 --- /dev/null +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java @@ -0,0 +1,25 @@ +/* + * 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.domain.verify; + +import android.content.pm.domain.verify.DomainVerificationManager; + +public interface DomainVerificationManagerInternal extends DomainVerificationManager { + + // TODO(b/159952358): Skeleton checked in to prepare for future internal methods + +} diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerStub.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerStub.java new file mode 100644 index 0000000000000..6147cdee23106 --- /dev/null +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerStub.java @@ -0,0 +1,121 @@ +/* + * 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.domain.verify; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.domain.verify.DomainVerificationManager.InvalidDomainSetException; +import android.content.pm.domain.verify.DomainVerificationManagerImpl; +import android.content.pm.domain.verify.DomainVerificationSet; +import android.content.pm.domain.verify.DomainVerificationUserSelection; +import android.content.pm.domain.verify.IDomainVerificationManager; +import android.os.ServiceSpecificException; +import android.util.ArraySet; + +import java.util.List; +import java.util.UUID; + +class DomainVerificationManagerStub extends IDomainVerificationManager.Stub { + + @NonNull + private DomainVerificationService mService; + + DomainVerificationManagerStub(DomainVerificationService service) { + mService = service; + } + + @NonNull + @Override + public List getValidVerificationPackageNames() { + try { + return mService.getValidVerificationPackageNames(); + } catch (Exception e) { + throw rethrow(e); + } + } + + @Nullable + @Override + public DomainVerificationSet getDomainVerificationSet(String packageName) { + try { + return mService.getDomainVerificationSet(packageName); + } catch (Exception e) { + throw rethrow(e); + } + } + + @Override + public void setDomainVerificationStatus(String domainSetId, List domains, + int state) { + try { + mService.setDomainVerificationStatus(UUID.fromString(domainSetId), + new ArraySet<>(domains), state); + } catch (Exception e) { + throw rethrow(e); + } + } + + @Override + public void setDomainVerificationLinkHandlingAllowed(String packageName, boolean allowed, + @UserIdInt int userId) { + try { + mService.setDomainVerificationLinkHandlingAllowed(packageName, allowed, userId); + } catch (Exception e) { + throw rethrow(e); + } + } + + @Override + public void setDomainVerificationUserSelection(String domainSetId, List domains, + boolean enabled, @UserIdInt int userId) { + try { + mService.setDomainVerificationUserSelection(UUID.fromString(domainSetId), + new ArraySet<>(domains), enabled, userId); + } catch (Exception e) { + throw rethrow(e); + } + } + + @Nullable + @Override + public DomainVerificationUserSelection getDomainVerificationUserSelection( + String packageName, @UserIdInt int userId) { + try { + return mService.getDomainVerificationUserSelection(packageName, userId); + } catch (Exception e) { + throw rethrow(e); + } + } + + private RuntimeException rethrow(Exception exception) throws RuntimeException { + if (exception instanceof InvalidDomainSetException) { + int packedErrorCode = DomainVerificationManagerImpl.ERROR_INVALID_DOMAIN_SET; + packedErrorCode |= ((InvalidDomainSetException) exception).getReason() << 16; + return new ServiceSpecificException(packedErrorCode, + ((InvalidDomainSetException) exception).getPackageName()); + } else if (exception instanceof NameNotFoundException) { + return new ServiceSpecificException( + DomainVerificationManagerImpl.ERROR_NAME_NOT_FOUND); + } else if (exception instanceof RuntimeException) { + return (RuntimeException) exception; + } else { + return new RuntimeException(exception); + } + } +} diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java new file mode 100644 index 0000000000000..e9fd3ae0452df --- /dev/null +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java @@ -0,0 +1,116 @@ +/* + * 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.domain.verify; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.content.Context; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.domain.verify.DomainVerificationSet; +import android.content.pm.domain.verify.DomainVerificationUserSelection; +import android.content.pm.domain.verify.IDomainVerificationManager; +import android.util.Singleton; + +import com.android.server.SystemService; + +import java.util.List; +import java.util.Set; +import java.util.UUID; + +public class DomainVerificationService extends SystemService + implements DomainVerificationManagerInternal { + + private static final String TAG = "DomainVerificationService"; + + @NonNull + private final Singleton mConnection; + + @NonNull + private final IDomainVerificationManager.Stub mStub = new DomainVerificationManagerStub(this); + + public DomainVerificationService(@NonNull Context context, + @NonNull Singleton connection) { + super(context); + mConnection = connection; + } + + @Override + public void onStart() { + publishBinderService(Context.DOMAIN_VERIFICATION_SERVICE, mStub); + } + + @NonNull + @Override + public List getValidVerificationPackageNames() { + return null; + } + + @Nullable + @Override + public DomainVerificationSet getDomainVerificationSet(@NonNull String packageName) + throws NameNotFoundException { + return null; + } + + @Override + public void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set domains, + int state) throws InvalidDomainSetException, NameNotFoundException { + + } + + @Override + public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName, + boolean allowed) throws NameNotFoundException { + + } + + public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName, + boolean allowed, @UserIdInt int userId) throws NameNotFoundException { + + } + + @Override + public void setDomainVerificationUserSelection(@NonNull UUID domainSetId, + @NonNull Set domains, boolean enabled) + throws InvalidDomainSetException, NameNotFoundException { + + } + + public void setDomainVerificationUserSelection(@NonNull UUID domainSetId, + @NonNull Set domains, boolean enabled, @UserIdInt int userId) + throws InvalidDomainSetException, NameNotFoundException { + + } + + @Nullable + @Override + public DomainVerificationUserSelection getDomainVerificationUserSelection( + @NonNull String packageName) throws NameNotFoundException { + return null; + } + + @Nullable + public DomainVerificationUserSelection getDomainVerificationUserSelection( + @NonNull String packageName, @UserIdInt int userId) throws NameNotFoundException { + return null; + } + + public interface Connection { + + } +} diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 636be4a359a10..b082909018c17 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -87,6 +87,7 @@ import android.util.DisplayMetrics; import android.util.EventLog; import android.util.IndentingPrintWriter; import android.util.Pair; +import android.util.Singleton; import android.util.Slog; import android.util.TimeUtils; import android.view.contentcapture.ContentCaptureManager; @@ -160,6 +161,7 @@ import com.android.server.pm.PackageManagerService; import com.android.server.pm.ShortcutService; import com.android.server.pm.UserManagerService; import com.android.server.pm.dex.SystemServerDexLoadReporter; +import com.android.server.pm.domain.verify.DomainVerificationService; import com.android.server.policy.PermissionPolicyService; import com.android.server.policy.PhoneWindowManager; import com.android.server.policy.role.RoleServicePlatformHelperImpl; @@ -1060,6 +1062,18 @@ public final class SystemServer implements Dumpable { SystemClock.elapsedRealtime()); } + t.traceBegin("StartDomainVerificationService"); + DomainVerificationService domainVerificationService = new DomainVerificationService( + mSystemContext, new Singleton() { + @Override + protected DomainVerificationService.Connection create() { + // TODO(b/159952358): Hook up to PackageManagerService + return null; + } + }); + mSystemServiceManager.startService(domainVerificationService); + t.traceEnd(); + t.traceBegin("StartPackageManagerService"); try { Watchdog.getInstance().pauseWatchingCurrentThread("packagemanagermain"); From 5a10db6a28789529448dc53a5f02ef69085186dc Mon Sep 17 00:00:00 2001 From: Winson Date: Tue, 15 Dec 2020 14:48:18 -0800 Subject: [PATCH 07/23] Add DomainVerificationPersistence Contains the XML serialization/parsing code for system state related to domain verification. Includes a change to Pair to support Kotlin deconstructing declarations. Exempt-From-Owner-Approval: Already approved by owners on main branch Bug: 163565712 Test: atest DomainVerificationPersistenceTest Change-Id: I4a3e03e9dfc33b4157e0505900c00c37be823ecd --- .../com/android/server/pm/SettingsXml.java | 404 ++++++++++++++++++ .../verify/DomainVerificationPersistence.java | 313 ++++++++++++++ .../DomainVerificationModelExtensions.kt | 7 + .../DomainVerificationPersistenceTest.kt | 212 +++++++++ 4 files changed, 936 insertions(+) create mode 100644 services/core/java/com/android/server/pm/SettingsXml.java create mode 100644 services/core/java/com/android/server/pm/domain/verify/DomainVerificationPersistence.java create mode 100644 services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationPersistenceTest.kt diff --git a/services/core/java/com/android/server/pm/SettingsXml.java b/services/core/java/com/android/server/pm/SettingsXml.java new file mode 100644 index 0000000000000..9588a279ececf --- /dev/null +++ b/services/core/java/com/android/server/pm/SettingsXml.java @@ -0,0 +1,404 @@ +/* + * 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; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; +import android.util.Xml; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Stack; + +/** + * A very specialized serialization/parsing wrapper around {@link TypedXmlSerializer} and {@link + * TypedXmlPullParser} intended for use with PackageManager related settings files. + * Assumptions/chosen behaviors: + *

    + *
  • No namespace support
  • + *
  • Data for a parent object is stored as attributes
  • + *
  • All attribute read methods return a default false, -1, or null
  • + *
  • Default values will not be written
  • + *
  • Children are sub-elements
  • + *
  • Collections are repeated sub-elements, no attribute support for collections
  • + *
+ */ +public class SettingsXml { + + private static final String TAG = "SettingsXml"; + + private static final boolean DEBUG_THROW_EXCEPTIONS = false; + + private static final String FEATURE_INDENT = + "http://xmlpull.org/v1/doc/features.html#indent-output"; + + private static final int DEFAULT_NUMBER = -1; + + public static Serializer serializer(TypedXmlSerializer serializer) { + return new Serializer(serializer); + } + + public static ReadSection parser(TypedXmlPullParser parser) + throws IOException, XmlPullParserException { + return new ReadSectionImpl(parser); + } + + public static class Serializer implements AutoCloseable { + + @NonNull + private final TypedXmlSerializer mXmlSerializer; + + private final WriteSectionImpl mWriteSection; + + private Serializer(TypedXmlSerializer serializer) { + mXmlSerializer = serializer; + mWriteSection = new WriteSectionImpl(mXmlSerializer); + } + + public WriteSection startSection(@NonNull String sectionName) throws IOException { + return mWriteSection.startSection(sectionName); + } + + @Override + public void close() throws IOException { + mWriteSection.closeCompletely(); + mXmlSerializer.endDocument(); + } + } + + public interface ReadSection extends AutoCloseable { + + @NonNull + String getName(); + + @NonNull + String getDescription(); + + boolean has(String attrName); + + @Nullable + String getString(String attrName); + + /** + * @return value as String or {@param defaultValue} if doesn't exist + */ + @NonNull + String getString(String attrName, @NonNull String defaultValue); + + /** + * @return value as boolean or false if doesn't exist + */ + boolean getBoolean(String attrName); + + /** + * @return value as boolean or {@param defaultValue} if doesn't exist + */ + boolean getBoolean(String attrName, boolean defaultValue); + + /** + * @return value as int or {@link #DEFAULT_NUMBER} if doesn't exist + */ + int getInt(String attrName); + + /** + * @return value as int or {@param defaultValue} if doesn't exist + */ + int getInt(String attrName, int defaultValue); + + /** + * @return value as long or {@link #DEFAULT_NUMBER} if doesn't exist + */ + long getLong(String attrName); + + /** + * @return value as long or {@param defaultValue} if doesn't exist + */ + long getLong(String attrName, int defaultValue); + + ChildSection children(); + } + + /** + *

+     * ChildSection child = parentSection.children();
+     * while (child.moveToNext(TAG_CHILD)) {
+     *     String readValue = child.getString(...);
+     *     ...
+     * }
+     * 
+ */ + public interface ChildSection extends ReadSection { + boolean moveToNext(); + + boolean moveToNext(@NonNull String expectedChildTagName); + } + + public static class ReadSectionImpl implements ChildSection { + + @Nullable + private final InputStream mInput; + + @NonNull + private final TypedXmlPullParser mParser; + + @NonNull + private final Stack mDepthStack = new Stack<>(); + + public ReadSectionImpl(@NonNull InputStream input) + throws IOException, XmlPullParserException { + mInput = input; + mParser = Xml.newFastPullParser(); + mParser.setInput(mInput, StandardCharsets.UTF_8.name()); + moveToFirstTag(); + } + + public ReadSectionImpl(@NonNull TypedXmlPullParser parser) + throws IOException, XmlPullParserException { + mInput = null; + mParser = parser; + moveToFirstTag(); + } + + private void moveToFirstTag() throws IOException, XmlPullParserException { + // Move to first tag + int type; + //noinspection StatementWithEmptyBody + while ((type = mParser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT) { + } + } + + @NonNull + @Override + public String getName() { + return mParser.getName(); + } + + @NonNull + @Override + public String getDescription() { + return mParser.getPositionDescription(); + } + + @Override + public boolean has(String attrName) { + return mParser.getAttributeValue(null, attrName) != null; + } + + @Nullable + @Override + public String getString(String attrName) { + return mParser.getAttributeValue(null, attrName); + } + + @NonNull + @Override + public String getString(String attrName, @NonNull String defaultValue) { + String value = mParser.getAttributeValue(null, attrName); + if (value == null) { + return defaultValue; + } + return value; + } + + @Override + public boolean getBoolean(String attrName) { + return getBoolean(attrName, false); + } + + @Override + public boolean getBoolean(String attrName, boolean defaultValue) { + return mParser.getAttributeBoolean(null, attrName, defaultValue); + } + + @Override + public int getInt(String attrName) { + return getInt(attrName, DEFAULT_NUMBER); + } + + @Override + public int getInt(String attrName, int defaultValue) { + return mParser.getAttributeInt(null, attrName, defaultValue); + } + + @Override + public long getLong(String attrName) { + return getLong(attrName, DEFAULT_NUMBER); + } + + @Override + public long getLong(String attrName, int defaultValue) { + return mParser.getAttributeLong(null, attrName, defaultValue); + } + + @Override + public ChildSection children() { + mDepthStack.push(mParser.getDepth()); + return this; + } + + @Override + public boolean moveToNext() { + return moveToNextInternal(null); + } + + @Override + public boolean moveToNext(@NonNull String expectedChildTagName) { + return moveToNextInternal(expectedChildTagName); + } + + private boolean moveToNextInternal(@Nullable String expectedChildTagName) { + try { + int depth = mDepthStack.peek(); + boolean hasTag = false; + int type; + while (!hasTag + && (type = mParser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || mParser.getDepth() > depth)) { + if (type != XmlPullParser.START_TAG) { + continue; + } + + if (expectedChildTagName != null + && !expectedChildTagName.equals(mParser.getName())) { + continue; + } + + hasTag = true; + } + + if (!hasTag) { + mDepthStack.pop(); + } + + return hasTag; + } catch (Exception ignored) { + return false; + } + } + + @Override + public void close() throws Exception { + if (mDepthStack.isEmpty()) { + Slog.wtf(TAG, "Children depth stack was not empty, data may have been lost", + new Exception()); + } + if (mInput != null) { + mInput.close(); + } + } + } + + public interface WriteSection extends AutoCloseable { + + WriteSection startSection(@NonNull String sectionName) throws IOException; + + WriteSection attribute(String attrName, @Nullable String value) throws IOException; + + WriteSection attribute(String attrName, int value) throws IOException; + + WriteSection attribute(String attrName, long value) throws IOException; + + WriteSection attribute(String attrName, boolean value) throws IOException; + + @Override + void close() throws IOException; + + void finish() throws IOException; + } + + private static class WriteSectionImpl implements WriteSection { + + @NonNull + private final TypedXmlSerializer mXmlSerializer; + + @NonNull + private final Stack mTagStack = new Stack<>(); + + private WriteSectionImpl(@NonNull TypedXmlSerializer xmlSerializer) { + mXmlSerializer = xmlSerializer; + } + + @Override + public WriteSection startSection(@NonNull String sectionName) throws IOException { + // Try to start the tag first before we push it to the stack + mXmlSerializer.startTag(null, sectionName); + mTagStack.push(sectionName); + return this; + } + + @Override + public WriteSection attribute(String attrName, String value) throws IOException { + if (value != null) { + mXmlSerializer.attribute(null, attrName, value); + } + return this; + } + + @Override + public WriteSection attribute(String attrName, int value) throws IOException { + if (value != DEFAULT_NUMBER) { + mXmlSerializer.attributeInt(null, attrName, value); + } + return this; + } + + @Override + public WriteSection attribute(String attrName, long value) throws IOException { + if (value != DEFAULT_NUMBER) { + mXmlSerializer.attributeLong(null, attrName, value); + } + return this; + } + + @Override + public WriteSection attribute(String attrName, boolean value) throws IOException { + if (value) { + mXmlSerializer.attributeBoolean(null, attrName, value); + } + return this; + } + + @Override + public void finish() throws IOException { + close(); + } + + @Override + public void close() throws IOException { + mXmlSerializer.endTag(null, mTagStack.pop()); + } + + private void closeCompletely() throws IOException { + if (DEBUG_THROW_EXCEPTIONS && mTagStack != null && !mTagStack.isEmpty()) { + throw new IllegalStateException( + "tag stack is not empty when closing, contains " + mTagStack); + } else if (mTagStack != null) { + while (!mTagStack.isEmpty()) { + close(); + } + } + } + } +} diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationPersistence.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationPersistence.java new file mode 100644 index 0000000000000..04adb003a7dad --- /dev/null +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationPersistence.java @@ -0,0 +1,313 @@ +/* + * 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.domain.verify; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.pm.domain.verify.DomainVerificationState; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.SparseArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; + +import com.android.server.pm.SettingsXml; +import com.android.server.pm.domain.verify.models.DomainVerificationPkgState; +import com.android.server.pm.domain.verify.models.DomainVerificationStateMap; +import com.android.server.pm.domain.verify.models.DomainVerificationUserState; + +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.Collection; +import java.util.UUID; + +public class DomainVerificationPersistence { + + private static final String TAG = "DomainVerificationPersistence"; + + public static final String TAG_DOMAIN_VERIFICATIONS = "domain-verifications"; + public static final String TAG_ACTIVE = "active"; + public static final String TAG_RESTORED = "restored"; + + public static final String TAG_PACKAGE_STATE = "package-state"; + private static final String ATTR_PACKAGE_NAME = "packageName"; + private static final String ATTR_ID = "id"; + private static final String ATTR_HAS_AUTO_VERIFY_DOMAINS = "hasAutoVerifyDomains"; + private static final String TAG_USER_STATES = "user-states"; + + public static final String TAG_USER_STATE = "user-state"; + public static final String ATTR_USER_ID = "userId"; + public static final String ATTR_DISALLOW_LINK_HANDLING = "disallowLinkHandling"; + public static final String TAG_ENABLED_HOSTS = "enabled-hosts"; + public static final String TAG_HOST = "host"; + + private static final String TAG_STATE = "state"; + public static final String TAG_DOMAIN = "domain"; + public static final String ATTR_NAME = "name"; + public static final String ATTR_STATE = "state"; + + public static void writeToXml(@NonNull TypedXmlSerializer xmlSerializer, + @NonNull DomainVerificationStateMap attached, + @NonNull ArrayMap pending, + @NonNull ArrayMap restored) throws IOException { + try (SettingsXml.Serializer serializer = SettingsXml.serializer(xmlSerializer)) { + try (SettingsXml.WriteSection ignored = serializer.startSection( + TAG_DOMAIN_VERIFICATIONS)) { + // Both attached and pending states are written to the active set, since both + // should be restored when the device reboots or runs a backup. They're merged into + // the same list because at read time the distinction isn't relevant. The pending + // list should generally be empty at this point anyways. + ArraySet active = new ArraySet<>(); + + int attachedSize = attached.size(); + for (int attachedIndex = 0; attachedIndex < attachedSize; attachedIndex++) { + active.add(attached.valueAt(attachedIndex)); + } + + int pendingSize = pending.size(); + for (int pendingIndex = 0; pendingIndex < pendingSize; pendingIndex++) { + active.add(pending.valueAt(pendingIndex)); + } + + try (SettingsXml.WriteSection activeSection = serializer.startSection(TAG_ACTIVE)) { + writePackageStates(activeSection, active); + } + + try (SettingsXml.WriteSection restoredSection = serializer.startSection( + TAG_RESTORED)) { + writePackageStates(restoredSection, restored.values()); + } + } + } + } + + private static void writePackageStates(@NonNull SettingsXml.WriteSection section, + @NonNull Collection states) throws IOException { + if (states.isEmpty()) { + return; + } + + for (DomainVerificationPkgState state : states) { + writePkgStateToXml(section, state); + } + } + + @NonNull + public static ReadResult readFromXml(@NonNull TypedXmlPullParser parentParser) + throws IOException, XmlPullParserException { + ArrayMap active = new ArrayMap<>(); + ArrayMap restored = new ArrayMap<>(); + + SettingsXml.ChildSection child = SettingsXml.parser(parentParser).children(); + while (child.moveToNext()) { + switch (child.getName()) { + case TAG_ACTIVE: + readPackageStates(child, active); + break; + case TAG_RESTORED: + readPackageStates(child, restored); + break; + } + } + + return new ReadResult(active, restored); + } + + private static void readPackageStates(@NonNull SettingsXml.ReadSection section, + @NonNull ArrayMap map) { + SettingsXml.ChildSection child = section.children(); + while (child.moveToNext(TAG_PACKAGE_STATE)) { + DomainVerificationPkgState pkgState = createPkgStateFromXml(child); + if (pkgState != null) { + // State is unique by package name + map.put(pkgState.getPackageName(), pkgState); + } + } + } + + /** + * Reads a package state from XML. Assumes the starting {@link #TAG_PACKAGE_STATE} has already + * been entered. + */ + @Nullable + public static DomainVerificationPkgState createPkgStateFromXml( + @NonNull SettingsXml.ReadSection section) { + String packageName = section.getString(ATTR_PACKAGE_NAME); + String idString = section.getString(ATTR_ID); + boolean hasAutoVerifyDomains = section.getBoolean(ATTR_HAS_AUTO_VERIFY_DOMAINS); + if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(idString)) { + return null; + } + UUID id = UUID.fromString(idString); + + final ArrayMap stateMap = new ArrayMap<>(); + final SparseArray userStates = new SparseArray<>(); + + SettingsXml.ChildSection child = section.children(); + while (child.moveToNext()) { + switch (child.getName()) { + case TAG_STATE: + readDomainStates(child, stateMap); + break; + case TAG_USER_STATES: + readUserStates(child, userStates); + break; + } + } + + return new DomainVerificationPkgState(packageName, id, hasAutoVerifyDomains, stateMap, + userStates); + } + + private static void readUserStates(@NonNull SettingsXml.ReadSection section, + @NonNull SparseArray userStates) { + SettingsXml.ChildSection child = section.children(); + while (child.moveToNext(TAG_USER_STATE)) { + DomainVerificationUserState userState = createUserStateFromXml(child); + if (userState != null) { + userStates.put(userState.getUserId(), userState); + } + } + } + + private static void readDomainStates(@NonNull SettingsXml.ReadSection stateSection, + @NonNull ArrayMap stateMap) { + SettingsXml.ChildSection child = stateSection.children(); + while (child.moveToNext(TAG_DOMAIN)) { + String name = child.getString(ATTR_NAME); + int state = child.getInt(ATTR_STATE, DomainVerificationState.STATE_NO_RESPONSE); + stateMap.put(name, state); + } + } + + public static void writePkgStateToXml(@NonNull SettingsXml.WriteSection parentSection, + @NonNull DomainVerificationPkgState pkgState) throws IOException { + try (SettingsXml.WriteSection ignored = + parentSection.startSection(TAG_PACKAGE_STATE) + .attribute(ATTR_PACKAGE_NAME, pkgState.getPackageName()) + .attribute(ATTR_ID, pkgState.getId().toString()) + .attribute(ATTR_HAS_AUTO_VERIFY_DOMAINS, + pkgState.isHasAutoVerifyDomains())) { + writeStateMap(parentSection, pkgState.getStateMap()); + writeUserStates(parentSection, pkgState.getUserSelectionStates()); + } + } + + private static void writeUserStates(@NonNull SettingsXml.WriteSection parentSection, + @NonNull SparseArray states) throws IOException { + int size = states.size(); + if (size == 0) { + return; + } + + try (SettingsXml.WriteSection section = parentSection.startSection(TAG_USER_STATES)) { + for (int index = 0; index < size; index++) { + writeUserStateToXml(section, states.valueAt(index)); + } + } + } + + private static void writeStateMap(@NonNull SettingsXml.WriteSection parentSection, + @NonNull ArrayMap stateMap) throws IOException { + if (stateMap.isEmpty()) { + return; + } + + try (SettingsXml.WriteSection stateSection = parentSection.startSection(TAG_STATE)) { + int size = stateMap.size(); + for (int index = 0; index < size; index++) { + stateSection.startSection(TAG_DOMAIN) + .attribute(ATTR_NAME, stateMap.keyAt(index)) + .attribute(ATTR_STATE, stateMap.valueAt(index)) + .finish(); + } + } + } + + /** + * Reads a user state from XML. Assumes the starting {@link #TAG_USER_STATE} has already been + * entered. + */ + @Nullable + public static DomainVerificationUserState createUserStateFromXml( + @NonNull SettingsXml.ReadSection section) { + int userId = section.getInt(ATTR_USER_ID); + if (userId == -1) { + return null; + } + + boolean disallowLinkHandling = section.getBoolean(ATTR_DISALLOW_LINK_HANDLING); + ArraySet enabledHosts = new ArraySet<>(); + + SettingsXml.ChildSection child = section.children(); + while (child.moveToNext(TAG_ENABLED_HOSTS)) { + readEnabledHosts(child, enabledHosts); + } + + return new DomainVerificationUserState(userId, enabledHosts, disallowLinkHandling); + } + + private static void readEnabledHosts(@NonNull SettingsXml.ReadSection section, + @NonNull ArraySet enabledHosts) { + SettingsXml.ChildSection child = section.children(); + while (child.moveToNext(TAG_HOST)) { + String hostName = child.getString(ATTR_NAME); + if (!TextUtils.isEmpty(hostName)) { + enabledHosts.add(hostName); + } + } + } + + public static void writeUserStateToXml(@NonNull SettingsXml.WriteSection parentSection, + @NonNull DomainVerificationUserState userState) throws IOException { + try (SettingsXml.WriteSection section = + parentSection.startSection(TAG_USER_STATE) + .attribute(ATTR_USER_ID, userState.getUserId()) + .attribute(ATTR_DISALLOW_LINK_HANDLING, + userState.isDisallowLinkHandling())) { + ArraySet enabledHosts = userState.getEnabledHosts(); + if (!enabledHosts.isEmpty()) { + try (SettingsXml.WriteSection enabledHostsSection = + section.startSection(TAG_ENABLED_HOSTS)) { + int size = enabledHosts.size(); + for (int index = 0; index < size; index++) { + enabledHostsSection.startSection(TAG_HOST) + .attribute(ATTR_NAME, enabledHosts.valueAt(index)) + .finish(); + } + } + } + } + } + + public static class ReadResult { + + @NonNull + public final ArrayMap active; + + @NonNull + public final ArrayMap restored; + + public ReadResult(@NonNull ArrayMap active, + @NonNull ArrayMap restored) { + this.active = active; + this.restored = restored; + } + } +} diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationModelExtensions.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationModelExtensions.kt index 6997c78199cec..41344c9e1e25f 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationModelExtensions.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationModelExtensions.kt @@ -19,6 +19,10 @@ package com.android.server.pm.test.domain.verify import android.content.pm.domain.verify.DomainVerificationRequest import android.content.pm.domain.verify.DomainVerificationSet import android.content.pm.domain.verify.DomainVerificationUserSelection +import com.android.server.pm.domain.verify.DomainVerificationPersistence + +operator fun android.util.Pair.component1() = first +operator fun android.util.Pair<*, S>.component2() = second operator fun DomainVerificationRequest.component1() = packageNames @@ -31,3 +35,6 @@ operator fun DomainVerificationUserSelection.component2() = packageName operator fun DomainVerificationUserSelection.component3() = user operator fun DomainVerificationUserSelection.component4() = isLinkHandlingAllowed operator fun DomainVerificationUserSelection.component5() = hostToUserSelectionMap + +operator fun DomainVerificationPersistence.ReadResult.component1() = active +operator fun DomainVerificationPersistence.ReadResult.component2() = restored diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationPersistenceTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationPersistenceTest.kt new file mode 100644 index 0000000000000..cf331d5ce7758 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationPersistenceTest.kt @@ -0,0 +1,212 @@ +/* + * 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.test.domain.verify + +import android.content.pm.domain.verify.DomainVerificationManager +import android.util.ArrayMap +import android.util.TypedXmlSerializer +import android.util.Xml +import com.android.server.pm.domain.verify.DomainVerificationPersistence +import com.android.server.pm.domain.verify.models.DomainVerificationPkgState +import com.android.server.pm.domain.verify.models.DomainVerificationStateMap +import com.android.server.pm.domain.verify.models.DomainVerificationUserState +import com.google.common.truth.Truth.assertThat +import com.google.common.truth.Truth.assertWithMessage +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import java.util.UUID + +class DomainVerificationPersistenceTest { + + companion object { + private val PKG_PREFIX = DomainVerificationPersistenceTest::class.java.`package`!!.name + } + + @Rule + @JvmField + val tempFolder = TemporaryFolder() + + @Test + fun writeAndReadBackNormal() { + val attached = DomainVerificationStateMap().apply { + mockPkgState(0).let { put(it.packageName, it.id, it) } + mockPkgState(1).let { put(it.packageName, it.id, it) } + } + val pending = ArrayMap().apply { + mockPkgState(2).let { put(it.packageName, it) } + mockPkgState(3).let { put(it.packageName, it) } + } + val restored = ArrayMap().apply { + mockPkgState(4).let { put(it.packageName, it) } + mockPkgState(5).let { put(it.packageName, it) } + } + + val file = writeXml { + DomainVerificationPersistence.writeToXml(it, attached, pending, restored) + } + + val xml = file.readText() + + val (readActive, readRestored) = file.inputStream() + .use { DomainVerificationPersistence.readFromXml(Xml.resolvePullParser(it)) } + + assertWithMessage(xml).that(readActive.values) + .containsExactlyElementsIn(attached.values() + pending.values) + assertWithMessage(xml).that(readRestored.values).containsExactlyElementsIn(restored.values) + } + + @Test + fun readMalformed() { + val stateZero = mockEmptyPkgState(0).apply { + stateMap["example.com"] = DomainVerificationManager.STATE_SUCCESS + stateMap["example.org"] = DomainVerificationManager.STATE_FIRST_VERIFIER_DEFINED + + // A domain without a written state falls back to default + stateMap["missing-state.com"] = DomainVerificationManager.STATE_NO_RESPONSE + + userSelectionStates[1] = DomainVerificationUserState(1).apply { + addHosts(setOf("example-user1.com", "example-user1.org")) + isDisallowLinkHandling = false + } + } + val stateOne = mockEmptyPkgState(1).apply { + // It's valid to have a user selection without any autoVerify domains + userSelectionStates[1] = DomainVerificationUserState(1).apply { + addHosts(setOf("example-user1.com", "example-user1.org")) + isDisallowLinkHandling = true + } + } + + // Also valid to have neither autoVerify domains nor any active user states + val stateTwo = mockEmptyPkgState(2, hasAutoVerifyDomains = false) + + // language=XML + val xml = """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.trimIndent() + + val (active, restored) = DomainVerificationPersistence + .readFromXml(Xml.resolvePullParser(xml.byteInputStream())) + + assertThat(active.values).containsExactly(stateZero) + assertThat(restored.values).containsExactly(stateOne, stateTwo) + } + + private fun writeXml(block: (TypedXmlSerializer) -> Unit) = tempFolder.newFile() + .apply { + outputStream().use { + Xml.resolveSerializer(it) + .apply { + startDocument(null, true) + setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true) + } + .apply(block) + .endDocument() + } + } + + private fun mockEmptyPkgState( + id: Int, + hasAutoVerifyDomains: Boolean = true + ): DomainVerificationPkgState { + val pkgName = pkgName(id) + val domainSetId = UUID(0L, id.toLong()) + return DomainVerificationPkgState(pkgName, domainSetId, hasAutoVerifyDomains) + } + + private fun mockPkgState(id: Int) = mockEmptyPkgState(id).apply { + stateMap["$packageName.com"] = id + userSelectionStates[id] = DomainVerificationUserState(id).apply { + addHosts(setOf("$packageName-user.com")) + isDisallowLinkHandling = true + } + } + + private fun pkgName(id: Int) = "${PKG_PREFIX}.pkg$id" +} From 9056557d347b33d04ed9f118fa6c47e01d076254 Mon Sep 17 00:00:00 2001 From: Winson Date: Wed, 16 Dec 2020 11:00:12 -0800 Subject: [PATCH 08/23] Add DomainVerificationCollector Gathers the domains declared in a package's manifest under entries for use with domain verification auto verification and user selection. The behavior is split between v1 and v2, for apps that target pre-S, as the set of domains that are verified must be maintained until the app author is able to update to the stricter S schema. The S schema is defined as part of the JavaDoc on the @ChangeId, but will also be included in the developer docs once these changes are merged. Exempt-From-Owner-Approval: Already approved by owners on main branch Bug: 163565712 Test: atest DomainVerificationCollectorTest Change-Id: I58c9d2b3001f48b9904aa4617d546a8cee0d926e --- .../verify/DomainVerificationCollector.java | 216 +++++++++++++ .../unit/Android.bp | 1 + .../verify/DomainVerificationCollectorTest.kt | 304 ++++++++++++++++++ .../android/server/testutils/MockitoUtils.kt | 35 +- 4 files changed, 543 insertions(+), 13 deletions(-) create mode 100644 services/core/java/com/android/server/pm/domain/verify/DomainVerificationCollector.java create mode 100644 services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationCollectorTest.kt diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationCollector.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationCollector.java new file mode 100644 index 0000000000000..5aaa37e9dadd5 --- /dev/null +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationCollector.java @@ -0,0 +1,216 @@ +/* + * 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.domain.verify; + +import android.annotation.NonNull; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.parsing.component.ParsedActivity; +import android.content.pm.parsing.component.ParsedIntentInfo; +import android.os.Binder; +import android.os.Build; +import android.util.ArraySet; +import android.util.Patterns; + +import com.android.server.SystemConfig; +import com.android.server.compat.PlatformCompat; +import com.android.server.pm.parsing.pkg.AndroidPackage; + +import java.util.List; +import java.util.Set; + +public class DomainVerificationCollector { + + @NonNull + private final PlatformCompat mPlatformCompat; + + @NonNull + private final SystemConfig mSystemConfig; + + public DomainVerificationCollector(@NonNull PlatformCompat platformCompat, + @NonNull SystemConfig systemConfig) { + mPlatformCompat = platformCompat; + mSystemConfig = systemConfig; + } + + /** + * With the updated form of the app links verification APIs, an app will be required to declare + * domains inside an intent filter which includes all of the following: + *
    + *
  • - android:autoVerify="true"
  • + *
  • - Intent.ACTION_VIEW
  • + *
  • - Intent.CATEGORY_BROWSABLE
  • + *
  • - Intent.CATEGORY_DEFAULT
  • + *
  • - Only IntentFilter.SCHEME_HTTP and/or IntentFilter.SCHEME_HTTPS, + * with no other schemes
  • + *
+ * + * On prior versions of Android, Intent.CATEGORY_BROWSABLE was not a requirement, other + * schemes were allowed, and setting autoVerify to true in any intent filter would implicitly + * pretend that all intent filters were set to autoVerify="true". + */ + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S) + public static final long RESTRICT_DOMAINS = 175408749L; + + @NonNull + public ArraySet collectAllWebDomains(@NonNull AndroidPackage pkg) { + return collectDomains(pkg, false); + } + + /** + * Effectively {@link #collectAllWebDomains(AndroidPackage)}, but requires + * {@link IntentFilter#getAutoVerify()} == true. + */ + @NonNull + public ArraySet collectAutoVerifyDomains(@NonNull AndroidPackage pkg) { + return collectDomains(pkg, true); + } + + @NonNull + private ArraySet collectDomains(@NonNull AndroidPackage pkg, + boolean checkAutoVerify) { + @SuppressWarnings("ConstantConditions") + boolean restrictDomains = Binder.withCleanCallingIdentity( + () -> mPlatformCompat.isChangeEnabled(RESTRICT_DOMAINS, buildMockAppInfo(pkg))); + + ArraySet domains = new ArraySet<>(); + + if (restrictDomains) { + collectDomains(domains, pkg, checkAutoVerify); + } else { + collectDomainsLegacy(domains, pkg, checkAutoVerify); + } + + return domains; + } + + /** @see #RESTRICT_DOMAINS */ + private void collectDomainsLegacy(@NonNull Set domains, + @NonNull AndroidPackage pkg, boolean checkAutoVerify) { + if (!checkAutoVerify) { + // Per-domain user selection state doesn't have a V1 equivalent on S, so just use V2 + collectDomains(domains, pkg, false); + return; + } + + List activities = pkg.getActivities(); + int activitiesSize = activities.size(); + + // Due to a bug in the platform, for backwards compatibility, assume that all linked apps + // require auto verification, even if they forget to mark their manifest as such. + boolean needsAutoVerify = mSystemConfig.getLinkedApps().contains(pkg.getPackageName()); + if (!needsAutoVerify) { + for (int activityIndex = 0; activityIndex < activitiesSize && !needsAutoVerify; + activityIndex++) { + ParsedActivity activity = activities.get(activityIndex); + List intents = activity.getIntents(); + int intentsSize = intents.size(); + for (int intentIndex = 0; intentIndex < intentsSize && !needsAutoVerify; + intentIndex++) { + ParsedIntentInfo intent = intents.get(intentIndex); + needsAutoVerify = intent.needsVerification(); + } + } + + if (!needsAutoVerify) { + return; + } + } + + for (int activityIndex = 0; activityIndex < activitiesSize; activityIndex++) { + ParsedActivity activity = activities.get(activityIndex); + List intents = activity.getIntents(); + int intentsSize = intents.size(); + for (int intentIndex = 0; intentIndex < intentsSize; intentIndex++) { + ParsedIntentInfo intent = intents.get(intentIndex); + if (intent.handlesWebUris(false)) { + int authorityCount = intent.countDataAuthorities(); + for (int index = 0; index < authorityCount; index++) { + domains.add(intent.getDataAuthority(index).getHost()); + } + } + } + } + } + + /** @see #RESTRICT_DOMAINS */ + private void collectDomains(@NonNull Set domains, + @NonNull AndroidPackage pkg, boolean checkAutoVerify) { + List activities = pkg.getActivities(); + int activitiesSize = activities.size(); + for (int activityIndex = 0; activityIndex < activitiesSize; activityIndex++) { + ParsedActivity activity = activities.get(activityIndex); + List intents = activity.getIntents(); + int intentsSize = intents.size(); + for (int intentIndex = 0; intentIndex < intentsSize; intentIndex++) { + ParsedIntentInfo intent = intents.get(intentIndex); + if (checkAutoVerify && !intent.getAutoVerify()) { + continue; + } + + if (!intent.hasCategory(Intent.CATEGORY_DEFAULT) + || !intent.handlesWebUris(checkAutoVerify)) { + continue; + } + + // TODO(b/159952358): There seems to be no way to associate the exact host + // with its scheme, meaning all hosts have to be verified as if they were + // web schemes. This means that given the following: + // + // ... + // + // + // + // + // + // The verification agent will be asked to verify four.example.com, which the + // app will probably fail. This can be re-configured to work properly by the + // app developer by declaring a separate intent-filter. This may not be worth + // fixing. + int authorityCount = intent.countDataAuthorities(); + for (int index = 0; index < authorityCount; index++) { + String host = intent.getDataAuthority(index).getHost(); + // It's easy to misconfigure autoVerify intent filters, so to avoid + // adding unintended hosts, check if the host is an HTTP domain. + if (Patterns.DOMAIN_NAME.matcher(host).matches()) { + domains.add(host); + } + } + } + } + } + + /** + * Passed to {@link PlatformCompat} because this can be invoked mid-install process, and + * {@link PlatformCompat} will not be able to query the pending {@link ApplicationInfo} from + * {@link PackageManager}. + * + * TODO(b/177613575): Can a different API be used? + */ + @NonNull + private ApplicationInfo buildMockAppInfo(@NonNull AndroidPackage pkg) { + ApplicationInfo appInfo = new ApplicationInfo(); + appInfo.packageName = pkg.getPackageName(); + appInfo.targetSdkVersion = pkg.getTargetSdkVersion(); + return appInfo; + } +} diff --git a/services/tests/PackageManagerServiceTests/unit/Android.bp b/services/tests/PackageManagerServiceTests/unit/Android.bp index fed5f45e6ffd3..b9a3a6500ebe7 100644 --- a/services/tests/PackageManagerServiceTests/unit/Android.bp +++ b/services/tests/PackageManagerServiceTests/unit/Android.bp @@ -20,6 +20,7 @@ android_test { "androidx.test.runner", "junit", "services.core", + "servicestests-utils", "truth-prebuilt", ], platform_apis: true, diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationCollectorTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationCollectorTest.kt new file mode 100644 index 0000000000000..414a5e4e0b51c --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationCollectorTest.kt @@ -0,0 +1,304 @@ +/* + * 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.test.domain.verify + +import android.content.Intent +import android.content.pm.ApplicationInfo +import android.content.pm.parsing.component.ParsedActivity +import android.content.pm.parsing.component.ParsedIntentInfo +import android.os.Build +import android.os.PatternMatcher +import android.util.ArraySet +import com.android.server.SystemConfig +import com.android.server.compat.PlatformCompat +import com.android.server.pm.domain.verify.DomainVerificationCollector +import com.android.server.pm.parsing.pkg.AndroidPackage +import com.android.server.testutils.mockThrowOnUnmocked +import com.android.server.testutils.whenever +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.mockito.Mockito.any +import org.mockito.Mockito.eq + +class DomainVerificationCollectorTest { + + companion object { + private const val TEST_PKG_NAME = "com.test.pkg" + } + + private val platformCompat: PlatformCompat = mockThrowOnUnmocked { + whenever(isChangeEnabled(eq(DomainVerificationCollector.RESTRICT_DOMAINS), any())) { + (arguments[1] as ApplicationInfo).targetSdkVersion >= Build.VERSION_CODES.S + } + } + + @Test + fun verifyV1() { + val pkg = mockPkg(useV2 = false, autoVerify = true) + val collector = mockCollector() + assertThat(collector.collectAllWebDomains(pkg)) + .containsExactly("example1.com", "example2.com", "example3.com") + assertThat(collector.collectAutoVerifyDomains(pkg)) + .containsExactly("example1.com", "example2.com", "example3.com", "example4.com") + } + + @Test + fun verifyV1NoAutoVerify() { + val pkg = mockPkg(useV2 = false, autoVerify = false) + val collector = mockCollector() + assertThat(collector.collectAllWebDomains(pkg)) + .containsExactly("example1.com", "example2.com", "example3.com") + assertThat(collector.collectAutoVerifyDomains(pkg)).isEmpty() + } + + @Test + fun verifyV1ForceAutoVerify() { + val pkg = mockPkg(useV2 = false, autoVerify = false) + val collector = mockCollector(linkedApps = setOf(TEST_PKG_NAME)) + assertThat(collector.collectAllWebDomains(pkg)) + .containsExactly("example1.com", "example2.com", "example3.com") + assertThat(collector.collectAutoVerifyDomains(pkg)) + .containsExactly("example1.com", "example2.com", "example3.com", "example4.com") + } + + @Test + fun verifyV1NoValidIntentFilter() { + val pkg = mockThrowOnUnmocked { + whenever(packageName) { TEST_PKG_NAME } + whenever(targetSdkVersion) { Build.VERSION_CODES.R } + + val activityList = listOf( + ParsedActivity().apply { + addIntent( + ParsedIntentInfo().apply { + addAction(Intent.ACTION_VIEW) + addCategory(Intent.CATEGORY_BROWSABLE) + addCategory(Intent.CATEGORY_DEFAULT) + addDataScheme("http") + addDataScheme("https") + addDataPath("/sub", PatternMatcher.PATTERN_LITERAL) + addDataAuthority("example1.com", null) + } + ) + }, + ParsedActivity().apply { + addIntent( + ParsedIntentInfo().apply { + setAutoVerify(true) + addAction(Intent.ACTION_VIEW) + addCategory(Intent.CATEGORY_BROWSABLE) + addCategory(Intent.CATEGORY_DEFAULT) + addDataScheme("http") + addDataScheme("https") + + // The presence of a non-web-scheme as the only autoVerify + // intent-filter, when non-forced, means that v1 will not pick + // up the package for verification. + addDataScheme("nonWebScheme") + addDataPath("/sub", PatternMatcher.PATTERN_LITERAL) + addDataAuthority("example2.com", null) + } + ) + }, + ) + + whenever(activities) { activityList } + } + + val collector = mockCollector() + assertThat(collector.collectAllWebDomains(pkg)) + .containsExactly("example1.com", "example2.com") + assertThat(collector.collectAutoVerifyDomains(pkg)).isEmpty() + } + + @Test + fun verifyV2() { + val pkg = mockPkg(useV2 = true, autoVerify = true) + val collector = mockCollector() + + assertThat(collector.collectAllWebDomains(pkg)) + .containsExactly("example1.com", "example2.com", "example3.com") + assertThat(collector.collectAutoVerifyDomains(pkg)) + .containsExactly("example1.com", "example3.com") + } + + @Test + fun verifyV2NoAutoVerify() { + val pkg = mockPkg(useV2 = true, autoVerify = false) + val collector = mockCollector() + + assertThat(collector.collectAllWebDomains(pkg)) + .containsExactly("example1.com", "example2.com", "example3.com") + assertThat(collector.collectAutoVerifyDomains(pkg)).isEmpty() + } + + @Test + fun verifyV2ForceAutoVerifyIgnored() { + val pkg = mockPkg(useV2 = true, autoVerify = false) + val collector = mockCollector(linkedApps = setOf(TEST_PKG_NAME)) + + assertThat(collector.collectAllWebDomains(pkg)) + .containsExactly("example1.com", "example2.com", "example3.com") + assertThat(collector.collectAutoVerifyDomains(pkg)).isEmpty() + } + + private fun mockCollector(linkedApps: Set = emptySet()): DomainVerificationCollector { + val systemConfig = mockThrowOnUnmocked { + whenever(this.linkedApps) { ArraySet(linkedApps) } + } + + return DomainVerificationCollector(platformCompat, systemConfig) + } + + private fun mockPkg(useV2: Boolean, autoVerify: Boolean): AndroidPackage { + // Translate equivalent of the following manifest declaration. This string isn't actually + // parsed, but it's a far easier to read representation of the test data. + // language=XML + """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.trimIndent() + + return mockThrowOnUnmocked { + whenever(packageName) { TEST_PKG_NAME } + whenever(targetSdkVersion) { + if (useV2) Build.VERSION_CODES.S else Build.VERSION_CODES.R + } + + // The intents are split into separate Activities to test that multiple are collected + val activityList = listOf( + ParsedActivity().apply { + addIntent( + ParsedIntentInfo().apply { + setAutoVerify(autoVerify) + addAction(Intent.ACTION_VIEW) + addCategory(Intent.CATEGORY_BROWSABLE) + addCategory(Intent.CATEGORY_DEFAULT) + addDataScheme("http") + addDataScheme("https") + addDataPath("/sub", PatternMatcher.PATTERN_LITERAL) + addDataAuthority("example1.com", null) + } + ) + addIntent( + ParsedIntentInfo().apply { + addAction(Intent.ACTION_VIEW) + addCategory(Intent.CATEGORY_BROWSABLE) + addCategory(Intent.CATEGORY_DEFAULT) + addDataScheme("http") + addDataPath("/sub2", PatternMatcher.PATTERN_LITERAL) + addDataAuthority("example2.com", null) + } + ) + }, + ParsedActivity().apply { + addIntent( + ParsedIntentInfo().apply { + setAutoVerify(autoVerify) + addAction(Intent.ACTION_VIEW) + addCategory(Intent.CATEGORY_BROWSABLE) + addCategory(Intent.CATEGORY_DEFAULT) + addDataScheme("https") + addDataPath("/sub3", PatternMatcher.PATTERN_LITERAL) + addDataAuthority("example3.com", null) + } + ) + }, + ParsedActivity().apply { + addIntent( + ParsedIntentInfo().apply { + setAutoVerify(autoVerify) + addAction(Intent.ACTION_VIEW) + addCategory(Intent.CATEGORY_BROWSABLE) + addDataScheme("https") + addDataPath("/sub4", PatternMatcher.PATTERN_LITERAL) + addDataAuthority("example4.com", null) + } + ) + addIntent( + ParsedIntentInfo().apply { + setAutoVerify(autoVerify) + addAction(Intent.ACTION_VIEW) + addCategory(Intent.CATEGORY_DEFAULT) + addDataScheme("https") + addDataPath("/sub5", PatternMatcher.PATTERN_LITERAL) + addDataAuthority("example5.com", null) + } + ) + addIntent( + ParsedIntentInfo().apply { + setAutoVerify(autoVerify) + addCategory(Intent.CATEGORY_BROWSABLE) + addCategory(Intent.CATEGORY_DEFAULT) + addDataScheme("https") + addDataPath("/sub6", PatternMatcher.PATTERN_LITERAL) + addDataAuthority("example6.com", null) + } + ) + }, + ) + + whenever(activities) { activityList } + } + } +} diff --git a/services/tests/servicestests/utils-mockito/com/android/server/testutils/MockitoUtils.kt b/services/tests/servicestests/utils-mockito/com/android/server/testutils/MockitoUtils.kt index 4c82818f71e44..c6e35cf84355c 100644 --- a/services/tests/servicestests/utils-mockito/com/android/server/testutils/MockitoUtils.kt +++ b/services/tests/servicestests/utils-mockito/com/android/server/testutils/MockitoUtils.kt @@ -26,21 +26,17 @@ import org.mockito.stubbing.Stubber object MockitoUtils { val ANSWER_THROWS = Answer { when (val name = it.method.name) { - "toString" -> return@Answer Answers.CALLS_REAL_METHODS.answer(it) + "toString" -> return@Answer try { + Answers.CALLS_REAL_METHODS.answer(it) + } catch (e: Exception) { + "failure calling toString" + } else -> { val arguments = it.arguments ?.takeUnless { it.isEmpty() } - ?.mapIndexed { index, arg -> - try { - arg?.toString() - } catch (e: Exception) { - "toString[$index] threw ${e.message}" - } - } - ?.joinToString() - ?.let { - "with $it" - } + ?.mapIndexed { index, arg -> arg.attemptToString(index) } + ?.joinToString { it.attemptToString(null) } + ?.let { "with $it" } .orEmpty() throw UnsupportedOperationException("${it.mock::class.java.simpleName}#$name " + @@ -48,6 +44,19 @@ object MockitoUtils { } } } + + // Sometimes mocks won't have a toString method, so try-catch and return some default + private fun Any?.attemptToString(id: Any? = null): String { + return try { + toString() + } catch (e: Exception) { + if (id == null) { + e.message ?: "ERROR" + } else { + "$id ${e.message}" + } + } + } } inline fun mock(block: T.() -> Unit = {}) = Mockito.mock(T::class.java).apply(block) @@ -83,4 +92,4 @@ inline fun spyThrowOnUnmocked(value: T?, block: T.() -> Unit = {}): inline fun mockThrowOnUnmocked(block: T.() -> Unit = {}) = spyThrowOnUnmocked(null, block) -inline fun nullable() = ArgumentMatchers.nullable(T::class.java) \ No newline at end of file +inline fun nullable() = ArgumentMatchers.nullable(T::class.java) From dd04535e9b8a06ec7d8f88bdeb9e0eaeb9903833 Mon Sep 17 00:00:00 2001 From: Winson Date: Wed, 16 Dec 2020 13:28:24 -0800 Subject: [PATCH 09/23] Wire up DomainVerificationService to PackageManagerService Sets up the callback and the actual service initialization so that future domain verification changes can access PackageManager APIs. Exempt-From-Owner-Approval: Already approved by owners on main branch Bug: 163565712 Test: none, device boots, tested implicitly as part of other changes Change-Id: Ie75ad0ee002ae92c21387777ff41c2504c69b836 --- .../android/app/SystemServiceRegistry.java | 17 +++++++++ .../server/pm/PackageManagerService.java | 36 +++++++++++++++++-- .../java/com/android/server/SystemServer.java | 9 +++-- .../src/com/android/server/pm/MockSystem.kt | 7 ++++ 4 files changed, 64 insertions(+), 5 deletions(-) diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index f8c33b58b6890..a7f8331bd0a09 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -69,6 +69,9 @@ import android.content.pm.IShortcutService; import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.content.pm.ShortcutManager; +import android.content.pm.domain.verify.DomainVerificationManager; +import android.content.pm.domain.verify.DomainVerificationManagerImpl; +import android.content.pm.domain.verify.IDomainVerificationManager; import android.content.res.Resources; import android.content.rollback.RollbackManagerFrameworkInitializer; import android.debug.AdbManager; @@ -1388,6 +1391,20 @@ public final class SystemServiceRegistry { } }); + // TODO(b/159952358): Only register this service for the domain verification agent? + registerService(Context.DOMAIN_VERIFICATION_SERVICE, DomainVerificationManager.class, + new CachedServiceFetcher() { + @Override + public DomainVerificationManager createService(ContextImpl context) + throws ServiceNotFoundException { + IBinder binder = ServiceManager.getServiceOrThrow( + Context.DOMAIN_VERIFICATION_SERVICE); + IDomainVerificationManager service = + IDomainVerificationManager.Stub.asInterface(binder); + return new DomainVerificationManagerImpl(context, service); + } + }); + sInitializing = true; try { // Note: the following functions need to be @SystemApis, once they become mainline diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 32d699f9875fa..45fb512806a8c 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -377,6 +377,8 @@ 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.domain.verify.DomainVerificationManagerInternal; +import com.android.server.pm.domain.verify.DomainVerificationService; 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; @@ -1071,6 +1073,8 @@ public class PackageManagerService extends IPackageManager.Stub private final Singleton mModuleInfoProviderProducer; private final Singleton mIntentFilterVerificationManagerProducer; + private final Singleton + mDomainVerificationManagerInternalProducer; private final Singleton mHandlerProducer; Injector(Context context, Object lock, Installer installer, @@ -1101,6 +1105,8 @@ public class PackageManagerService extends IPackageManager.Stub Producer moduleInfoProviderProducer, Producer legacyPermissionManagerInternalProducer, Producer intentFilterVerificationManagerProducer, + Producer + domainVerificationManagerInternalProducer, Producer handlerProducer, SystemWrapper systemWrapper, ServiceProducer getLocalServiceProducer, @@ -1141,6 +1147,8 @@ public class PackageManagerService extends IPackageManager.Stub mGetSystemServiceProducer = getSystemServiceProducer; mIntentFilterVerificationManagerProducer = new Singleton<>(intentFilterVerificationManagerProducer); + mDomainVerificationManagerInternalProducer = + new Singleton<>(domainVerificationManagerInternalProducer); mHandlerProducer = new Singleton<>(handlerProducer); } @@ -1292,6 +1300,10 @@ public class PackageManagerService extends IPackageManager.Stub return mIntentFilterVerificationManagerProducer.get(this, mPackageManager); } + public DomainVerificationManagerInternal getDomainVerificationManagerInternal() { + return mDomainVerificationManagerInternalProducer.get(this, mPackageManager); + } + public Handler getHandler() { return mHandlerProducer.get(this, mPackageManager); } @@ -1471,7 +1483,11 @@ public class PackageManagerService extends IPackageManager.Stub boolean mResolverReplaced = false; - private final @NonNull IntentFilterVerificationManager mIntentFilterVerificationManager; + @NonNull + private final IntentFilterVerificationManager mIntentFilterVerificationManager; + + @NonNull + private final DomainVerificationManagerInternal mDomainVerificationManager; /** The service connection to the ephemeral resolver */ final InstantAppResolverConnection mInstantAppResolverConnection; @@ -1801,6 +1817,13 @@ public class PackageManagerService extends IPackageManager.Stub } }; + private final DomainVerificationConnection mDomainVerificationConnection = + new DomainVerificationConnection(); + + private class DomainVerificationConnection implements + DomainVerificationService.Connection { + } + /** * Invalidate the package info cache, which includes updating the cached computer. * @hide @@ -5837,7 +5860,8 @@ public class PackageManagerService extends IPackageManager.Stub } public static PackageManagerService main(Context context, Installer installer, - boolean factoryTest, boolean onlyCore) { + @NonNull DomainVerificationService domainVerificationService, boolean factoryTest, + boolean onlyCore) { // Self-check for initial settings. PackageManagerServiceCompilerMapping.checkProperties(); final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing", @@ -5897,6 +5921,7 @@ public class PackageManagerService extends IPackageManager.Stub (i, pm) -> new IntentFilterVerificationManager(pm.mContext, i.getHandler(), pm.mIntentFilterVerificationConnection, SystemConfig.getInstance(), i.getUserManagerService()), + (i, pm) -> domainVerificationService, (i, pm) -> { HandlerThread thread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/); @@ -6075,6 +6100,7 @@ public class PackageManagerService extends IPackageManager.Stub mSettings = injector.getSettings(); mUserManager = injector.getUserManagerService(); mIntentFilterVerificationManager = injector.getIntentFilterVerificationManager(); + mDomainVerificationManager = injector.getDomainVerificationManagerInternal(); mHandler = injector.getHandler(); mApexManager = testParams.apexManager; @@ -6322,6 +6348,7 @@ public class PackageManagerService extends IPackageManager.Stub mProcessLoggingHandler = new ProcessLoggingHandler(); Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT); mIntentFilterVerificationManager = injector.getIntentFilterVerificationManager(); + mDomainVerificationManager = injector.getDomainVerificationManagerInternal(); ArrayMap libConfig = systemConfig.getSharedLibraries(); @@ -27935,6 +27962,11 @@ public class PackageManagerService extends IPackageManager.Stub duration); return bOptions; } + + @NonNull + public DomainVerificationService.Connection getDomainVerificationConnection() { + return mDomainVerificationConnection; + } } interface PackageSender { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index b082909018c17..b39a9a8d2297c 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -1067,8 +1067,10 @@ public final class SystemServer implements Dumpable { mSystemContext, new Singleton() { @Override protected DomainVerificationService.Connection create() { - // TODO(b/159952358): Hook up to PackageManagerService - return null; + // Deferred retrieval from PackageManagerService, since PMS is initialized after + // DVS. The alternative would be to expose this through the PackageManagerInternal + // local service, but making it visible to consumers of that interface isn't useful. + return mPackageManagerService.getDomainVerificationConnection(); } }); mSystemServiceManager.startService(domainVerificationService); @@ -1078,7 +1080,8 @@ public final class SystemServer implements Dumpable { try { Watchdog.getInstance().pauseWatchingCurrentThread("packagemanagermain"); mPackageManagerService = PackageManagerService.main(mSystemContext, installer, - mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore); + domainVerificationService, mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, + mOnlyCore); } finally { Watchdog.getInstance().resumeWatchingCurrentThread("packagemanagermain"); } diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt index c522541b166f5..e304725e3d51f 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt @@ -59,11 +59,13 @@ import com.android.server.SystemServerInitThreadPool import com.android.server.compat.PlatformCompat import com.android.server.extendedtestutils.wheneverStatic import com.android.server.pm.dex.DexManager +import com.android.server.pm.domain.verify.DomainVerificationManagerInternal import com.android.server.pm.parsing.PackageParser2 import com.android.server.pm.parsing.pkg.AndroidPackage import com.android.server.pm.parsing.pkg.PackageImpl import com.android.server.pm.parsing.pkg.ParsedPackage import com.android.server.pm.permission.PermissionManagerServiceInternal +import com.android.server.testutils.TestHandler import com.android.server.testutils.mock import com.android.server.testutils.nullable import com.android.server.testutils.whenever @@ -183,6 +185,8 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { val dexManager: DexManager = mock() val installer: Installer = mock() val displayMetrics: DisplayMetrics = mock() + val domainVerificationManagerInternal: DomainVerificationManagerInternal = mock() + val handler = TestHandler(null) } companion object { @@ -258,6 +262,9 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { whenever(mocks.injector.userManagerInternal).thenReturn(mocks.userManagerInternal) whenever(mocks.injector.installer).thenReturn(mocks.installer) whenever(mocks.injector.displayMetrics).thenReturn(mocks.displayMetrics) + whenever(mocks.injector.domainVerificationManagerInternal) + .thenReturn(mocks.domainVerificationManagerInternal) + whenever(mocks.injector.handler) { mocks.handler } wheneverStatic { SystemConfig.getInstance() }.thenReturn(mocks.systemConfig) whenever(mocks.systemConfig.availableFeatures).thenReturn(DEFAULT_AVAILABLE_FEATURES_MAP) whenever(mocks.systemConfig.sharedLibraries).thenReturn(DEFAULT_SHARED_LIBRARIES_LIST) From 39ef1f3fb37a90e72dc7d3478799b73106791014 Mon Sep 17 00:00:00 2001 From: Winson Date: Wed, 16 Dec 2020 14:39:59 -0800 Subject: [PATCH 10/23] Add DomainVerificationSettings write/read to Settings Introduces the concept of a domainSetId which represents an immutable set of domains declared by the package. This is currently updated whenever the package updates, hooked into the process for updating a PackageSetting object. Exempt-From-Owner-Approval: Already approved by owners on main branch Bug: 163565712 Test: atest PackageManagerSettingsTests Change-Id: I7c2c541cfdad7ddca7739424ee9e034fe616988b --- .../server/pm/PackageManagerService.java | 26 +- .../com/android/server/pm/PackageSetting.java | 20 +- .../java/com/android/server/pm/Settings.java | 59 +++- .../DomainVerificationManagerInternal.java | 67 ++++- .../verify/DomainVerificationService.java | 100 ++++++- .../verify/DomainVerificationSettings.java | 256 ++++++++++++++++++ ...geManagerComponentLabelIconOverrideTest.kt | 10 +- .../src/com/android/server/pm/MockSystem.kt | 2 +- .../server/pm/KeySetManagerServiceTest.java | 7 +- .../server/pm/PackageManagerServiceTest.java | 13 +- .../pm/PackageManagerSettingsTests.java | 46 +++- .../android/server/pm/PackageParserTest.java | 17 +- .../server/pm/PackageSettingBuilder.java | 10 +- .../server/pm/PackageSignaturesTest.java | 11 +- .../src/com/android/server/pm/ScanTests.java | 9 + 15 files changed, 593 insertions(+), 60 deletions(-) create mode 100644 services/core/java/com/android/server/pm/domain/verify/DomainVerificationSettings.java diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 45fb512806a8c..ba30247744a35 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1822,6 +1822,13 @@ public class PackageManagerService extends IPackageManager.Stub private class DomainVerificationConnection implements DomainVerificationService.Connection { + + @Override + public void scheduleWriteSettings() { + synchronized (mLock) { + PackageManagerService.this.scheduleWriteSettingsLocked(); + } + } } /** @@ -5885,7 +5892,8 @@ public class PackageManagerService extends IPackageManager.Stub (i, pm) -> new Settings(Environment.getDataDirectory(), RuntimePermissionsPersistence.createInstance(), i.getPermissionManagerServiceInternal(), - i.getIntentFilterVerificationManager(), lock), + i.getIntentFilterVerificationManager(), + domainVerificationService, lock), (i, pm) -> AppsFilter.create(pm.mPmInternal, i), (i, pm) -> (PlatformCompat) ServiceManager.getService("platform_compat"), (i, pm) -> SystemConfig.getInstance(), @@ -13296,7 +13304,7 @@ public class PackageManagerService extends IPackageManager.Stub final int userId = user == null ? 0 : user.getIdentifier(); // Modify state for the given package setting - commitPackageSettings(pkg, oldPkg, pkgSetting, scanFlags, + commitPackageSettings(pkg, oldPkg, pkgSetting, oldPkgSetting, scanFlags, (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0 /*chatty*/, reconciledPkg); if (pkgSetting.getInstantApp(userId)) { mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.appId); @@ -13553,6 +13561,9 @@ public class PackageManagerService extends IPackageManager.Stub usesStaticLibraries = new String[parsedPackage.getUsesStaticLibraries().size()]; parsedPackage.getUsesStaticLibraries().toArray(usesStaticLibraries); } + + final UUID newDomainSetId = injector.getDomainVerificationManagerInternal().generateNewId(); + // TODO(b/135203078): Remove appInfoFlag usage in favor of individually assigned booleans // to avoid adding something that's unsupported due to lack of state, since it's called // with null. @@ -13576,7 +13587,8 @@ public class PackageManagerService extends IPackageManager.Stub parsedPackage.getVersionCode(), pkgFlags, pkgPrivateFlags, user, true /*allowInstall*/, instantApp, virtualPreload, UserManagerService.getInstance(), usesStaticLibraries, - parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups()); + parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups(), + newDomainSetId); } else { // make a deep copy to avoid modifying any existing system state. pkgSetting = new PackageSetting(pkgSetting); @@ -13595,7 +13607,7 @@ public class PackageManagerService extends IPackageManager.Stub PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting), UserManagerService.getInstance(), usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(), - parsedPackage.getMimeGroups()); + parsedPackage.getMimeGroups(), newDomainSetId); } if (createNewPackage && originalPkgSetting != null) { // This is the initial transition from the original package, so, @@ -14440,8 +14452,8 @@ public class PackageManagerService extends IPackageManager.Stub * Adds a scanned package to the system. When this method is finished, the package will * be available for query, resolution, etc... */ - private void commitPackageSettings(AndroidPackage pkg, - @Nullable AndroidPackage oldPkg, PackageSetting pkgSetting, + private void commitPackageSettings(@NonNull AndroidPackage pkg, @Nullable AndroidPackage oldPkg, + @NonNull PackageSetting pkgSetting, @Nullable PackageSetting oldPkgSetting, final @ScanFlags int scanFlags, boolean chatty, ReconciledPackage reconciledPkg) { final String pkgName = pkg.getPackageName(); if (mCustomResolverComponentName != null && @@ -20950,6 +20962,7 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { mIntentFilterVerificationManager.clearIntentFilterVerificationsLocked( deletedPs.name, UserHandle.USER_ALL, true); + mDomainVerificationManager.clearPackage(deletedPs.name); clearDefaultBrowserIfNeeded(packageName); mSettings.getKeySetManagerService().removeAppKeySetDataLPw(packageName); mAppsFilter.removePackage(getPackageSetting(packageName)); @@ -22084,6 +22097,7 @@ public class PackageManagerService extends IPackageManager.Stub mSettings.applyDefaultPreferredAppsLPw(userId); mIntentFilterVerificationManager.clearIntentFilterVerificationsLocked(userId, mPackages); + mDomainVerificationManager.clearUser(userId); primeDomainVerificationsLPw(userId); final int numPackages = mPackages.size(); for (int i = 0; i < numPackages; i++) { diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index ade087be30c98..69e84b536004f 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -39,6 +39,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.UUID; /** * Settings data for a particular package we know about. @@ -99,18 +100,23 @@ public class PackageSetting extends PackageSettingBase { @NonNull private PackageStateUnserialized pkgState = new PackageStateUnserialized(); + @NonNull + private UUID mDomainSetId; + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public PackageSetting(String name, String realName, @NonNull File codePath, String legacyNativeLibraryPathString, String primaryCpuAbiString, String secondaryCpuAbiString, String cpuAbiOverrideString, long pVersionCode, int pkgFlags, int privateFlags, int sharedUserId, String[] usesStaticLibraries, - long[] usesStaticLibrariesVersions, Map> mimeGroups) { + long[] usesStaticLibrariesVersions, Map> mimeGroups, + @NonNull UUID domainSetId) { super(name, realName, codePath, legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString, pVersionCode, pkgFlags, privateFlags, usesStaticLibraries, usesStaticLibrariesVersions); this.sharedUserId = sharedUserId; + mDomainSetId = domainSetId; copyMimeGroups(mimeGroups); } @@ -168,6 +174,7 @@ public class PackageSetting extends PackageSettingBase { sharedUser = orig.sharedUser; sharedUserId = orig.sharedUserId; copyMimeGroups(orig.mimeGroups); + mDomainSetId = orig.getDomainSetId(); } private void copyMimeGroups(@Nullable Map> newMimeGroups) { @@ -374,6 +381,7 @@ public class PackageSetting extends PackageSettingBase { pkg = other.pkg; sharedUserId = other.sharedUserId; sharedUser = other.sharedUser; + mDomainSetId = other.mDomainSetId; Set mimeGroupNames = other.mimeGroups != null ? other.mimeGroups.keySet() : null; updateMimeGroups(mimeGroupNames); @@ -385,4 +393,14 @@ public class PackageSetting extends PackageSettingBase { public PackageStateUnserialized getPkgState() { return pkgState; } + + @NonNull + public UUID getDomainSetId() { + return mDomainSetId; + } + + public PackageSetting setDomainSetId(@NonNull UUID domainSetId) { + mDomainSetId = domainSetId; + return this; + } } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 89b6bfbbfecb5..689830bd6b984 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -106,6 +106,8 @@ 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.domain.verify.DomainVerificationManagerInternal; +import com.android.server.pm.domain.verify.DomainVerificationPersistence; import com.android.server.pm.intent.verify.legacy.IntentFilterVerificationManager; import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.pkg.AndroidPackage; @@ -154,6 +156,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; +import java.util.UUID; /** * Holds information about dynamic settings. @@ -510,6 +513,8 @@ public final class Settings implements Watchable, Snappable { private final IntentFilterVerificationManager mIntentFilterVerificationManager; + private final DomainVerificationManagerInternal mDomainVerificationManager; + /** * The observer that watches for changes from array members */ @@ -537,6 +542,7 @@ public final class Settings implements Watchable, Snappable { mBackupStoppedPackagesFilename = null; mKernelMappingFilename = null; mIntentFilterVerificationManager = null; + mDomainVerificationManager = null; mPackages.registerObserver(mObserver); mInstallerPackages.registerObserver(mObserver); mKernelMapping.registerObserver(mObserver); @@ -558,7 +564,9 @@ public final class Settings implements Watchable, Snappable { Settings(File dataDir, RuntimePermissionsPersistence runtimePermissionsPersistence, LegacyPermissionDataProvider permissionDataProvider, - IntentFilterVerificationManager intentFilterVerificationManager, Object lock) { + @NonNull IntentFilterVerificationManager intentFilterVerificationManager, + @NonNull DomainVerificationManagerInternal domainVerificationManager, + @NonNull Object lock) { mLock = lock; mAppIds = new WatchedArrayList<>(); mOtherAppIds = new WatchedSparseArray<>(); @@ -586,6 +594,7 @@ public final class Settings implements Watchable, Snappable { mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml"); mIntentFilterVerificationManager = intentFilterVerificationManager; + mDomainVerificationManager = domainVerificationManager; mPackages.registerObserver(mObserver); mInstallerPackages.registerObserver(mObserver); @@ -629,6 +638,7 @@ public final class Settings implements Watchable, Snappable { mKernelMappingFilename = null; mIntentFilterVerificationManager = r.mIntentFilterVerificationManager; + mDomainVerificationManager = r.mDomainVerificationManager; mInstallerPackages.addAll(r.mInstallerPackages); mKernelMapping.putAll(r.mKernelMapping); @@ -766,7 +776,8 @@ public final class Settings implements Watchable, Snappable { p.legacyNativeLibraryPathString, p.primaryCpuAbiString, p.secondaryCpuAbiString, p.cpuAbiOverrideString, p.appId, p.versionCode, p.pkgFlags, p.pkgPrivateFlags, - p.usesStaticLibraries, p.usesStaticLibrariesVersions, p.mimeGroups); + p.usesStaticLibraries, p.usesStaticLibrariesVersions, p.mimeGroups, + mDomainVerificationManager.generateNewId()); if (ret != null) { ret.getPkgState().setUpdatedSystemApp(false); } @@ -786,7 +797,8 @@ public final class Settings implements Watchable, Snappable { String legacyNativeLibraryPathString, String primaryCpuAbiString, String secondaryCpuAbiString, String cpuAbiOverrideString, int uid, long vc, int pkgFlags, int pkgPrivateFlags, String[] usesStaticLibraries, - long[] usesStaticLibraryNames, Map> mimeGroups) { + long[] usesStaticLibraryNames, Map> mimeGroups, + @NonNull UUID domainSetId) { PackageSetting p = mPackages.get(name); if (p != null) { if (p.appId == uid) { @@ -799,7 +811,7 @@ public final class Settings implements Watchable, Snappable { p = new PackageSetting(name, realName, codePath, legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString, vc, pkgFlags, pkgPrivateFlags, 0 /*userId*/, usesStaticLibraries, usesStaticLibraryNames, - mimeGroups); + mimeGroups, domainSetId); p.appId = uid; if (registerExistingAppIdLPw(uid, p, name)) { mPackages.put(name, p); @@ -863,7 +875,7 @@ public final class Settings implements Watchable, Snappable { UserHandle installUser, boolean allowInstall, boolean instantApp, boolean virtualPreload, UserManagerService userManager, String[] usesStaticLibraries, long[] usesStaticLibrariesVersions, - Set mimeGroupNames) { + Set mimeGroupNames, @NonNull UUID domainSetId) { final PackageSetting pkgSetting; if (originalPkg != null) { if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package " @@ -883,12 +895,13 @@ public final class Settings implements Watchable, Snappable { pkgSetting.usesStaticLibrariesVersions = usesStaticLibrariesVersions; // Update new package state. pkgSetting.setTimeStamp(codePath.lastModified()); + pkgSetting.setDomainSetId(domainSetId); } else { pkgSetting = new PackageSetting(pkgName, realPkgName, codePath, legacyNativeLibraryPath, primaryCpuAbi, secondaryCpuAbi, null /*cpuAbiOverrideString*/, versionCode, pkgFlags, pkgPrivateFlags, 0 /*sharedUserId*/, usesStaticLibraries, - usesStaticLibrariesVersions, createMimeGroups(mimeGroupNames)); + usesStaticLibrariesVersions, createMimeGroups(mimeGroupNames), domainSetId); pkgSetting.setTimeStamp(codePath.lastModified()); pkgSetting.sharedUser = sharedUser; // If this is not a system app, it starts out stopped. @@ -983,7 +996,7 @@ public final class Settings implements Watchable, Snappable { @Nullable String primaryCpuAbi, @Nullable String secondaryCpuAbi, int pkgFlags, int pkgPrivateFlags, @NonNull UserManagerService userManager, @Nullable String[] usesStaticLibraries, @Nullable long[] usesStaticLibrariesVersions, - @Nullable Set mimeGroupNames) + @Nullable Set mimeGroupNames, @NonNull UUID domainSetId) throws PackageManagerException { final String pkgName = pkgSetting.name; if (pkgSetting.sharedUser != sharedUser) { @@ -1059,6 +1072,7 @@ public final class Settings implements Watchable, Snappable { pkgSetting.usesStaticLibrariesVersions = null; } pkgSetting.updateMimeGroups(mimeGroupNames); + pkgSetting.setDomainSetId(domainSetId); } /** @@ -2342,6 +2356,8 @@ public final class Settings implements Watchable, Snappable { mIntentFilterVerificationManager.writeRestoredIntentFilterVerifications(serializer); + mDomainVerificationManager.writeSettings(serializer); + mKeySetManagerService.writeKeySetManagerServiceLPr(serializer); serializer.endTag(null, "packages"); @@ -2717,6 +2733,8 @@ public final class Settings implements Watchable, Snappable { serializer.attributeFloat(null, "loadingProgress", pkg.getIncrementalStates().getProgress()); + serializer.attribute(null, "domainSetId", pkg.getDomainSetId().toString()); + writeUsesStaticLibLPw(serializer, pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions); pkg.signatures.writeXml(serializer, "sigs", mPastSignatures); @@ -2904,7 +2922,9 @@ public final class Settings implements Watchable, Snappable { ver.sdkVersion = parser.getAttributeInt(null, ATTR_SDK_VERSION); ver.databaseVersion = parser.getAttributeInt(null, ATTR_DATABASE_VERSION); ver.fingerprint = XmlUtils.readStringAttribute(parser, ATTR_FINGERPRINT); - } else { + } else if (tagName.equals(DomainVerificationPersistence.TAG_DOMAIN_VERIFICATIONS)) { + mDomainVerificationManager.readSettings(parser); + }else { Slog.w(PackageManagerService.TAG, "Unknown element under : " + parser.getName()); XmlUtils.skipCurrentTag(parser); @@ -3385,9 +3405,15 @@ public final class Settings implements Watchable, Snappable { if (codePathStr.contains("/priv-app/")) { pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED; } + + // When reading a disabled setting, use a disabled domainSetId, which makes it easier to + // debug invalid entries. The actual logic for migrating to a new ID is done in other + // methods that use DomainVerificationManagerInternal#generateNewId + UUID domainSetId = DomainVerificationManagerInternal.DISABLED_ID; PackageSetting ps = new PackageSetting(name, realName, new File(codePathStr), legacyNativeLibraryPathStr, primaryCpuAbiStr, secondaryCpuAbiStr, cpuAbiOverrideStr, - versionCode, pkgFlags, pkgPrivateFlags, 0 /*sharedUserId*/, null, null, null); + versionCode, pkgFlags, pkgPrivateFlags, 0 /*sharedUserId*/, null, null, null, + domainSetId); long timeStamp = parser.getAttributeLongHex(null, "ft", 0); if (timeStamp == 0) { timeStamp = parser.getAttributeLong(null, "ts", 0); @@ -3460,6 +3486,7 @@ public final class Settings implements Watchable, Snappable { boolean isStartable = false; boolean isLoading = false; float loadingProgress = 0; + UUID domainSetId; try { name = parser.getAttributeValue(null, ATTR_NAME); realName = parser.getAttributeValue(null, "realName"); @@ -3496,6 +3523,15 @@ public final class Settings implements Watchable, Snappable { categoryHint = parser.getAttributeInt(null, "categoryHint", ApplicationInfo.CATEGORY_UNDEFINED); + String domainSetIdString = parser.getAttributeValue(null, "domainSetId"); + + if (TextUtils.isEmpty(domainSetIdString)) { + // If empty, assume restoring from previous platform version and generate an ID + domainSetId = mDomainVerificationManager.generateNewId(); + } else { + domainSetId = UUID.fromString(domainSetIdString); + } + systemStr = parser.getAttributeValue(null, "publicFlags"); if (systemStr != null) { try { @@ -3567,7 +3603,7 @@ public final class Settings implements Watchable, Snappable { legacyNativeLibraryPathStr, primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString, userId, versionCode, pkgFlags, pkgPrivateFlags, null /*usesStaticLibraries*/, null /*usesStaticLibraryVersions*/, - null /*mimeGroups*/); + null /*mimeGroups*/, domainSetId); if (PackageManagerService.DEBUG_SETTINGS) Log.i(PackageManagerService.TAG, "Reading package " + name + ": userId=" + userId + " pkg=" + packageSetting); @@ -3588,7 +3624,7 @@ public final class Settings implements Watchable, Snappable { versionCode, pkgFlags, pkgPrivateFlags, sharedUserId, null /*usesStaticLibraries*/, null /*usesStaticLibraryVersions*/, - null /*mimeGroups*/); + null /*mimeGroups*/, domainSetId); packageSetting.setTimeStamp(timeStamp); packageSetting.firstInstallTime = firstInstallTime; packageSetting.lastUpdateTime = lastUpdateTime; @@ -3974,6 +4010,7 @@ public final class Settings implements Watchable, Snappable { removeCrossProfileIntentFiltersLPw(userId); mRuntimePermissionsPersistence.onUserRemovedLPw(userId); + mDomainVerificationManager.clearUser(userId); writePackageListLPr(); diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java index 1927e9fffcc87..d83b49dee11eb 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java @@ -16,10 +16,75 @@ package com.android.server.pm.domain.verify; +import android.annotation.NonNull; +import android.annotation.UserIdInt; import android.content.pm.domain.verify.DomainVerificationManager; +import android.content.pm.domain.verify.DomainVerificationSet; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; + +import com.android.server.pm.domain.verify.models.DomainVerificationPkgState; + +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.UUID; public interface DomainVerificationManagerInternal extends DomainVerificationManager { - // TODO(b/159952358): Skeleton checked in to prepare for future internal methods + UUID DISABLED_ID = new UUID(0, 0); + + /** + * Generate a new domain set ID to be used for attaching new packages. + */ + @NonNull + UUID generateNewId(); + + /** + * Serializes the entire internal state. This is equivalent to a full backup of the existing + * verification state. + */ + void writeSettings(@NonNull TypedXmlSerializer serializer) throws IOException; + + /** + * Read back a list of {@link DomainVerificationPkgState}s previously written by {@link + * #writeSettings(TypedXmlSerializer)}. Assumes that the + * {@link DomainVerificationPersistence#TAG_DOMAIN_VERIFICATIONS} + * tag has already been entered. + *

+ * This is expected to only be used to re-attach states for packages already known to be on the + * device. If restoring from a backup, use {@link #restoreSettings(TypedXmlPullParser)}. + */ + void readSettings(@NonNull TypedXmlPullParser parser) + throws IOException, XmlPullParserException; + + /** + * Remove all state for the given package. + */ + void clearPackage(@NonNull String packageName); + + /** + * Delete all the state for a user. This can be because the user has been removed from the + * device, or simply that the state for a user should be deleted. + */ + void clearUser(@UserIdInt int userId); + + /** + * Restore a list of {@link DomainVerificationPkgState}s previously written by {@link + * #writeSettings(TypedXmlSerializer)}. Assumes that the + * {@link DomainVerificationPersistence#TAG_DOMAIN_VERIFICATIONS} + * tag has already been entered. + *

+ * This is only for restore, and will override package states, ignoring if their {@link + * DomainVerificationSet#getIdentifier()}s match. It's expected that any restored domains marked + * as success verify against the server correctly, although the verification agent may decide to + * re-verify them when it gets the chance. + */ + /* + * TODO(b/170746586): Figure out how to verify that package signatures match at snapshot time + * and restore time. + */ + void restoreSettings(@NonNull TypedXmlPullParser parser) + throws IOException, XmlPullParserException; } diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java index e9fd3ae0452df..4d4a8484c440a 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java @@ -25,9 +25,17 @@ import android.content.pm.domain.verify.DomainVerificationSet; import android.content.pm.domain.verify.DomainVerificationUserSelection; import android.content.pm.domain.verify.IDomainVerificationManager; import android.util.Singleton; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; +import com.android.internal.annotations.GuardedBy; import com.android.server.SystemService; +import com.android.server.pm.domain.verify.models.DomainVerificationPkgState; +import com.android.server.pm.domain.verify.models.DomainVerificationStateMap; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; import java.util.List; import java.util.Set; import java.util.UUID; @@ -37,9 +45,27 @@ public class DomainVerificationService extends SystemService private static final String TAG = "DomainVerificationService"; + /** + * States that are currently alive and attached to a package. Entries are exclusive with the + * state stored in {@link DomainVerificationSettings}, as any pending/restored state should be + * immediately attached once its available. + **/ + @GuardedBy("mLock") + @NonNull + private final DomainVerificationStateMap mAttachedPkgStates = + new DomainVerificationStateMap<>(); + + /** + * Lock for all state reads/writes. + */ + private final Object mLock = new Object(); + @NonNull private final Singleton mConnection; + @NonNull + private final DomainVerificationSettings mSettings; + @NonNull private final IDomainVerificationManager.Stub mStub = new DomainVerificationManagerStub(this); @@ -47,6 +73,7 @@ public class DomainVerificationService extends SystemService @NonNull Singleton connection) { super(context); mConnection = connection; + mSettings = new DomainVerificationSettings(); } @Override @@ -70,31 +97,36 @@ public class DomainVerificationService extends SystemService @Override public void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set domains, int state) throws InvalidDomainSetException, NameNotFoundException { - + //TODO(b/163565712): Implement method + mConnection.get().scheduleWriteSettings(); } @Override public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName, boolean allowed) throws NameNotFoundException { - + //TODO(b/163565712): Implement method + mConnection.get().scheduleWriteSettings(); } public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName, boolean allowed, @UserIdInt int userId) throws NameNotFoundException { - + //TODO(b/163565712): Implement method + mConnection.get().scheduleWriteSettings(); } @Override public void setDomainVerificationUserSelection(@NonNull UUID domainSetId, @NonNull Set domains, boolean enabled) throws InvalidDomainSetException, NameNotFoundException { - + //TODO(b/163565712): Implement method + mConnection.get().scheduleWriteSettings(); } public void setDomainVerificationUserSelection(@NonNull UUID domainSetId, @NonNull Set domains, boolean enabled, @UserIdInt int userId) throws InvalidDomainSetException, NameNotFoundException { - + //TODO(b/163565712): Implement method + mConnection.get().scheduleWriteSettings(); } @Nullable @@ -110,7 +142,65 @@ public class DomainVerificationService extends SystemService return null; } + @NonNull + @Override + public UUID generateNewId() { + // TODO(b/159952358): Domain set ID collisions + return UUID.randomUUID(); + } + + @Override + public void writeSettings(@NonNull TypedXmlSerializer serializer) throws IOException { + synchronized (mLock) { + mSettings.writeSettings(serializer, mAttachedPkgStates); + } + } + + @Override + public void readSettings(@NonNull TypedXmlPullParser parser) + throws IOException, XmlPullParserException { + synchronized (mLock) { + mSettings.readSettings(parser, mAttachedPkgStates); + } + } + + @Override + public void restoreSettings(@NonNull TypedXmlPullParser parser) + throws IOException, XmlPullParserException { + synchronized (mLock) { + mSettings.restoreSettings(parser, mAttachedPkgStates); + } + } + + @Override + public void clearPackage(@NonNull String packageName) { + synchronized (mLock) { + mAttachedPkgStates.remove(packageName); + } + + mConnection.get().scheduleWriteSettings(); + } + + @Override + public void clearUser(@UserIdInt int userId) { + synchronized (mLock) { + int attachedSize = mAttachedPkgStates.size(); + for (int index = 0; index < attachedSize; index++) { + mAttachedPkgStates.valueAt(index).removeUser(userId); + } + + mSettings.removeUser(userId); + } + + mConnection.get().scheduleWriteSettings(); + } + public interface Connection { + /** + * Notify that a settings change has been made and that eventually + * {@link #writeSettings(TypedXmlSerializer)} should be invoked by the parent. + */ + void scheduleWriteSettings(); } } diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationSettings.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationSettings.java new file mode 100644 index 0000000000000..8de2ae614fe68 --- /dev/null +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationSettings.java @@ -0,0 +1,256 @@ +/* + * 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.domain.verify; + +import android.annotation.NonNull; +import android.annotation.UserIdInt; +import android.content.pm.domain.verify.DomainVerificationState; +import android.os.UserHandle; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.Pair; +import android.util.SparseArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.pm.domain.verify.models.DomainVerificationPkgState; +import com.android.server.pm.domain.verify.models.DomainVerificationStateMap; +import com.android.server.pm.domain.verify.models.DomainVerificationUserState; + +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +class DomainVerificationSettings { + + /** + * States read from disk that have yet to attach to a package, but are expected to, generally in + * the context of scanning packages already on disk. This is expected to be empty once the boot + * package scan completes. + **/ + @GuardedBy("mLock") + @NonNull + private final ArrayMap mPendingPkgStates = new ArrayMap<>(); + + /** + * States from restore that have yet to attach to a package. These are special in that their IDs + * are dropped when the package is installed/otherwise becomes available, because the ID will + * not match if the data is restored from a different device install. + *

+ * If multiple restore calls come in and they overlap, the latest entry added for a package name + * will be taken, dropping any previous versions. + **/ + @GuardedBy("mLock") + @NonNull + private final ArrayMap mRestoredPkgStates = + new ArrayMap<>(); + + /** + * Lock for all state reads/writes. + */ + private final Object mLock = new Object(); + + + public void writeSettings(@NonNull TypedXmlSerializer xmlSerializer, + @NonNull DomainVerificationStateMap liveState) + throws IOException { + synchronized (mLock) { + DomainVerificationPersistence.writeToXml(xmlSerializer, liveState, + mPendingPkgStates, mRestoredPkgStates); + } + } + + /** + * Parses a previously stored set of states and merges them with {@param liveState}, directly + * mutating the values. This is intended for reading settings written by {@link + * #writeSettings(TypedXmlSerializer, DomainVerificationStateMap)} on the same device setup. + */ + public void readSettings(@NonNull TypedXmlPullParser parser, + @NonNull DomainVerificationStateMap liveState) + throws IOException, XmlPullParserException { + DomainVerificationPersistence.ReadResult result = + DomainVerificationPersistence.readFromXml(parser); + ArrayMap active = result.active; + ArrayMap restored = result.restored; + + synchronized (mLock) { + int activeSize = active.size(); + for (int activeIndex = 0; activeIndex < activeSize; activeIndex++) { + DomainVerificationPkgState pkgState = active.valueAt(activeIndex); + String pkgName = pkgState.getPackageName(); + DomainVerificationPkgState existingState = liveState.get(pkgName); + if (existingState != null) { + // This branch should never be possible. Settings should be read from disk + // before any states are attached. But just in case, handle it. + if (!existingState.getId().equals(pkgState.getId())) { + mergePkgState(existingState, pkgState); + } + } else { + mPendingPkgStates.put(pkgName, pkgState); + } + } + + int restoredSize = restored.size(); + for (int restoredIndex = 0; restoredIndex < restoredSize; restoredIndex++) { + DomainVerificationPkgState pkgState = restored.valueAt(restoredIndex); + mRestoredPkgStates.put(pkgState.getPackageName(), pkgState); + } + } + } + + /** + * Parses a previously stored set of states and merges them with {@param liveState}, directly + * mutating the values. This is intended for restoration across device setups. + */ + public void restoreSettings(@NonNull TypedXmlPullParser parser, + @NonNull DomainVerificationStateMap liveState) + throws IOException, XmlPullParserException { + // TODO(b/170746586): Restoration assumes user IDs match, which is probably not the case on + // a new device. + + DomainVerificationPersistence.ReadResult result = + DomainVerificationPersistence.readFromXml(parser); + + // When restoring settings, both active and previously restored are merged, since they + // should both go into the newly restored data. Active is added on top of restored just + // in case a duplicate is found. Active should be preferred. + ArrayMap stateList = result.restored; + stateList.putAll(result.active); + + synchronized (mLock) { + for (int stateIndex = 0; stateIndex < stateList.size(); stateIndex++) { + DomainVerificationPkgState newState = stateList.valueAt(stateIndex); + String pkgName = newState.getPackageName(); + DomainVerificationPkgState existingState = liveState.get(pkgName); + if (existingState == null) { + existingState = mPendingPkgStates.get(pkgName); + } + if (existingState == null) { + existingState = mRestoredPkgStates.get(pkgName); + } + + if (existingState != null) { + mergePkgState(existingState, newState); + } else { + // If there's no existing state, that means the new state has to be transformed + // in preparation for attaching to brand new package that may eventually be + // installed. This means coercing STATE_SUCCESS and STATE_RESTORED to + // STATE_RESTORED and dropping everything else, the same logic that + // mergePkgState runs, without the merge part. + ArrayMap stateMap = newState.getStateMap(); + int size = stateMap.size(); + for (int index = 0; index < size; index++) { + Integer stateInteger = stateMap.valueAt(index); + if (stateInteger != null) { + int state = stateInteger; + if (state == DomainVerificationState.STATE_SUCCESS + || state == DomainVerificationState.STATE_RESTORED) { + stateMap.setValueAt(index, state); + } + } + } + } + } + } + } + + /** + * Merges a newly restored state with existing state. This should only be called for restore, + * when the IDs aren't required to match. + *

+ * If the existing state for a domain is + * {@link DomainVerificationState#STATE_NO_RESPONSE}, then it will be overridden with + * {@link DomainVerificationState#STATE_RESTORED} if the restored state is + * {@link DomainVerificationState#STATE_SUCCESS} or + * {@link DomainVerificationState#STATE_RESTORED}. + *

+ * Otherwise the existing state is preserved, assuming any system rules, success state, or + * specific error codes are fresher than the restored state. Essentially state is only restored + * to grant additional verifications to an app. + *

+ * For user selection state, presence in either state will be considered an enabled host. NOTE: + * only {@link UserHandle#USER_SYSTEM} is merged. There is no restore path in place for + * multiple users. + *

+ * TODO(b/170746586): Figure out the restore path for multiple users + *

+ * This will mutate {@param oldState} to contain the merged state. + */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + public static void mergePkgState(@NonNull DomainVerificationPkgState oldState, + @NonNull DomainVerificationPkgState newState) { + ArrayMap oldStateMap = oldState.getStateMap(); + ArrayMap newStateMap = newState.getStateMap(); + int size = newStateMap.size(); + for (int index = 0; index < size; index++) { + String domain = newStateMap.keyAt(index); + Integer newStateCode = newStateMap.valueAt(index); + Integer oldStateCodeInteger = oldStateMap.get(domain); + if (oldStateCodeInteger == null) { + // Cannot add domains to an app + continue; + } + + int oldStateCode = oldStateCodeInteger; + if (oldStateCode == DomainVerificationState.STATE_NO_RESPONSE) { + if (newStateCode == DomainVerificationState.STATE_SUCCESS + || newStateCode == DomainVerificationState.STATE_RESTORED) { + oldStateMap.put(domain, DomainVerificationState.STATE_RESTORED); + } + } + } + + SparseArray oldSelectionStates = + oldState.getUserSelectionStates(); + + SparseArray newSelectionStates = + newState.getUserSelectionStates(); + + DomainVerificationUserState newUserState = newSelectionStates.get(UserHandle.USER_SYSTEM); + if (newUserState != null) { + ArraySet newEnabledHosts = newUserState.getEnabledHosts(); + DomainVerificationUserState oldUserState = + oldSelectionStates.get(UserHandle.USER_SYSTEM); + + boolean disallowLinkHandling = newUserState.isDisallowLinkHandling(); + if (oldUserState == null) { + oldUserState = new DomainVerificationUserState(UserHandle.USER_SYSTEM, + newEnabledHosts, disallowLinkHandling); + oldSelectionStates.put(UserHandle.USER_SYSTEM, oldUserState); + } else { + oldUserState.addHosts(newEnabledHosts) + .setDisallowLinkHandling(disallowLinkHandling); + } + } + } + + public void removeUser(@UserIdInt int userId) { + int pendingSize = mPendingPkgStates.size(); + for (int index = 0; index < pendingSize; index++) { + mPendingPkgStates.valueAt(index).removeUser(userId); + } + + // TODO(b/170746586): Restored assumes user IDs match, which is probably not the case + // on a new device + int restoredSize = mRestoredPkgStates.size(); + for (int index = 0; index < restoredSize; index++) { + mRestoredPkgStates.valueAt(index).removeUser(userId); + } + } +} diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt index 21c863dde3f68..6c5c1d4b59ebf 100644 --- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt +++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt @@ -54,6 +54,7 @@ import org.mockito.Mockito.same import org.mockito.Mockito.verify import org.testng.Assert.assertThrows import java.io.File +import java.util.UUID @RunWith(Parameterized::class) class PackageManagerComponentLabelIconOverrideTest { @@ -262,8 +263,13 @@ class PackageManagerComponentLabelIconOverrideTest { .apply(block) .hideAsFinal() - private fun makePkgSetting(pkgName: String) = spy(PackageSetting(pkgName, null, File("/test"), - null, null, null, null, 0, 0, 0, 0, null, null, null)) { + private fun makePkgSetting(pkgName: String) = spy( + PackageSetting( + pkgName, null, File("/test"), + null, null, null, null, 0, 0, 0, 0, null, null, null, + UUID.fromString("3f9d52b7-d7b4-406a-a1da-d9f19984c72c") + ) + ) { this.pkgState.isUpdatedSystemApp = params.isUpdatedSystemApp } diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt index e304725e3d51f..c78b24a153bf7 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt @@ -144,7 +144,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { } whenever(mocks.settings.addPackageLPw(nullable(), nullable(), nullable(), nullable(), nullable(), nullable(), nullable(), nullable(), nullable(), nullable(), nullable(), - nullable(), nullable(), nullable())) { + nullable(), nullable(), nullable(), nullable())) { val name: String = getArgument(0) val pendingAdd = mPendingPackageAdds.firstOrNull { it.first == name } ?: return@whenever null diff --git a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java index 90edaef4294fc..709b009c2feb8 100644 --- a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java @@ -37,9 +37,10 @@ public class KeySetManagerServiceTest extends AndroidTestCase { private KeySetManagerService mKsms; public PackageSetting generateFakePackageSetting(String name) { - return new PackageSetting(name, name, new File(mContext.getCacheDir(), "fakeCodePath"), - "", "", "", "", 1, 0, 0, 0 /*sharedUserId*/, null /*usesStaticLibraries*/, - null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/); + return new PackageSettingBuilder() + .setName(name) + .setCodePath(new File(mContext.getCacheDir(), "fakeCodePath").getAbsolutePath()) + .build(); } @Override diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java index 4ce1bbc0d017d..558fb309ad987 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java @@ -107,10 +107,15 @@ public class PackageManagerServiceTest { // Create a real (non-null) PackageSetting and confirm that the removed // users are copied properly - setting = new PackageSetting("name", "realName", new File("codePath"), - "legacyNativeLibraryPathString", "primaryCpuAbiString", "secondaryCpuAbiString", - "cpuAbiOverrideString", 0, 0, 0, 0, - null, null, null); + setting = new PackageSettingBuilder() + .setName("name") + .setRealName("realName") + .setCodePath("codePath") + .setLegacyNativeLibraryPathString("legacyNativeLibraryPathString") + .setPrimaryCpuAbiString("primaryCpuAbiString") + .setSecondaryCpuAbiString("secondaryCpuAbiString") + .setCpuAbiOverrideString("cpuAbiOverrideString") + .build(); pri.populateUsers(new int[] { 1, 2, 3, 4, 5 }, setting); 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 75c69872bb524..850a031d7ad6c 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -33,6 +33,7 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.Mockito.when; import android.annotation.NonNull; import android.app.PropertyInvalidatedCache; @@ -58,6 +59,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.permission.persistence.RuntimePermissionsPersistence; import com.android.server.LocalServices; +import com.android.server.pm.domain.verify.DomainVerificationManagerInternal; 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; @@ -80,6 +82,7 @@ import java.security.PublicKey; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.UUID; @RunWith(AndroidJUnit4.class) @SmallTest @@ -96,10 +99,14 @@ public class PackageManagerSettingsTests { LegacyPermissionDataProvider mPermissionDataProvider; @Mock IntentFilterVerificationManager mIntentFilterVerificationManager; + @Mock + DomainVerificationManagerInternal mDomainVerificationManager; @Before public void initializeMocks() { MockitoAnnotations.initMocks(this); + when(mDomainVerificationManager.generateNewId()) + .thenAnswer(invocation -> UUID.randomUUID()); } @Before @@ -558,7 +565,8 @@ public class PackageManagerSettingsTests { 0, null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, - null /*mimeGroups*/); + null /*mimeGroups*/, + UUID.randomUUID()); final PackageSetting testPkgSetting01 = new PackageSetting(origPkgSetting01); verifySettingCopy(origPkgSetting01, testPkgSetting01); } @@ -579,7 +587,8 @@ public class PackageManagerSettingsTests { 0, null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, - null /*mimeGroups*/); + null /*mimeGroups*/, + UUID.randomUUID()); final PackageSetting testPkgSetting01 = new PackageSetting( PACKAGE_NAME /*pkgName*/, REAL_PACKAGE_NAME /*realPkgName*/, @@ -594,7 +603,8 @@ public class PackageManagerSettingsTests { 0, null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, - null /*mimeGroups*/); + null /*mimeGroups*/, + UUID.randomUUID()); testPkgSetting01.copyFrom(origPkgSetting01); verifySettingCopy(origPkgSetting01, testPkgSetting01); } @@ -621,7 +631,8 @@ public class PackageManagerSettingsTests { UserManagerService.getInstance(), null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, - null /*mimeGroups*/); + null /*mimeGroups*/, + UUID.randomUUID()); assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a")); assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi")); assertThat(testPkgSetting01.pkgFlags, is(0)); @@ -654,7 +665,8 @@ public class PackageManagerSettingsTests { UserManagerService.getInstance(), null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, - null /*mimeGroups*/); + null /*mimeGroups*/, + UUID.randomUUID()); assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a")); assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi")); assertThat(testPkgSetting01.pkgFlags, is(ApplicationInfo.FLAG_SYSTEM)); @@ -690,7 +702,8 @@ public class PackageManagerSettingsTests { UserManagerService.getInstance(), null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, - null /*mimeGroups*/); + null /*mimeGroups*/, + UUID.randomUUID()); fail("Expected a PackageManagerException"); } catch (PackageManagerException expected) { } @@ -722,7 +735,8 @@ public class PackageManagerSettingsTests { UserManagerService.getInstance(), null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, - null /*mimeGroups*/); + null /*mimeGroups*/, + UUID.randomUUID()); assertThat(testPkgSetting01.getPath(), is(UPDATED_CODE_PATH)); assertThat(testPkgSetting01.name, is(PACKAGE_NAME)); assertThat(testPkgSetting01.pkgFlags, is(ApplicationInfo.FLAG_SYSTEM)); @@ -760,7 +774,8 @@ public class PackageManagerSettingsTests { UserManagerService.getInstance(), null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, - null /*mimeGroups*/); + null /*mimeGroups*/, + UUID.randomUUID()); assertThat(testPkgSetting01.appId, is(0)); assertThat(testPkgSetting01.getPath(), is(INITIAL_CODE_PATH)); assertThat(testPkgSetting01.name, is(PACKAGE_NAME)); @@ -801,7 +816,8 @@ public class PackageManagerSettingsTests { UserManagerService.getInstance(), null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, - null /*mimeGroups*/); + null /*mimeGroups*/, + UUID.randomUUID()); assertThat(testPkgSetting01.appId, is(10064)); assertThat(testPkgSetting01.getPath(), is(INITIAL_CODE_PATH)); assertThat(testPkgSetting01.name, is(PACKAGE_NAME)); @@ -842,7 +858,8 @@ public class PackageManagerSettingsTests { UserManagerService.getInstance(), null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, - null /*mimeGroups*/); + null /*mimeGroups*/, + UUID.randomUUID()); assertThat(testPkgSetting01.appId, is(10064)); assertThat(testPkgSetting01.getPath(), is(UPDATED_CODE_PATH)); assertThat(testPkgSetting01.name, is(PACKAGE_NAME)); @@ -901,6 +918,7 @@ public class PackageManagerSettingsTests { assertThat(origPkgSetting.getPathString(), is(testPkgSetting.getPathString())); assertSame(origPkgSetting.cpuAbiOverrideString, testPkgSetting.cpuAbiOverrideString); assertThat(origPkgSetting.cpuAbiOverrideString, is(testPkgSetting.cpuAbiOverrideString)); + assertThat(origPkgSetting.getDomainSetId(), is(testPkgSetting.getDomainSetId())); assertThat(origPkgSetting.firstInstallTime, is(testPkgSetting.firstInstallTime)); assertSame(origPkgSetting.installSource, testPkgSetting.installSource); assertThat(origPkgSetting.installPermissionsFixed, @@ -973,7 +991,8 @@ public class PackageManagerSettingsTests { sharedUserId, null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, - null /*mimeGroups*/); + null /*mimeGroups*/, + UUID.randomUUID()); } private PackageSetting createPackageSetting(String packageName) { @@ -991,7 +1010,8 @@ public class PackageManagerSettingsTests { 0, null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, - null /*mimeGroups*/); + null /*mimeGroups*/, + UUID.randomUUID()); } private @NonNull List createFakeUsers() { @@ -1182,7 +1202,7 @@ public class PackageManagerSettingsTests { private Settings makeSettings() { return new Settings(InstrumentationRegistry.getContext().getFilesDir(), mRuntimePermissionsPersistence, mPermissionDataProvider, - mIntentFilterVerificationManager, new Object()); + mIntentFilterVerificationManager, mDomainVerificationManager, new Object()); } private void verifyKeySetMetaData(Settings settings) diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java index 90c29824409f7..e6a238a775c63 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java @@ -546,12 +546,17 @@ public class PackageParserTest { } private static PackageSetting mockPkgSetting(AndroidPackage pkg) { - return new PackageSetting(pkg.getPackageName(), pkg.getRealPackage(), - new File(pkg.getPath()), null, pkg.getPrimaryCpuAbi(), pkg.getSecondaryCpuAbi(), - null, pkg.getVersionCode(), - PackageInfoUtils.appInfoFlags(pkg, null), - PackageInfoUtils.appInfoPrivateFlags(pkg, null), - pkg.getSharedUserLabel(), null, null, null); + return new PackageSettingBuilder() + .setName(pkg.getPackageName()) + .setRealName(pkg.getRealPackage()) + .setCodePath(pkg.getPath()) + .setPrimaryCpuAbiString(pkg.getPrimaryCpuAbi()) + .setSecondaryCpuAbiString(pkg.getSecondaryCpuAbi()) + .setPVersionCode(pkg.getLongVersionCode()) + .setPkgFlags(PackageInfoUtils.appInfoFlags(pkg, null)) + .setPrivateFlags(PackageInfoUtils.appInfoPrivateFlags(pkg, null)) + .setSharedUserId(pkg.getSharedUserLabel()) + .build(); } // NOTE: The equality assertions below are based on code autogenerated by IntelliJ. diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java index 84551c51052cf..f75751bf54ae0 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java @@ -22,10 +22,10 @@ import android.util.ArraySet; import android.util.SparseArray; import com.android.server.pm.parsing.pkg.AndroidPackage; -import com.android.server.pm.parsing.pkg.PackageImpl; import java.io.File; import java.util.Map; +import java.util.UUID; public class PackageSettingBuilder { private String mName; @@ -48,6 +48,7 @@ public class PackageSettingBuilder { private long[] mUsesStaticLibrariesVersions; private Map> mMimeGroups; private PackageParser.SigningDetails mSigningDetails; + private UUID mDomainSetId = UUID.randomUUID(); public PackageSettingBuilder setPackage(AndroidPackage pkg) { this.mPkg = pkg; @@ -163,12 +164,17 @@ public class PackageSettingBuilder { return this; } + public PackageSettingBuilder setDomainSetId(UUID domainSetId) { + mDomainSetId = domainSetId; + return this; + } + public PackageSetting build() { final PackageSetting packageSetting = new PackageSetting(mName, mRealName, new File(mCodePath), mLegacyNativeLibraryPathString, mPrimaryCpuAbiString, mSecondaryCpuAbiString, mCpuAbiOverrideString, mPVersionCode, mPkgFlags, mPrivateFlags, mSharedUserId, mUsesStaticLibraries, mUsesStaticLibrariesVersions, - mMimeGroups); + mMimeGroups, mDomainSetId); packageSetting.signatures = mSigningDetails != null ? new PackageSignatures(mSigningDetails) : new PackageSignatures(); diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java index 90658055ad6fb..27f3eec655e3b 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java @@ -465,10 +465,11 @@ public class PackageSignaturesTest { private static PackageSetting createPackageSetting() { // Generic PackageSetting object with values from a test app installed on a device to be // used to test the methods under the PackageSignatures signatures data member. - File appPath = new File("/data/app/app"); - PackageSetting result = new PackageSetting("test.app", null, appPath, - "/data/app/app", null, null, null, 1, 940097092, 0, 0 /*userId*/, null, null, - null /*mimeGroups*/); - return result; + return new PackageSettingBuilder() + .setName("test.app") + .setCodePath("/data/app/app") + .setPVersionCode(1) + .setPkgFlags(940097092) + .build(); } } diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java index d8c3979c9cf9e..70abf820b594e 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java @@ -50,6 +50,7 @@ import android.platform.test.annotations.Presubmit; import android.util.Pair; import com.android.server.compat.PlatformCompat; +import com.android.server.pm.domain.verify.DomainVerificationManagerInternal; import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.PackageImpl; @@ -91,6 +92,14 @@ public class ScanTests { when(mMockInjector.getAbiHelper()).thenReturn(mMockPackageAbiHelper); when(mMockInjector.getUserManagerInternal()).thenReturn(mMockUserManager); when(mMockInjector.getCompatibility()).thenReturn(mMockCompatibility); + + DomainVerificationManagerInternal domainVerificationManager = + mock(DomainVerificationManagerInternal.class); + when(domainVerificationManager.generateNewId()) + .thenAnswer(invocation -> UUID.randomUUID()); + + when(mMockInjector.getDomainVerificationManagerInternal()) + .thenReturn(domainVerificationManager); } @Before From 96694aa7bd2821e89fef2aba789cf2a88e212ef0 Mon Sep 17 00:00:00 2001 From: Winson Date: Wed, 16 Dec 2020 17:14:00 -0800 Subject: [PATCH 11/23] Attach domain verification states during package scan/install Handles adding a new package (either from a brand new install or a boot scan) and migrating between packages when installing an update. Will merge the package states in the update case to preserve successful verifications while removing failed domains so that they can be re-requested by the verification agent. Exempt-From-Owner-Approval: Already approved by owners on main branch Bug: 163565712 Test: manual, device boots, will be tested as part of later changes Change-Id: I54dc8415e10544e618905e269ab95ec0a2af0fb0 --- .../server/pm/PackageManagerService.java | 6 + .../android/server/pm/PackageSettingBase.java | 15 +- .../DomainVerificationManagerInternal.java | 39 +++ .../verify/DomainVerificationService.java | 230 +++++++++++++++++- .../verify/DomainVerificationSettings.java | 15 ++ .../java/com/android/server/SystemServer.java | 3 +- 6 files changed, 303 insertions(+), 5 deletions(-) diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index ba30247744a35..3d35f10b3c490 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -14576,6 +14576,12 @@ public class PackageManagerService extends IPackageManager.Stub mAppsFilter.addPackage(pkgSetting, isReplace); mPackageProperty.addAllProperties(pkg); + if (oldPkgSetting == null || oldPkgSetting.getPkg() == null) { + mDomainVerificationManager.addPackage(pkgSetting); + } else { + mDomainVerificationManager.migrateState(oldPkgSetting, pkgSetting); + } + int collectionSize = ArrayUtils.size(pkg.getInstrumentations()); StringBuilder r = null; int i; diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index d123c7770cfe2..b69d2b015d6cd 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -42,6 +42,8 @@ import android.util.SparseArray; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.pm.domain.verify.DomainVerificationManagerInternal; +import com.android.server.pm.domain.verify.DomainVerificationService; import com.android.server.pm.parsing.pkg.AndroidPackage; import java.io.File; @@ -350,9 +352,18 @@ public abstract class PackageSettingBase extends SettingBase { return readUserState(userId).getSharedLibraryOverlayPaths(); } - /** Only use for testing. Do NOT use in production code. */ + /** + * Only use for testing. Do NOT use in production code. + * + * Unless you're {@link DomainVerificationService} and you need to migrate legacy state. + * This is done rather than passing in the user IDs to + * {@link DomainVerificationManagerInternal#addPackage(PackageSetting)} to make the v2 APIs + * completely correct, without legacy details, since that method inherently does not care about + * the users on the device. + */ @VisibleForTesting - SparseArray getUserState() { + @Deprecated + public SparseArray getUserState() { return mUserState; } diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java index d83b49dee11eb..7e41eccc4f4ad 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java @@ -23,6 +23,7 @@ import android.content.pm.domain.verify.DomainVerificationSet; import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; +import com.android.server.pm.PackageSetting; import com.android.server.pm.domain.verify.models.DomainVerificationPkgState; import org.xmlpull.v1.XmlPullParserException; @@ -40,6 +41,44 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan @NonNull UUID generateNewId(); + /** + * Restores or creates internal state for the new package. This can either be from scanning a + * package at boot, or a truly new installation on the device. It is expected that the {@link + * PackageSetting#getDomainSetId()} already be set to the correct value. + *

+ * If this is from scan, there should be a pending state that was previous read using {@link + * #readSettings(TypedXmlPullParser)}, which will be attached as-is to the package. In this + * case, a broadcast will not be sent to the domain verification agent on device, as it is + * assumed nothing has changed since the device rebooted. + *

+ * If this is a new install, state will be restored from a previous call to {@link + * #restoreSettings(TypedXmlPullParser)}, or a new one will be generated. In either case, a + * broadcast will be sent to the domain verification agent so it may re-run any verification + * logic for the newly associated domains. + *

+ * This will mutate internal {@link DomainVerificationPkgState} and so will hold the internal + * lock. This should never be called from within the domain verification classes themselves. + *

+ * This will NOT call {@link #writeSettings(TypedXmlSerializer)}. That must be handled by the + * caller. + */ + void addPackage(@NonNull PackageSetting newPkgSetting); + + /** + * Migrates verification state from a previous install to a new one. It is expected that the + * {@link PackageSetting#getDomainSetId()} already be set to the correct value, usually from + * {@link #generateNewId()}. This will preserve {@link #STATE_SUCCESS} domains under the + * assumption that the new package will pass the same server side config as the previous + * package, as they have matching signatures. + *

+ * This will mutate internal {@link DomainVerificationPkgState} and so will hold the internal + * lock. This should never be called from within the domain verification classes themselves. + *

+ * This will NOT call {@link #writeSettings(TypedXmlSerializer)}. That must be handled by the + * caller. + */ + void migrateState(@NonNull PackageSetting oldPkgSetting, @NonNull PackageSetting newPkgSetting); + /** * Serializes the entire internal state. This is equivalent to a full backup of the existing * verification state. diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java index 4d4a8484c440a..2494d715f821e 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java @@ -20,18 +20,32 @@ 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.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.PackageUserState; +import android.content.pm.domain.verify.DomainVerificationManager; import android.content.pm.domain.verify.DomainVerificationSet; +import android.content.pm.domain.verify.DomainVerificationState; import android.content.pm.domain.verify.DomainVerificationUserSelection; import android.content.pm.domain.verify.IDomainVerificationManager; +import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Singleton; +import android.util.Slog; +import android.util.SparseArray; import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; import com.android.internal.annotations.GuardedBy; +import com.android.server.SystemConfig; import com.android.server.SystemService; +import com.android.server.compat.PlatformCompat; +import com.android.server.pm.PackageSetting; import com.android.server.pm.domain.verify.models.DomainVerificationPkgState; import com.android.server.pm.domain.verify.models.DomainVerificationStateMap; +import com.android.server.pm.domain.verify.models.DomainVerificationUserState; +import com.android.server.pm.parsing.pkg.AndroidPackage; import org.xmlpull.v1.XmlPullParserException; @@ -63,17 +77,25 @@ public class DomainVerificationService extends SystemService @NonNull private final Singleton mConnection; + @NonNull + private final SystemConfig mSystemConfig; + @NonNull private final DomainVerificationSettings mSettings; + @NonNull + private final DomainVerificationCollector mCollector; + @NonNull private final IDomainVerificationManager.Stub mStub = new DomainVerificationManagerStub(this); - public DomainVerificationService(@NonNull Context context, - @NonNull Singleton connection) { + public DomainVerificationService(@NonNull Context context, @NonNull SystemConfig systemConfig, + @NonNull PlatformCompat platformCompat, @NonNull Singleton connection) { super(context); mConnection = connection; + mSystemConfig = systemConfig; mSettings = new DomainVerificationSettings(); + mCollector = new DomainVerificationCollector(platformCompat, systemConfig); } @Override @@ -149,6 +171,206 @@ public class DomainVerificationService extends SystemService return UUID.randomUUID(); } + @Override + public void migrateState(@NonNull PackageSetting oldPkgSetting, + @NonNull PackageSetting newPkgSetting) { + String pkgName = newPkgSetting.name; + boolean sendBroadcast; + + synchronized (mLock) { + UUID oldDomainSetId = oldPkgSetting.getDomainSetId(); + UUID newDomainSetId = newPkgSetting.getDomainSetId(); + DomainVerificationPkgState oldPkgState = mAttachedPkgStates.remove(oldDomainSetId); + + AndroidPackage oldPkg = oldPkgSetting.getPkg(); + AndroidPackage newPkg = newPkgSetting.getPkg(); + + ArrayMap newStateMap = new ArrayMap<>(); + SparseArray newUserStates = new SparseArray<>(); + + if (oldPkgState == null || oldPkg == null || newPkg == null) { + // Should be impossible, but to be safe, continue with a new blank state instead + Slog.wtf(TAG, "Invalid state nullability old state = " + oldPkgState + + ", old pkgSetting = " + oldPkgSetting + + ", new pkgSetting = " + newPkgSetting + + ", old pkg = " + oldPkg + + ", new pkg = " + newPkg, new Exception()); + + DomainVerificationPkgState newPkgState = new DomainVerificationPkgState( + pkgName, newDomainSetId, true, newStateMap, newUserStates); + mAttachedPkgStates.put(pkgName, newDomainSetId, newPkgState); + return; + } + + ArrayMap oldStateMap = oldPkgState.getStateMap(); + ArraySet newAutoVerifyDomains = mCollector.collectAutoVerifyDomains(newPkg); + int newDomainsSize = newAutoVerifyDomains.size(); + + for (int newDomainsIndex = 0; newDomainsIndex < newDomainsSize; newDomainsIndex++) { + String domain = newAutoVerifyDomains.valueAt(newDomainsIndex); + Integer oldStateInteger = oldStateMap.get(domain); + if (oldStateInteger != null) { + int oldState = oldStateInteger; + switch (oldState) { + case DomainVerificationState.STATE_SUCCESS: + case DomainVerificationState.STATE_RESTORED: + case DomainVerificationState.STATE_MIGRATED: + newStateMap.put(domain, oldState); + break; + default: + // In all other cases, the state code is left unset + // (STATE_NO_RESPONSE) to signal to the verification agent that any + // existing error has been cleared and the domain should be + // re-attempted. This makes update of a package a signal to + // re-verify. + break; + } + } + } + + SparseArray oldUserStates = + oldPkgState.getUserSelectionStates(); + int oldUserStatesSize = oldUserStates.size(); + if (oldUserStatesSize > 0) { + ArraySet newWebDomains = mCollector.collectAutoVerifyDomains(newPkg); + for (int oldUserStatesIndex = 0; oldUserStatesIndex < oldUserStatesSize; + oldUserStatesIndex++) { + int userId = oldUserStates.keyAt(oldUserStatesIndex); + DomainVerificationUserState oldUserState = oldUserStates.valueAt( + oldUserStatesIndex); + ArraySet oldEnabledHosts = oldUserState.getEnabledHosts(); + ArraySet newEnabledHosts = new ArraySet<>(oldEnabledHosts); + newEnabledHosts.retainAll(newWebDomains); + DomainVerificationUserState newUserState = new DomainVerificationUserState( + userId, newEnabledHosts, oldUserState.isDisallowLinkHandling()); + newUserStates.put(userId, newUserState); + } + } + + boolean hasAutoVerifyDomains = newDomainsSize > 0; + boolean stateApplied = applyImmutableState(pkgName, newStateMap, newAutoVerifyDomains); + + // TODO(b/159952358): sendBroadcast should be abstracted so it doesn't have to be aware + // of whether/what state was applied. Probably some method which iterates the map to + // check for any domains that actually have state changeable by the domain verification + // agent. + sendBroadcast = hasAutoVerifyDomains && !stateApplied; + + mAttachedPkgStates.put(pkgName, newDomainSetId, new DomainVerificationPkgState( + pkgName, newDomainSetId, hasAutoVerifyDomains, newStateMap, newUserStates)); + } + + if (sendBroadcast) { + sendBroadcastForPackage(pkgName); + } + } + + // TODO(b/159952358): Handle valid domainSetIds for PackageSettings with no AndroidPackage + @Override + public void addPackage(@NonNull PackageSetting newPkgSetting) { + // TODO(b/159952358): Optimize packages without any domains. Those wouldn't have to be in + // the state map, but it would require handling the "migration" case where an app either + // gains or loses all domains. + + UUID domainSetId = newPkgSetting.getDomainSetId(); + String pkgName = newPkgSetting.name; + + boolean sendBroadcast = true; + + DomainVerificationPkgState pkgState; + pkgState = mSettings.getPendingState(pkgName); + if (pkgState != null) { + // Don't send when attaching from pending read, which is usually boot scan. Re-send on + // boot is handled in a separate method once all packages are added. + sendBroadcast = false; + } else { + pkgState = mSettings.getRestoredState(pkgName); + } + + AndroidPackage pkg = newPkgSetting.getPkg(); + ArraySet domains = mCollector.collectAutoVerifyDomains(pkg); + boolean hasAutoVerifyDomains = !domains.isEmpty(); + boolean isPendingOrRestored = pkgState != null; + if (isPendingOrRestored) { + pkgState.setId(domainSetId); + } else { + pkgState = new DomainVerificationPkgState(pkgName, domainSetId, hasAutoVerifyDomains); + } + + boolean stateApplied = applyImmutableState(pkgState, domains); + if (!stateApplied && !isPendingOrRestored) { + // TODO(b/159952358): Test this behavior + // Attempt to preserve user experience by automatically verifying all domains from + // legacy state if they were previously approved, or by automatically enabling all + // hosts through user selection if legacy state indicates a user previously made the + // choice in settings to allow supported links. The domain verification agent should + // re-verify these links (set to STATE_MIGRATED) at the next possible opportunity, + // and disable them if appropriate. + ArraySet webDomains = null; + + @SuppressWarnings("deprecation") + SparseArray userState = newPkgSetting.getUserState(); + int userStateSize = userState.size(); + for (int index = 0; index < userStateSize; index++) { + int userId = userState.keyAt(index); + int legacyStatus = userState.valueAt(index).domainVerificationStatus; + if (legacyStatus + == PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) { + if (webDomains == null) { + webDomains = mCollector.collectAllWebDomains(pkg); + } + + pkgState.getOrCreateUserSelectionState(userId).addHosts(webDomains); + } + } + + IntentFilterVerificationInfo legacyInfo = + newPkgSetting.getIntentFilterVerificationInfo(); + if (legacyInfo != null + && legacyInfo.getStatus() + == PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) { + ArrayMap stateMap = pkgState.getStateMap(); + int domainsSize = domains.size(); + for (int index = 0; index < domainsSize; index++) { + stateMap.put(domains.valueAt(index), DomainVerificationState.STATE_MIGRATED); + } + } + } + + synchronized (mLock) { + mAttachedPkgStates.put(pkgName, domainSetId, pkgState); + } + + if (sendBroadcast && hasAutoVerifyDomains) { + sendBroadcastForPackage(pkgName); + } + } + + private boolean applyImmutableState(@NonNull DomainVerificationPkgState pkgState, + @NonNull ArraySet autoVerifyDomains) { + return applyImmutableState(pkgState.getPackageName(), pkgState.getStateMap(), + autoVerifyDomains); + } + + /** + * Applies any immutable state as the final step when adding or migrating state. Currently only + * applies {@link SystemConfig#getLinkedApps()}, which approves all domains for a package. + */ + private boolean applyImmutableState(@NonNull String packageName, + @NonNull ArrayMap stateMap, + @NonNull ArraySet autoVerifyDomains) { + if (mSystemConfig.getLinkedApps().contains(packageName)) { + int domainsSize = autoVerifyDomains.size(); + for (int index = 0; index < domainsSize; index++) { + stateMap.put(autoVerifyDomains.valueAt(index), + DomainVerificationState.STATE_APPROVED); + } + return true; + } + + return false; + } + @Override public void writeSettings(@NonNull TypedXmlSerializer serializer) throws IOException { synchronized (mLock) { @@ -195,6 +417,10 @@ public class DomainVerificationService extends SystemService mConnection.get().scheduleWriteSettings(); } + private void sendBroadcastForPackage(@NonNull String packageName) { + // TODO(b/159952358): Implement proxy + } + public interface Connection { /** diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationSettings.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationSettings.java index 8de2ae614fe68..185fd62411570 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationSettings.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationSettings.java @@ -17,6 +17,7 @@ package com.android.server.pm.domain.verify; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UserIdInt; import android.content.pm.domain.verify.DomainVerificationState; import android.os.UserHandle; @@ -253,4 +254,18 @@ class DomainVerificationSettings { mRestoredPkgStates.valueAt(index).removeUser(userId); } } + + @Nullable + public DomainVerificationPkgState getPendingState(@NonNull String pkgName) { + synchronized (mLock) { + return mPendingPkgStates.get(pkgName); + } + } + + @Nullable + public DomainVerificationPkgState getRestoredState(@NonNull String pkgName) { + synchronized (mLock) { + return mRestoredPkgStates.get(pkgName); + } + } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index b39a9a8d2297c..b09d8a67577fb 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -1064,7 +1064,8 @@ public final class SystemServer implements Dumpable { t.traceBegin("StartDomainVerificationService"); DomainVerificationService domainVerificationService = new DomainVerificationService( - mSystemContext, new Singleton() { + mSystemContext, SystemConfig.getInstance(), platformCompat, + new Singleton() { @Override protected DomainVerificationService.Connection create() { // Deferred retrieval from PackageManagerService, since PMS is initialized after From 5429dfc35efa75c80dc958a1f179378faed26066 Mon Sep 17 00:00:00 2001 From: Winson Date: Wed, 16 Dec 2020 18:01:12 -0800 Subject: [PATCH 12/23] Add DomainVerificationProxyV2 for new APIs Implements the request broadcast to the new domain verification agent. The DomainVerificationService delegates through the PackageManagerService Handler, to mirror what happened with v1. A message code is reserved for the domain verification feature and all feature specific messages are wrapped in that code to isolate them from other PackageManagerService messages. Prepares interface for backporting the broadcast to the v1 broadcast, but that will be done in a future change. Exempt-From-Owner-Approval: Already approved by owners on main branch Bug: 163565712 Test: TBD in later change when proxies are combined Change-Id: If77f0c7facebedadfd00b82120949ed4720e9b58 --- .../server/pm/PackageManagerService.java | 83 +++++++++++++++- .../DomainVerificationManagerInternal.java | 13 +++ .../DomainVerificationMessageCodes.java | 34 +++++++ .../verify/DomainVerificationService.java | 25 ++++- .../verify/proxy/DomainVerificationProxy.java | 66 +++++++++++++ .../DomainVerificationProxyUnavailable.java | 21 ++++ .../proxy/DomainVerificationProxyV2.java | 96 +++++++++++++++++++ 7 files changed, 334 insertions(+), 4 deletions(-) create mode 100644 services/core/java/com/android/server/pm/domain/verify/DomainVerificationMessageCodes.java create mode 100644 services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxy.java create mode 100644 services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyUnavailable.java create mode 100644 services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV2.java diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 3d35f10b3c490..6a1f5013cb943 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -379,6 +379,8 @@ import com.android.server.pm.dex.PackageDexUsage; import com.android.server.pm.dex.ViewCompiler; import com.android.server.pm.domain.verify.DomainVerificationManagerInternal; import com.android.server.pm.domain.verify.DomainVerificationService; +import com.android.server.pm.domain.verify.proxy.DomainVerificationProxy; +import com.android.server.pm.domain.verify.proxy.DomainVerificationProxyV2; 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; @@ -1606,6 +1608,7 @@ public class PackageManagerService extends IPackageManager.Stub static final int DEFERRED_NO_KILL_INSTALL_OBSERVER = 24; static final int INTEGRITY_VERIFICATION_COMPLETE = 25; static final int CHECK_PENDING_INTEGRITY_VERIFICATION = 26; + static final int DOMAIN_VERIFICATION = 27; static final int DEFERRED_NO_KILL_POST_DELETE_DELAY_MS = 3 * 1000; static final int DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS = 500; @@ -1821,7 +1824,7 @@ public class PackageManagerService extends IPackageManager.Stub new DomainVerificationConnection(); private class DomainVerificationConnection implements - DomainVerificationService.Connection { + DomainVerificationService.Connection, DomainVerificationProxy.Connection { @Override public void scheduleWriteSettings() { @@ -1829,6 +1832,30 @@ public class PackageManagerService extends IPackageManager.Stub PackageManagerService.this.scheduleWriteSettingsLocked(); } } + + @Override + public void schedule(int code, @Nullable Object object) { + Message message = mHandler.obtainMessage(DOMAIN_VERIFICATION); + message.arg1 = code; + message.obj = object; + mHandler.sendMessage(message); + } + + @Override + public long getPowerSaveTempWhitelistAppDuration() { + return PackageManagerService.this.getVerificationTimeout(); + } + + @Override + public DeviceIdleInternal getDeviceIdleInternal() { + return mInjector.getLocalService(DeviceIdleInternal.class); + } + + @Override + public boolean isCallerPackage(int callingUid, @NonNull String packageName) { + final int callingUserId = UserHandle.getUserId(callingUid); + return callingUid == getPackageUid(packageName, 0, callingUserId); + } } /** @@ -5301,6 +5328,12 @@ public class PackageManagerService extends IPackageManager.Stub } break; } + case DOMAIN_VERIFICATION: { + int messageCode = msg.arg1; + Object object = msg.obj; + mDomainVerificationManager.runMessage(messageCode, object); + break; + } } } } @@ -6946,8 +6979,18 @@ public class PackageManagerService extends IPackageManager.Stub mRequiredVerifierPackage = getRequiredButNotReallyRequiredVerifierLPr(); mRequiredInstallerPackage = getRequiredInstallerLPr(); mRequiredUninstallerPackage = getRequiredUninstallerLPr(); - mIntentFilterVerificationManager.setVerifierComponent( - getIntentFilterVerifierComponentNameLPr()); + ComponentName domainVerificationAgent = + getDomainVerificationAgentComponentNameLPr(); + if (domainVerificationAgent != null) { + mDomainVerificationManager.setProxy( + new DomainVerificationProxyV2(mContext, mDomainVerificationConnection, + domainVerificationAgent)); + } else { + // TODO(b/159952358): DomainVerificationProxyV1 + mIntentFilterVerificationManager.setVerifierComponent( + getIntentFilterVerifierComponentNameLPr()); + } + mServicesExtensionPackageName = getRequiredServicesExtensionPackageLPr(); mSharedSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr( PackageManager.SYSTEM_SHARED_LIBRARY_SHARED, @@ -7490,6 +7533,40 @@ public class PackageManagerService extends IPackageManager.Stub return null; } + @Nullable + private ComponentName getDomainVerificationAgentComponentNameLPr() { + Intent intent = new Intent(Intent.ACTION_DOMAINS_NEED_VERIFICATION); + List matches = queryIntentReceiversInternal(intent, null, + MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, + UserHandle.USER_SYSTEM, false /*allowDynamicSplits*/); + ResolveInfo best = null; + final int N = matches.size(); + for (int i = 0; i < N; i++) { + final ResolveInfo cur = matches.get(i); + final String packageName = cur.getComponentInfo().packageName; + if (checkPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, + packageName, UserHandle.USER_SYSTEM) != PackageManager.PERMISSION_GRANTED) { + Slog.w(TAG, "Domain verification agent found but does not hold permission: " + + packageName); + continue; + } + + if (best == null || cur.priority > best.priority) { + if (cur.getComponentInfo().enabled) { + best = cur; + } else { + Slog.w(TAG, "Domain verification agent found but not enabled"); + } + } + } + + if (best != null) { + return best.getComponentInfo().getComponentName(); + } + Slog.w(TAG, "Domain verification agent not found"); + return null; + } + @Override public @Nullable ComponentName getInstantAppResolverComponent() { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java index 7e41eccc4f4ad..ee895d048e672 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java @@ -25,6 +25,7 @@ import android.util.TypedXmlSerializer; import com.android.server.pm.PackageSetting; import com.android.server.pm.domain.verify.models.DomainVerificationPkgState; +import com.android.server.pm.domain.verify.proxy.DomainVerificationProxy; import org.xmlpull.v1.XmlPullParserException; @@ -41,6 +42,18 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan @NonNull UUID generateNewId(); + /** + * Update the proxy implementation that talks to the domain verification agent on device. The + * default proxy is a stub that does nothing, and broadcast functionality will only work once a + * real implementation is attached. + */ + void setProxy(@NonNull DomainVerificationProxy proxy); + + /** + * @see DomainVerificationProxy.Connection#runMessage(int, Object) + */ + boolean runMessage(int messageCode, Object object); + /** * Restores or creates internal state for the new package. This can either be from scanning a * package at boot, or a truly new installation on the device. It is expected that the {@link diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationMessageCodes.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationMessageCodes.java new file mode 100644 index 0000000000000..f4bf96bae59a9 --- /dev/null +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationMessageCodes.java @@ -0,0 +1,34 @@ +/* + * 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.domain.verify; + +import android.os.Handler; + +import com.android.server.pm.PackageManagerService; +import com.android.server.pm.domain.verify.proxy.DomainVerificationProxy; + +/** + * Codes that are sent through the {@link PackageManagerService} {@link Handler} and eventually + * delegated to {@link DomainVerificationService} and {@link DomainVerificationProxy}. + * + * These codes are wrapped and thus exclusive to the domain verification APIs. They do not have be + * distinct from any of the codes inside {@link PackageManagerService}. + */ +public final class DomainVerificationMessageCodes { + + public static final int SEND_REQUEST = 1; +} diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java index 2494d715f821e..eed4dda72b6ae 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java @@ -45,11 +45,14 @@ import com.android.server.pm.PackageSetting; import com.android.server.pm.domain.verify.models.DomainVerificationPkgState; import com.android.server.pm.domain.verify.models.DomainVerificationStateMap; import com.android.server.pm.domain.verify.models.DomainVerificationUserState; +import com.android.server.pm.domain.verify.proxy.DomainVerificationProxy; +import com.android.server.pm.domain.verify.proxy.DomainVerificationProxyUnavailable; import com.android.server.pm.parsing.pkg.AndroidPackage; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; +import java.util.Collections; import java.util.List; import java.util.Set; import java.util.UUID; @@ -89,6 +92,9 @@ public class DomainVerificationService extends SystemService @NonNull private final IDomainVerificationManager.Stub mStub = new DomainVerificationManagerStub(this); + @NonNull + private DomainVerificationProxy mProxy = new DomainVerificationProxyUnavailable(); + public DomainVerificationService(@NonNull Context context, @NonNull SystemConfig systemConfig, @NonNull PlatformCompat platformCompat, @NonNull Singleton connection) { super(context); @@ -103,6 +109,11 @@ public class DomainVerificationService extends SystemService publishBinderService(Context.DOMAIN_VERIFICATION_SERVICE, mStub); } + @Override + public void setProxy(@NonNull DomainVerificationProxy proxy) { + mProxy = proxy; + } + @NonNull @Override public List getValidVerificationPackageNames() { @@ -417,8 +428,17 @@ public class DomainVerificationService extends SystemService mConnection.get().scheduleWriteSettings(); } + @Override + public boolean runMessage(int messageCode, Object object) { + return mProxy.runMessage(messageCode, object); + } + private void sendBroadcastForPackage(@NonNull String packageName) { - // TODO(b/159952358): Implement proxy + mProxy.sendBroadcastForPackages(Collections.singleton(packageName)); + } + + private boolean hasRealVerifier() { + return !(mProxy instanceof DomainVerificationProxyUnavailable); } public interface Connection { @@ -428,5 +448,8 @@ public class DomainVerificationService extends SystemService * {@link #writeSettings(TypedXmlSerializer)} should be invoked by the parent. */ void scheduleWriteSettings(); + + /** @see DomainVerificationProxy.Connection#schedule(int, java.lang.Object) */ + void schedule(int code, @Nullable Object object); } } diff --git a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxy.java b/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxy.java new file mode 100644 index 0000000000000..18d18485768b3 --- /dev/null +++ b/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxy.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2021 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.domain.verify.proxy; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +import com.android.server.DeviceIdleInternal; +import com.android.server.pm.domain.verify.DomainVerificationMessageCodes; + +import java.util.Set; + +public interface DomainVerificationProxy { + + default void sendBroadcastForPackages(@NonNull Set packageNames) { + } + + /** + * Runs a message on the caller's Handler as a result of {@link Connection#schedule(int, + * Object)}. Abstracts the actual scheduling/running from the manager class. This is also + * necessary so that different what codes can be used depending on the verifier proxy on device, + * to allow backporting v1. The backport proxy may schedule more or less messages than the v2 + * proxy. + * + * @param messageCode One of the values in {@link DomainVerificationMessageCodes}. + * @param object Arbitrary object that was originally included. + */ + default boolean runMessage(int messageCode, Object object) { + return false; + } + + default boolean isCallerVerifier(int callingUid) { + return false; + } + + interface Connection { + + /** + * Schedule something to be run later. The implementation is left up to the caller. + * + * @param code One of the values in {@link DomainVerificationMessageCodes}. + * @param object Arbitrary object to include with the message. + */ + void schedule(int code, @Nullable Object object); + + long getPowerSaveTempWhitelistAppDuration(); + + DeviceIdleInternal getDeviceIdleInternal(); + + boolean isCallerPackage(int callingUid, @NonNull String packageName); + } +} diff --git a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyUnavailable.java b/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyUnavailable.java new file mode 100644 index 0000000000000..f376f48874fa6 --- /dev/null +++ b/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyUnavailable.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2021 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.domain.verify.proxy; + +/** Stub implementation for when the verification agent is unavailable */ +public class DomainVerificationProxyUnavailable implements DomainVerificationProxy { +} diff --git a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV2.java b/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV2.java new file mode 100644 index 0000000000000..44e64f349d475 --- /dev/null +++ b/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV2.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2021 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.domain.verify.proxy; + +import android.annotation.NonNull; +import android.app.BroadcastOptions; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.domain.verify.DomainVerificationManager; +import android.content.pm.domain.verify.DomainVerificationRequest; +import android.os.Process; +import android.os.UserHandle; +import android.util.Slog; + +import com.android.server.pm.domain.verify.DomainVerificationMessageCodes; + +import java.util.Set; + +public class DomainVerificationProxyV2 implements DomainVerificationProxy { + + private static final String TAG = "DomainVerificationProxyV2"; + + private static final boolean DEBUG_BROADCASTS = true; + + @NonNull + private final Context mContext; + + @NonNull + private final Connection mConnection; + + @NonNull + private final ComponentName mVerifierComponent; + + public DomainVerificationProxyV2(@NonNull Context context, @NonNull Connection connection, + @NonNull ComponentName verifierComponent) { + mContext = context; + mConnection = connection; + mVerifierComponent = verifierComponent; + } + + @Override + public void sendBroadcastForPackages(@NonNull Set packageNames) { + mConnection.schedule(DomainVerificationMessageCodes.SEND_REQUEST, packageNames); + } + + @Override + public boolean runMessage(int messageCode, Object object) { + switch (messageCode) { + case DomainVerificationMessageCodes.SEND_REQUEST: + @SuppressWarnings("unchecked") Set packageNames = (Set) object; + DomainVerificationRequest request = new DomainVerificationRequest(packageNames); + + final long allowListTimeout = mConnection.getPowerSaveTempWhitelistAppDuration(); + final BroadcastOptions options = BroadcastOptions.makeBasic(); + options.setTemporaryAppWhitelistDuration(allowListTimeout); + + mConnection.getDeviceIdleInternal().addPowerSaveTempWhitelistApp(Process.myUid(), + mVerifierComponent.getPackageName(), allowListTimeout, + UserHandle.USER_SYSTEM, true, "domain verification agent"); + + Intent intent = new Intent(Intent.ACTION_DOMAINS_NEED_VERIFICATION) + .setComponent(mVerifierComponent) + .putExtra(DomainVerificationManager.EXTRA_VERIFICATION_REQUEST, request) + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + + if (DEBUG_BROADCASTS) { + Slog.d(TAG, "Requesting domain verification for " + packageNames); + } + + mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM, null, options.toBundle()); + return true; + default: + return false; + } + } + + @Override + public boolean isCallerVerifier(int callingUid) { + return mConnection.isCallerPackage(callingUid, mVerifierComponent.getPackageName()); + } +} From ef0cac74dbf342989ff0bed508cfe4821868b196 Mon Sep 17 00:00:00 2001 From: Winson Date: Wed, 16 Dec 2020 18:20:00 -0800 Subject: [PATCH 13/23] Add DomainVerificationEnforcer Abstracts the permission/caller enforcement code from DomainVerificationService. There's 3 types of callers the service cares about: 1. Able to change internal state, which for now is only system and shell - Not currently used, but will be for debug/shell commands 2. Approved verifier, which is system, shell, or the verification agent 3. Approved user state selector, which requires the permission Exempt-From-Owner-Approval: Already approved by owners on main branch Bug: 163565712 Test: atest DomainVerificationEnforcerTest Change-Id: I7c00e4ebd843537e0b20e60c82a1c4abf2904596 --- .../server/pm/PackageManagerService.java | 23 + .../verify/DomainVerificationEnforcer.java | 121 +++++ .../verify/DomainVerificationService.java | 63 ++- .../unit/Android.bp | 1 + .../verify/DomainVerificationEnforcerTest.kt | 446 ++++++++++++++++++ 5 files changed, 648 insertions(+), 6 deletions(-) create mode 100644 services/core/java/com/android/server/pm/domain/verify/DomainVerificationEnforcer.java create mode 100644 services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationEnforcerTest.kt diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 6a1f5013cb943..92016c6d1d0e0 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1833,6 +1833,17 @@ public class PackageManagerService extends IPackageManager.Stub } } + @Override + public int getCallingUid() { + return Binder.getCallingUid(); + } + + @UserIdInt + @Override + public int getCallingUserId() { + return UserHandle.getCallingUserId(); + } + @Override public void schedule(int code, @Nullable Object object) { Message message = mHandler.obtainMessage(DOMAIN_VERIFICATION); @@ -1856,6 +1867,18 @@ public class PackageManagerService extends IPackageManager.Stub final int callingUserId = UserHandle.getUserId(callingUid); return callingUid == getPackageUid(packageName, 0, callingUserId); } + + @Nullable + @Override + public PackageSetting getPackageSettingLocked(@NonNull String pkgName) { + return PackageManagerService.this.getPackageSetting(pkgName); + } + + @Nullable + @Override + public AndroidPackage getPackageLocked(@NonNull String pkgName) { + return PackageManagerService.this.getPackage(pkgName); + } } /** diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationEnforcer.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationEnforcer.java new file mode 100644 index 0000000000000..05b1c47f8b277 --- /dev/null +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationEnforcer.java @@ -0,0 +1,121 @@ +/* + * 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.domain.verify; + +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.UserIdInt; +import android.content.Context; +import android.os.Binder; +import android.os.Process; + +import com.android.server.pm.domain.verify.proxy.DomainVerificationProxy; + +public class DomainVerificationEnforcer { + + @NonNull + private final Context mContext; + + public DomainVerificationEnforcer(@NonNull Context context) { + mContext = context; + } + + /** + * Enforced when mutating any state from shell or internally in the system process. + */ + public void assertInternal(int callingUid) { + switch (callingUid) { + case Process.ROOT_UID: + case Process.SHELL_UID: + case Process.SYSTEM_UID: + break; + default: + throw new SecurityException( + "Caller " + callingUid + " is not allowed to change internal state"); + } + } + + /** + * Enforced when retrieving state for a package. The system, the verifier, and anyone approved + * to mutate user selections are allowed through. + */ + public void assertApprovedQuerent(int callingUid, @NonNull DomainVerificationProxy proxy) { + switch (callingUid) { + case Process.ROOT_UID: + case Process.SHELL_UID: + case Process.SYSTEM_UID: + break; + default: + if (!proxy.isCallerVerifier(callingUid)) { + mContext.enforcePermission( + android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION, + Binder.getCallingPid(), callingUid, + "Caller " + callingUid + + " is not allowed to query domain verification state"); + } + break; + } + } + + /** + * Enforced when mutating domain verification state inside an exposed API method. + */ + public void assertApprovedVerifier(int callingUid, @NonNull DomainVerificationProxy proxy) + throws SecurityException { + boolean isAllowed; + switch (callingUid) { + case Process.ROOT_UID: + case Process.SHELL_UID: + case Process.SYSTEM_UID: + isAllowed = true; + break; + default: + // TODO(b/159952358): Remove permission check? The component package should + // have been checked when the verifier component was first scanned in PMS. + mContext.enforcePermission( + android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, + Binder.getCallingPid(), callingUid, + "Caller " + callingUid + " does not hold DOMAIN_VERIFICATION_AGENT"); + isAllowed = proxy.isCallerVerifier(callingUid); + break; + } + + if (!isAllowed) { + throw new SecurityException("Caller " + callingUid + + " is not the approved domain verification agent, isVerifier = " + + proxy.isCallerVerifier(callingUid)); + } + } + + /** + * Enforced when mutating user selection state inside an exposed API method. + */ + public void assertApprovedUserSelector(int callingUid, @UserIdInt int callingUserId, + @UserIdInt int targetUserId) throws SecurityException { + if (callingUserId != targetUserId) { + mContext.enforcePermission( + Manifest.permission.INTERACT_ACROSS_USERS, + Binder.getCallingPid(), callingUid, + "Caller is not allowed to edit other users"); + } + + mContext.enforcePermission( + android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION, + Binder.getCallingPid(), callingUid, + "Caller is not allowed to edit user selections"); + } +} diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java index eed4dda72b6ae..64942635a7e21 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java @@ -29,6 +29,8 @@ import android.content.pm.domain.verify.DomainVerificationSet; import android.content.pm.domain.verify.DomainVerificationState; import android.content.pm.domain.verify.DomainVerificationUserSelection; import android.content.pm.domain.verify.IDomainVerificationManager; +import android.os.Binder; +import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Singleton; @@ -41,6 +43,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.server.SystemConfig; import com.android.server.SystemService; import com.android.server.compat.PlatformCompat; +import com.android.server.pm.PackageManagerService; import com.android.server.pm.PackageSetting; import com.android.server.pm.domain.verify.models.DomainVerificationPkgState; import com.android.server.pm.domain.verify.models.DomainVerificationStateMap; @@ -89,6 +92,9 @@ public class DomainVerificationService extends SystemService @NonNull private final DomainVerificationCollector mCollector; + @NonNull + private final DomainVerificationEnforcer mEnforcer; + @NonNull private final IDomainVerificationManager.Stub mStub = new DomainVerificationManagerStub(this); @@ -102,6 +108,7 @@ public class DomainVerificationService extends SystemService mSystemConfig = systemConfig; mSettings = new DomainVerificationSettings(); mCollector = new DomainVerificationCollector(platformCompat, systemConfig); + mEnforcer = new DomainVerificationEnforcer(context); } @Override @@ -117,6 +124,7 @@ public class DomainVerificationService extends SystemService @NonNull @Override public List getValidVerificationPackageNames() { + mEnforcer.assertApprovedVerifier(mConnection.get().getCallingUid(), mProxy); return null; } @@ -124,12 +132,14 @@ public class DomainVerificationService extends SystemService @Override public DomainVerificationSet getDomainVerificationSet(@NonNull String packageName) throws NameNotFoundException { + mEnforcer.assertApprovedQuerent(mConnection.get().getCallingUid(), mProxy); return null; } @Override public void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set domains, int state) throws InvalidDomainSetException, NameNotFoundException { + mEnforcer.assertApprovedVerifier(mConnection.get().getCallingUid(), mProxy); //TODO(b/163565712): Implement method mConnection.get().scheduleWriteSettings(); } @@ -137,12 +147,15 @@ public class DomainVerificationService extends SystemService @Override public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName, boolean allowed) throws NameNotFoundException { - //TODO(b/163565712): Implement method - mConnection.get().scheduleWriteSettings(); + setDomainVerificationLinkHandlingAllowed(packageName, allowed, + mConnection.get().getCallingUserId()); } public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName, boolean allowed, @UserIdInt int userId) throws NameNotFoundException { + Connection connection = mConnection.get(); + mEnforcer.assertApprovedUserSelector(connection.getCallingUid(), + connection.getCallingUserId(), userId); //TODO(b/163565712): Implement method mConnection.get().scheduleWriteSettings(); } @@ -151,13 +164,16 @@ public class DomainVerificationService extends SystemService public void setDomainVerificationUserSelection(@NonNull UUID domainSetId, @NonNull Set domains, boolean enabled) throws InvalidDomainSetException, NameNotFoundException { - //TODO(b/163565712): Implement method - mConnection.get().scheduleWriteSettings(); + setDomainVerificationUserSelection(domainSetId, domains, enabled, + mConnection.get().getCallingUserId()); } public void setDomainVerificationUserSelection(@NonNull UUID domainSetId, @NonNull Set domains, boolean enabled, @UserIdInt int userId) throws InvalidDomainSetException, NameNotFoundException { + Connection connection = mConnection.get(); + mEnforcer.assertApprovedUserSelector(connection.getCallingUid(), + connection.getCallingUserId(), userId); //TODO(b/163565712): Implement method mConnection.get().scheduleWriteSettings(); } @@ -166,12 +182,16 @@ public class DomainVerificationService extends SystemService @Override public DomainVerificationUserSelection getDomainVerificationUserSelection( @NonNull String packageName) throws NameNotFoundException { - return null; + return getDomainVerificationUserSelection(packageName, + mConnection.get().getCallingUserId()); } @Nullable public DomainVerificationUserSelection getDomainVerificationUserSelection( @NonNull String packageName, @UserIdInt int userId) throws NameNotFoundException { + Connection connection = mConnection.get(); + mEnforcer.assertApprovedUserSelector(connection.getCallingUid(), + connection.getCallingUserId(), userId); return null; } @@ -449,7 +469,38 @@ public class DomainVerificationService extends SystemService */ void scheduleWriteSettings(); - /** @see DomainVerificationProxy.Connection#schedule(int, java.lang.Object) */ + /** + * Delegate to {@link Binder#getCallingUid()} to allow mocking in tests. + */ + int getCallingUid(); + + /** + * Delegate to {@link UserHandle#getCallingUserId()} to allow mocking in tests. + */ + @UserIdInt + int getCallingUserId(); + + /** + * @see DomainVerificationProxy.Connection#schedule(int, java.lang.Object) + */ void schedule(int code, @Nullable Object object); + + boolean isCallerPackage(int callingUid, @NonNull String packageName); + + /** + * This can only be called when the internal {@link #mLock} is held. Otherwise it's possible + * to deadlock with {@link PackageManagerService}. + */ + @GuardedBy("mLock") + @Nullable + PackageSetting getPackageSettingLocked(@NonNull String pkgName); + + /** + * This can only be called when the internal {@link #mLock} is held. Otherwise it's possible + * to deadlock with {@link PackageManagerService}. + */ + @GuardedBy("mLock") + @Nullable + AndroidPackage getPackageLocked(@NonNull String pkgName); } } diff --git a/services/tests/PackageManagerServiceTests/unit/Android.bp b/services/tests/PackageManagerServiceTests/unit/Android.bp index b9a3a6500ebe7..4aa8abc843923 100644 --- a/services/tests/PackageManagerServiceTests/unit/Android.bp +++ b/services/tests/PackageManagerServiceTests/unit/Android.bp @@ -21,6 +21,7 @@ android_test { "junit", "services.core", "servicestests-utils", + "testng", "truth-prebuilt", ], platform_apis: true, diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationEnforcerTest.kt new file mode 100644 index 0000000000000..b8cb3b1600265 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationEnforcerTest.kt @@ -0,0 +1,446 @@ +/* + * Copyright (C) 2021 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.test.domain.verify + +import android.content.Context +import android.content.Intent +import android.content.pm.PackageUserState +import android.content.pm.domain.verify.DomainVerificationManager +import android.content.pm.parsing.component.ParsedActivity +import android.content.pm.parsing.component.ParsedIntentInfo +import android.os.Build +import android.os.Process +import android.util.ArraySet +import android.util.Singleton +import android.util.SparseArray +import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.pm.PackageSetting +import com.android.server.pm.domain.verify.DomainVerificationEnforcer +import com.android.server.pm.domain.verify.DomainVerificationService +import com.android.server.pm.domain.verify.proxy.DomainVerificationProxy +import com.android.server.pm.parsing.pkg.AndroidPackage +import com.android.server.testutils.mockThrowOnUnmocked +import com.android.server.testutils.spyThrowOnUnmocked +import com.android.server.testutils.whenever +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import org.mockito.Mockito.any +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.anyLong +import org.mockito.Mockito.anyString +import org.mockito.Mockito.eq +import org.mockito.Mockito.verifyNoMoreInteractions +import org.testng.Assert.assertThrows +import java.io.File +import java.util.UUID +import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.AtomicInteger + +private typealias Enforcer = DomainVerificationEnforcer + +@RunWith(Parameterized::class) +class DomainVerificationEnforcerTest { + + val context: Context = InstrumentationRegistry.getInstrumentation().context + + companion object { + private val INTERNAL_UIDS = listOf(Process.ROOT_UID, Process.SHELL_UID, Process.SYSTEM_UID) + private const val VERIFIER_UID = Process.FIRST_APPLICATION_UID + 1 + private const val NON_VERIFIER_UID = Process.FIRST_APPLICATION_UID + 2 + + private const val TEST_PKG = "com.test" + + @JvmStatic + @Parameterized.Parameters(name = "{0}") + fun parameters(): Array { + val makeEnforcer: (Context) -> DomainVerificationEnforcer = { + DomainVerificationEnforcer(it) + } + + val mockPkg = mockThrowOnUnmocked { + whenever(packageName) { TEST_PKG } + whenever(targetSdkVersion) { Build.VERSION_CODES.S } + whenever(activities) { + listOf( + ParsedActivity().apply { + addIntent( + ParsedIntentInfo().apply { + autoVerify = true + addAction(Intent.ACTION_VIEW) + addCategory(Intent.CATEGORY_BROWSABLE) + addCategory(Intent.CATEGORY_DEFAULT) + addDataScheme("https") + addDataAuthority("example.com", null) + } + ) + } + ) + } + } + + val uuid = UUID.randomUUID() + + // TODO: PackageSetting field encapsulation to move to whenever(name) + val mockPkgSetting = spyThrowOnUnmocked( + PackageSetting( + TEST_PKG, + TEST_PKG, + File("/test"), + null, + null, + null, + null, + 1, + 0, + 0, + 0, + null, + null, + null, + uuid + ) + ) { + whenever(getPkg()) { mockPkg } + whenever(domainSetId) { uuid } + whenever(userState) { + SparseArray().apply { + this[0] = PackageUserState() + } + } + whenever(intentFilterVerificationInfo) { null } + } + + val makeService: (Context) -> Triple = + { + val callingUidInt = AtomicInteger(-1) + val callingUserIdInt = AtomicInteger(-1) + Triple( + callingUidInt, callingUserIdInt, DomainVerificationService( + it, + mockThrowOnUnmocked { whenever(linkedApps) { ArraySet() } }, + mockThrowOnUnmocked { + whenever( + isChangeEnabled( + anyLong(), + any() + ) + ) { true } + }, + object : Singleton() { + override fun create(): DomainVerificationService.Connection = + mockThrowOnUnmocked { + whenever(callingUid) { callingUidInt.get() } + whenever(callingUserId) { callingUserIdInt.get() } + whenever(getPackageSettingLocked(TEST_PKG)) { mockPkgSetting } + whenever(getPackageLocked(TEST_PKG)) { mockPkg } + whenever(schedule(anyInt(), any())) + } + }) + ) + } + + fun enforcer( + type: Type, + name: String, + block: DomainVerificationEnforcer.( + callingUid: Int, callingUserId: Int, userId: Int, proxy: DomainVerificationProxy + ) -> Unit + ) = Params( + type, + makeEnforcer, + name + ) { enforcer, callingUid, callingUserId, userId, proxy -> + enforcer.block(callingUid, callingUserId, userId, proxy) + } + + fun service( + type: Type, + name: String, + block: DomainVerificationService.( + callingUid: Int, callingUserId: Int, userId: Int + ) -> Unit + ) = Params( + type, + makeService, + name + ) { uidAndUserIdAndService, callingUid, callingUserId, userId, proxy -> + val (callingUidInt, callingUserIdInt, service) = uidAndUserIdAndService + callingUidInt.set(callingUid) + callingUserIdInt.set(callingUserId) + service.setProxy(proxy) + service.addPackage(mockPkgSetting) + service.block(callingUid, callingUserId, userId) + } + + return arrayOf( + enforcer(Type.INTERNAL, "internal") { callingUid, _, _, _ -> + assertInternal(callingUid) + }, + enforcer(Type.QUERENT, "approvedQuerent") { callingUid, _, _, proxy -> + assertApprovedQuerent(callingUid, proxy) + }, + enforcer(Type.VERIFIER, "approvedVerifier") { callingUid, _, _, proxy -> + assertApprovedVerifier(callingUid, proxy) + }, + enforcer( + Type.SELECTOR, + "approvedUserSelector" + ) { callingUid, callingUserId, userId, _ -> + assertApprovedUserSelector(callingUid, callingUserId, userId) + }, + service(Type.VERIFIER, "getPackageNames") { _, _, _ -> + validVerificationPackageNames + }, + service(Type.QUERENT, "getSet") { _, _, _ -> + getDomainVerificationSet(TEST_PKG) + }, + service(Type.VERIFIER, "setStatus") { _, _, _ -> + setDomainVerificationStatus( + uuid, + setOf("example.com"), + DomainVerificationManager.STATE_SUCCESS + ) + }, + service(Type.SELECTOR, "setLinkHandlingAllowed") { _, _, _ -> + setDomainVerificationLinkHandlingAllowed(TEST_PKG, true) + }, + service(Type.SELECTOR_USER, "setLinkHandlingAllowedUserId") { _, _, userId -> + setDomainVerificationLinkHandlingAllowed(TEST_PKG, true, userId) + }, + service(Type.SELECTOR, "getUserSelection") { _, _, _ -> + getDomainVerificationUserSelection(TEST_PKG) + }, + service(Type.SELECTOR_USER, "getUserSelectionUserId") { _, _, userId -> + getDomainVerificationUserSelection(TEST_PKG, userId) + }, + service(Type.SELECTOR, "setUserSelection") { _, _, _ -> + setDomainVerificationUserSelection(uuid, setOf("example.com"), true) + }, + service(Type.SELECTOR_USER, "setUserSelectionUserId") { _, _, userId -> + setDomainVerificationUserSelection(uuid, setOf("example.com"), true, userId) + }, + ) + } + + data class Params( + val type: Type, + val construct: (context: Context) -> T, + val name: String, + private val method: ( + T, callingUid: Int, callingUserId: Int, userId: Int, proxy: DomainVerificationProxy + ) -> Unit + ) { + override fun toString() = "${type}_$name" + + fun runMethod( + target: Any, + callingUid: Int, + callingUserId: Int, + userId: Int, + proxy: DomainVerificationProxy + ) { + @Suppress("UNCHECKED_CAST") + method(target as T, callingUid, callingUserId, userId, proxy) + } + } + } + + @Parameterized.Parameter(0) + lateinit var params: Params<*> + + private val proxy: DomainVerificationProxy = mockThrowOnUnmocked { + whenever(isCallerVerifier(VERIFIER_UID)) { true } + whenever(isCallerVerifier(NON_VERIFIER_UID)) { false } + whenever(sendBroadcastForPackages(any())) + } + + @Test + fun verify() { + when (params.type) { + Type.INTERNAL -> internal() + Type.QUERENT -> approvedQuerent() + Type.VERIFIER -> approvedVerifier() + Type.SELECTOR -> approvedUserSelector(verifyCrossUser = false) + Type.SELECTOR_USER -> approvedUserSelector(verifyCrossUser = true) + }.run { /*exhaust*/ } + } + + fun internal() { + val context: Context = mockThrowOnUnmocked() + val target = params.construct(context) + + INTERNAL_UIDS.forEach { runMethod(target, it) } + assertThrows(SecurityException::class.java) { runMethod(target, VERIFIER_UID) } + assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) } + } + + fun approvedQuerent() { + val allowUserSelection = AtomicBoolean(false) + val context: Context = mockThrowOnUnmocked { + whenever( + enforcePermission( + eq(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION), + anyInt(), anyInt(), anyString() + ) + ) { + if (!allowUserSelection.get()) { + throw SecurityException() + } + } + } + val target = params.construct(context) + + INTERNAL_UIDS.forEach { runMethod(target, it) } + + verifyNoMoreInteractions(context) + + runMethod(target, VERIFIER_UID) + assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) } + + allowUserSelection.set(true) + + runMethod(target, NON_VERIFIER_UID) + } + + fun approvedVerifier() { + val shouldThrow = AtomicBoolean(false) + val context: Context = mockThrowOnUnmocked { + whenever( + enforcePermission( + eq(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT), + anyInt(), anyInt(), anyString() + ) + ) { + if (shouldThrow.get()) { + throw SecurityException() + } + } + } + val target = params.construct(context) + + INTERNAL_UIDS.forEach { runMethod(target, it) } + + verifyNoMoreInteractions(context) + + runMethod(target, VERIFIER_UID) + assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) } + + shouldThrow.set(true) + + assertThrows(SecurityException::class.java) { runMethod(target, VERIFIER_UID) } + assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) } + } + + fun approvedUserSelector(verifyCrossUser: Boolean) { + val allowUserSelection = AtomicBoolean(true) + val allowInteractAcrossUsers = AtomicBoolean(true) + val context: Context = mockThrowOnUnmocked { + whenever( + enforcePermission( + eq(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION), + anyInt(), anyInt(), anyString() + ) + ) { + if (!allowUserSelection.get()) { + throw SecurityException() + } + } + whenever( + enforcePermission( + eq(android.Manifest.permission.INTERACT_ACROSS_USERS), + anyInt(), anyInt(), anyString() + ) + ) { + if (!allowInteractAcrossUsers.get()) { + throw SecurityException() + } + } + } + val target = params.construct(context) + + fun runEachTestCaseWrapped( + callingUserId: Int, + targetUserId: Int, + block: (testCase: () -> Unit) -> Unit = { it.invoke() } + ) { + block { runMethod(target, VERIFIER_UID, callingUserId, targetUserId) } + block { runMethod(target, NON_VERIFIER_UID, callingUserId, targetUserId) } + } + + val callingUserId = 0 + val notCallingUserId = 1 + + runEachTestCaseWrapped(callingUserId, callingUserId) + if (verifyCrossUser) { + runEachTestCaseWrapped(callingUserId, notCallingUserId) + } + + allowInteractAcrossUsers.set(false) + + runEachTestCaseWrapped(callingUserId, callingUserId) + + if (verifyCrossUser) { + runEachTestCaseWrapped(callingUserId, notCallingUserId) { + assertThrows(SecurityException::class.java, it) + } + } + + allowUserSelection.set(false) + + runEachTestCaseWrapped(callingUserId, callingUserId) { + assertThrows(SecurityException::class.java, it) + } + if (verifyCrossUser) { + runEachTestCaseWrapped(callingUserId, notCallingUserId) { + assertThrows(SecurityException::class.java, it) + } + } + + allowInteractAcrossUsers.set(true) + + runEachTestCaseWrapped(callingUserId, callingUserId) { + assertThrows(SecurityException::class.java, it) + } + if (verifyCrossUser) { + runEachTestCaseWrapped(callingUserId, notCallingUserId) { + assertThrows(SecurityException::class.java, it) + } + } + } + + private fun runMethod(target: Any, callingUid: Int, callingUserId: Int = 0, userId: Int = 0) { + params.runMethod(target, callingUid, callingUserId, userId, proxy) + } + + enum class Type { + // System/shell only + INTERNAL, + + // INTERNAL || domain verification agent || user setting permission holder + QUERENT, + + // INTERNAL || domain verification agent + VERIFIER, + + // Holding the user setting permission + SELECTOR, + + // Holding the user setting permission, but targeting cross user + SELECTOR_USER + } +} From 6ad29543a5507472e17721de8eb4849c39e39a91 Mon Sep 17 00:00:00 2001 From: Winson Date: Wed, 16 Dec 2020 17:56:32 -0800 Subject: [PATCH 14/23] Implement domain verification core system APIs All the APIs inside the core DomainVerificationManager. Exempt-From-Owner-Approval: Already approved by owners on main branch Bug: 163565712 Test: TODO Change-Id: I4804bc52573d94d5d37c9d6f0344f371365c9d24 --- .../verify/DomainVerificationService.java | 173 +++++++++++++++++- .../verify/DomainVerificationUtils.java | 34 ++++ 2 files changed, 201 insertions(+), 6 deletions(-) create mode 100644 services/core/java/com/android/server/pm/domain/verify/DomainVerificationUtils.java diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java index 64942635a7e21..2c94eec11015f 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java @@ -40,6 +40,7 @@ import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.CollectionUtils; import com.android.server.SystemConfig; import com.android.server.SystemService; import com.android.server.compat.PlatformCompat; @@ -55,8 +56,10 @@ import com.android.server.pm.parsing.pkg.AndroidPackage; 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; import java.util.Set; import java.util.UUID; @@ -69,6 +72,11 @@ public class DomainVerificationService extends SystemService * States that are currently alive and attached to a package. Entries are exclusive with the * state stored in {@link DomainVerificationSettings}, as any pending/restored state should be * immediately attached once its available. + *

+ * Generally this should be not accessed directly. Prefer calling {@link + * #getAndValidateAttachedLocked(UUID, Set, boolean)}. + * + * @see #getAndValidateAttachedLocked(UUID, Set, boolean) **/ @GuardedBy("mLock") @NonNull @@ -125,7 +133,17 @@ public class DomainVerificationService extends SystemService @Override public List getValidVerificationPackageNames() { mEnforcer.assertApprovedVerifier(mConnection.get().getCallingUid(), mProxy); - return null; + List packageNames = new ArrayList<>(); + synchronized (mLock) { + int size = mAttachedPkgStates.size(); + for (int index = 0; index < size; index++) { + DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index); + if (pkgState.isHasAutoVerifyDomains()) { + packageNames.add(pkgState.getPackageName()); + } + } + } + return packageNames; } @Nullable @@ -133,14 +151,63 @@ public class DomainVerificationService extends SystemService public DomainVerificationSet getDomainVerificationSet(@NonNull String packageName) throws NameNotFoundException { mEnforcer.assertApprovedQuerent(mConnection.get().getCallingUid(), mProxy); - return null; + synchronized (mLock) { + DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName); + if (pkgState == null) { + return null; + } + + AndroidPackage pkg = mConnection.get().getPackageLocked(packageName); + if (pkg == null) { + throw DomainVerificationUtils.throwPackageUnavailable(packageName); + } + + Map hostToStateMap = new ArrayMap<>(pkgState.getStateMap()); + + // TODO(b/159952358): Should the domain list be cached? + ArraySet domains = mCollector.collectAutoVerifyDomains(pkg); + if (domains.isEmpty()) { + return null; + } + + int size = domains.size(); + for (int index = 0; index < size; index++) { + hostToStateMap.putIfAbsent(domains.valueAt(index), + DomainVerificationState.STATE_NO_RESPONSE); + } + + // TODO(b/159952358): Do not return if no values are editable (all ignored states)? + return new DomainVerificationSet(pkgState.getId(), packageName, hostToStateMap); + } } @Override public void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set domains, int state) throws InvalidDomainSetException, NameNotFoundException { mEnforcer.assertApprovedVerifier(mConnection.get().getCallingUid(), mProxy); - //TODO(b/163565712): Implement method + if (state < DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED) { + if (state != DomainVerificationState.STATE_SUCCESS) { + throw new IllegalArgumentException( + "External callers can only set STATE_SUCCESS or codes greater than or " + + "equal to STATE_FIRST_CALLER_DEFINED"); + } + } + + synchronized (mLock) { + DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains, + true /* forAutoVerify */); + ArrayMap stateMap = pkgState.getStateMap(); + for (String domain : domains) { + Integer previousState = stateMap.get(domain); + if (previousState != null + && !DomainVerificationManager.isStateModifiable(previousState)) { + continue; + } + + stateMap.put(domain, state); + } + } + mConnection.get().scheduleWriteSettings(); } @@ -156,7 +223,16 @@ public class DomainVerificationService extends SystemService Connection connection = mConnection.get(); mEnforcer.assertApprovedUserSelector(connection.getCallingUid(), connection.getCallingUserId(), userId); - //TODO(b/163565712): Implement method + synchronized (mLock) { + DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName); + if (pkgState == null) { + throw DomainVerificationUtils.throwPackageUnavailable(packageName); + } + + pkgState.getOrCreateUserSelectionState(userId) + .setDisallowLinkHandling(!allowed); + } + mConnection.get().scheduleWriteSettings(); } @@ -174,7 +250,17 @@ public class DomainVerificationService extends SystemService Connection connection = mConnection.get(); mEnforcer.assertApprovedUserSelector(connection.getCallingUid(), connection.getCallingUserId(), userId); - //TODO(b/163565712): Implement method + synchronized (mLock) { + DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains, + false /* forAutoVerify */); + DomainVerificationUserState userState = pkgState.getOrCreateUserSelectionState(userId); + if (enabled) { + userState.addHosts(domains); + } else { + userState.removeHosts(domains); + } + } + mConnection.get().scheduleWriteSettings(); } @@ -192,7 +278,39 @@ public class DomainVerificationService extends SystemService Connection connection = mConnection.get(); mEnforcer.assertApprovedUserSelector(connection.getCallingUid(), connection.getCallingUserId(), userId); - return null; + synchronized (mLock) { + DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName); + if (pkgState == null) { + return null; + } + + AndroidPackage pkg = connection.getPackageLocked(packageName); + if (pkg == null) { + throw DomainVerificationUtils.throwPackageUnavailable(packageName); + } + + ArrayMap hostToUserSelectionMap = new ArrayMap<>(); + + ArraySet domains = mCollector.collectAllWebDomains(pkg); + int domainsSize = domains.size(); + for (int index = 0; index < domainsSize; index++) { + hostToUserSelectionMap.put(domains.valueAt(index), false); + } + + boolean openVerifiedLinks = false; + DomainVerificationUserState userState = pkgState.getUserSelectionState(userId); + if (userState != null) { + openVerifiedLinks = !userState.isDisallowLinkHandling(); + ArraySet enabledHosts = userState.getEnabledHosts(); + int hostsSize = enabledHosts.size(); + for (int index = 0; index < hostsSize; index++) { + hostToUserSelectionMap.put(enabledHosts.valueAt(index), true); + } + } + + return new DomainVerificationUserSelection(pkgState.getId(), packageName, + UserHandle.of(userId), openVerifiedLinks, hostToUserSelectionMap); + } } @NonNull @@ -461,6 +579,49 @@ public class DomainVerificationService extends SystemService return !(mProxy instanceof DomainVerificationProxyUnavailable); } + /** + * Validates parameters provided by an external caller. Checks that an ID is still live and that + * any provided domains are valid. Should be called at the beginning of each API that takes in a + * {@link UUID} domain set ID. + */ + @GuardedBy("mLock") + private DomainVerificationPkgState getAndValidateAttachedLocked(@NonNull UUID domainSetId, + @NonNull Set domains, boolean forAutoVerify) + throws InvalidDomainSetException, NameNotFoundException { + if (domainSetId == null) { + throw new InvalidDomainSetException(null, null, + InvalidDomainSetException.REASON_ID_NULL); + } + + DomainVerificationPkgState pkgState = mAttachedPkgStates.get(domainSetId); + if (pkgState == null) { + throw new InvalidDomainSetException(domainSetId, null, + InvalidDomainSetException.REASON_ID_INVALID); + } + + String pkgName = pkgState.getPackageName(); + PackageSetting pkgSetting = mConnection.get().getPackageSettingLocked(pkgName); + if (pkgSetting == null || pkgSetting.getPkg() == null) { + throw DomainVerificationUtils.throwPackageUnavailable(pkgName); + } + + if (CollectionUtils.isEmpty(domains)) { + throw new InvalidDomainSetException(domainSetId, pkgState.getPackageName(), + InvalidDomainSetException.REASON_SET_NULL_OR_EMPTY); + } + AndroidPackage pkg = pkgSetting.getPkg(); + ArraySet declaredDomains = forAutoVerify + ? mCollector.collectAutoVerifyDomains(pkg) + : mCollector.collectAllWebDomains(pkg); + + if (domains.retainAll(declaredDomains)) { + throw new InvalidDomainSetException(domainSetId, pkgState.getPackageName(), + InvalidDomainSetException.REASON_UNKNOWN_DOMAIN); + } + + return pkgState; + } + public interface Connection { /** diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationUtils.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationUtils.java new file mode 100644 index 0000000000000..0f902a1fc84f7 --- /dev/null +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationUtils.java @@ -0,0 +1,34 @@ +/* + * 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.domain.verify; + +import android.annotation.CheckResult; +import android.annotation.NonNull; +import android.content.pm.PackageManager.NameNotFoundException; + +final class DomainVerificationUtils { + + /** + * Consolidates package exception messages. A generic unavailable message is included since + * the caller doesn't bother to check why the package isn't available. + */ + @CheckResult + static NameNotFoundException throwPackageUnavailable(@NonNull String packageName) + throws NameNotFoundException { + throw new NameNotFoundException("Package " + packageName + " unavailable"); + } +} From e94d0cfd0859585263f16a57736ffbeb1d174b38 Mon Sep 17 00:00:00 2001 From: Winson Date: Thu, 17 Dec 2020 11:04:25 -0800 Subject: [PATCH 15/23] Add DomainVerificationDebug Includes functionality for printing current domain verification state and user selections for any/all packages/users. Exempt-From-Owner-Approval: Already approved by owners on main branch Bug: 163565712 Test: manual, looks right Change-Id: I2930f9ebf2ecbdf0a0f64a5894059c9145988fe8 --- .../java/com/android/server/pm/DumpState.java | 2 +- .../server/pm/PackageManagerService.java | 90 ++----- .../verify/DomainVerificationDebug.java | 228 ++++++++++++++++++ .../DomainVerificationManagerInternal.java | 15 ++ .../verify/DomainVerificationService.java | 23 +- .../verify/proxy/DomainVerificationProxy.java | 6 + .../proxy/DomainVerificationProxyV2.java | 7 + 7 files changed, 301 insertions(+), 70 deletions(-) create mode 100644 services/core/java/com/android/server/pm/domain/verify/DomainVerificationDebug.java diff --git a/services/core/java/com/android/server/pm/DumpState.java b/services/core/java/com/android/server/pm/DumpState.java index 4f986bd5276b9..2a1fc87fc29fe 100644 --- a/services/core/java/com/android/server/pm/DumpState.java +++ b/services/core/java/com/android/server/pm/DumpState.java @@ -33,7 +33,7 @@ public final class DumpState { public static final int DUMP_KEYSETS = 1 << 14; public static final int DUMP_VERSION = 1 << 15; public static final int DUMP_INSTALLS = 1 << 16; - public static final int DUMP_INTENT_FILTER_VERIFIERS = 1 << 17; + public static final int DUMP_DOMAIN_VERIFIER = 1 << 17; public static final int DUMP_DOMAIN_PREFERRED = 1 << 18; public static final int DUMP_FROZEN = 1 << 19; public static final int DUMP_DEXOPT = 1 << 20; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 92016c6d1d0e0..fec92ddcb96ee 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -23948,9 +23948,8 @@ public class PackageManagerService extends IPackageManager.Stub dumpState.setDump(DumpState.DUMP_MESSAGES); } else if ("v".equals(cmd) || "verifiers".equals(cmd)) { dumpState.setDump(DumpState.DUMP_VERIFIERS); - } else if ("i".equals(cmd) || "ifv".equals(cmd) - || "intent-filter-verifiers".equals(cmd)) { - dumpState.setDump(DumpState.DUMP_INTENT_FILTER_VERIFIERS); + } else if ("dv".equals(cmd) || "domain-verifier".equals(cmd)) { + dumpState.setDump(DumpState.DUMP_DOMAIN_VERIFIER); } else if ("version".equals(cmd)) { dumpState.setDump(DumpState.DUMP_VERSION); } else if ("k".equals(cmd) || "keysets".equals(cmd)) { @@ -24044,16 +24043,16 @@ public class PackageManagerService extends IPackageManager.Stub } } - if (dumpState.isDumping(DumpState.DUMP_INTENT_FILTER_VERIFIERS) && + if (dumpState.isDumping(DumpState.DUMP_DOMAIN_VERIFIER) && packageName == null) { - ComponentName verifierComponent = - mIntentFilterVerificationManager.getVerifierComponent(); + DomainVerificationProxy proxy = mDomainVerificationManager.getProxy(); + ComponentName verifierComponent = proxy.getComponentName(); if (verifierComponent != null) { String verifierPackageName = verifierComponent.getPackageName(); if (!checkin) { if (dumpState.onTitlePrinted()) pw.println(); - pw.println("Intent Filter Verifier:"); + pw.println("Domain Verifier:"); pw.print(" Using: "); pw.print(verifierPackageName); pw.print(" (uid="); @@ -24061,14 +24060,14 @@ public class PackageManagerService extends IPackageManager.Stub UserHandle.USER_SYSTEM)); pw.println(")"); } else if (verifierPackageName != null) { - pw.print("ifv,"); pw.print(verifierPackageName); + pw.print("dv,"); pw.print(verifierPackageName); pw.print(","); pw.println(getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING, UserHandle.USER_SYSTEM)); } } else { pw.println(); - pw.println("No Intent Filter Verifier available!"); + pw.println("No Domain Verifier available!"); } } @@ -24185,63 +24184,20 @@ public class PackageManagerService extends IPackageManager.Stub } } - if (!checkin - && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED) - && packageName == null) { - pw.println(); - int count = mSettings.getPackagesLocked().size(); - if (count == 0) { - pw.println("No applications!"); - pw.println(); - } else { - final String prefix = " "; - Collection allPackageSettings = - mSettings.getPackagesLocked().values(); - if (allPackageSettings.size() == 0) { - pw.println("No domain preferred apps!"); - pw.println(); - } else { - pw.println("App verification status:"); - pw.println(); - count = 0; - for (PackageSetting ps : allPackageSettings) { - IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo(); - if (ivi == null || ivi.getPackageName() == null) continue; - pw.println(prefix + "Package: " + ivi.getPackageName()); - pw.println(prefix + "Domains: " + ivi.getDomainsString()); - pw.println(prefix + "Status: " + ivi.getStatusString()); - pw.println(); - count++; - } - if (count == 0) { - pw.println(prefix + "No app verification established."); - pw.println(); - } - for (int userId : mUserManager.getUserIds()) { - pw.println("App linkages for user " + userId + ":"); - pw.println(); - count = 0; - for (PackageSetting ps : allPackageSettings) { - final long status = ps.getDomainVerificationStatusForUser(userId); - if (status >> 32 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED - && !DEBUG_DOMAIN_VERIFICATION) { - continue; - } - pw.println(prefix + "Package: " + ps.name); - pw.println(prefix + "Domains: " + dumpDomainString(ps.name)); - String statusStr = IntentFilterVerificationInfo. - getStatusStringFromValue(status); - pw.println(prefix + "Status: " + statusStr); - pw.println(); - count++; - } - if (count == 0) { - pw.println(prefix + "No configured app linkages."); - pw.println(); - } - } - } + if (!checkin && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) { + android.util.IndentingPrintWriter writer = + new android.util.IndentingPrintWriter(pw); + if (dumpState.onTitlePrinted()) pw.println(); + + writer.println("Domain verification status:"); + writer.increaseIndent(); + try { + mDomainVerificationManager.printState(writer, packageName, UserHandle.USER_ALL); + } catch (PackageManager.NameNotFoundException e) { + pw.println("Failure printing domain verification information"); + Slog.e(TAG, "Failure printing domain verification information", e); } + writer.decreaseIndent(); } if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) { @@ -24430,8 +24386,8 @@ public class PackageManagerService extends IPackageManager.Stub UserHandle.USER_SYSTEM)); proto.end(requiredVerifierPackageToken); - ComponentName verifierComponent = - mIntentFilterVerificationManager.getVerifierComponent(); + DomainVerificationProxy proxy = mDomainVerificationManager.getProxy(); + ComponentName verifierComponent = proxy.getComponentName(); if (verifierComponent != null) { String verifierPackageName = verifierComponent.getPackageName(); final long verifierPackageToken = diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationDebug.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationDebug.java new file mode 100644 index 0000000000000..ff674d3b56020 --- /dev/null +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationDebug.java @@ -0,0 +1,228 @@ +/* + * 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.domain.verify; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.Signature; +import android.content.pm.domain.verify.DomainVerificationManager; +import android.content.pm.domain.verify.DomainVerificationState; +import android.os.UserHandle; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.IndentingPrintWriter; +import android.util.PackageUtils; +import android.util.SparseArray; + +import com.android.internal.util.CollectionUtils; +import com.android.server.pm.PackageSetting; +import com.android.server.pm.domain.verify.models.DomainVerificationPkgState; +import com.android.server.pm.domain.verify.models.DomainVerificationStateMap; +import com.android.server.pm.domain.verify.models.DomainVerificationUserState; +import com.android.server.pm.parsing.pkg.AndroidPackage; + +import java.util.Arrays; + +public class DomainVerificationDebug { + + @NonNull + private final DomainVerificationCollector mCollector; + + DomainVerificationDebug(DomainVerificationCollector collector) { + mCollector = collector; + } + + public void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName, + @Nullable @UserIdInt Integer userId, + @NonNull DomainVerificationService.Connection connection, + @NonNull DomainVerificationStateMap stateMap) + throws NameNotFoundException { + ArrayMap reusedMap = new ArrayMap<>(); + ArraySet reusedSet = new ArraySet<>(); + + if (packageName == null) { + int size = stateMap.size(); + for (int index = 0; index < size; index++) { + DomainVerificationPkgState pkgState = stateMap.valueAt(index); + String pkgName = pkgState.getPackageName(); + PackageSetting pkgSetting = connection.getPackageSettingLocked(pkgName); + if (pkgSetting == null || pkgSetting.getPkg() == null) { + continue; + } + + boolean wasHeaderPrinted = printState(writer, pkgState, pkgSetting.getPkg(), + reusedMap, false); + printState(writer, pkgState, pkgSetting.getPkg(), userId, reusedSet, + wasHeaderPrinted); + } + } else { + DomainVerificationPkgState pkgState = stateMap.get(packageName); + if (pkgState == null) { + throw DomainVerificationUtils.throwPackageUnavailable(packageName); + } + + PackageSetting pkgSetting = connection.getPackageSettingLocked(packageName); + if (pkgSetting == null || pkgSetting.getPkg() == null) { + throw DomainVerificationUtils.throwPackageUnavailable(packageName); + } + + AndroidPackage pkg = pkgSetting.getPkg(); + printState(writer, pkgState, pkg, reusedMap, false); + printState(writer, pkgState, pkg, userId, reusedSet, true); + } + } + + boolean printState(@NonNull IndentingPrintWriter writer, + @NonNull DomainVerificationPkgState pkgState, @NonNull AndroidPackage pkg, + @NonNull ArrayMap reusedMap, boolean wasHeaderPrinted) { + reusedMap.clear(); + reusedMap.putAll(pkgState.getStateMap()); + + ArraySet declaredDomains = mCollector.collectAutoVerifyDomains(pkg); + int declaredSize = declaredDomains.size(); + for (int declaredIndex = 0; declaredIndex < declaredSize; declaredIndex++) { + String domain = declaredDomains.valueAt(declaredIndex); + reusedMap.putIfAbsent(domain, DomainVerificationState.STATE_NO_RESPONSE); + } + + boolean printedHeader = false; + + if (!reusedMap.isEmpty()) { + if (!wasHeaderPrinted) { + Signature[] signatures = pkg.getSigningDetails().signatures; + String signaturesDigest = signatures == null ? null : Arrays.toString( + PackageUtils.computeSignaturesSha256Digests( + pkg.getSigningDetails().signatures)); + + writer.println(pkgState.getPackageName() + " " + pkgState.getId() + ":"); + writer.increaseIndent(); + writer.println("Signatures: " + signaturesDigest); + writer.decreaseIndent(); + printedHeader = true; + } + + writer.increaseIndent(); + writer.println("Domain verification state:"); + writer.increaseIndent(); + int stateSize = reusedMap.size(); + for (int stateIndex = 0; stateIndex < stateSize; stateIndex++) { + String domain = reusedMap.keyAt(stateIndex); + Integer state = reusedMap.valueAt(stateIndex); + writer.print(domain); + writer.print(": "); + writer.println(DomainVerificationManager.stateToDebugString(state)); + } + writer.decreaseIndent(); + writer.decreaseIndent(); + } + + return printedHeader; + } + + void printState(@NonNull IndentingPrintWriter writer, + @NonNull DomainVerificationPkgState pkgState, @NonNull AndroidPackage pkg, + @Nullable @UserIdInt Integer userId, @NonNull ArraySet reusedSet, + boolean wasHeaderPrinted) { + if (userId == null) { + return; + } + + ArraySet allWebDomains = mCollector.collectAllWebDomains(pkg); + SparseArray userStates = + pkgState.getUserSelectionStates(); + if (userId == UserHandle.USER_ALL) { + int size = userStates.size(); + if (size == 0) { + printState(writer, pkgState, userId, null, reusedSet, allWebDomains, + wasHeaderPrinted); + } else { + for (int index = 0; index < size; index++) { + DomainVerificationUserState userState = userStates.valueAt(index); + printState(writer, pkgState, userState.getUserId(), userState, reusedSet, + allWebDomains, wasHeaderPrinted); + } + } + } else { + DomainVerificationUserState userState = userStates.get(userId); + printState(writer, pkgState, userId, userState, reusedSet, allWebDomains, + wasHeaderPrinted); + } + } + + boolean printState(@NonNull IndentingPrintWriter writer, + @NonNull DomainVerificationPkgState pkgState, @UserIdInt int userId, + @Nullable DomainVerificationUserState userState, @NonNull ArraySet reusedSet, + @NonNull ArraySet allWebDomains, boolean wasHeaderPrinted) { + reusedSet.clear(); + reusedSet.addAll(allWebDomains); + if (userState != null) { + reusedSet.removeAll(userState.getEnabledHosts()); + } + + boolean printedHeader = false; + + ArraySet enabledHosts = userState == null ? null : userState.getEnabledHosts(); + int enabledSize = CollectionUtils.size(enabledHosts); + int disabledSize = reusedSet.size(); + if (enabledSize > 0 || disabledSize > 0) { + if (!wasHeaderPrinted) { + writer.println(pkgState.getPackageName() + " " + pkgState.getId() + ":"); + printedHeader = true; + } + + boolean isLinkHandlingAllowed = userState == null + || !userState.isDisallowLinkHandling(); + + writer.increaseIndent(); + writer.print("User "); + writer.print(userId == UserHandle.USER_ALL ? "all" : userId); + writer.println(":"); + writer.increaseIndent(); + writer.print("Verification link handling allowed: "); + writer.println(isLinkHandlingAllowed); + writer.println("Selection state:"); + writer.increaseIndent(); + + if (enabledSize > 0) { + writer.println("Enabled:"); + writer.increaseIndent(); + for (int enabledIndex = 0; enabledIndex < enabledSize; enabledIndex++) { + //noinspection ConstantConditions + writer.println(enabledHosts.valueAt(enabledIndex)); + } + writer.decreaseIndent(); + } + + if (disabledSize > 0) { + writer.println("Disabled:"); + writer.increaseIndent(); + for (int disabledIndex = 0; disabledIndex < disabledSize; disabledIndex++) { + writer.println(reusedSet.valueAt(disabledIndex)); + } + writer.decreaseIndent(); + } + + writer.decreaseIndent(); + writer.decreaseIndent(); + writer.decreaseIndent(); + } + + return printedHeader; + } +} diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java index ee895d048e672..29df823367b7d 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java @@ -17,9 +17,12 @@ package com.android.server.pm.domain.verify; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.domain.verify.DomainVerificationManager; import android.content.pm.domain.verify.DomainVerificationSet; +import android.util.IndentingPrintWriter; import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; @@ -42,6 +45,9 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan @NonNull UUID generateNewId(); + @NonNull + DomainVerificationProxy getProxy(); + /** * Update the proxy implementation that talks to the domain verification agent on device. The * default proxy is a stub that does nothing, and broadcast functionality will only work once a @@ -139,4 +145,13 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan void restoreSettings(@NonNull TypedXmlPullParser parser) throws IOException, XmlPullParserException; + /** + * Print the verification state and user selection state of a package. + * + * @param packageName the package whose state to change, or all packages if none is specified + * @param userId the specific user to print, or null to skip printing user selection + * states, supports {@link android.os.UserHandle#USER_ALL} + */ + void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName, + @Nullable @UserIdInt Integer userId) throws NameNotFoundException; } diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java index 2c94eec11015f..a9888eaf02b22 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java @@ -33,6 +33,7 @@ import android.os.Binder; import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.IndentingPrintWriter; import android.util.Singleton; import android.util.Slog; import android.util.SparseArray; @@ -103,6 +104,9 @@ public class DomainVerificationService extends SystemService @NonNull private final DomainVerificationEnforcer mEnforcer; + @NonNull + private final DomainVerificationDebug mDebug; + @NonNull private final IDomainVerificationManager.Stub mStub = new DomainVerificationManagerStub(this); @@ -117,6 +121,7 @@ public class DomainVerificationService extends SystemService mSettings = new DomainVerificationSettings(); mCollector = new DomainVerificationCollector(platformCompat, systemConfig); mEnforcer = new DomainVerificationEnforcer(context); + mDebug = new DomainVerificationDebug(mCollector); } @Override @@ -124,6 +129,12 @@ public class DomainVerificationService extends SystemService publishBinderService(Context.DOMAIN_VERIFICATION_SERVICE, mStub); } + @NonNull + @Override + public DomainVerificationProxy getProxy() { + return mProxy; + } + @Override public void setProxy(@NonNull DomainVerificationProxy proxy) { mProxy = proxy; @@ -188,8 +199,8 @@ public class DomainVerificationService extends SystemService if (state < DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED) { if (state != DomainVerificationState.STATE_SUCCESS) { throw new IllegalArgumentException( - "External callers can only set STATE_SUCCESS or codes greater than or " - + "equal to STATE_FIRST_CALLER_DEFINED"); + "Verifier can only set STATE_SUCCESS or codes greater than or equal to " + + "STATE_FIRST_VERIFIER_DEFINED"); } } @@ -571,6 +582,14 @@ public class DomainVerificationService extends SystemService return mProxy.runMessage(messageCode, object); } + @Override + public void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName, + @Nullable @UserIdInt Integer userId) throws NameNotFoundException { + synchronized (mLock) { + mDebug.printState(writer, packageName, userId, mConnection.get(), mAttachedPkgStates); + } + } + private void sendBroadcastForPackage(@NonNull String packageName) { mProxy.sendBroadcastForPackages(Collections.singleton(packageName)); } diff --git a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxy.java b/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxy.java index 18d18485768b3..b311c0f2c2c70 100644 --- a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxy.java +++ b/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxy.java @@ -18,6 +18,7 @@ package com.android.server.pm.domain.verify.proxy; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.ComponentName; import com.android.server.DeviceIdleInternal; import com.android.server.pm.domain.verify.DomainVerificationMessageCodes; @@ -47,6 +48,11 @@ public interface DomainVerificationProxy { return false; } + @Nullable + default ComponentName getComponentName() { + return null; + } + interface Connection { /** diff --git a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV2.java b/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV2.java index 44e64f349d475..e483268be3a6d 100644 --- a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV2.java +++ b/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV2.java @@ -17,6 +17,7 @@ package com.android.server.pm.domain.verify.proxy; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.BroadcastOptions; import android.content.ComponentName; import android.content.Context; @@ -93,4 +94,10 @@ public class DomainVerificationProxyV2 implements DomainVerificationProxy { public boolean isCallerVerifier(int callingUid) { return mConnection.isCallerPackage(callingUid, mVerifierComponent.getPackageName()); } + + @Nullable + @Override + public ComponentName getComponentName() { + return mVerifierComponent; + } } From fa302df9d9e61017b87f7d50e039a1d60424912b Mon Sep 17 00:00:00 2001 From: Winson Date: Thu, 17 Dec 2020 12:00:00 -0800 Subject: [PATCH 16/23] Add DomainVerificationShell and boot broadcast Support for mutating internal state using pm shell commands. Also wires up the boot broadcast now that the ability to verify all unverified packages has been added. Exempt-From-Owner-Approval: Already approved by owners on main branch Bug: 163565712 Test: TODO Change-Id: I809436c6b8ff04cdc68430303859d2d7f40d9557 --- .../verify/DomainVerificationManager.java | 3 + .../server/pm/PackageManagerService.java | 4 +- .../server/pm/PackageManagerShellCommand.java | 152 +----- .../verify/DomainVerificationDebug.java | 3 +- .../DomainVerificationManagerInternal.java | 3 + .../verify/DomainVerificationService.java | 363 ++++++++++++- .../verify/DomainVerificationShell.java | 502 ++++++++++++++++++ .../verify/DomainVerificationEnforcerTest.kt | 25 + 8 files changed, 913 insertions(+), 142 deletions(-) create mode 100644 services/core/java/com/android/server/pm/domain/verify/DomainVerificationShell.java diff --git a/core/java/android/content/pm/domain/verify/DomainVerificationManager.java b/core/java/android/content/pm/domain/verify/DomainVerificationManager.java index 91dcdd562a057..a63e7d8ddf730 100644 --- a/core/java/android/content/pm/domain/verify/DomainVerificationManager.java +++ b/core/java/android/content/pm/domain/verify/DomainVerificationManager.java @@ -27,6 +27,9 @@ import android.content.Intent; import android.content.pm.PackageManager.NameNotFoundException; import android.os.UserHandle; import android.util.AndroidException; +import android.util.ArrayMap; + +import com.android.server.pm.domain.verify.models.DomainVerificationPkgState; import java.util.List; import java.util.Set; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index fec92ddcb96ee..df2f7a3ca8f52 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -23765,8 +23765,8 @@ public class PackageManagerService extends IPackageManager.Stub public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver) { - (new PackageManagerShellCommand(this, mContext)).exec( - this, in, out, err, args, callback, resultReceiver); + (new PackageManagerShellCommand(this, mContext,mDomainVerificationManager.getShell())) + .exec(this, in, out, err, args, callback, resultReceiver); } @SuppressWarnings("resource") diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 212edf6d0daa7..b6b6fb60d1815 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -17,13 +17,9 @@ package com.android.server.pm; import static android.content.pm.PackageInstaller.LOCATION_DATA_APP; -import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS; -import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK; -import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK; -import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER; -import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; import android.accounts.IAccountManager; +import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityManagerInternal; @@ -108,6 +104,7 @@ import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemConfig; import com.android.server.pm.PackageManagerShellCommandDataLoader.Metadata; +import com.android.server.pm.domain.verify.DomainVerificationShell; import com.android.server.pm.permission.LegacyPermissionManagerInternal; import dalvik.system.DexFile; @@ -149,6 +146,7 @@ class PackageManagerShellCommand extends ShellCommand { final LegacyPermissionManagerInternal mLegacyPermissionManager; final PermissionManager mPermissionManager; final Context mContext; + final DomainVerificationShell mDomainVerificationShell; final private WeakHashMap mResourceCache = new WeakHashMap(); int mTargetUser; @@ -158,11 +156,13 @@ class PackageManagerShellCommand extends ShellCommand { private static final SecureRandom RANDOM = new SecureRandom(); - PackageManagerShellCommand(PackageManagerService service, Context context) { + PackageManagerShellCommand(@NonNull PackageManagerService service, + @NonNull Context context, @NonNull DomainVerificationShell domainVerificationShell) { mInterface = service; mLegacyPermissionManager = LocalServices.getService(LegacyPermissionManagerInternal.class); mPermissionManager = context.getSystemService(PermissionManager.class); mContext = context; + mDomainVerificationShell = domainVerificationShell; } @Override @@ -267,10 +267,6 @@ class PackageManagerShellCommand extends ShellCommand { return runGetPrivappDenyPermissions(); case "get-oem-permissions": return runGetOemPermissions(); - case "set-app-link": - return runSetAppLink(); - case "get-app-link": - return runGetAppLink(); case "trim-caches": return runTrimCaches(); case "create-user": @@ -309,6 +305,12 @@ class PackageManagerShellCommand extends ShellCommand { case "bypass-staged-installer-check": return runBypassStagedInstallerCheck(); default: { + Boolean domainVerificationResult = + mDomainVerificationShell.runCommand(this, cmd); + if (domainVerificationResult != null) { + return domainVerificationResult ? 0 : 1; + } + String nextArg = getNextArg(); if (nextArg == null) { if (cmd.equalsIgnoreCase("-l")) { @@ -2427,134 +2429,6 @@ class PackageManagerShellCommand extends ShellCommand { return 0; } - private String linkStateToString(int state) { - switch (state) { - case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED: return "undefined"; - case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK: return "ask"; - case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS: return "always"; - case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER: return "never"; - case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK : return "always ask"; - } - return "Unknown link state: " + state; - } - - // pm set-app-link [--user USER_ID] PACKAGE {always|ask|always-ask|never|undefined} - private int runSetAppLink() throws RemoteException { - int userId = UserHandle.USER_SYSTEM; - - String opt; - while ((opt = getNextOption()) != null) { - if (opt.equals("--user")) { - userId = UserHandle.parseUserArg(getNextArgRequired()); - } else { - getErrPrintWriter().println("Error: unknown option: " + opt); - return 1; - } - } - - // Package name to act on; required - final String pkg = getNextArg(); - if (pkg == null) { - getErrPrintWriter().println("Error: no package specified."); - return 1; - } - - // State to apply; {always|ask|never|undefined}, required - final String modeString = getNextArg(); - if (modeString == null) { - getErrPrintWriter().println("Error: no app link state specified."); - return 1; - } - - final int newMode; - switch (modeString.toLowerCase()) { - case "undefined": - newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; - break; - - case "always": - newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS; - break; - - case "ask": - newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK; - break; - - case "always-ask": - newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK; - break; - - case "never": - newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER; - break; - - default: - getErrPrintWriter().println("Error: unknown app link state '" + modeString + "'"); - return 1; - } - - final int translatedUserId = - translateUserId(userId, UserHandle.USER_NULL, "runSetAppLink"); - final PackageInfo info = mInterface.getPackageInfo(pkg, 0, translatedUserId); - if (info == null) { - getErrPrintWriter().println("Error: package " + pkg + " not found."); - return 1; - } - - if ((info.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) == 0) { - getErrPrintWriter().println("Error: package " + pkg + " does not handle web links."); - return 1; - } - - if (!mInterface.updateIntentVerificationStatus(pkg, newMode, translatedUserId)) { - getErrPrintWriter().println("Error: unable to update app link status for " + pkg); - return 1; - } - - return 0; - } - - // pm get-app-link [--user USER_ID] PACKAGE - private int runGetAppLink() throws RemoteException { - int userId = UserHandle.USER_SYSTEM; - - String opt; - while ((opt = getNextOption()) != null) { - if (opt.equals("--user")) { - userId = UserHandle.parseUserArg(getNextArgRequired()); - } else { - getErrPrintWriter().println("Error: unknown option: " + opt); - return 1; - } - } - - // Package name to act on; required - final String pkg = getNextArg(); - if (pkg == null) { - getErrPrintWriter().println("Error: no package specified."); - return 1; - } - - final int translatedUserId = - translateUserId(userId, UserHandle.USER_NULL, "runGetAppLink"); - final PackageInfo info = mInterface.getPackageInfo(pkg, 0, translatedUserId); - if (info == null) { - getErrPrintWriter().println("Error: package " + pkg + " not found."); - return 1; - } - - if ((info.applicationInfo.privateFlags - & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) == 0) { - getErrPrintWriter().println("Error: package " + pkg + " does not handle web links."); - return 1; - } - - getOutPrintWriter().println(linkStateToString( - mInterface.getIntentVerificationStatus(pkg, translatedUserId))); - - return 0; - } - private int runTrimCaches() throws RemoteException { String size = getNextArg(); if (size == null) { @@ -3915,6 +3789,8 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(" --enable: turn on debug logging (default)"); pw.println(" --disable: turn off debug logging"); pw.println(""); + mDomainVerificationShell.printHelp(pw); + pw.println(""); Intent.printIntentArgsHelp(pw , ""); } diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationDebug.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationDebug.java index ff674d3b56020..9bf65b43ae576 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationDebug.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationDebug.java @@ -110,8 +110,9 @@ public class DomainVerificationDebug { PackageUtils.computeSignaturesSha256Digests( pkg.getSigningDetails().signatures)); - writer.println(pkgState.getPackageName() + " " + pkgState.getId() + ":"); + writer.println(pkgState.getPackageName() + ":"); writer.increaseIndent(); + writer.println("ID: " + pkgState.getId()); writer.println("Signatures: " + signaturesDigest); writer.decreaseIndent(); printedHeader = true; diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java index 29df823367b7d..fec2f62848def 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java @@ -154,4 +154,7 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan */ void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName, @Nullable @UserIdInt Integer userId) throws NameNotFoundException; + + @NonNull + DomainVerificationShell getShell(); } diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java index a9888eaf02b22..8813d57671d0f 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java @@ -58,6 +58,7 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -65,7 +66,7 @@ import java.util.Set; import java.util.UUID; public class DomainVerificationService extends SystemService - implements DomainVerificationManagerInternal { + implements DomainVerificationManagerInternal, DomainVerificationShell.Callback { private static final String TAG = "DomainVerificationService"; @@ -107,6 +108,9 @@ public class DomainVerificationService extends SystemService @NonNull private final DomainVerificationDebug mDebug; + @NonNull + private final DomainVerificationShell mShell; + @NonNull private final IDomainVerificationManager.Stub mStub = new DomainVerificationManagerStub(this); @@ -122,6 +126,7 @@ public class DomainVerificationService extends SystemService mCollector = new DomainVerificationCollector(platformCompat, systemConfig); mEnforcer = new DomainVerificationEnforcer(context); mDebug = new DomainVerificationDebug(mCollector); + mShell = new DomainVerificationShell(this); } @Override @@ -135,6 +140,16 @@ public class DomainVerificationService extends SystemService return mProxy; } + @Override + public void onBootPhase(int phase) { + super.onBootPhase(phase); + if (phase != SystemService.PHASE_BOOT_COMPLETED || !hasRealVerifier()) { + return; + } + + verifyPackages(null, false); + } + @Override public void setProxy(@NonNull DomainVerificationProxy proxy) { mProxy = proxy; @@ -222,6 +237,85 @@ public class DomainVerificationService extends SystemService mConnection.get().scheduleWriteSettings(); } + @Override + public void setDomainVerificationStatusInternal(@Nullable String packageName, int state, + @Nullable ArraySet domains) throws NameNotFoundException { + mEnforcer.assertInternal(mConnection.get().getCallingUid()); + + switch (state) { + case DomainVerificationState.STATE_NO_RESPONSE: + case DomainVerificationState.STATE_SUCCESS: + case DomainVerificationState.STATE_APPROVED: + case DomainVerificationState.STATE_DENIED: + break; + default: + throw new IllegalArgumentException( + "State must be one of NO_RESPONSE, SUCCESS, APPROVED, or DENIED"); + } + + if (packageName == null) { + synchronized (mLock) { + ArraySet validDomains = new ArraySet<>(); + + int size = mAttachedPkgStates.size(); + for (int index = 0; index < size; index++) { + DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index); + String pkgName = pkgState.getPackageName(); + PackageSetting pkgSetting = mConnection.get().getPackageSettingLocked(pkgName); + if (pkgSetting == null || pkgSetting.getPkg() == null) { + continue; + } + + AndroidPackage pkg = pkgSetting.getPkg(); + + validDomains.clear(); + + ArraySet autoVerifyDomains = mCollector.collectAutoVerifyDomains(pkg); + if (domains == null) { + validDomains.addAll(autoVerifyDomains); + } else { + validDomains.addAll(domains); + validDomains.retainAll(autoVerifyDomains); + } + + setDomainVerificationStatusInternal(pkgState, state, validDomains); + } + } + } else { + synchronized (mLock) { + DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName); + if (pkgState == null) { + throw DomainVerificationUtils.throwPackageUnavailable(packageName); + } + + PackageSetting pkgSetting = mConnection.get().getPackageSettingLocked(packageName); + if (pkgSetting == null || pkgSetting.getPkg() == null) { + throw DomainVerificationUtils.throwPackageUnavailable(packageName); + } + + AndroidPackage pkg = pkgSetting.getPkg(); + if (domains == null) { + domains = mCollector.collectAutoVerifyDomains(pkg); + } else { + domains.retainAll(mCollector.collectAutoVerifyDomains(pkg)); + } + + setDomainVerificationStatusInternal(pkgState, state, domains); + } + } + + mConnection.get().scheduleWriteSettings(); + } + + private void setDomainVerificationStatusInternal(@NonNull DomainVerificationPkgState pkgState, + int state, @NonNull ArraySet validDomains) { + ArrayMap stateMap = pkgState.getStateMap(); + int size = validDomains.size(); + for (int index = 0; index < size; index++) { + stateMap.put(validDomains.valueAt(index), state); + } + } + @Override public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName, boolean allowed) throws NameNotFoundException { @@ -247,6 +341,44 @@ public class DomainVerificationService extends SystemService mConnection.get().scheduleWriteSettings(); } + @Override + public void setDomainVerificationLinkHandlingAllowedInternal(@Nullable String packageName, + boolean allowed, @UserIdInt int userId) throws NameNotFoundException { + mEnforcer.assertInternal(mConnection.get().getCallingUid()); + if (packageName == null) { + synchronized (mLock) { + int pkgStateSize = mAttachedPkgStates.size(); + for (int pkgStateIndex = 0; pkgStateIndex < pkgStateSize; pkgStateIndex++) { + DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(pkgStateIndex); + if (userId == UserHandle.USER_ALL) { + SparseArray userStates = + pkgState.getUserSelectionStates(); + int userStatesSize = userStates.size(); + for (int userStateIndex = 0; userStateIndex < userStatesSize; + userStateIndex++) { + userStates.valueAt(userStateIndex) + .setDisallowLinkHandling(!allowed); + } + } else { + pkgState.getOrCreateUserSelectionState(userId) + .setDisallowLinkHandling(!allowed); + } + } + + } + } else { + synchronized (mLock) { + DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName); + if (pkgState == null) { + throw DomainVerificationUtils.throwPackageUnavailable(packageName); + } + + pkgState.getOrCreateUserSelectionState(userId) + .setDisallowLinkHandling(!allowed); + } + } + } + @Override public void setDomainVerificationUserSelection(@NonNull UUID domainSetId, @NonNull Set domains, boolean enabled) @@ -275,6 +407,77 @@ public class DomainVerificationService extends SystemService mConnection.get().scheduleWriteSettings(); } + @Override + public void setDomainVerificationUserSelectionInternal(@UserIdInt int userId, + @Nullable String packageName, boolean enabled, @NonNull ArraySet domains) + throws NameNotFoundException { + mEnforcer.assertInternal(mConnection.get().getCallingUid()); + + if (packageName == null) { + synchronized (mLock) { + Set validDomains = new ArraySet<>(); + + int size = mAttachedPkgStates.size(); + for (int index = 0; index < size; index++) { + DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index); + String pkgName = pkgState.getPackageName(); + PackageSetting pkgSetting = mConnection.get().getPackageSettingLocked(pkgName); + if (pkgSetting == null || pkgSetting.getPkg() == null) { + continue; + } + + validDomains.clear(); + validDomains.addAll(domains); + + setDomainVerificationUserSelectionInternal(userId, pkgState, + pkgSetting.getPkg(), enabled, validDomains); + } + } + } else { + synchronized (mLock) { + DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName); + if (pkgState == null) { + throw DomainVerificationUtils.throwPackageUnavailable(packageName); + } + + PackageSetting pkgSetting = mConnection.get().getPackageSettingLocked(packageName); + if (pkgSetting == null || pkgSetting.getPkg() == null) { + throw DomainVerificationUtils.throwPackageUnavailable(packageName); + } + + setDomainVerificationUserSelectionInternal(userId, pkgState, pkgSetting.getPkg(), + enabled, domains); + } + } + } + + private void setDomainVerificationUserSelectionInternal(int userId, + @NonNull DomainVerificationPkgState pkgState, @NonNull AndroidPackage pkg, + boolean enabled, Set domains) { + domains.retainAll(mCollector.collectAllWebDomains(pkg)); + + SparseArray userStates = + pkgState.getUserSelectionStates(); + if (userId == UserHandle.USER_ALL) { + int size = userStates.size(); + for (int index = 0; index < size; index++) { + DomainVerificationUserState userState = userStates.valueAt(index); + if (enabled) { + userState.addHosts(domains); + } else { + userState.removeHosts(domains); + } + } + } else { + DomainVerificationUserState userState = pkgState.getOrCreateUserSelectionState(userId); + if (enabled) { + userState.addHosts(domains); + } else { + userState.removeHosts(domains); + } + } + } + @Nullable @Override public DomainVerificationUserSelection getDomainVerificationUserSelection( @@ -284,6 +487,7 @@ public class DomainVerificationService extends SystemService } @Nullable + @Override public DomainVerificationUserSelection getDomainVerificationUserSelection( @NonNull String packageName, @UserIdInt int userId) throws NameNotFoundException { Connection connection = mConnection.get(); @@ -590,6 +794,12 @@ public class DomainVerificationService extends SystemService } } + @NonNull + @Override + public DomainVerificationShell getShell() { + return mShell; + } + private void sendBroadcastForPackage(@NonNull String packageName) { mProxy.sendBroadcastForPackages(Collections.singleton(packageName)); } @@ -641,6 +851,157 @@ public class DomainVerificationService extends SystemService return pkgState; } + @Override + public void verifyPackages(@Nullable List packageNames, boolean reVerify) { + mEnforcer.assertInternal(mConnection.get().getCallingUid()); + Set packagesToBroadcast = new ArraySet<>(); + + if (packageNames == null) { + synchronized (mLock) { + int pkgStatesSize = mAttachedPkgStates.size(); + for (int pkgStateIndex = 0; pkgStateIndex < pkgStatesSize; pkgStateIndex++) { + DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(pkgStateIndex); + addIfShouldBroadcastLocked(packagesToBroadcast, pkgState, reVerify); + } + } + } else { + synchronized (mLock) { + int size = packageNames.size(); + for (int index = 0; index < size; index++) { + String packageName = packageNames.get(index); + DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName); + if (pkgState != null) { + addIfShouldBroadcastLocked(packagesToBroadcast, pkgState, reVerify); + } + } + } + } + + if (!packagesToBroadcast.isEmpty()) { + mProxy.sendBroadcastForPackages(packagesToBroadcast); + } + } + + @GuardedBy("mLock") + private void addIfShouldBroadcastLocked(@NonNull Collection packageNames, + @NonNull DomainVerificationPkgState pkgState, boolean reVerify) { + if ((reVerify && pkgState.isHasAutoVerifyDomains()) || shouldReBroadcastPackage(pkgState)) { + packageNames.add(pkgState.getPackageName()); + } + } + + /** + * Determine whether or not a broadcast should be sent at boot for the given {@param pkgState}. + * Sends only if the only states recorded are default as decided by {@link + * DomainVerificationManager#isStateDefault(int)}. + * + * If any other state is set, it's assumed that the domain verification agent is aware of the + * package and has already scheduled future verification requests. + */ + private boolean shouldReBroadcastPackage(DomainVerificationPkgState pkgState) { + if (!pkgState.isHasAutoVerifyDomains()) { + return false; + } + + ArrayMap stateMap = pkgState.getStateMap(); + int statesSize = stateMap.size(); + for (int stateIndex = 0; stateIndex < statesSize; stateIndex++) { + Integer state = stateMap.valueAt(stateIndex); + if (!DomainVerificationManager.isStateDefault(state)) { + return false; + } + } + + return true; + } + + @Override + public void clearDomainVerificationState(@Nullable List packageNames) { + mEnforcer.assertInternal(mConnection.get().getCallingUid()); + synchronized (mLock) { + if (packageNames == null) { + int size = mAttachedPkgStates.size(); + for (int index = 0; index < size; index++) { + DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index); + String pkgName = pkgState.getPackageName(); + PackageSetting pkgSetting = mConnection.get().getPackageSettingLocked(pkgName); + if (pkgSetting == null || pkgSetting.getPkg() == null) { + continue; + } + resetDomainState(pkgState, pkgSetting.getPkg()); + } + } else { + int size = packageNames.size(); + for (int index = 0; index < size; index++) { + String pkgName = packageNames.get(index); + DomainVerificationPkgState pkgState = mAttachedPkgStates.get(pkgName); + PackageSetting pkgSetting = mConnection.get().getPackageSettingLocked(pkgName); + if (pkgSetting == null || pkgSetting.getPkg() == null) { + continue; + } + resetDomainState(pkgState, pkgSetting.getPkg()); + } + } + } + } + + /** + * Reset states that are mutable by the domain verification agent. + */ + private void resetDomainState(@NonNull DomainVerificationPkgState pkgState, + @NonNull AndroidPackage pkg) { + ArrayMap stateMap = pkgState.getStateMap(); + int size = stateMap.size(); + for (int index = size - 1; index >= 0; index--) { + Integer state = stateMap.valueAt(index); + boolean reset; + switch (state) { + case DomainVerificationState.STATE_SUCCESS: + case DomainVerificationState.STATE_RESTORED: + reset = true; + break; + default: + reset = state >= DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED; + break; + } + + if (reset) { + stateMap.removeAt(index); + } + } + + applyImmutableState(pkgState, mCollector.collectAutoVerifyDomains(pkg)); + } + + @Override + public void clearUserSelections(@Nullable List packageNames, @UserIdInt int userId) { + mEnforcer.assertInternal(mConnection.get().getCallingUid()); + synchronized (mLock) { + if (packageNames == null) { + int size = mAttachedPkgStates.size(); + for (int index = 0; index < size; index++) { + DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index); + if (userId == UserHandle.USER_ALL) { + pkgState.removeAllUsers(); + } else { + pkgState.removeUser(userId); + } + } + } else { + int size = packageNames.size(); + for (int index = 0; index < size; index++) { + String pkgName = packageNames.get(index); + DomainVerificationPkgState pkgState = mAttachedPkgStates.get(pkgName); + if (userId == UserHandle.USER_ALL) { + pkgState.removeAllUsers(); + } else { + pkgState.removeUser(userId); + } + } + } + } + } + public interface Connection { /** diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationShell.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationShell.java new file mode 100644 index 0000000000000..c3efc67662549 --- /dev/null +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationShell.java @@ -0,0 +1,502 @@ +/* + * 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.domain.verify; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.ActivityManager; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.domain.verify.DomainVerificationManager; +import android.content.pm.domain.verify.DomainVerificationState; +import android.content.pm.domain.verify.DomainVerificationUserSelection; +import android.os.Binder; +import android.os.UserHandle; +import android.text.TextUtils; +import android.util.ArraySet; +import android.util.IndentingPrintWriter; + +import com.android.modules.utils.BasicShellCommandHandler; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class DomainVerificationShell { + + @NonNull + private final Callback mCallback; + + public DomainVerificationShell(@NonNull Callback callback) { + mCallback = callback; + } + + public void printHelp(@NonNull PrintWriter pw) { + pw.println(" get-app-links [--user ] []"); + pw.println(" Prints the domain verification state for the given package, or for all"); + pw.println(" packages if none is specified."); + pw.println(" --user : include user selections (includes all domains, not"); + pw.println(" just autoVerify ones)"); + pw.println(" reset-app-links [--user ] []"); + pw.println(" Resets domain verification state for the given package, or for all"); + pw.println(" packages if none is specified."); + pw.println(" --user : clear user selection state instead; note this means"); + pw.println(" domain verification state will NOT be cleared"); + pw.println(" : the package to reset, or \"all\" to reset all packages"); + pw.println(" verify-app-links [--re-verify] []"); + pw.println(" Broadcasts a verification request for the given package, or for all"); + pw.println(" packages if none is specified. Only sends if the package has previously"); + pw.println(" not recorded a response."); + pw.println(" --re-verify: send even if the package has recorded a response"); + pw.println(" set-app-links [--package ] ..."); + pw.println(" Manually set the state of a domain for a package. The domain must be"); + pw.println(" declared by the package as autoVerify for this to work. This command"); + pw.println(" will not report a failure for domains that could not be applied."); + pw.println(" --package : the package to set, or \"all\" to set all packages"); + pw.println(" : the code to set the domains to, valid values are:"); + pw.println(" STATE_NO_RESPONSE (0): reset as if no response was ever recorded."); + pw.println(" STATE_SUCCESS (1): treat domain as successfully verified by domain."); + pw.println(" verification agent. Note that the domain verification agent can"); + pw.println(" override this."); + pw.println(" STATE_APPROVED (2): treat domain as always approved, preventing the"); + pw.println(" domain verification agent from changing it."); + pw.println(" STATE_DENIED (3): treat domain as always denied, preveting the domain"); + pw.println(" verification agent from changing it."); + pw.println(" : space separated list of domains to change, or \"all\" to"); + pw.println(" change every domain."); + pw.println(" set-app-links-user-selection --user [--package ]"); + pw.println(" ..."); + pw.println(" Manually set the state of a host user selection for a package. The domain"); + pw.println(" must be declared by the package for this to work. This command will not"); + pw.println(" report a failure for domains that could not be applied."); + pw.println(" --user : the user to change selections for"); + pw.println(" --package : the package to set, or \"all\" to set all packages"); + pw.println(" : whether or not to approve the domain"); + pw.println(" : space separated list of domains to change, or \"all\" to"); + pw.println(" change every domain."); + pw.println(" set-app-links-allowed --user [--package ] "); + pw.println(" ..."); + pw.println(" Toggle the auto verified link handling setting for a package."); + pw.println(" --user : the user to change selections for"); + pw.println(" --package : the package to set, or \"all\" to set all packages"); + pw.println(" packages will be reset if no one package is specified."); + pw.println(" : true to allow the package to open auto verified links, false"); + pw.println(" to disable"); + } + + /** + * Run a shell/debugging command. + * + * @return null if the command is unhandled, true if the command succeeded, false if it failed + */ + public Boolean runCommand(@NonNull BasicShellCommandHandler commandHandler, + @NonNull String command) { + switch (command) { + case "get-app-links": + return runGetAppLinks(commandHandler); + case "reset-app-links": + return runResetAppLinks(commandHandler); + case "verify-app-links": + return runVerifyAppLinks(commandHandler); + case "set-app-links": + return runSetAppLinks(commandHandler); + case "set-app-links-user-selection": + return runSetAppLinksUserSelection(commandHandler); + case "set-app-links-allowed": + return runSetAppLinksAllowed(commandHandler); + } + + return null; + } + + + // pm set-app-links [--package ] ... + private boolean runSetAppLinks(@NonNull BasicShellCommandHandler commandHandler) { + String packageName = null; + + String option; + while ((option = commandHandler.getNextOption()) != null) { + if (option.equals("--package")) { + packageName = commandHandler.getNextArgRequired(); + } else { + commandHandler.getErrPrintWriter().println("Error: unknown option: " + option); + return false; + } + } + + if (TextUtils.isEmpty(packageName)) { + commandHandler.getErrPrintWriter().println("Error: no package specified"); + return false; + } else if (packageName.equalsIgnoreCase("all")) { + packageName = null; + } + + String state = commandHandler.getNextArgRequired(); + int stateInt; + switch (state) { + case "STATE_NO_RESPONSE": + case "0": + stateInt = DomainVerificationState.STATE_NO_RESPONSE; + break; + case "STATE_SUCCESS": + case "1": + stateInt = DomainVerificationState.STATE_SUCCESS; + break; + case "STATE_APPROVED": + case "2": + stateInt = DomainVerificationState.STATE_APPROVED; + break; + case "STATE_DENIED": + case "3": + stateInt = DomainVerificationState.STATE_DENIED; + break; + default: + commandHandler.getErrPrintWriter().println("Invalid state option: " + state); + return false; + } + + ArraySet domains = new ArraySet<>(getRemainingArgs(commandHandler)); + if (domains.isEmpty()) { + commandHandler.getErrPrintWriter().println("No domains specified"); + return false; + } + + if (domains.size() == 1 && domains.contains("all")) { + domains = null; + } + + try { + mCallback.setDomainVerificationStatusInternal(packageName, stateInt, + domains); + } catch (NameNotFoundException e) { + commandHandler.getErrPrintWriter().println("Package not found: " + packageName); + return false; + } + return true; + } + + // pm set-app-links-user-selection --user [--package ] ... + private boolean runSetAppLinksUserSelection(@NonNull BasicShellCommandHandler commandHandler) { + Integer userId = null; + String packageName = null; + + String option; + while ((option = commandHandler.getNextOption()) != null) { + switch (option) { + case "--user": + userId = UserHandle.parseUserArg(commandHandler.getNextArgRequired()); + break; + case "--package": + packageName = commandHandler.getNextArgRequired(); + break; + default: + commandHandler.getErrPrintWriter().println("Error: unknown option: " + option); + return false; + } + } + + if (TextUtils.isEmpty(packageName)) { + commandHandler.getErrPrintWriter().println("Error: no package specified"); + return false; + } else if (packageName.equalsIgnoreCase("all")) { + packageName = null; + } + + if (userId == null) { + commandHandler.getErrPrintWriter().println("Error: User ID not specified"); + return false; + } + + userId = translateUserId(userId, "runSetAppLinksUserSelection"); + + String enabledString = commandHandler.getNextArgRequired(); + + // Manually ensure that "true" and "false" are the only options, to ensure a domain isn't + // accidentally parsed as a boolean + boolean enabled; + switch (enabledString) { + case "true": + enabled = true; + break; + case "false": + enabled = false; + break; + default: + commandHandler.getErrPrintWriter().println( + "Invalid enabled param: " + enabledString); + return false; + } + + ArraySet domains = new ArraySet<>(getRemainingArgs(commandHandler)); + if (domains.isEmpty()) { + commandHandler.getErrPrintWriter().println("No domains specified"); + return false; + } + + try { + mCallback.setDomainVerificationUserSelectionInternal(userId, + packageName, enabled, domains); + } catch (NameNotFoundException e) { + commandHandler.getErrPrintWriter().println("Package not found: " + packageName); + return false; + } + return true; + } + + // pm get-app-links [--user ] [] + private boolean runGetAppLinks(@NonNull BasicShellCommandHandler commandHandler) { + Integer userId = null; + + String option; + while ((option = commandHandler.getNextOption()) != null) { + if (option.equals("--user")) { + userId = UserHandle.parseUserArg(commandHandler.getNextArgRequired()); + } else { + commandHandler.getErrPrintWriter().println("Error: unknown option: " + option); + return false; + } + } + + userId = userId == null ? null : translateUserId(userId, "runGetAppLinks"); + + String packageName = commandHandler.getNextArg(); + + try (IndentingPrintWriter writer = new IndentingPrintWriter( + commandHandler.getOutPrintWriter(), /* singleIndent */ " ", /* wrapLength */ + 120)) { + writer.increaseIndent(); + try { + mCallback.printState(writer, packageName, userId); + } catch (NameNotFoundException e) { + commandHandler.getErrPrintWriter().println( + "Error: package " + packageName + " unavailable"); + return false; + } + writer.decreaseIndent(); + return true; + } + } + + // pm reset-app-links [--user USER_ID] [] + private boolean runResetAppLinks(@NonNull BasicShellCommandHandler commandHandler) { + Integer userId = null; + + String option; + while ((option = commandHandler.getNextOption()) != null) { + if (option.equals("--user")) { + userId = UserHandle.parseUserArg(commandHandler.getNextArgRequired()); + } else { + commandHandler.getErrPrintWriter().println("Error: unknown option: " + option); + return false; + } + } + + userId = userId == null ? null : translateUserId(userId, "runResetAppLinks"); + + List packageNames; + String pkgNameArg = commandHandler.peekNextArg(); + if (TextUtils.isEmpty(pkgNameArg)) { + commandHandler.getErrPrintWriter().println("Error: no package specified"); + return false; + } else if (pkgNameArg.equalsIgnoreCase("all")) { + packageNames = null; + } else { + packageNames = Arrays.asList(commandHandler.peekRemainingArgs()); + } + + if (userId != null) { + mCallback.clearUserSelections(packageNames, userId); + } else { + mCallback.clearDomainVerificationState(packageNames); + } + + return true; + } + + // pm verify-app-links [--re-verify] [] + private boolean runVerifyAppLinks(@NonNull BasicShellCommandHandler commandHandler) { + boolean reVerify = false; + String option; + while ((option = commandHandler.getNextOption()) != null) { + if (option.equals("--re-verify")) { + reVerify = true; + } else { + commandHandler.getErrPrintWriter().println("Error: unknown option: " + option); + return false; + } + } + + List packageNames = null; + String pkgNameArg = commandHandler.getNextArg(); + if (!TextUtils.isEmpty(pkgNameArg)) { + packageNames = Collections.singletonList(pkgNameArg); + } + + mCallback.verifyPackages(packageNames, reVerify); + + return true; + } + + // pm set-app-links-allowed [--package ] [--user ] + private boolean runSetAppLinksAllowed(@NonNull BasicShellCommandHandler commandHandler) { + String packageName = null; + Integer userId = null; + Boolean allowed = null; + String option; + while ((option = commandHandler.getNextOption()) != null) { + if (option.equals("--package")) { + packageName = commandHandler.getNextArgRequired(); + } if (option.equals("--user")) { + userId = UserHandle.parseUserArg(commandHandler.getNextArgRequired()); + } else if (allowed == null) { + allowed = Boolean.valueOf(option); + } else { + commandHandler.getErrPrintWriter().println("Error: unexpected option: " + option); + return false; + } + } + + if (TextUtils.isEmpty(packageName)) { + commandHandler.getErrPrintWriter().println("Error: no package specified"); + return false; + } else if (packageName.equalsIgnoreCase("all")) { + packageName = null; + } + + if (userId == null) { + commandHandler.getErrPrintWriter().println("Error: user ID not specified"); + return false; + } + + if (allowed == null) { + commandHandler.getErrPrintWriter().println("Error: allowed setting not specified"); + return false; + } + + userId = translateUserId(userId, "runSetAppLinksAllowed"); + + try { + mCallback.setDomainVerificationLinkHandlingAllowedInternal(packageName, allowed, + userId); + } catch (NameNotFoundException e) { + commandHandler.getErrPrintWriter().println("Package not found: " + packageName); + return false; + } + + return true; + } + + private ArrayList getRemainingArgs(@NonNull BasicShellCommandHandler commandHandler) { + ArrayList args = new ArrayList<>(); + String arg; + while ((arg = commandHandler.getNextArg()) != null) { + args.add(arg); + } + return args; + } + + private int translateUserId(@UserIdInt int userId, @NonNull String logContext) { + return ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), + userId, true, true, logContext, "pm command"); + } + + /** + * Separated interface from {@link DomainVerificationManagerInternal} to hide methods that are + * even more internal, and so that testing is easier. + */ + public interface Callback { + + /** + * Variant for use by PackageManagerShellCommand to allow the system/developer to override + * the state for a domain. + * + * @param packageName the package whose state to change, or all packages if none is + * specified + * @param state the new state code, valid values are + * {@link DomainVerificationState#STATE_NO_RESPONSE}, + * {@link DomainVerificationState#STATE_SUCCESS}, {@link + * DomainVerificationState#STATE_APPROVED}, and {@link + * DomainVerificationState#STATE_DENIED} + * @param domains the set of domains to change, or null to change all of them + */ + void setDomainVerificationStatusInternal(@Nullable String packageName, int state, + @Nullable ArraySet domains) throws PackageManager.NameNotFoundException; + + /** + * Variant for use by PackageManagerShellCommand to allow the system/developer to override + * the state for a domain. + * + * @param packageName the package whose state to change, or all packages if non is + * specified + * @param enabled whether the domain is now approved by the user + * @param domains the set of domains to change + */ + void setDomainVerificationUserSelectionInternal(@UserIdInt int userId, + @Nullable String packageName, boolean enabled, @NonNull ArraySet domains) + throws PackageManager.NameNotFoundException; + + /** + * @see DomainVerificationManager#getDomainVerificationUserSelection(String) + */ + @Nullable + DomainVerificationUserSelection getDomainVerificationUserSelection( + @NonNull String packageName, @UserIdInt int userId) + throws PackageManager.NameNotFoundException; + + /** + * Variant for use by PackageManagerShellCommand to allow the system/developer to override + * the setting for a package. + * + * @param packageName the package whose state to change, or all packages if non is + * specified + * @param allowed whether the package is allowed to automatically open links through + * domain verification + */ + void setDomainVerificationLinkHandlingAllowedInternal(@Nullable String packageName, + boolean allowed, @UserIdInt int userId) throws NameNotFoundException; + + /** + * Reset all the domain verification states for all domains for the given package names, or + * all package names if null is provided. + */ + void clearDomainVerificationState(@Nullable List packageNames); + + /** + * Reset all the user selections for the given package names, or all package names if null + * is provided. + */ + void clearUserSelections(@Nullable List packageNames, @UserIdInt int userId); + + /** + * Broadcast a verification request for the given package names, or all package names if + * null is provided. By default only re-broadcasts if a package has not recorded a + * response. + * + * @param reVerify send even if the package has previously recorded a response + */ + void verifyPackages(@Nullable List packageNames, boolean reVerify); + + /** + * @see DomainVerificationManagerInternal#printState(IndentingPrintWriter, String, Integer) + */ + void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName, + @Nullable @UserIdInt Integer userId) throws NameNotFoundException; + } +} diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationEnforcerTest.kt index b8cb3b1600265..36e683cf00b49 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationEnforcerTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationEnforcerTest.kt @@ -203,6 +203,31 @@ class DomainVerificationEnforcerTest { ) { callingUid, callingUserId, userId, _ -> assertApprovedUserSelector(callingUid, callingUserId, userId) }, + + service(Type.INTERNAL, "setStatusInternalPackageName") { _, _, _ -> + setDomainVerificationStatusInternal( + TEST_PKG, + DomainVerificationManager.STATE_SUCCESS, + ArraySet(setOf("example.com")) + ) + }, + service(Type.INTERNAL, "setUserSelectionInternal") { _, _, userId -> + setDomainVerificationUserSelectionInternal( + userId, + TEST_PKG, + false, + ArraySet(setOf("example.com")) + ) + }, + service(Type.INTERNAL, "verifyPackages") { _, _, _ -> + verifyPackages(listOf(TEST_PKG), true) + }, + service(Type.INTERNAL, "clearState") { _, _, _ -> + clearDomainVerificationState(listOf(TEST_PKG)) + }, + service(Type.INTERNAL, "clearUserSelections") { _, _, userId -> + clearUserSelections(listOf(TEST_PKG), userId) + }, service(Type.VERIFIER, "getPackageNames") { _, _, _ -> validVerificationPackageNames }, From 0cab12964e3662ffef148243769b0520d090425a Mon Sep 17 00:00:00 2001 From: Winson Date: Thu, 17 Dec 2020 14:11:21 -0800 Subject: [PATCH 17/23] Use new always open during PMS Activity resolution Implements the behavior of either delegating to an approved app for a web domain, or the browsers, removing the "always ask" and "never" options for applications. This also considers cross profile intents, and will allow those to be shown if the other profile has an approved handler. This also migrates instant app state to the new behavior. Instant apps will now be granted completely immutable autoVerify approval, to preserve their automatic opening behavior. The user would have to disable the app in settings similar to an auto approved system app. This is flagged through USE_DOMAIN_VERIFICATION_V2. For now, both v1 and v2 run entirely parallel, until the v1 APIs are removed in a follow up. Exempt-From-Owner-Approval: Already approved by owners on main branch Bug: 163564991 Test: TODO Change-Id: I1a90f6b2b9bf2f108c5dc1b944985bc150c405a7 --- .../verify/DomainVerificationManager.java | 5 + .../verify/DomainVerificationState.java | 11 +- .../server/pm/PackageManagerService.java | 127 ++++++++++++---- .../DomainVerificationManagerInternal.java | 9 ++ .../verify/DomainVerificationService.java | 139 ++++++++++++++++-- .../verify/DomainVerificationUtils.java | 7 + 6 files changed, 257 insertions(+), 41 deletions(-) diff --git a/core/java/android/content/pm/domain/verify/DomainVerificationManager.java b/core/java/android/content/pm/domain/verify/DomainVerificationManager.java index a63e7d8ddf730..96adeb4cf76dd 100644 --- a/core/java/android/content/pm/domain/verify/DomainVerificationManager.java +++ b/core/java/android/content/pm/domain/verify/DomainVerificationManager.java @@ -100,6 +100,8 @@ public interface DomainVerificationManager { return "restored"; case DomainVerificationState.STATE_LEGACY_FAILURE: return "legacy_failure"; + case DomainVerificationState.STATE_SYS_CONFIG: + return "system_configured"; default: return String.valueOf(state); } @@ -115,6 +117,7 @@ public interface DomainVerificationManager { case DomainVerificationState.STATE_APPROVED: case DomainVerificationState.STATE_MIGRATED: case DomainVerificationState.STATE_RESTORED: + case DomainVerificationState.STATE_SYS_CONFIG: return true; case DomainVerificationState.STATE_NO_RESPONSE: case DomainVerificationState.STATE_DENIED: @@ -140,6 +143,7 @@ public interface DomainVerificationManager { return true; case DomainVerificationState.STATE_APPROVED: case DomainVerificationState.STATE_DENIED: + case DomainVerificationState.STATE_SYS_CONFIG: return false; default: return state >= DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED; @@ -161,6 +165,7 @@ public interface DomainVerificationManager { case DomainVerificationState.STATE_APPROVED: case DomainVerificationState.STATE_DENIED: case DomainVerificationState.STATE_LEGACY_FAILURE: + case DomainVerificationState.STATE_SYS_CONFIG: default: return false; } diff --git a/core/java/android/content/pm/domain/verify/DomainVerificationState.java b/core/java/android/content/pm/domain/verify/DomainVerificationState.java index a9adab91056eb..6e257b2fa9988 100644 --- a/core/java/android/content/pm/domain/verify/DomainVerificationState.java +++ b/core/java/android/content/pm/domain/verify/DomainVerificationState.java @@ -34,6 +34,7 @@ public interface DomainVerificationState { STATE_APPROVED, STATE_DENIED, STATE_LEGACY_FAILURE, + STATE_SYS_CONFIG, STATE_FIRST_VERIFIER_DEFINED }) @interface State { @@ -53,8 +54,6 @@ public interface DomainVerificationState { /** * The system has chosen to ignore the verification agent's opinion on whether the domain should * be verified. This will treat the domain as verified. - *

- * TODO: This currently combines SysConfig and instant app. Is it worth separating those? */ int STATE_APPROVED = 2; @@ -86,6 +85,14 @@ public interface DomainVerificationState { */ int STATE_LEGACY_FAILURE = 6; + /** + * The application has been granted auto verification for all domains by configuration on the + * system image. + * + * TODO: Can be stored per-package rather than for all domains for a package to save memory. + */ + int STATE_SYS_CONFIG = 7; + /** * @see DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED */ diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index df2f7a3ca8f52..86be023e1c3a0 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -238,6 +238,7 @@ import android.content.pm.VersionedPackage; import android.content.pm.dex.ArtManager; import android.content.pm.dex.DexMetadataHelper; import android.content.pm.dex.IArtManager; +import android.content.pm.domain.verify.DomainVerificationManager; import android.content.pm.parsing.ApkLiteParseUtils; import android.content.pm.parsing.PackageLite; import android.content.pm.parsing.ParsingPackageUtils; @@ -1624,6 +1625,8 @@ public class PackageManagerService extends IPackageManager.Stub private static final long DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD = 2 * 60 * 60 * 1000L; /* two hours */ + private static final boolean USE_DOMAIN_VERIFICATION_V2 = true; + final UserManagerService mUserManager; // Stores a list of users whose package restrictions file needs to be updated @@ -2154,6 +2157,7 @@ public class PackageManagerService extends IPackageManager.Stub private final ComponentResolver mComponentResolver; private final InstantAppResolverConnection mInstantAppResolverConnection; private final DefaultAppProvider mDefaultAppProvider; + private final DomainVerificationManagerInternal mDomainVerificationManager; // PackageManagerService attributes that are primitives are referenced through the // pms object directly. Primitives are the only attributes so referenced. @@ -2199,6 +2203,7 @@ public class PackageManagerService extends IPackageManager.Stub mComponentResolver = args.service.mComponentResolver; mInstantAppResolverConnection = args.service.mInstantAppResolverConnection; mDefaultAppProvider = args.service.mDefaultAppProvider; + mDomainVerificationManager = args.service.mDomainVerificationManager; // Used to reference PMS attributes that are primitives and which are not // updated under control of the PMS lock. @@ -2727,6 +2732,18 @@ public class PackageManagerService extends IPackageManager.Stub matchAllList.add(info); continue; } + + if (USE_DOMAIN_VERIFICATION_V2) { + boolean isAlways = mDomainVerificationManager + .isApprovedForDomain(ps, intent, userId); + if (isAlways) { + alwaysList.add(info); + } else { + undefinedList.add(info); + } + continue; + } + // Try to get the status from User settings first long packedStatus = IntentVerifyUtils.getDomainVerificationStatus(ps, userId); int status = (int)(packedStatus >> 32); @@ -2777,10 +2794,15 @@ public class PackageManagerService extends IPackageManager.Stub // Add all undefined apps as we want them to appear in the disambiguation dialog. result.addAll(undefinedList); // Maybe add one for the other profile. - if (xpDomainInfo != null && ( - xpDomainInfo.bestDomainVerificationStatus - != INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER)) { - result.add(xpDomainInfo.resolveInfo); + if (xpDomainInfo != null) { + if (USE_DOMAIN_VERIFICATION_V2) { + if (xpDomainInfo.wereAnyDomainsVerificationApproved) { + result.add(xpDomainInfo.resolveInfo); + } + } else if (xpDomainInfo.bestDomainVerificationStatus + != INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { + result.add(xpDomainInfo.resolveInfo); + } } includeBrowser = true; } @@ -2940,23 +2962,33 @@ public class PackageManagerService extends IPackageManager.Stub if (ps == null) { continue; } - long verificationState = - IntentVerifyUtils.getDomainVerificationStatus(ps, parentUserId); - int status = (int)(verificationState >> 32); if (result == null) { result = new CrossProfileDomainInfo(); result.resolveInfo = createForwardingResolveInfoUnchecked(new IntentFilter(), sourceUserId, parentUserId); - result.bestDomainVerificationStatus = status; + } + + if (USE_DOMAIN_VERIFICATION_V2) { + result.wereAnyDomainsVerificationApproved |= mDomainVerificationManager + .isApprovedForDomain(ps, intent, riTargetUser.targetUserId); } else { + long verificationState = + IntentVerifyUtils.getDomainVerificationStatus(ps, parentUserId); + int status = (int) (verificationState >> 32); result.bestDomainVerificationStatus = bestDomainVerificationStatus(status, result.bestDomainVerificationStatus); } } - // Don't consider matches with status NEVER across profiles. - if (result != null && result.bestDomainVerificationStatus - == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { - return null; + if (result != null) { + if (USE_DOMAIN_VERIFICATION_V2) { + if (!result.wereAnyDomainsVerificationApproved) { + return null; + } + } else if (result.bestDomainVerificationStatus + == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { + // Don't consider matches with status NEVER across profiles. + return null; + } } return result; } @@ -3198,6 +3230,25 @@ public class PackageManagerService extends IPackageManager.Stub final String packageName = info.activityInfo.packageName; final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps.getInstantApp(userId)) { + if (USE_DOMAIN_VERIFICATION_V2) { + if (mDomainVerificationManager + .isApprovedForDomain(ps, intent, userId)) { + if (DEBUG_INSTANT) { + Slog.v(TAG, "Instant app approvd for intent; pkg: " + + packageName); + } + localInstantApp = info; + break; + } else { + if (DEBUG_INSTANT) { + Slog.v(TAG, "Instant app not approved for intent; pkg: " + + packageName); + } + blockResolution = true; + break; + } + } + final long packedStatus = IntentVerifyUtils.getDomainVerificationStatus(ps, userId); final int status = (int)(packedStatus >> 32); @@ -4132,17 +4183,29 @@ public class PackageManagerService extends IPackageManager.Stub if (ps != null) { // 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 = - 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) { - if (DEBUG_INSTANT) { - Slog.v(TAG, "DENY instant app;" - + " pkg: " + packageName + ", status: " + status); + if (USE_DOMAIN_VERIFICATION_V2) { + if (mDomainVerificationManager + .isApprovedForDomain(ps, intent, userId)) { + if (DEBUG_INSTANT) { + Slog.v(TAG, "DENY instant app;" + " pkg: " + packageName + + ", approved"); + } + return false; + } + } else { + // Try to get the status from User settings first + 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) { + if (DEBUG_INSTANT) { + Slog.v(TAG, "DENY instant app;" + + " pkg: " + packageName + ", status: " + status); + } + return false; } - return false; } } if (ps.getInstantApp(userId)) { @@ -9573,11 +9636,18 @@ 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 = - IntentVerifyUtils.getDomainVerificationStatus(ps, userId); - final int status = (int)(packedStatus >> 32); - if (status != INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) { - return ri; + if (USE_DOMAIN_VERIFICATION_V2) { + if (ps != null && mDomainVerificationManager + .isApprovedForDomain(ps, intent, userId)) { + return ri; + } + } else { + final long packedStatus = + IntentVerifyUtils.getDomainVerificationStatus(ps, userId); + final int status = (int) (packedStatus >> 32); + if (status != INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) { + return ri; + } } } } @@ -10069,7 +10139,8 @@ public class PackageManagerService extends IPackageManager.Stub /* ResolveInfo for IntentForwarderActivity to send the intent to the other profile */ ResolveInfo resolveInfo; /* Best domain verification status of the activities found in the other profile */ - int bestDomainVerificationStatus; + int bestDomainVerificationStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER; + boolean wereAnyDomainsVerificationApproved; } private CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent, diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java index fec2f62848def..d0ee6375f7776 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java @@ -19,6 +19,7 @@ package com.android.server.pm.domain.verify; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.content.Intent; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.domain.verify.DomainVerificationManager; import android.content.pm.domain.verify.DomainVerificationSet; @@ -157,4 +158,12 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan @NonNull DomainVerificationShell getShell(); + + /** + * Check if a resolving URI is approved to takeover the domain as the sole resolved target. + * This can be because the domain was auto-verified for the package, or if the user manually + * chose to enable the domain for the package. + */ + boolean isApprovedForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent, + @UserIdInt int userId); } diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java index 8813d57671d0f..0df95e555e714 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.content.Context; +import android.content.Intent; import android.content.pm.IntentFilterVerificationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -70,6 +71,8 @@ public class DomainVerificationService extends SystemService private static final String TAG = "DomainVerificationService"; + public static final boolean DEBUG_APPROVAL = true; + /** * States that are currently alive and attached to a package. Entries are exclusive with the * state stored in {@link DomainVerificationSettings}, as any pending/restored state should be @@ -612,13 +615,10 @@ public class DomainVerificationService extends SystemService } boolean hasAutoVerifyDomains = newDomainsSize > 0; - boolean stateApplied = applyImmutableState(pkgName, newStateMap, newAutoVerifyDomains); + boolean needsBroadcast = + applyImmutableState(pkgName, newStateMap, newAutoVerifyDomains); - // TODO(b/159952358): sendBroadcast should be abstracted so it doesn't have to be aware - // of whether/what state was applied. Probably some method which iterates the map to - // check for any domains that actually have state changeable by the domain verification - // agent. - sendBroadcast = hasAutoVerifyDomains && !stateApplied; + sendBroadcast = hasAutoVerifyDomains && needsBroadcast; mAttachedPkgStates.put(pkgName, newDomainSetId, new DomainVerificationPkgState( pkgName, newDomainSetId, hasAutoVerifyDomains, newStateMap, newUserStates)); @@ -661,8 +661,8 @@ public class DomainVerificationService extends SystemService pkgState = new DomainVerificationPkgState(pkgName, domainSetId, hasAutoVerifyDomains); } - boolean stateApplied = applyImmutableState(pkgState, domains); - if (!stateApplied && !isPendingOrRestored) { + boolean needsBroadcast = applyImmutableState(pkgState, domains); + if (needsBroadcast && !isPendingOrRestored) { // TODO(b/159952358): Test this behavior // Attempt to preserve user experience by automatically verifying all domains from // legacy state if they were previously approved, or by automatically enabling all @@ -719,6 +719,8 @@ public class DomainVerificationService extends SystemService /** * Applies any immutable state as the final step when adding or migrating state. Currently only * applies {@link SystemConfig#getLinkedApps()}, which approves all domains for a package. + * + * @return whether or not a broadcast is necessary for this package */ private boolean applyImmutableState(@NonNull String packageName, @NonNull ArrayMap stateMap, @@ -727,12 +729,21 @@ public class DomainVerificationService extends SystemService int domainsSize = autoVerifyDomains.size(); for (int index = 0; index < domainsSize; index++) { stateMap.put(autoVerifyDomains.valueAt(index), - DomainVerificationState.STATE_APPROVED); + DomainVerificationState.STATE_SYS_CONFIG); } + return false; + } else { + int size = stateMap.size(); + for (int index = size - 1; index >= 0; index--) { + Integer state = stateMap.valueAt(index); + // If no longer marked in SysConfig, demote any previous SysConfig state + if (state == DomainVerificationState.STATE_SYS_CONFIG) { + stateMap.removeAt(index); + } + } + return true; } - - return false; } @Override @@ -1002,6 +1013,112 @@ public class DomainVerificationService extends SystemService } } + @Override + public boolean isApprovedForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent, + @UserIdInt int userId) { + String packageName = pkgSetting.name; + if (!DomainVerificationUtils.isDomainVerificationIntent(intent)) { + if (DEBUG_APPROVAL) { + debugApproval(packageName, intent, userId, false, "not valid intent"); + } + return false; + } + + // To allow an instant app to immediately open domains after being installed by the user, + // auto approve them for any declared autoVerify domains. + String host = intent.getData().getHost(); + final AndroidPackage pkg = pkgSetting.getPkg(); + if (pkgSetting.getInstantApp(userId) && pkg != null + && mCollector.collectAutoVerifyDomains(pkg).contains(host)) { + return true; + } + + synchronized (mLock) { + DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName); + if (pkgState == null) { + if (DEBUG_APPROVAL) { + debugApproval(packageName, intent, userId, false, "pkgState unavailable"); + } + return false; + } + + ArrayMap stateMap = pkgState.getStateMap(); + DomainVerificationUserState userState = pkgState.getUserSelectionState(userId); + + // Only allow autoVerify approval if the user hasn't disabled it + if (userState == null || !userState.isDisallowLinkHandling()) { + // Check if the exact host matches + Integer state = stateMap.get(host); + if (state != null && DomainVerificationManager.isStateVerified(state)) { + if (DEBUG_APPROVAL) { + debugApproval(packageName, intent, userId, true, "host verified exactly"); + } + return true; + } + + // Otherwise see if the host matches a verified domain by wildcard + int stateMapSize = stateMap.size(); + for (int index = 0; index < stateMapSize; index++) { + if (!DomainVerificationManager.isStateVerified(stateMap.valueAt(index))) { + continue; + } + + String domain = stateMap.keyAt(index); + if (domain.startsWith("*.") && host.endsWith(domain.substring(2))) { + if (DEBUG_APPROVAL) { + debugApproval(packageName, intent, userId, true, + "host verified by wildcard"); + } + return true; + } + } + } + + // Check user state if available + if (userState == null) { + if (DEBUG_APPROVAL) { + debugApproval(packageName, intent, userId, false, "userState unavailable"); + } + return false; + } + + // See if the user has approved the exact host + ArraySet enabledHosts = userState.getEnabledHosts(); + if (enabledHosts.contains(host)) { + if (DEBUG_APPROVAL) { + debugApproval(packageName, intent, userId, true, + "host enabled by user exactly"); + } + return true; + } + + // See if the host matches a user selection by wildcard + int enabledHostsSize = enabledHosts.size(); + for (int index = 0; index < enabledHostsSize; index++) { + String domain = enabledHosts.valueAt(index); + if (domain.startsWith("*.") && host.endsWith(domain.substring(2))) { + if (DEBUG_APPROVAL) { + debugApproval(packageName, intent, userId, true, + "host enabled by user through wildcard"); + } + return true; + } + } + + if (DEBUG_APPROVAL) { + debugApproval(packageName, intent, userId, false, "not approved"); + } + return false; + } + } + + private void debugApproval(@NonNull String packageName, @NonNull Intent intent, + @UserIdInt int userId, boolean approved, @NonNull String reason) { + String approvalString = approved ? "approved" : "denied"; + Slog.d(TAG + "Approval", packageName + " was " + approvalString + " for " + intent + + " for user " + userId + ": " + reason); + } + public interface Connection { /** diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationUtils.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationUtils.java index 0f902a1fc84f7..ff030710274e4 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationUtils.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationUtils.java @@ -18,6 +18,7 @@ package com.android.server.pm.domain.verify; import android.annotation.CheckResult; import android.annotation.NonNull; +import android.content.Intent; import android.content.pm.PackageManager.NameNotFoundException; final class DomainVerificationUtils { @@ -31,4 +32,10 @@ final class DomainVerificationUtils { throws NameNotFoundException { throw new NameNotFoundException("Package " + packageName + " unavailable"); } + + static boolean isDomainVerificationIntent(Intent intent) { + return intent.isWebIntent() + && intent.hasCategory(Intent.CATEGORY_BROWSABLE) + && intent.hasCategory(Intent.CATEGORY_DEFAULT); + } } From 346d2769b071d5156a99acc571700a4af7222a31 Mon Sep 17 00:00:00 2001 From: Winson Date: Thu, 17 Dec 2020 14:16:06 -0800 Subject: [PATCH 18/23] Add DomainVerificationProxyV1 Backporting the new per-domain state to the old v1 intent filter verification agent API. Uses a new STATE_LEGACY_FAILURE to track the failedDomains returned by the v1 verification agent, to support per-domain state with the old API. Exempt-From-Owner-Approval: Already approved by owners on main branch Bug: 170321181 Test: TBD in later change when proxies are combined Change-Id: I2953f7e0100d425dcb806c607fdeae101803d8f4 --- .../verify/DomainVerificationManager.java | 3 - .../server/pm/PackageManagerService.java | 25 +- .../DomainVerificationManagerInternal.java | 19 +- .../DomainVerificationMessageCodes.java | 1 + .../verify/DomainVerificationService.java | 31 ++- .../verify/proxy/DomainVerificationProxy.java | 5 +- .../proxy/DomainVerificationProxyV1.java | 260 ++++++++++++++++++ .../proxy/DomainVerificationProxyV2.java | 3 + .../verify/DomainVerificationEnforcerTest.kt | 8 + 9 files changed, 344 insertions(+), 11 deletions(-) create mode 100644 services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV1.java diff --git a/core/java/android/content/pm/domain/verify/DomainVerificationManager.java b/core/java/android/content/pm/domain/verify/DomainVerificationManager.java index 96adeb4cf76dd..ddae8f9d1295c 100644 --- a/core/java/android/content/pm/domain/verify/DomainVerificationManager.java +++ b/core/java/android/content/pm/domain/verify/DomainVerificationManager.java @@ -27,9 +27,6 @@ import android.content.Intent; import android.content.pm.PackageManager.NameNotFoundException; import android.os.UserHandle; import android.util.AndroidException; -import android.util.ArrayMap; - -import com.android.server.pm.domain.verify.models.DomainVerificationPkgState; import java.util.List; import java.util.Set; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 86be023e1c3a0..0a7787b659c13 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -381,6 +381,7 @@ import com.android.server.pm.dex.ViewCompiler; import com.android.server.pm.domain.verify.DomainVerificationManagerInternal; import com.android.server.pm.domain.verify.DomainVerificationService; import com.android.server.pm.domain.verify.proxy.DomainVerificationProxy; +import com.android.server.pm.domain.verify.proxy.DomainVerificationProxyV1; import com.android.server.pm.domain.verify.proxy.DomainVerificationProxyV2; import com.android.server.pm.intent.verify.legacy.IntentFilterVerificationManager; import com.android.server.pm.intent.verify.legacy.IntentFilterVerificationParams; @@ -1827,7 +1828,8 @@ public class PackageManagerService extends IPackageManager.Stub new DomainVerificationConnection(); private class DomainVerificationConnection implements - DomainVerificationService.Connection, DomainVerificationProxy.Connection { + DomainVerificationService.Connection, DomainVerificationProxyV1.Connection, + DomainVerificationProxyV2.Connection { @Override public void scheduleWriteSettings() { @@ -1882,6 +1884,12 @@ public class PackageManagerService extends IPackageManager.Stub public AndroidPackage getPackageLocked(@NonNull String pkgName) { return PackageManagerService.this.getPackage(pkgName); } + + @Nullable + @Override + public AndroidPackage getPackage(@NonNull String packageName) { + return getPackageLocked(packageName); + } } /** @@ -7073,8 +7081,17 @@ public class PackageManagerService extends IPackageManager.Stub domainVerificationAgent)); } else { // TODO(b/159952358): DomainVerificationProxyV1 - mIntentFilterVerificationManager.setVerifierComponent( - getIntentFilterVerifierComponentNameLPr()); + ComponentName intentFilterVerifierComponent = + getIntentFilterVerifierComponentNameLPr(); + if (intentFilterVerifierComponent != null) { + mDomainVerificationManager.setProxy( + new DomainVerificationProxyV1(mContext, mDomainVerificationManager, + mDomainVerificationManager.getCollector(), + mDomainVerificationConnection, + intentFilterVerifierComponent)); + mIntentFilterVerificationManager.setVerifierComponent( + intentFilterVerifierComponent); + } } mServicesExtensionPackageName = getRequiredServicesExtensionPackageLPr(); @@ -16427,6 +16444,8 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void verifyIntentFilter(int id, int verificationCode, List failedDomains) { + DomainVerificationProxyV1.queueLegacyVerifyResult(mContext, mDomainVerificationConnection, + id, verificationCode, failedDomains, Binder.getCallingUid()); mIntentFilterVerificationManager.queueVerifyResult(id, verificationCode, failedDomains); } diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java index d0ee6375f7776..9ef498780393b 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java @@ -18,6 +18,7 @@ package com.android.server.pm.domain.verify; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.UserIdInt; import android.content.Intent; import android.content.pm.PackageManager.NameNotFoundException; @@ -34,6 +35,7 @@ import com.android.server.pm.domain.verify.proxy.DomainVerificationProxy; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; +import java.util.Set; import java.util.UUID; public interface DomainVerificationManagerInternal extends DomainVerificationManager { @@ -57,7 +59,7 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan void setProxy(@NonNull DomainVerificationProxy proxy); /** - * @see DomainVerificationProxy.Connection#runMessage(int, Object) + * @see DomainVerificationProxy.BaseConnection#runMessage(int, Object) */ boolean runMessage(int messageCode, Object object); @@ -159,6 +161,9 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan @NonNull DomainVerificationShell getShell(); + @NonNull + DomainVerificationCollector getCollector(); + /** * Check if a resolving URI is approved to takeover the domain as the sole resolved target. * This can be because the domain was auto-verified for the package, or if the user manually @@ -166,4 +171,16 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan */ boolean isApprovedForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent, @UserIdInt int userId); + + /** + * @return the domain verification set ID for the given package, or null if the ID is + * unavailable + */ + @Nullable + UUID getDomainVerificationSetId(@NonNull String packageName); + + @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) + void setDomainVerificationStatusInternal(int callingUid, @NonNull UUID domainSetId, + @NonNull Set domains, int state) + throws InvalidDomainSetException, NameNotFoundException; } diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationMessageCodes.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationMessageCodes.java index f4bf96bae59a9..feb3be7a2a3f0 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationMessageCodes.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationMessageCodes.java @@ -31,4 +31,5 @@ import com.android.server.pm.domain.verify.proxy.DomainVerificationProxy; public final class DomainVerificationMessageCodes { public static final int SEND_REQUEST = 1; + public static final int LEGACY_ON_INTENT_FILTER_VERIFIED = 2; } diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java index 0df95e555e714..66f0a6d2dd2ae 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java @@ -175,6 +175,19 @@ public class DomainVerificationService extends SystemService return packageNames; } + @Nullable + @Override + public UUID getDomainVerificationSetId(@NonNull String packageName) { + synchronized (mLock) { + DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName); + if (pkgState != null) { + return pkgState.getId(); + } else { + return null; + } + } + } + @Nullable @Override public DomainVerificationSet getDomainVerificationSet(@NonNull String packageName) @@ -213,7 +226,6 @@ public class DomainVerificationService extends SystemService @Override public void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set domains, int state) throws InvalidDomainSetException, NameNotFoundException { - mEnforcer.assertApprovedVerifier(mConnection.get().getCallingUid(), mProxy); if (state < DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED) { if (state != DomainVerificationState.STATE_SUCCESS) { throw new IllegalArgumentException( @@ -222,6 +234,15 @@ public class DomainVerificationService extends SystemService } } + setDomainVerificationStatusInternal(mConnection.get().getCallingUid(), domainSetId, domains, + state); + } + + @Override + public void setDomainVerificationStatusInternal(int callingUid, @NonNull UUID domainSetId, + @NonNull Set domains, int state) + throws InvalidDomainSetException, NameNotFoundException { + mEnforcer.assertApprovedVerifier(callingUid, mProxy); synchronized (mLock) { DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains, true /* forAutoVerify */); @@ -811,6 +832,12 @@ public class DomainVerificationService extends SystemService return mShell; } + @NonNull + @Override + public DomainVerificationCollector getCollector() { + return mCollector; + } + private void sendBroadcastForPackage(@NonNull String packageName) { mProxy.sendBroadcastForPackages(Collections.singleton(packageName)); } @@ -1139,7 +1166,7 @@ public class DomainVerificationService extends SystemService int getCallingUserId(); /** - * @see DomainVerificationProxy.Connection#schedule(int, java.lang.Object) + * @see DomainVerificationProxy.BaseConnection#schedule(int, java.lang.Object) */ void schedule(int code, @Nullable Object object); diff --git a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxy.java b/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxy.java index b311c0f2c2c70..2582230984bb8 100644 --- a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxy.java +++ b/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxy.java @@ -25,13 +25,14 @@ import com.android.server.pm.domain.verify.DomainVerificationMessageCodes; import java.util.Set; +// TODO(b/170321181): Combine the proxy versions for supporting v1 and v2 at once public interface DomainVerificationProxy { default void sendBroadcastForPackages(@NonNull Set packageNames) { } /** - * Runs a message on the caller's Handler as a result of {@link Connection#schedule(int, + * Runs a message on the caller's Handler as a result of {@link BaseConnection#schedule(int, * Object)}. Abstracts the actual scheduling/running from the manager class. This is also * necessary so that different what codes can be used depending on the verifier proxy on device, * to allow backporting v1. The backport proxy may schedule more or less messages than the v2 @@ -53,7 +54,7 @@ public interface DomainVerificationProxy { return null; } - interface Connection { + interface BaseConnection { /** * Schedule something to be run later. The implementation is left up to the caller. diff --git a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV1.java b/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV1.java new file mode 100644 index 0000000000000..6229ede45988c --- /dev/null +++ b/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV1.java @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2021 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.domain.verify.proxy; + +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; +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.domain.verify.DomainVerificationManager; +import android.content.pm.domain.verify.DomainVerificationSet; +import android.content.pm.domain.verify.DomainVerificationState; +import android.os.Process; +import android.os.UserHandle; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.Pair; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; +import com.android.server.pm.domain.verify.DomainVerificationCollector; +import com.android.server.pm.domain.verify.DomainVerificationManagerInternal; +import com.android.server.pm.domain.verify.DomainVerificationMessageCodes; +import com.android.server.pm.parsing.pkg.AndroidPackage; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; + +public class DomainVerificationProxyV1 implements DomainVerificationProxy { + + private static final String TAG = "DomainVerificationProxyV1"; + + private static final boolean DEBUG_BROADCASTS = false; + + @NonNull + private final Context mContext; + + @NonNull + private final Connection mConnection; + + @NonNull + private final ComponentName mVerifierComponent; + + @NonNull + private final DomainVerificationManagerInternal mManager; + + @NonNull + private final DomainVerificationCollector mCollector; + + @NonNull + private final Object mLock = new Object(); + + @NonNull + @GuardedBy("mLock") + private final ArrayMap> mRequests = new ArrayMap<>(); + + // TODO(b/159952358): For now, IDs start at a really high number to avoid conflict with the + // legacy manager, which is still active in code. Should be set to 0 once + // IntentFilterVerificationManager is removed. + @GuardedBy("mLock") + private int mVerificationToken = Integer.MAX_VALUE / 2; + + public DomainVerificationProxyV1(@NonNull Context context, + @NonNull DomainVerificationManagerInternal manager, + @NonNull DomainVerificationCollector collector, @NonNull Connection connection, + @NonNull ComponentName verifierComponent) { + mContext = context; + mConnection = connection; + mVerifierComponent = verifierComponent; + mManager = manager; + mCollector = collector; + } + + public static void queueLegacyVerifyResult(@NonNull Context context, + @NonNull DomainVerificationProxyV1.Connection connection, int verificationId, + int verificationCode, @Nullable List failedDomains, int callingUid) { + context.enforceCallingOrSelfPermission( + Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT, + "Only the intent filter verification agent can verify applications"); + + connection.schedule(DomainVerificationMessageCodes.LEGACY_ON_INTENT_FILTER_VERIFIED, + new Response(callingUid, verificationId, verificationCode, failedDomains)); + } + + @Override + public void sendBroadcastForPackages(@NonNull Set packageNames) { + synchronized (mLock) { + int size = mRequests.size(); + for (int index = size - 1; index >= 0; index--) { + Pair pair = mRequests.valueAt(index); + if (packageNames.contains(pair.second)) { + mRequests.removeAt(index); + } + } + } + mConnection.schedule(DomainVerificationMessageCodes.SEND_REQUEST, packageNames); + } + + @SuppressWarnings("deprecation") + @Override + public boolean runMessage(int messageCode, Object object) { + switch (messageCode) { + case DomainVerificationMessageCodes.SEND_REQUEST: + @SuppressWarnings("unchecked") Set packageNames = (Set) object; + if (DEBUG_BROADCASTS) { + Slog.d(TAG, "Requesting domain verification for " + packageNames); + } + + ArrayMap> newRequests = new ArrayMap<>( + packageNames.size()); + synchronized (mLock) { + for (String packageName : packageNames) { + UUID domainSetId = mManager.getDomainVerificationSetId(packageName); + if (domainSetId == null) { + continue; + } + + newRequests.put(mVerificationToken++, + Pair.create(domainSetId, packageName)); + } + mRequests.putAll(newRequests); + } + + sendBroadcasts(newRequests); + return true; + case DomainVerificationMessageCodes.LEGACY_ON_INTENT_FILTER_VERIFIED: + Response response = (Response) object; + + Pair pair = mRequests.get(response.verificationId); + if (pair == null) { + return true; + } + + UUID domainSetId = pair.first; + String packageName = pair.second; + DomainVerificationSet set; + try { + set = mManager.getDomainVerificationSet(packageName); + } catch (PackageManager.NameNotFoundException ignored) { + return true; + } + + if (!Objects.equals(domainSetId, set.getIdentifier())) { + return true; + } + + Set successfulDomains = new ArraySet<>(set.getHostToStateMap().keySet()); + successfulDomains.removeAll(response.failedDomains); + + int callingUid = response.callingUid; + try { + mManager.setDomainVerificationStatusInternal(callingUid, domainSetId, + successfulDomains, DomainVerificationState.STATE_SUCCESS); + } catch (DomainVerificationManager.InvalidDomainSetException + | PackageManager.NameNotFoundException ignored) { + } + try { + mManager.setDomainVerificationStatusInternal(callingUid, domainSetId, + new ArraySet<>(response.failedDomains), + DomainVerificationState.STATE_LEGACY_FAILURE); + } catch (DomainVerificationManager.InvalidDomainSetException + | PackageManager.NameNotFoundException ignored) { + } + + return true; + default: + return false; + } + } + + @Override + public boolean isCallerVerifier(int callingUid) { + return mConnection.isCallerPackage(callingUid, mVerifierComponent.getPackageName()); + } + + @SuppressWarnings("deprecation") + private void sendBroadcasts(@NonNull ArrayMap> verifications) { + final long allowListTimeout = mConnection.getPowerSaveTempWhitelistAppDuration(); + mConnection.getDeviceIdleInternal().addPowerSaveTempWhitelistApp(Process.myUid(), + mVerifierComponent.getPackageName(), allowListTimeout, + UserHandle.USER_SYSTEM, true, "domain verification agent"); + + int size = verifications.size(); + for (int index = 0; index < size; index++) { + int verificationId = verifications.keyAt(index); + String packageName = verifications.valueAt(index).second; + AndroidPackage pkg = mConnection.getPackage(packageName); + + String hostsString = buildHostsString(pkg); + + Intent intent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION) + .setComponent(mVerifierComponent) + .putExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID, + verificationId) + .putExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME, + IntentFilter.SCHEME_HTTPS) + .putExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS, + hostsString) + .putExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME, + packageName) + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + + final BroadcastOptions options = BroadcastOptions.makeBasic(); + options.setTemporaryAppWhitelistDuration(allowListTimeout); + mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM, null, options.toBundle()); + } + } + + @NonNull + private String buildHostsString(@NonNull AndroidPackage pkg) { + // The collector itself handles the v1 vs v2 behavior, which is based on targetSdkVersion, + // not the version of the verification agent on device. + ArraySet domains = mCollector.collectAutoVerifyDomains(pkg); + return TextUtils.join(" ", domains); + } + + private static class Response { + public final int callingUid; + public final int verificationId; + public final int verificationCode; + @NonNull + public final List failedDomains; + + private Response(int callingUid, int verificationId, int verificationCode, + @Nullable List failedDomains) { + this.callingUid = callingUid; + this.verificationId = verificationId; + this.verificationCode = verificationCode; + this.failedDomains = failedDomains == null ? Collections.emptyList() : failedDomains; + } + } + + public interface Connection extends BaseConnection { + + @Nullable + AndroidPackage getPackage(@NonNull String packageName); + } +} diff --git a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV2.java b/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV2.java index e483268be3a6d..d9d03813bb2bc 100644 --- a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV2.java +++ b/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV2.java @@ -100,4 +100,7 @@ public class DomainVerificationProxyV2 implements DomainVerificationProxy { public ComponentName getComponentName() { return mVerifierComponent; } + + public interface Connection extends BaseConnection { + } } diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationEnforcerTest.kt index 36e683cf00b49..13a37ffe77d69 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationEnforcerTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationEnforcerTest.kt @@ -241,6 +241,14 @@ class DomainVerificationEnforcerTest { DomainVerificationManager.STATE_SUCCESS ) }, + service(Type.VERIFIER, "setStatusInternalUid") { callingUid, _, _ -> + setDomainVerificationStatusInternal( + callingUid, + uuid, + setOf("example.com"), + DomainVerificationManager.STATE_SUCCESS + ) + }, service(Type.SELECTOR, "setLinkHandlingAllowed") { _, _, _ -> setDomainVerificationLinkHandlingAllowed(TEST_PKG, true) }, From c89bd75a18c53e7205eb0bbfbb4179edcec465c1 Mon Sep 17 00:00:00 2001 From: Winson Date: Mon, 21 Dec 2020 13:58:37 -0800 Subject: [PATCH 19/23] Combine v1 and v2 DomainVerficationProxies In case both v1 and v2 are on the device, have a delegate that supports sending requests to both receivers. The verification agent will then decide which API to use. This allows deferred migration to the v2 APIs. Note that the old user state API is still non-functional, even for a v1 verification agent. Exempt-From-Owner-Approval: Already approved by owners on main branch Bug: 170321181 Test: atest DomainVerificationProxyTest Change-Id: I951fec5690c1f0d848d86c8859c3e9a171fca887 --- .../server/pm/PackageManagerService.java | 30 +- .../DomainVerificationMessageCodes.java | 5 +- .../verify/DomainVerificationService.java | 2 - .../verify/proxy/DomainVerificationProxy.java | 52 ++ .../DomainVerificationProxyCombined.java | 54 ++ .../proxy/DomainVerificationProxyV1.java | 4 +- .../proxy/DomainVerificationProxyV2.java | 2 +- .../verify/DomainVerificationProxyTest.kt | 496 ++++++++++++++++++ 8 files changed, 621 insertions(+), 24 deletions(-) create mode 100644 services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyCombined.java create mode 100644 services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationProxyTest.kt diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 0a7787b659c13..42d900f04060c 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -7073,25 +7073,21 @@ public class PackageManagerService extends IPackageManager.Stub mRequiredVerifierPackage = getRequiredButNotReallyRequiredVerifierLPr(); mRequiredInstallerPackage = getRequiredInstallerLPr(); mRequiredUninstallerPackage = getRequiredUninstallerLPr(); + ComponentName intentFilterVerifierComponent = + getIntentFilterVerifierComponentNameLPr(); ComponentName domainVerificationAgent = getDomainVerificationAgentComponentNameLPr(); - if (domainVerificationAgent != null) { - mDomainVerificationManager.setProxy( - new DomainVerificationProxyV2(mContext, mDomainVerificationConnection, - domainVerificationAgent)); - } else { - // TODO(b/159952358): DomainVerificationProxyV1 - ComponentName intentFilterVerifierComponent = - getIntentFilterVerifierComponentNameLPr(); - if (intentFilterVerifierComponent != null) { - mDomainVerificationManager.setProxy( - new DomainVerificationProxyV1(mContext, mDomainVerificationManager, - mDomainVerificationManager.getCollector(), - mDomainVerificationConnection, - intentFilterVerifierComponent)); - mIntentFilterVerificationManager.setVerifierComponent( - intentFilterVerifierComponent); - } + + DomainVerificationProxy domainVerificationProxy = DomainVerificationProxy.makeProxy( + intentFilterVerifierComponent, domainVerificationAgent, mContext, + mDomainVerificationManager, mDomainVerificationManager.getCollector(), + mDomainVerificationConnection); + + mDomainVerificationManager.setProxy(domainVerificationProxy); + + if (intentFilterVerifierComponent != null) { + mIntentFilterVerificationManager.setVerifierComponent( + intentFilterVerifierComponent); } mServicesExtensionPackageName = getRequiredServicesExtensionPackageLPr(); diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationMessageCodes.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationMessageCodes.java index feb3be7a2a3f0..7fb0067738ad9 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationMessageCodes.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationMessageCodes.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2021 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. @@ -31,5 +31,6 @@ import com.android.server.pm.domain.verify.proxy.DomainVerificationProxy; public final class DomainVerificationMessageCodes { public static final int SEND_REQUEST = 1; - public static final int LEGACY_ON_INTENT_FILTER_VERIFIED = 2; + public static final int LEGACY_SEND_REQUEST = 2; + public static final int LEGACY_ON_INTENT_FILTER_VERIFIED = 3; } diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java index 66f0a6d2dd2ae..0a28069017d46 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java @@ -1170,8 +1170,6 @@ public class DomainVerificationService extends SystemService */ void schedule(int code, @Nullable Object object); - boolean isCallerPackage(int callingUid, @NonNull String packageName); - /** * This can only be called when the internal {@link #mLock} is held. Otherwise it's possible * to deadlock with {@link PackageManagerService}. diff --git a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxy.java b/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxy.java index 2582230984bb8..c641caaa8c1bb 100644 --- a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxy.java +++ b/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxy.java @@ -19,15 +19,67 @@ package com.android.server.pm.domain.verify.proxy; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; +import android.content.Context; +import android.util.Slog; import com.android.server.DeviceIdleInternal; import com.android.server.pm.domain.verify.DomainVerificationMessageCodes; +import com.android.server.pm.domain.verify.DomainVerificationCollector; +import com.android.server.pm.domain.verify.DomainVerificationManagerInternal; +import java.util.Objects; import java.util.Set; // TODO(b/170321181): Combine the proxy versions for supporting v1 and v2 at once public interface DomainVerificationProxy { + String TAG = "DomainVerificationProxy"; + + boolean DEBUG_PROXIES = false; + + static DomainVerificationProxy makeProxy( + @Nullable ComponentName componentV1, @Nullable ComponentName componentV2, + @NonNull Context context, @NonNull DomainVerificationManagerInternal manager, + @NonNull DomainVerificationCollector collector, @NonNull ConnectionType connection) { + if (DEBUG_PROXIES) { + Slog.d(TAG, "Intent filter verification agent: " + componentV1); + Slog.d(TAG, "Domain verification agent: " + componentV2); + } + + if (componentV2 != null && componentV1 != null + && !Objects.equals(componentV2.getPackageName(), componentV1.getPackageName())) { + // Only allow a legacy verifier if it's in the same package as the v2 verifier + componentV1 = null; + } + + DomainVerificationProxy proxyV1 = null; + DomainVerificationProxy proxyV2 = null; + + if (componentV1 != null) { + proxyV1 = new DomainVerificationProxyV1(context, manager, collector, connection, + componentV1); + } + + if (componentV2 != null) { + proxyV2 = new DomainVerificationProxyV2(context, connection, componentV2); + } + + if (proxyV1 != null && proxyV2 != null) { + return new DomainVerificationProxyCombined(proxyV1, proxyV2); + } + + if (proxyV1 != null) { + return proxyV1; + } + + if (proxyV2 != null) { + return proxyV2; + } + + return new DomainVerificationProxyUnavailable(); + } + default void sendBroadcastForPackages(@NonNull Set packageNames) { } diff --git a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyCombined.java b/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyCombined.java new file mode 100644 index 0000000000000..eb63d52c0d1f5 --- /dev/null +++ b/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyCombined.java @@ -0,0 +1,54 @@ +/* + * 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.domain.verify.proxy; + +import android.annotation.NonNull; + +import java.util.Set; + +class DomainVerificationProxyCombined implements DomainVerificationProxy { + + @NonNull + private final DomainVerificationProxy mProxyV1; + @NonNull + private final DomainVerificationProxy mProxyV2; + + DomainVerificationProxyCombined(@NonNull DomainVerificationProxy proxyV1, + @NonNull DomainVerificationProxy proxyV2) { + mProxyV1 = proxyV1; + mProxyV2 = proxyV2; + } + + @Override + public void sendBroadcastForPackages(@NonNull Set packageNames) { + mProxyV2.sendBroadcastForPackages(packageNames); + mProxyV1.sendBroadcastForPackages(packageNames); + } + + @Override + public boolean runMessage(int messageCode, Object object) { + // Both proxies must run, so cannot use a direct ||, which may skip the right hand side + boolean resultV2 = mProxyV2.runMessage(messageCode, object); + boolean resultV1 = mProxyV1.runMessage(messageCode, object); + return resultV2 || resultV1; + } + + @Override + public boolean isCallerVerifier(int callingUid) { + return mProxyV2.isCallerVerifier(callingUid) || mProxyV1.isCallerVerifier(callingUid); + } +} diff --git a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV1.java b/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV1.java index 6229ede45988c..c156c3461daea 100644 --- a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV1.java +++ b/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV1.java @@ -115,14 +115,14 @@ public class DomainVerificationProxyV1 implements DomainVerificationProxy { } } } - mConnection.schedule(DomainVerificationMessageCodes.SEND_REQUEST, packageNames); + mConnection.schedule(DomainVerificationMessageCodes.LEGACY_SEND_REQUEST, packageNames); } @SuppressWarnings("deprecation") @Override public boolean runMessage(int messageCode, Object object) { switch (messageCode) { - case DomainVerificationMessageCodes.SEND_REQUEST: + case DomainVerificationMessageCodes.LEGACY_SEND_REQUEST: @SuppressWarnings("unchecked") Set packageNames = (Set) object; if (DEBUG_BROADCASTS) { Slog.d(TAG, "Requesting domain verification for " + packageNames); diff --git a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV2.java b/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV2.java index d9d03813bb2bc..1595374dd1d3e 100644 --- a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV2.java +++ b/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV2.java @@ -56,7 +56,7 @@ public class DomainVerificationProxyV2 implements DomainVerificationProxy { @Override public void sendBroadcastForPackages(@NonNull Set packageNames) { - mConnection.schedule(DomainVerificationMessageCodes.SEND_REQUEST, packageNames); + mConnection.schedule(com.android.server.pm.domain.verify.DomainVerificationMessageCodes.SEND_REQUEST, packageNames); } @Override diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationProxyTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationProxyTest.kt new file mode 100644 index 0000000000000..7519ff013458e --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationProxyTest.kt @@ -0,0 +1,496 @@ +/* + * Copyright (C) 2021 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.test.domain.verify + +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.domain.verify.DomainVerificationManager +import android.content.pm.domain.verify.DomainVerificationRequest +import android.content.pm.domain.verify.DomainVerificationSet +import android.content.pm.domain.verify.DomainVerificationState +import android.os.Bundle +import android.os.UserHandle +import android.util.ArraySet +import com.android.server.DeviceIdleInternal +import com.android.server.pm.domain.verify.DomainVerificationCollector +import com.android.server.pm.domain.verify.DomainVerificationManagerInternal +import com.android.server.pm.domain.verify.proxy.DomainVerificationProxy +import com.android.server.pm.domain.verify.proxy.DomainVerificationProxyV1 +import com.android.server.pm.domain.verify.proxy.DomainVerificationProxyV2 +import com.android.server.pm.parsing.pkg.AndroidPackage +import com.android.server.testutils.mockThrowOnUnmocked +import com.android.server.testutils.whenever +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.mockito.ArgumentCaptor +import org.mockito.Captor +import org.mockito.Mockito.any +import org.mockito.Mockito.anyBoolean +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.anyLong +import org.mockito.Mockito.anyString +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.eq +import org.mockito.Mockito.isNull +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.Mockito.verifyNoMoreInteractions +import org.mockito.MockitoAnnotations +import java.util.UUID + +@Suppress("DEPRECATION") +class DomainVerificationProxyTest { + + companion object { + private const val TEST_PKG_NAME_ONE = "com.test.pkg.one" + private const val TEST_PKG_NAME_TWO = "com.test.pkg.two" + private const val TEST_PKG_NAME_TARGET_ONE = "com.test.target.one" + private const val TEST_PKG_NAME_TARGET_TWO = "com.test.target.two" + private const val TEST_CALLING_UID_ACCEPT = 40 + private const val TEST_CALLING_UID_REJECT = 41 + private val TEST_UUID_ONE = UUID.fromString("f7fbb7dd-7b5f-4609-a95e-c6c7765fb9cd") + private val TEST_UUID_TWO = UUID.fromString("4a09b361-a967-43ac-9d18-07a385dff740") + } + + private val componentOne = ComponentName(TEST_PKG_NAME_ONE, ".ReceiverOne") + private val componentTwo = ComponentName(TEST_PKG_NAME_TWO, ".ReceiverTwo") + private val componentThree = ComponentName(TEST_PKG_NAME_TWO, ".ReceiverThree") + + private lateinit var context: Context + private lateinit var manager: DomainVerificationManagerInternal + private lateinit var collector: DomainVerificationCollector + + // Must be declared as field to support generics + @Captor + lateinit var hostCaptor: ArgumentCaptor> + + @Before + fun setUpMocks() { + MockitoAnnotations.initMocks(this) + context = mockThrowOnUnmocked { + whenever(sendBroadcastAsUser(any(), any(), any(), any())) + whenever( + enforceCallingOrSelfPermission( + eq(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT), + anyString() + ) + ) + } + manager = mockThrowOnUnmocked { + whenever(getDomainVerificationSetId(any())) { + when (val pkgName = arguments[0] as String) { + TEST_PKG_NAME_TARGET_ONE -> TEST_UUID_ONE + TEST_PKG_NAME_TARGET_TWO -> TEST_UUID_TWO + else -> throw IllegalArgumentException("Unexpected package name $pkgName") + } + } + whenever(getDomainVerificationSet(anyString())) { + when (val pkgName = arguments[0] as String) { + TEST_PKG_NAME_TARGET_ONE -> DomainVerificationSet(TEST_UUID_ONE, pkgName, mapOf( + "example1.com" to DomainVerificationManager.STATE_NO_RESPONSE, + "example2.com" to DomainVerificationManager.STATE_NO_RESPONSE + )) + TEST_PKG_NAME_TARGET_TWO -> DomainVerificationSet(TEST_UUID_TWO, pkgName, mapOf( + "example3.com" to DomainVerificationManager.STATE_NO_RESPONSE, + "example4.com" to DomainVerificationManager.STATE_NO_RESPONSE + )) + else -> throw IllegalArgumentException("Unexpected package name $pkgName") + } + } + whenever(setDomainVerificationStatusInternal(anyInt(), any(), any(), anyInt())) + } + collector = mockThrowOnUnmocked { + whenever(collectAutoVerifyDomains(any())) { + when (val pkgName = (arguments[0] as AndroidPackage).packageName) { + TEST_PKG_NAME_TARGET_ONE -> ArraySet(setOf("example1.com", "example2.com")) + TEST_PKG_NAME_TARGET_TWO -> ArraySet(setOf("example3.com", "example4.com")) + else -> throw IllegalArgumentException("Unexpected package name $pkgName") + } + } + } + } + + @Test + fun isCallerVerifierV1() { + val connection = mockConnection() + val proxyV1 = DomainVerificationProxy.makeProxy( + componentOne, null, context, + manager, collector, connection + ) + + assertThat(proxyV1.isCallerVerifier(TEST_CALLING_UID_ACCEPT)).isTrue() + verify(connection).isCallerPackage(TEST_CALLING_UID_ACCEPT, TEST_PKG_NAME_ONE) + verifyNoMoreInteractions(connection) + clearInvocations(connection) + + assertThat(proxyV1.isCallerVerifier(TEST_CALLING_UID_REJECT)).isFalse() + verify(connection).isCallerPackage(TEST_CALLING_UID_REJECT, TEST_PKG_NAME_ONE) + verifyNoMoreInteractions(connection) + } + + @Test + fun isCallerVerifierV2() { + val connection = mockConnection() + val proxyV2 = DomainVerificationProxy.makeProxy( + null, componentTwo, context, + manager, collector, connection + ) + + assertThat(proxyV2.isCallerVerifier(TEST_CALLING_UID_ACCEPT)).isTrue() + verify(connection).isCallerPackage(TEST_CALLING_UID_ACCEPT, TEST_PKG_NAME_TWO) + verifyNoMoreInteractions(connection) + clearInvocations(connection) + + assertThat(proxyV2.isCallerVerifier(TEST_CALLING_UID_REJECT)).isFalse() + verify(connection).isCallerPackage(TEST_CALLING_UID_REJECT, TEST_PKG_NAME_TWO) + verifyNoMoreInteractions(connection) + } + + @Test + fun isCallerVerifierBoth() { + val connection = mockConnection() + val proxyBoth = DomainVerificationProxy.makeProxy( + componentTwo, componentThree, + context, manager, collector, connection + ) + + // The combined proxy should only ever call v2 when it succeeds + assertThat(proxyBoth.isCallerVerifier(TEST_CALLING_UID_ACCEPT)).isTrue() + verify(connection).isCallerPackage(TEST_CALLING_UID_ACCEPT, TEST_PKG_NAME_TWO) + verifyNoMoreInteractions(connection) + clearInvocations(connection) + + val callingUidCaptor = ArgumentCaptor.forClass(Int::class.java) + + // But will call both when v2 fails + assertThat(proxyBoth.isCallerVerifier(TEST_CALLING_UID_REJECT)).isFalse() + verify(connection, times(2)) + .isCallerPackage(callingUidCaptor.capture(), eq(TEST_PKG_NAME_TWO)) + verifyNoMoreInteractions(connection) + + assertThat(callingUidCaptor.allValues.toSet()).containsExactly(TEST_CALLING_UID_REJECT) + } + + @Test + fun differentPackagesResolvesOnlyV2() { + assertThat(DomainVerificationProxy.makeProxy( + componentOne, componentTwo, + context, manager, collector, mockConnection() + )).isInstanceOf(DomainVerificationProxyV2::class.java) + } + + private fun prepareProxyV1(): ProxyV1Setup { + val messages = mutableListOf>() + val connection = mockConnection { + whenever(schedule(anyInt(), any())) { + messages.add((arguments[0] as Int) to arguments[1]) + } + } + + val proxy = DomainVerificationProxy.makeProxy( + componentOne, + null, + context, + manager, + collector, + connection + ) + return ProxyV1Setup(messages, connection, proxy) + } + + @Test + fun sendBroadcastForPackagesV1() { + val (messages, _, proxy) = prepareProxyV1() + + proxy.sendBroadcastForPackages(setOf(TEST_PKG_NAME_TARGET_ONE, TEST_PKG_NAME_TARGET_TWO)) + messages.forEach { (code, value) -> proxy.runMessage(code, value) } + + val intentCaptor = ArgumentCaptor.forClass(Intent::class.java) + + verify(context, times(2)).sendBroadcastAsUser( + intentCaptor.capture(), eq(UserHandle.SYSTEM), isNull(), any() + ) + verifyNoMoreInteractions(context) + + val intents = intentCaptor.allValues + assertThat(intents).hasSize(2) + intents.forEach { + assertThat(it.action).isEqualTo(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION) + assertThat(it.getStringExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME)) + .isEqualTo(IntentFilter.SCHEME_HTTPS) + assertThat(it.getIntExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID, -1)) + .isNotEqualTo(-1) + } + + intents[0].apply { + assertThat(getStringExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME)) + .isEqualTo(TEST_PKG_NAME_TARGET_ONE) + assertThat(getStringExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS)) + .isEqualTo("example1.com example2.com") + } + + intents[1].apply { + assertThat(getStringExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME)) + .isEqualTo(TEST_PKG_NAME_TARGET_TWO) + assertThat(getStringExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS)) + .isEqualTo("example3.com example4.com") + } + } + + private fun prepareProxyOnIntentFilterVerifiedV1(): Pair> { + val (messages, connection, proxy) = prepareProxyV1() + + proxy.sendBroadcastForPackages(setOf(TEST_PKG_NAME_TARGET_ONE, TEST_PKG_NAME_TARGET_TWO)) + messages.forEach { (code, value) -> proxy.runMessage(code, value) } + messages.clear() + + val intentCaptor = ArgumentCaptor.forClass(Intent::class.java) + + verify(context, times(2)).sendBroadcastAsUser( + intentCaptor.capture(), eq(UserHandle.SYSTEM), isNull(), any() + ) + + val verificationIds = intentCaptor.allValues.map { + it.getIntExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID, -1) + } + + assertThat(verificationIds).doesNotContain(-1) + + return ProxyV1Setup(messages, connection, proxy) to + (verificationIds[0] to verificationIds[1]) + } + + @Test + fun proxyOnIntentFilterVerifiedFullSuccessV1() { + val setup = prepareProxyOnIntentFilterVerifiedV1() + val (messages, connection, proxy) = setup.first + val (idOne, idTwo) = setup.second + + DomainVerificationProxyV1.queueLegacyVerifyResult( + context, + connection, + idOne, + PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS, + emptyList(), + TEST_CALLING_UID_ACCEPT + ) + + DomainVerificationProxyV1.queueLegacyVerifyResult( + context, + connection, + idTwo, + PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS, + emptyList(), + TEST_CALLING_UID_ACCEPT + ) + + assertThat(messages).hasSize(2) + messages.forEach { (code, value) -> proxy.runMessage(code, value) } + + val idCaptor = ArgumentCaptor.forClass(UUID::class.java) + + @Suppress("UNCHECKED_CAST") + verify(manager, times(2)).setDomainVerificationStatusInternal( + eq(TEST_CALLING_UID_ACCEPT), + idCaptor.capture(), + hostCaptor.capture(), + eq(DomainVerificationManager.STATE_SUCCESS) + ) + + assertThat(idCaptor.allValues).containsExactly(TEST_UUID_ONE, TEST_UUID_TWO) + + assertThat(hostCaptor.allValues.toSet()).containsExactly( + setOf("example1.com", "example2.com"), + setOf("example3.com", "example4.com") + ) + } + + @Test + fun proxyOnIntentFilterVerifiedPartialSuccessV1() { + val setup = prepareProxyOnIntentFilterVerifiedV1() + val (messages, connection, proxy) = setup.first + val (idOne, idTwo) = setup.second + + DomainVerificationProxyV1.queueLegacyVerifyResult( + context, + connection, + idOne, + PackageManager.INTENT_FILTER_VERIFICATION_FAILURE, + listOf("example1.com"), + TEST_CALLING_UID_ACCEPT + ) + + DomainVerificationProxyV1.queueLegacyVerifyResult( + context, + connection, + idTwo, + PackageManager.INTENT_FILTER_VERIFICATION_FAILURE, + listOf("example3.com"), + TEST_CALLING_UID_ACCEPT + ) + + messages.forEach { (code, value) -> proxy.runMessage(code, value) } + + val idCaptor = ArgumentCaptor.forClass(UUID::class.java) + val stateCaptor = ArgumentCaptor.forClass(Int::class.java) + + @Suppress("UNCHECKED_CAST") + verify(manager, times(4)).setDomainVerificationStatusInternal( + eq(TEST_CALLING_UID_ACCEPT), + idCaptor.capture(), + hostCaptor.capture(), + stateCaptor.capture() + ) + + assertThat(idCaptor.allValues) + .containsExactly(TEST_UUID_ONE, TEST_UUID_ONE, TEST_UUID_TWO, TEST_UUID_TWO) + + val hostToStates: Map, Int> = hostCaptor.allValues.zip(stateCaptor.allValues).toMap() + assertThat(hostToStates).isEqualTo(mapOf( + setOf("example1.com") to DomainVerificationState.STATE_LEGACY_FAILURE, + setOf("example2.com") to DomainVerificationState.STATE_SUCCESS, + setOf("example3.com") to DomainVerificationState.STATE_LEGACY_FAILURE, + setOf("example4.com") to DomainVerificationState.STATE_SUCCESS, + )) + } + + @Test + fun proxyOnIntentFilterVerifiedFailureV1() { + val setup = prepareProxyOnIntentFilterVerifiedV1() + val (messages, connection, proxy) = setup.first + val (idOne, idTwo) = setup.second + + DomainVerificationProxyV1.queueLegacyVerifyResult( + context, + connection, + idOne, + PackageManager.INTENT_FILTER_VERIFICATION_FAILURE, + listOf("example1.com", "example2.com"), + TEST_CALLING_UID_ACCEPT + ) + + DomainVerificationProxyV1.queueLegacyVerifyResult( + context, + connection, + idTwo, + PackageManager.INTENT_FILTER_VERIFICATION_FAILURE, + listOf("example3.com", "example4.com"), + TEST_CALLING_UID_ACCEPT + ) + + messages.forEach { (code, value) -> proxy.runMessage(code, value) } + + val idCaptor = ArgumentCaptor.forClass(UUID::class.java) + + @Suppress("UNCHECKED_CAST") + verify(manager, times(2)).setDomainVerificationStatusInternal( + eq(TEST_CALLING_UID_ACCEPT), + idCaptor.capture(), + hostCaptor.capture(), + eq(DomainVerificationState.STATE_LEGACY_FAILURE) + ) + + assertThat(idCaptor.allValues).containsExactly(TEST_UUID_ONE, TEST_UUID_TWO) + + assertThat(hostCaptor.allValues.toSet()).containsExactly( + setOf("example1.com", "example2.com"), + setOf("example3.com", "example4.com") + ) + } + + @Test + fun sendBroadcastForPackagesV2() { + val componentTwo = ComponentName(TEST_PKG_NAME_TWO, ".ReceiverOne") + val messages = mutableListOf>() + + val connection = mockConnection { + whenever(schedule(anyInt(), any())) { + messages.add((arguments[0] as Int) to arguments[1]) + } + } + + val proxy = DomainVerificationProxy.makeProxy( + null, + componentTwo, + context, + manager, + collector, + connection + ) + + proxy.sendBroadcastForPackages(setOf(TEST_PKG_NAME_TARGET_ONE, TEST_PKG_NAME_TARGET_TWO)) + + messages.forEach { (code, value) -> proxy.runMessage(code, value) } + + val intentCaptor = ArgumentCaptor.forClass(Intent::class.java) + + verify(context).sendBroadcastAsUser( + intentCaptor.capture(), eq(UserHandle.SYSTEM), isNull(), any() + ) + verifyNoMoreInteractions(context) + + val intents = intentCaptor.allValues + assertThat(intents).hasSize(1) + intents.single().apply { + assertThat(this.action).isEqualTo(Intent.ACTION_DOMAINS_NEED_VERIFICATION) + val request: DomainVerificationRequest? = + getParcelableExtra(DomainVerificationManager.EXTRA_VERIFICATION_REQUEST) + assertThat(request?.packageNames).containsExactly( + TEST_PKG_NAME_TARGET_ONE, + TEST_PKG_NAME_TARGET_TWO + ) + } + } + + private fun mockConnection(block: Connection.() -> Unit = {}) = + mockThrowOnUnmocked { + whenever(isCallerPackage(TEST_CALLING_UID_ACCEPT, TEST_PKG_NAME_ONE)) { true } + whenever(isCallerPackage(TEST_CALLING_UID_ACCEPT, TEST_PKG_NAME_TWO)) { true } + whenever(isCallerPackage(TEST_CALLING_UID_REJECT, TEST_PKG_NAME_ONE)) { false } + whenever(isCallerPackage(TEST_CALLING_UID_REJECT, TEST_PKG_NAME_TWO)) { false } + whenever(getPackage(anyString())) { mockPkg(arguments[0] as String) } + whenever(powerSaveTempWhitelistAppDuration) { 1000 } + whenever(deviceIdleInternal) { + mockThrowOnUnmocked { + whenever( + addPowerSaveTempWhitelistApp( + anyInt(), anyString(), anyLong(), anyInt(), + anyBoolean(), anyString() + ) + ) + } + } + block() + } + + private fun mockPkg(pkgName: String): AndroidPackage { + return mockThrowOnUnmocked { whenever(packageName) { pkgName } } + } + + private data class ProxyV1Setup( + val messages: MutableList>, + val connection: Connection, + val proxy: DomainVerificationProxy + ) + + interface Connection : DomainVerificationProxyV1.Connection, + DomainVerificationProxyV2.Connection +} From 0af8c46c2fa7653e15c21c29b4a0f9dfb986f086 Mon Sep 17 00:00:00 2001 From: Winson Date: Tue, 19 Jan 2021 11:35:33 -0800 Subject: [PATCH 20/23] Migrate legacy intent filter verification settings These legacy settings need to be migrated once to the new domain verification API, so this pulls the parsing out of the legacy classes into a DomainVerificationLegacySettings. This does NOT handle serializing the legacy settings. It's assumed that migration is a one-time best effort attempt, and that dropping the legacy settings is an acceptable fallback. The old setting was a result of an unexplained and unintuitive user path, set through ResolverActivity, and so it's not clear whether we should migrate at all. Worst case, the user can always re-do their preferences. NOTE: This change should only be merged if merged together with the change that removes the legacy code. It's invalid to run these separately, as this change breaks the legacy manager class. These are only separate to make review easier, to separate their concerns. Exempt-From-Owner-Approval: Already approved by owners on main branch Bug: 171251883 Test: TODO Change-Id: Iefde9f2cd8ab73cb5f592abc7d2163dd7d244789 --- .../android/content/pm/PackageUserState.java | 14 -- .../server/pm/PackageManagerService.java | 31 ++- .../android/server/pm/PackageSettingBase.java | 50 +--- .../java/com/android/server/pm/Settings.java | 54 +---- .../verify/DomainVerificationCollector.java | 20 +- .../verify/DomainVerificationEnforcer.java | 7 + .../DomainVerificationLegacySettings.java | 228 ++++++++++++++++++ .../DomainVerificationManagerInternal.java | 42 +++- .../verify/DomainVerificationService.java | 105 +++++++- .../verify/DomainVerificationUtils.java | 33 ++- .../IntentFilterVerificationSettings.java | 27 ++- .../verify/legacy/IntentVerifyUtils.java | 15 -- .../verify/DomainVerificationEnforcerTest.kt | 1 + .../DomainVerificationLegacySettingsTest.kt | 103 ++++++++ .../DomainVerificationPersistenceTest.kt | 46 ++-- .../pm/PackageManagerSettingsTests.java | 2 - .../server/pm/PackageUserStateTest.java | 8 - 17 files changed, 573 insertions(+), 213 deletions(-) create mode 100644 services/core/java/com/android/server/pm/domain/verify/DomainVerificationLegacySettings.java create mode 100644 services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationLegacySettingsTest.kt diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java index 99258712030c1..5cc74c0a1c8e4 100644 --- a/core/java/android/content/pm/PackageUserState.java +++ b/core/java/android/content/pm/PackageUserState.java @@ -77,8 +77,6 @@ public class PackageUserState { public boolean virtualPreload; public int enabled; public String lastDisableAppCaller; - public int domainVerificationStatus; - public int appLinkGeneration; public int categoryHint = ApplicationInfo.CATEGORY_UNDEFINED; public int installReason; public @PackageManager.UninstallReason int uninstallReason; @@ -100,8 +98,6 @@ public class PackageUserState { hidden = false; suspended = false; enabled = COMPONENT_ENABLED_STATE_DEFAULT; - domainVerificationStatus = - PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; installReason = PackageManager.INSTALL_REASON_UNKNOWN; uninstallReason = PackageManager.UNINSTALL_REASON_UNKNOWN; } @@ -120,8 +116,6 @@ public class PackageUserState { virtualPreload = o.virtualPreload; enabled = o.enabled; lastDisableAppCaller = o.lastDisableAppCaller; - domainVerificationStatus = o.domainVerificationStatus; - appLinkGeneration = o.appLinkGeneration; categoryHint = o.categoryHint; installReason = o.installReason; uninstallReason = o.uninstallReason; @@ -416,12 +410,6 @@ public class PackageUserState { && !lastDisableAppCaller.equals(oldState.lastDisableAppCaller))) { return false; } - if (domainVerificationStatus != oldState.domainVerificationStatus) { - return false; - } - if (appLinkGeneration != oldState.appLinkGeneration) { - return false; - } if (categoryHint != oldState.categoryHint) { return false; } @@ -481,8 +469,6 @@ public class PackageUserState { hashCode = 31 * hashCode + Boolean.hashCode(virtualPreload); hashCode = 31 * hashCode + enabled; hashCode = 31 * hashCode + Objects.hashCode(lastDisableAppCaller); - hashCode = 31 * hashCode + domainVerificationStatus; - hashCode = 31 * hashCode + appLinkGeneration; hashCode = 31 * hashCode + categoryHint; hashCode = 31 * hashCode + installReason; hashCode = 31 * hashCode + uninstallReason; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 42d900f04060c..cbc66ac644364 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -238,7 +238,6 @@ import android.content.pm.VersionedPackage; import android.content.pm.dex.ArtManager; import android.content.pm.dex.DexMetadataHelper; import android.content.pm.dex.IArtManager; -import android.content.pm.domain.verify.DomainVerificationManager; import android.content.pm.parsing.ApkLiteParseUtils; import android.content.pm.parsing.PackageLite; import android.content.pm.parsing.ParsingPackageUtils; @@ -386,7 +385,6 @@ import com.android.server.pm.domain.verify.proxy.DomainVerificationProxyV2; 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; @@ -1814,7 +1812,7 @@ public class PackageManagerService extends IPackageManager.Stub @NonNull @Override public WatchedSparseIntArray getNextAppLinkGeneration() { - return mSettings.mNextAppLinkGeneration; + return null; } @NonNull @@ -2753,7 +2751,8 @@ public class PackageManagerService extends IPackageManager.Stub } // Try to get the status from User settings first - long packedStatus = IntentVerifyUtils.getDomainVerificationStatus(ps, userId); + long packedStatus = 0; + //IntentVerifyUtils.getDomainVerificationStatus(ps, userId); int status = (int)(packedStatus >> 32); int linkGeneration = (int)(packedStatus & 0xFFFFFFFF); if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) { @@ -2980,8 +2979,8 @@ public class PackageManagerService extends IPackageManager.Stub result.wereAnyDomainsVerificationApproved |= mDomainVerificationManager .isApprovedForDomain(ps, intent, riTargetUser.targetUserId); } else { - long verificationState = - IntentVerifyUtils.getDomainVerificationStatus(ps, parentUserId); + long verificationState = 0; + //IntentVerifyUtils.getDomainVerificationStatus(ps, parentUserId); int status = (int) (verificationState >> 32); result.bestDomainVerificationStatus = bestDomainVerificationStatus(status, result.bestDomainVerificationStatus); @@ -3257,8 +3256,8 @@ public class PackageManagerService extends IPackageManager.Stub } } - final long packedStatus = - IntentVerifyUtils.getDomainVerificationStatus(ps, userId); + final long packedStatus = 0; + //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 @@ -4202,8 +4201,8 @@ public class PackageManagerService extends IPackageManager.Stub } } else { // Try to get the status from User settings first - final long packedStatus = - IntentVerifyUtils.getDomainVerificationStatus(ps, userId); + final long packedStatus = 0; + //IntentVerifyUtils.getDomainVerificationStatus(ps, userId); final int status = (int) (packedStatus >> 32); if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS || status @@ -9655,8 +9654,8 @@ public class PackageManagerService extends IPackageManager.Stub return ri; } } else { - final long packedStatus = - IntentVerifyUtils.getDomainVerificationStatus(ps, userId); + final long packedStatus = 0; + //IntentVerifyUtils.getDomainVerificationStatus(ps, userId); final int status = (int) (packedStatus >> 32); if (status != INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) { return ri; @@ -16447,13 +16446,13 @@ public class PackageManagerService extends IPackageManager.Stub @Override public int getIntentVerificationStatus(String packageName, int userId) { - return mIntentFilterVerificationManager.getIntentVerificationStatus(packageName, userId); + return mDomainVerificationManager.getLegacyState(packageName, userId); } @Override public boolean updateIntentVerificationStatus(String packageName, int status, int userId) { - return mIntentFilterVerificationManager.updateIntentVerificationStatus(packageName, status, - userId); + mDomainVerificationManager.setLegacyUserState(packageName, userId, status); + return true; } @Override @@ -21681,8 +21680,6 @@ public class PackageManagerService extends IPackageManager.Stub null /*lastDisableAppCaller*/, null /*enabledComponents*/, null /*disabledComponents*/, - ps.readUserState(nextUserId).domainVerificationStatus, - 0 /*linkGeneration*/, PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.UNINSTALL_REASON_UNKNOWN, null /*harmfulAppWarning*/); diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index b69d2b015d6cd..d3005184e0878 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -133,8 +133,6 @@ public abstract class PackageSettingBase extends SettingBase { /** Whether or not an update is available. Ostensibly only for instant apps. */ boolean updateAvailable; - IntentFilterVerificationInfo verificationInfo; - boolean forceQueryableOverride; @NonNull @@ -260,7 +258,6 @@ public abstract class PackageSettingBase extends SettingBase { for (int i = 0; i < orig.mUserState.size(); i++) { mUserState.put(orig.mUserState.keyAt(i), orig.mUserState.valueAt(i)); } - verificationInfo = orig.verificationInfo; versionCode = orig.versionCode; volumeUuid = orig.volumeUuid; categoryHint = orig.categoryHint; @@ -354,12 +351,6 @@ public abstract class PackageSettingBase extends SettingBase { /** * Only use for testing. Do NOT use in production code. - * - * Unless you're {@link DomainVerificationService} and you need to migrate legacy state. - * This is done rather than passing in the user IDs to - * {@link DomainVerificationManagerInternal#addPackage(PackageSetting)} to make the v2 APIs - * completely correct, without legacy details, since that method inherently does not care about - * the users on the device. */ @VisibleForTesting @Deprecated @@ -507,8 +498,7 @@ public abstract class PackageSettingBase extends SettingBase { ArrayMap suspendParams, boolean instantApp, boolean virtualPreload, String lastDisableAppCaller, ArraySet enabledComponents, ArraySet disabledComponents, - int domainVerifState, int linkGeneration, int installReason, int uninstallReason, - String harmfulAppWarning) { + int installReason, int uninstallReason, String harmfulAppWarning) { PackageUserState state = modifyUserState(userId); state.ceDataInode = ceDataInode; state.enabled = enabled; @@ -522,8 +512,6 @@ public abstract class PackageSettingBase extends SettingBase { state.lastDisableAppCaller = lastDisableAppCaller; state.enabledComponents = enabledComponents; state.disabledComponents = disabledComponents; - state.domainVerificationStatus = domainVerifState; - state.appLinkGeneration = linkGeneration; state.installReason = installReason; state.uninstallReason = uninstallReason; state.instantApp = instantApp; @@ -539,7 +527,6 @@ public abstract class PackageSettingBase extends SettingBase { otherState.instantApp, otherState.virtualPreload, otherState.lastDisableAppCaller, otherState.enabledComponents, otherState.disabledComponents, - otherState.domainVerificationStatus, otherState.appLinkGeneration, otherState.installReason, otherState.uninstallReason, otherState.harmfulAppWarning); } @@ -655,40 +642,6 @@ public abstract class PackageSettingBase extends SettingBase { return excludedUserIds; } - public IntentFilterVerificationInfo getIntentFilterVerificationInfo() { - return verificationInfo; - } - - public void setIntentFilterVerificationInfo(IntentFilterVerificationInfo info) { - verificationInfo = info; - onChanged(); - } - - // 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 getDomainVerificationStatusForUser(int userId) { - PackageUserState state = readUserState(userId); - long result = (long) state.appLinkGeneration; - result |= ((long) state.domainVerificationStatus) << 32; - return result; - } - - 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) { - state.appLinkGeneration = generation; - onChanged(); - } - } - - public void clearDomainVerificationStatusForUser(int userId) { - modifyUserState(userId).domainVerificationStatus = - PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; - } - protected void writeUsersInfoToProto(ProtoOutputStream proto, long fieldId) { int count = mUserState.size(); for (int i = 0; i < count; i++) { @@ -856,7 +809,6 @@ public abstract class PackageSettingBase extends SettingBase { this.volumeUuid = other.volumeUuid; this.categoryHint = other.categoryHint; this.updateAvailable = other.updateAvailable; - this.verificationInfo = other.verificationInfo; this.forceQueryableOverride = other.forceQueryableOverride; this.incrementalStates = other.incrementalStates; diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 689830bd6b984..43617cf9a0c9a 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_UNDEFINED; import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY; import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN; import static android.content.pm.PackageManager.UNINSTALL_REASON_USER_TYPE; @@ -106,6 +105,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.domain.verify.DomainVerificationLegacySettings; import com.android.server.pm.domain.verify.DomainVerificationManagerInternal; import com.android.server.pm.domain.verify.DomainVerificationPersistence; import com.android.server.pm.intent.verify.legacy.IntentFilterVerificationManager; @@ -557,7 +557,6 @@ public final class Settings implements Watchable, Snappable { mOtherAppIds.registerObserver(mObserver); mRenamedPackages.registerObserver(mObserver); mDefaultBrowserApp.registerObserver(mObserver); - mNextAppLinkGeneration.registerObserver(mObserver); Watchable.verifyWatchedAttributes(this, mObserver); } @@ -610,7 +609,6 @@ public final class Settings implements Watchable, Snappable { mOtherAppIds.registerObserver(mObserver); mRenamedPackages.registerObserver(mObserver); mDefaultBrowserApp.registerObserver(mObserver); - mNextAppLinkGeneration.registerObserver(mObserver); Watchable.verifyWatchedAttributes(this, mObserver); } @@ -659,7 +657,6 @@ public final class Settings implements Watchable, Snappable { mKeySetRefs.putAll(r.mKeySetRefs); mRenamedPackages.snapshot(r.mRenamedPackages); mDefaultBrowserApp.snapshot(r.mDefaultBrowserApp); - mNextAppLinkGeneration.snapshot(r.mNextAppLinkGeneration); // mReadMessages mPendingPackages.addAll(r.mPendingPackages); mSystemDir = null; @@ -938,8 +935,6 @@ public final class Settings implements Watchable, Snappable { null /*lastDisableAppCaller*/, null /*enabledComponents*/, null /*disabledComponents*/, - INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, - 0 /*linkGeneration*/, PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.UNINSTALL_REASON_UNKNOWN, null /*harmfulAppWarning*/); @@ -1182,12 +1177,6 @@ public final class Settings implements Watchable, Snappable { replaceAppIdLPw(p.appId, sharedUser); } } - - IntentFilterVerificationInfo info = - mIntentFilterVerificationManager.getRestoredIntentFilterVerificationInfo(p.name); - if (info != null) { - p.setIntentFilterVerificationInfo(info); - } } int removePackageLPw(String name) { @@ -1582,8 +1571,6 @@ public final class Settings implements Watchable, Snappable { null /*lastDisableAppCaller*/, null /*enabledComponents*/, null /*disabledComponents*/, - INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, - 0 /*linkGeneration*/, PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.UNINSTALL_REASON_UNKNOWN, null /*harmfulAppWarning*/); @@ -1608,8 +1595,6 @@ public final class Settings implements Watchable, Snappable { return; } - int maxAppLinkGeneration = 0; - int outerDepth = parser.getDepth(); PackageSetting ps = null; while ((type=parser.next()) != XmlPullParser.END_DOCUMENT @@ -1672,11 +1657,6 @@ public final class Settings implements Watchable, Snappable { final int verifState = parser.getAttributeInt(null, ATTR_DOMAIN_VERIFICATON_STATE, PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED); - final int linkGeneration = - parser.getAttributeInt(null, ATTR_APP_LINK_GENERATION, 0); - if (linkGeneration > maxAppLinkGeneration) { - maxAppLinkGeneration = linkGeneration; - } final int installReason = parser.getAttributeInt(null, ATTR_INSTALL_REASON, PackageManager.INSTALL_REASON_UNKNOWN); final int uninstallReason = parser.getAttributeInt(null, ATTR_UNINSTALL_REASON, @@ -1752,9 +1732,10 @@ public final class Settings implements Watchable, Snappable { } ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched, hidden, distractionFlags, suspended, suspendParamsMap, - instantApp, virtualPreload, - enabledCaller, enabledComponents, disabledComponents, verifState, - linkGeneration, installReason, uninstallReason, harmfulAppWarning); + instantApp, virtualPreload, enabledCaller, enabledComponents, + disabledComponents, installReason, uninstallReason, harmfulAppWarning); + + mDomainVerificationManager.setLegacyUserState(name, userId, verifState); } else if (tagName.equals("preferred-activities")) { readPreferredActivitiesLPw(parser, userId); } else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) { @@ -1773,9 +1754,6 @@ public final class Settings implements Watchable, Snappable { } str.close(); - - mNextAppLinkGeneration.put(userId, maxAppLinkGeneration + 1); - } catch (XmlPullParserException e) { mReadMessages.append("Error reading: " + e.toString()); PackageManagerService.reportSettingsProblem(Log.ERROR, @@ -2002,15 +1980,6 @@ public final class Settings implements Watchable, Snappable { ustate.lastDisableAppCaller); } } - if (ustate.domainVerificationStatus != - PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) { - serializer.attributeInt(null, ATTR_DOMAIN_VERIFICATON_STATE, - ustate.domainVerificationStatus); - } - if (ustate.appLinkGeneration != 0) { - serializer.attributeInt(null, ATTR_APP_LINK_GENERATION, - ustate.appLinkGeneration); - } if (ustate.installReason != PackageManager.INSTALL_REASON_UNKNOWN) { serializer.attributeInt(null, ATTR_INSTALL_REASON, ustate.installReason); } @@ -2747,8 +2716,7 @@ public final class Settings implements Watchable, Snappable { writeSigningKeySetLPr(serializer, pkg.keySetData); writeUpgradeKeySetsLPr(serializer, pkg.keySetData); writeKeySetAliasesLPr(serializer, pkg.keySetData); - mIntentFilterVerificationManager.writeDomainVerificationsLPr(serializer, - pkg.verificationInfo); + mDomainVerificationManager.writeLegacySettings(serializer, pkg.name); writeMimeGroupLPr(serializer, pkg.mimeGroups); serializer.endTag(null, "package"); @@ -2924,7 +2892,10 @@ public final class Settings implements Watchable, Snappable { ver.fingerprint = XmlUtils.readStringAttribute(parser, ATTR_FINGERPRINT); } else if (tagName.equals(DomainVerificationPersistence.TAG_DOMAIN_VERIFICATIONS)) { mDomainVerificationManager.readSettings(parser); - }else { + } else if (tagName.equals( + DomainVerificationLegacySettings.TAG_DOMAIN_VERIFICATIONS_LEGACY)) { + mDomainVerificationManager.readLegacySettings(parser); + } else { Slog.w(PackageManagerService.TAG, "Unknown element under : " + parser.getName()); XmlUtils.skipCurrentTag(parser); @@ -3739,9 +3710,8 @@ public final class Settings implements Watchable, Snappable { packageSetting.installSource = packageSetting.installSource.setInitiatingPackageSignatures(signatures); } else if (tagName.equals(TAG_DOMAIN_VERIFICATION)) { - IntentFilterVerificationInfo ivi = - mIntentFilterVerificationManager.readDomainVerificationLPw(parser); - packageSetting.setIntentFilterVerificationInfo(ivi); + IntentFilterVerificationInfo ivi = new IntentFilterVerificationInfo(parser); + mDomainVerificationManager.addLegacySetting(packageSetting.name, ivi); if (DEBUG_PARSER) { Log.d(TAG, "Read domain verification for package: " + ivi.getPackageName()); } diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationCollector.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationCollector.java index 5aaa37e9dadd5..832714f2bfe61 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationCollector.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationCollector.java @@ -88,9 +88,8 @@ public class DomainVerificationCollector { @NonNull private ArraySet collectDomains(@NonNull AndroidPackage pkg, boolean checkAutoVerify) { - @SuppressWarnings("ConstantConditions") - boolean restrictDomains = Binder.withCleanCallingIdentity( - () -> mPlatformCompat.isChangeEnabled(RESTRICT_DOMAINS, buildMockAppInfo(pkg))); + boolean restrictDomains = + DomainVerificationUtils.isChangeEnabled(mPlatformCompat, pkg, RESTRICT_DOMAINS); ArraySet domains = new ArraySet<>(); @@ -198,19 +197,4 @@ public class DomainVerificationCollector { } } } - - /** - * Passed to {@link PlatformCompat} because this can be invoked mid-install process, and - * {@link PlatformCompat} will not be able to query the pending {@link ApplicationInfo} from - * {@link PackageManager}. - * - * TODO(b/177613575): Can a different API be used? - */ - @NonNull - private ApplicationInfo buildMockAppInfo(@NonNull AndroidPackage pkg) { - ApplicationInfo appInfo = new ApplicationInfo(); - appInfo.packageName = pkg.getPackageName(); - appInfo.targetSdkVersion = pkg.getTargetSdkVersion(); - return appInfo; - } } diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationEnforcer.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationEnforcer.java index 05b1c47f8b277..cdcc5fcba7b5e 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationEnforcer.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationEnforcer.java @@ -118,4 +118,11 @@ public class DomainVerificationEnforcer { Binder.getCallingPid(), callingUid, "Caller is not allowed to edit user selections"); } + + public void callerIsLegacyUserSelector(int callingUid) { + mContext.enforcePermission( + android.Manifest.permission.SET_PREFERRED_APPLICATIONS, + Binder.getCallingPid(), callingUid, + "Caller is not allowed to edit user state"); + } } diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationLegacySettings.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationLegacySettings.java new file mode 100644 index 0000000000000..09307d2a2db1e --- /dev/null +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationLegacySettings.java @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2021 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.domain.verify; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.content.pm.IntentFilterVerificationInfo; +import android.content.pm.PackageManager; +import android.util.ArrayMap; +import android.util.SparseIntArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; + +import com.android.internal.annotations.GuardedBy; +import com.android.server.pm.SettingsXml; + +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.Map; + +/** + * Reads and writes the old {@link android.content.pm.IntentFilterVerificationInfo} so that it can + * be migrated in to the new API. Will throw away the state once it's successfully applied so that + * eventually there will be no legacy state on the device. + * + * This attempt is best effort, and if the legacy state is lost that's acceptable. The user setting + * in the legacy API may have been set incorrectly because it was never made obvious to the user + * what it actually toggled, so there's a strong argument to prevent migration anyways. The user + * can just set their preferences again, this time with finer grained control, if the legacy state + * gets dropped. + */ +public class DomainVerificationLegacySettings { + + public static final String TAG_DOMAIN_VERIFICATIONS_LEGACY = "domain-verifications-legacy"; + public static final String TAG_USER_STATES = "user-states"; + public static final String ATTR_PACKAGE_NAME = "packageName"; + public static final String TAG_USER_STATE = "user-state"; + public static final String ATTR_USER_ID = "userId"; + public static final String ATTR_STATE = "state"; + + @NonNull + private final Object mLock = new Object(); + + @NonNull + private final ArrayMap mStates = new ArrayMap<>(); + + public void add(@NonNull String packageName, @NonNull IntentFilterVerificationInfo info) { + synchronized (mLock) { + getOrCreateStateLocked(packageName).setInfo(info); + } + } + + public void add(@NonNull String packageName, @UserIdInt int userId, int state) { + synchronized (mLock) { + getOrCreateStateLocked(packageName).addUserState(userId, state); + } + } + + public int getUserState(@NonNull String packageName, @UserIdInt int userId) { + synchronized (mLock) { + LegacyState state = mStates.get(packageName); + if (state != null) { + return state.getUserState(userId); + } + } + return PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; + } + + @Nullable + public SparseIntArray getUserStates(@NonNull String packageName) { + synchronized (mLock) { + LegacyState state = mStates.get(packageName); + if (state != null) { + // Yes, this returns outside of the lock, but we assume that retrieval generally + // only happens after all adding has concluded from reading settings. + return state.getUserStates(); + } + } + return null; + } + + @Nullable + public IntentFilterVerificationInfo remove(@NonNull String packageName) { + synchronized (mLock) { + LegacyState state = mStates.get(packageName); + if (state != null && !state.isAttached()) { + state.markAttached(); + return state.getInfo(); + } + } + return null; + } + + @GuardedBy("mLock") + @NonNull + private LegacyState getOrCreateStateLocked(@NonNull String packageName) { + LegacyState state = mStates.get(packageName); + if (state == null) { + state = new LegacyState(); + mStates.put(packageName, state); + } + + return state; + } + + public void writeSettings(TypedXmlSerializer xmlSerializer) throws IOException { + try (SettingsXml.Serializer serializer = SettingsXml.serializer(xmlSerializer)) { + try (SettingsXml.WriteSection ignored = + serializer.startSection(TAG_DOMAIN_VERIFICATIONS_LEGACY)) { + synchronized (mLock) { + final int statesSize = mStates.size(); + for (int stateIndex = 0; stateIndex < statesSize; stateIndex++) { + final LegacyState state = mStates.valueAt(stateIndex); + final SparseIntArray userStates = state.getUserStates(); + if (userStates == null) { + continue; + } + + final String packageName = mStates.keyAt(stateIndex); + try (SettingsXml.WriteSection userStatesSection = + serializer.startSection(TAG_USER_STATES) + .attribute(ATTR_PACKAGE_NAME, packageName)) { + final int userStatesSize = userStates.size(); + for (int userStateIndex = 0; userStateIndex < userStatesSize; + userStateIndex++) { + final int userId = userStates.keyAt(userStateIndex); + final int userState = userStates.valueAt(userStateIndex); + userStatesSection.startSection(TAG_USER_STATE) + .attribute(ATTR_USER_ID, userId) + .attribute(ATTR_STATE, userState) + .finish(); + } + } + } + } + } + } + } + + public void readSettings(TypedXmlPullParser xmlParser) + throws IOException, XmlPullParserException { + final SettingsXml.ChildSection child = SettingsXml.parser(xmlParser).children(); + while (child.moveToNext()) { + if (TAG_USER_STATES.equals(child.getName())) { + readUserStates(child); + } + } + } + + private void readUserStates(SettingsXml.ReadSection section) { + String packageName = section.getString(ATTR_PACKAGE_NAME); + synchronized (mLock) { + final LegacyState legacyState = getOrCreateStateLocked(packageName); + final SettingsXml.ChildSection child = section.children(); + while (child.moveToNext()) { + if (TAG_USER_STATE.equals(child.getName())) { + readUserState(child, legacyState); + } + } + } + } + + private void readUserState(SettingsXml.ReadSection section, LegacyState legacyState) { + int userId = section.getInt(ATTR_USER_ID); + int state = section.getInt(ATTR_STATE); + legacyState.addUserState(userId, state); + } + + static class LegacyState { + @Nullable + private IntentFilterVerificationInfo mInfo; + + @Nullable + private SparseIntArray mUserStates; + + private boolean attached; + + @Nullable + public IntentFilterVerificationInfo getInfo() { + return mInfo; + } + + public int getUserState(int userId) { + return mUserStates.get(userId, + PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED); + } + + @Nullable + public SparseIntArray getUserStates() { + return mUserStates; + } + + public void setInfo(@NonNull IntentFilterVerificationInfo info) { + mInfo = info; + } + + public void addUserState(@UserIdInt int userId, int state) { + if (mUserStates == null) { + mUserStates = new SparseIntArray(1); + } + mUserStates.put(userId, state); + } + + public boolean isAttached() { + return attached; + } + + public void markAttached() { + attached = true; + } + } +} diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java index 9ef498780393b..56994c864870b 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.UserIdInt; import android.content.Intent; +import android.content.pm.IntentFilterVerificationInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.domain.verify.DomainVerificationManager; import android.content.pm.domain.verify.DomainVerificationSet; @@ -31,6 +32,7 @@ import android.util.TypedXmlSerializer; import com.android.server.pm.PackageSetting; import com.android.server.pm.domain.verify.models.DomainVerificationPkgState; import com.android.server.pm.domain.verify.proxy.DomainVerificationProxy; +import com.android.server.pm.parsing.pkg.AndroidPackage; import org.xmlpull.v1.XmlPullParserException; @@ -103,15 +105,14 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan /** * Serializes the entire internal state. This is equivalent to a full backup of the existing - * verification state. + * verification state. This write includes legacy state, as a sibling tag the modern state. */ void writeSettings(@NonNull TypedXmlSerializer serializer) throws IOException; /** * Read back a list of {@link DomainVerificationPkgState}s previously written by {@link * #writeSettings(TypedXmlSerializer)}. Assumes that the - * {@link DomainVerificationPersistence#TAG_DOMAIN_VERIFICATIONS} - * tag has already been entered. + * {@link DomainVerificationPersistence#TAG_DOMAIN_VERIFICATIONS} tag has already been entered. *

* This is expected to only be used to re-attach states for packages already known to be on the * device. If restoring from a backup, use {@link #restoreSettings(TypedXmlPullParser)}. @@ -119,6 +120,15 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan void readSettings(@NonNull TypedXmlPullParser parser) throws IOException, XmlPullParserException; + /** + * Read back data from + * {@link DomainVerificationLegacySettings#writeSettings(TypedXmlSerializer)}. Assumes that the + * {@link DomainVerificationLegacySettings#TAG_DOMAIN_VERIFICATIONS_LEGACY} tag has already + * been entered. + */ + void readLegacySettings(@NonNull TypedXmlPullParser parser) + throws IOException, XmlPullParserException; + /** * Remove all state for the given package. */ @@ -148,6 +158,32 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan void restoreSettings(@NonNull TypedXmlPullParser parser) throws IOException, XmlPullParserException; + /** + * Set aside a legacy {@link IntentFilterVerificationInfo} that will be restored to a pending + * {@link DomainVerificationPkgState} once it's added through + * {@link #addPackage(PackageSetting)}. + */ + void addLegacySetting(@NonNull String packageName, @NonNull IntentFilterVerificationInfo info); + + /** + * Set aside a legacy user selection that will be restored to a pending + * {@link DomainVerificationPkgState} once it's added through + * {@link #addPackage(PackageSetting)}. + */ + void setLegacyUserState(@NonNull String packageName, @UserIdInt int userId, int state); + + /** + * Until the legacy APIs are entirely removed, returns the legacy state from the previously + * written info stored in {@link com.android.server.pm.Settings}. + */ + int getLegacyState(@NonNull String packageName, @UserIdInt int userId); + + /** + * Serialize a legacy setting that wasn't attached yet. + * TODO: Does this even matter? Should consider for removal. + */ + void writeLegacySettings(TypedXmlSerializer serializer, String name); + /** * Print the verification state and user selection state of a package. * diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java index 0a28069017d46..ace72090193aa 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java @@ -19,6 +19,8 @@ package com.android.server.pm.domain.verify; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.compat.annotation.ChangeId; +import android.compat.annotation.Disabled; import android.content.Context; import android.content.Intent; import android.content.pm.IntentFilterVerificationInfo; @@ -38,6 +40,7 @@ import android.util.IndentingPrintWriter; import android.util.Singleton; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseIntArray; import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; @@ -73,6 +76,19 @@ public class DomainVerificationService extends SystemService public static final boolean DEBUG_APPROVAL = true; + /** + * The new user preference API for verifying domains marked autoVerify=true in + * AndroidManifest.xml intent filters is not yet implemented in the current platform preview. + * This is anticipated to ship before S releases. + * + * For now, it is possible to preview the new user preference changes by enabling this + * ChangeId and using the adb shell pm set-app-links-user-selection and similar + * commands. + */ + @ChangeId + @Disabled + private static final long SETTINGS_API_V2 = 178111421; + /** * States that are currently alive and attached to a package. Entries are exclusive with the * state stored in {@link DomainVerificationSettings}, as any pending/restored state should be @@ -99,6 +115,9 @@ public class DomainVerificationService extends SystemService @NonNull private final SystemConfig mSystemConfig; + @NonNull + private final PlatformCompat mPlatformCompat; + @NonNull private final DomainVerificationSettings mSettings; @@ -114,6 +133,9 @@ public class DomainVerificationService extends SystemService @NonNull private final DomainVerificationShell mShell; + @NonNull + private final DomainVerificationLegacySettings mLegacySettings; + @NonNull private final IDomainVerificationManager.Stub mStub = new DomainVerificationManagerStub(this); @@ -125,11 +147,13 @@ public class DomainVerificationService extends SystemService super(context); mConnection = connection; mSystemConfig = systemConfig; + mPlatformCompat = platformCompat; mSettings = new DomainVerificationSettings(); mCollector = new DomainVerificationCollector(platformCompat, systemConfig); mEnforcer = new DomainVerificationEnforcer(context); mDebug = new DomainVerificationDebug(mCollector); mShell = new DomainVerificationShell(this); + mLegacySettings = new DomainVerificationLegacySettings(); } @Override @@ -401,6 +425,8 @@ public class DomainVerificationService extends SystemService .setDisallowLinkHandling(!allowed); } } + + mConnection.get().scheduleWriteSettings(); } @Override @@ -473,6 +499,8 @@ public class DomainVerificationService extends SystemService enabled, domains); } } + + mConnection.get().scheduleWriteSettings(); } private void setDomainVerificationUserSelectionInternal(int userId, @@ -500,6 +528,8 @@ public class DomainVerificationService extends SystemService userState.removeHosts(domains); } } + + mConnection.get().scheduleWriteSettings(); } @Nullable @@ -693,12 +723,11 @@ public class DomainVerificationService extends SystemService // and disable them if appropriate. ArraySet webDomains = null; - @SuppressWarnings("deprecation") - SparseArray userState = newPkgSetting.getUserState(); - int userStateSize = userState.size(); + SparseIntArray legacyUserStates = mLegacySettings.getUserStates(pkgName); + int userStateSize = legacyUserStates == null ? 0 : legacyUserStates.size(); for (int index = 0; index < userStateSize; index++) { - int userId = userState.keyAt(index); - int legacyStatus = userState.valueAt(index).domainVerificationStatus; + int userId = legacyUserStates.keyAt(index); + int legacyStatus = legacyUserStates.valueAt(index); if (legacyStatus == PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) { if (webDomains == null) { @@ -709,8 +738,7 @@ public class DomainVerificationService extends SystemService } } - IntentFilterVerificationInfo legacyInfo = - newPkgSetting.getIntentFilterVerificationInfo(); + IntentFilterVerificationInfo legacyInfo = mLegacySettings.remove(pkgName); if (legacyInfo != null && legacyInfo.getStatus() == PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) { @@ -772,6 +800,8 @@ public class DomainVerificationService extends SystemService synchronized (mLock) { mSettings.writeSettings(serializer, mAttachedPkgStates); } + + mLegacySettings.writeSettings(serializer); } @Override @@ -782,6 +812,12 @@ public class DomainVerificationService extends SystemService } } + @Override + public void readLegacySettings(@NonNull TypedXmlPullParser parser) + throws IOException, XmlPullParserException { + mLegacySettings.readSettings(parser); + } + @Override public void restoreSettings(@NonNull TypedXmlPullParser parser) throws IOException, XmlPullParserException { @@ -790,6 +826,28 @@ public class DomainVerificationService extends SystemService } } + @Override + public void addLegacySetting(@NonNull String packageName, + @NonNull IntentFilterVerificationInfo info) { + mLegacySettings.add(packageName, info); + } + + @Override + public void setLegacyUserState(@NonNull String packageName, @UserIdInt int userId, int state) { + mEnforcer.callerIsLegacyUserSelector(mConnection.get().getCallingUid()); + mLegacySettings.add(packageName, userId, state); + } + + @Override + public int getLegacyState(@NonNull String packageName, @UserIdInt int userId) { + return mLegacySettings.getUserState(packageName, userId); + } + + @Override + public void writeLegacySettings(TypedXmlSerializer serializer, String name) { + + } + @Override public void clearPackage(@NonNull String packageName) { synchronized (mLock) { @@ -1051,13 +1109,36 @@ public class DomainVerificationService extends SystemService return false; } - // To allow an instant app to immediately open domains after being installed by the user, - // auto approve them for any declared autoVerify domains. String host = intent.getData().getHost(); final AndroidPackage pkg = pkgSetting.getPkg(); - if (pkgSetting.getInstantApp(userId) && pkg != null - && mCollector.collectAutoVerifyDomains(pkg).contains(host)) { - return true; + + // Should never be null, but if it is, skip this and assume that v2 is enabled + if (pkg != null) { + // To allow an instant app to immediately open domains after being installed by the + // user, auto approve them for any declared autoVerify domains. + if (pkgSetting.getInstantApp(userId) + && mCollector.collectAutoVerifyDomains(pkg).contains(host)) { + return true; + } + + if (!DomainVerificationUtils.isChangeEnabled(mPlatformCompat, pkg, SETTINGS_API_V2)) { + int legacyState = mLegacySettings.getUserState(packageName, userId); + switch (legacyState) { + case PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED: + // If nothing specifically set, assume v2 rules + break; + case PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK: + case PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS: + case PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK: + // With v2 split into 2 lists, always and undefined, the concept of whether + // or not to ask is irrelevant. Assume the user wants this application to + // open the domain. + return true; + case PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER: + // Never has the same semantics are before + return false; + } + } } synchronized (mLock) { diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationUtils.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationUtils.java index ff030710274e4..f704478b92a59 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationUtils.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationUtils.java @@ -19,13 +19,20 @@ package com.android.server.pm.domain.verify; import android.annotation.CheckResult; import android.annotation.NonNull; import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Binder; + +import com.android.server.compat.PlatformCompat; +import com.android.server.pm.PackageManagerService; +import com.android.server.pm.parsing.pkg.AndroidPackage; final class DomainVerificationUtils { /** - * Consolidates package exception messages. A generic unavailable message is included since - * the caller doesn't bother to check why the package isn't available. + * Consolidates package exception messages. A generic unavailable message is included since the + * caller doesn't bother to check why the package isn't available. */ @CheckResult static NameNotFoundException throwPackageUnavailable(@NonNull String packageName) @@ -38,4 +45,26 @@ final class DomainVerificationUtils { && intent.hasCategory(Intent.CATEGORY_BROWSABLE) && intent.hasCategory(Intent.CATEGORY_DEFAULT); } + + static boolean isChangeEnabled(PlatformCompat platformCompat, AndroidPackage pkg, + long changeId) { + //noinspection ConstantConditions + return Binder.withCleanCallingIdentity( + () -> platformCompat.isChangeEnabled(changeId, buildMockAppInfo(pkg))); + } + + /** + * Passed to {@link PlatformCompat} because this can be invoked mid-install process or when + * {@link PackageManagerService#mLock} is being held, and {@link PlatformCompat} will not be + * able to query the pending {@link ApplicationInfo} from {@link PackageManager}. + *

+ * TODO(b/177613575): Can a different API be used? + */ + @NonNull + private static ApplicationInfo buildMockAppInfo(@NonNull AndroidPackage pkg) { + ApplicationInfo appInfo = new ApplicationInfo(); + appInfo.packageName = pkg.getPackageName(); + appInfo.targetSdkVersion = pkg.getTargetSdkVersion(); + return appInfo; + } } 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 index 2aac51402d102..3ff770d5fb3a5 100644 --- 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 @@ -176,10 +176,10 @@ public class IntentFilterVerificationSettings { public IntentFilterVerificationInfo updatePackageSetting(@NonNull PackageSetting pkgSetting, ArraySet domains) { String pkgName = pkgSetting.name; - IntentFilterVerificationInfo ivi = pkgSetting.getIntentFilterVerificationInfo(); + IntentFilterVerificationInfo ivi = null;//pkgSetting.getIntentFilterVerificationInfo(); if (ivi == null) { ivi = new IntentFilterVerificationInfo(pkgName, domains); - pkgSetting.setIntentFilterVerificationInfo(ivi); + // pkgSetting.setIntentFilterVerificationInfo(ivi); mConnection.debugLog("Creating new IntentFilterVerificationInfo for pkg: " + pkgName); } else { ivi.setDomains(domains); @@ -197,7 +197,7 @@ public class IntentFilterVerificationSettings { mConnection.warnLog("No package known: " + packageName); return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; } - return (int) (pkgSetting.getDomainVerificationStatusForUser(userId) >> 32); + return 0;//(int) (pkgSetting.getDomainVerificationStatusForUser(userId) >> 32); } @Nullable @@ -208,7 +208,7 @@ public class IntentFilterVerificationSettings { mConnection.warnLog("No package known: " + packageName); return null; } - return ps.getIntentFilterVerificationInfo(); + return null;//ps.getIntentFilterVerificationInfo(); } boolean updateIntentFilterVerificationStatusLPw(String packageName, final int status, @@ -222,14 +222,14 @@ public class IntentFilterVerificationSettings { final int alwaysGeneration; if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) { - WatchedSparseIntArray nextAppLinkGeneration = mConnection.getNextAppLinkGeneration(); - alwaysGeneration = nextAppLinkGeneration.get(userId) + 1; - nextAppLinkGeneration.put(userId, alwaysGeneration); +// WatchedSparseIntArray nextAppLinkGeneration = mConnection.getNextAppLinkGeneration(); +// alwaysGeneration = nextAppLinkGeneration.get(userId) + 1; +// nextAppLinkGeneration.put(userId, alwaysGeneration); } else { alwaysGeneration = 0; } - current.setDomainVerificationStatusForUser(status, alwaysGeneration, userId); +// current.setDomainVerificationStatusForUser(status, alwaysGeneration, userId); return true; } @@ -241,9 +241,8 @@ public class IntentFilterVerificationSettings { return false; } if (alsoResetStatus) { - ps.clearDomainVerificationStatusForUser(userId); +// ps.clearDomainVerificationStatusForUser(userId); } - ps.setIntentFilterVerificationInfo(null); return true; } @@ -262,7 +261,7 @@ public class IntentFilterVerificationSettings { } ArrayList result = new ArrayList<>(); for (PackageSetting ps : mConnection.getPackageSettingsLPr().values()) { - IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo(); + IntentFilterVerificationInfo ivi = null;//ps.getIntentFilterVerificationInfo(); if (ivi == null || TextUtils.isEmpty(ivi.getPackageName()) || !ivi.getPackageName().equalsIgnoreCase(packageName)) { continue; @@ -278,7 +277,7 @@ public class IntentFilterVerificationSettings { throws IllegalArgumentException, IllegalStateException, IOException { serializer.startTag(null, Settings.TAG_ALL_INTENT_FILTER_VERIFICATION); for (PackageSetting value : pkgSettings.values()) { - IntentFilterVerificationInfo ivi = value.getIntentFilterVerificationInfo(); + IntentFilterVerificationInfo ivi = null;//value.getIntentFilterVerificationInfo(); if (ivi != null) { writeDomainVerificationsLPr(serializer, ivi); } @@ -318,7 +317,9 @@ public class IntentFilterVerificationSettings { final PackageSetting ps = mConnection.getPackageSettingLPr(pkgName); if (ps != null) { // known/existing package; update in place - ps.setIntentFilterVerificationInfo(ivi); + // TODO: Removed, commented out to allow compile, awaiting removal of entire + // class + // ps.setIntentFilterVerificationInfo(ivi); mConnection.debugLog("Restored IVI for existing app " + pkgName + " status=" + ivi.getStatusString()); } else { 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 index 389aa20e9ff04..b64ac94ba07fd 100644 --- 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 @@ -31,19 +31,4 @@ public class IntentVerifyUtils { && (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/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationEnforcerTest.kt index 13a37ffe77d69..c944cff8c04f1 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationEnforcerTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationEnforcerTest.kt @@ -149,6 +149,7 @@ class DomainVerificationEnforcerTest { whenever(getPackageSettingLocked(TEST_PKG)) { mockPkgSetting } whenever(getPackageLocked(TEST_PKG)) { mockPkg } whenever(schedule(anyInt(), any())) + whenever(scheduleWriteSettings()) } }) ) diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationLegacySettingsTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationLegacySettingsTest.kt new file mode 100644 index 0000000000000..47601a499d628 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationLegacySettingsTest.kt @@ -0,0 +1,103 @@ +/* + * 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.test.domain.verify + +import android.content.pm.IntentFilterVerificationInfo +import android.content.pm.PackageManager +import android.util.ArraySet +import com.android.server.pm.domain.verify.DomainVerificationLegacySettings +import com.android.server.pm.test.domain.verify.DomainVerificationPersistenceTest.Companion.readXml +import com.android.server.pm.test.domain.verify.DomainVerificationPersistenceTest.Companion.writeXml +import com.google.common.truth.Truth.assertWithMessage +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder + +class DomainVerificationLegacySettingsTest { + + @Rule + @JvmField + val tempFolder = TemporaryFolder() + + @Test + fun writeAndReadBackNormal() { + val settings = DomainVerificationLegacySettings().apply { + add( + "com.test.one", + IntentFilterVerificationInfo( + "com.test.one", + ArraySet(setOf("example1.com", "example2.com")) + ).apply { + status = PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK + } + ) + add( + "com.test.one", + 0, PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS + ) + add( + "com.test.one", + 10, PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER + ) + + add( + "com.test.two", + IntentFilterVerificationInfo( + "com.test.two", + ArraySet(setOf("example3.com")) + ) + ) + + add( + "com.test.three", + 11, PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS + ) + } + + + val file = tempFolder.newFile().writeXml(settings::writeSettings) + val newSettings = file.readXml { + DomainVerificationLegacySettings().apply { + readSettings(it) + } + } + + val xml = file.readText() + + // Legacy migrated settings doesn't bother writing the legacy verification info + assertWithMessage(xml).that(newSettings.remove("com.test.one")).isNull() + assertWithMessage(xml).that(newSettings.getUserState("com.test.one", 0)) + .isEqualTo(PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) + assertWithMessage(xml).that(newSettings.getUserState("com.test.one", 10)) + .isEqualTo(PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) + + val firstUserStates = newSettings.getUserStates("com.test.one") + assertWithMessage(xml).that(firstUserStates).isNotNull() + assertWithMessage(xml).that(firstUserStates!!.size()).isEqualTo(2) + assertWithMessage(xml).that(firstUserStates[0]) + .isEqualTo(PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) + assertWithMessage(xml).that(firstUserStates[10]) + .isEqualTo(PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) + + assertWithMessage(xml).that(newSettings.remove("com.test.two")).isNull() + assertWithMessage(xml).that(newSettings.getUserStates("com.test.two")).isNull() + + assertWithMessage(xml).that(newSettings.remove("com.test.three")).isNull() + assertWithMessage(xml).that(newSettings.getUserState("com.test.three", 11)) + .isEqualTo(PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) + } +} diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationPersistenceTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationPersistenceTest.kt index cf331d5ce7758..ada5c1b063fa0 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationPersistenceTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationPersistenceTest.kt @@ -18,6 +18,7 @@ package com.android.server.pm.test.domain.verify import android.content.pm.domain.verify.DomainVerificationManager import android.util.ArrayMap +import android.util.TypedXmlPullParser import android.util.TypedXmlSerializer import android.util.Xml import com.android.server.pm.domain.verify.DomainVerificationPersistence @@ -29,12 +30,33 @@ import com.google.common.truth.Truth.assertWithMessage import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder +import java.io.File +import java.nio.charset.StandardCharsets import java.util.UUID class DomainVerificationPersistenceTest { companion object { private val PKG_PREFIX = DomainVerificationPersistenceTest::class.java.`package`!!.name + + internal fun File.writeXml(block: (serializer: TypedXmlSerializer) -> Unit) = apply { + outputStream().use { + // Explicitly use string based XML so it can printed in the test failure output + Xml.newFastSerializer() + .apply { + setOutput(it, StandardCharsets.UTF_8.name()) + startDocument(null, true) + setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true) + } + .apply(block) + .endDocument() + } + } + + internal fun File.readXml(block: (parser: TypedXmlPullParser) -> T) = + inputStream().use { + block(Xml.resolvePullParser(it)) + } } @Rule @@ -56,17 +78,18 @@ class DomainVerificationPersistenceTest { mockPkgState(5).let { put(it.packageName, it) } } - val file = writeXml { + val file = tempFolder.newFile().writeXml { DomainVerificationPersistence.writeToXml(it, attached, pending, restored) } val xml = file.readText() - val (readActive, readRestored) = file.inputStream() - .use { DomainVerificationPersistence.readFromXml(Xml.resolvePullParser(it)) } + val (readActive, readRestored) = file.readXml { + DomainVerificationPersistence.readFromXml(it) + } assertWithMessage(xml).that(readActive.values) - .containsExactlyElementsIn(attached.values() + pending.values) + .containsExactlyElementsIn(attached.values() + pending.values) assertWithMessage(xml).that(readRestored.values).containsExactlyElementsIn(restored.values) } @@ -172,25 +195,12 @@ class DomainVerificationPersistenceTest { """.trimIndent() val (active, restored) = DomainVerificationPersistence - .readFromXml(Xml.resolvePullParser(xml.byteInputStream())) + .readFromXml(Xml.resolvePullParser(xml.byteInputStream())) assertThat(active.values).containsExactly(stateZero) assertThat(restored.values).containsExactly(stateOne, stateTwo) } - private fun writeXml(block: (TypedXmlSerializer) -> Unit) = tempFolder.newFile() - .apply { - outputStream().use { - Xml.resolveSerializer(it) - .apply { - startDocument(null, true) - setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true) - } - .apply(block) - .endDocument() - } - } - private fun mockEmptyPkgState( id: Int, hasAutoVerifyDomains: Boolean = true 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 850a031d7ad6c..beeae7df6956e 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -961,8 +961,6 @@ public class PackageManagerSettingsTests { assertNotSame(origPkgSetting.getUserState(), is(testPkgSetting.getUserState())); // No equals() method for SparseArray object // assertThat(origPkgSetting.getUserState(), is(testPkgSetting.getUserState())); - assertSame(origPkgSetting.verificationInfo, testPkgSetting.verificationInfo); - assertThat(origPkgSetting.verificationInfo, is(testPkgSetting.verificationInfo)); assertThat(origPkgSetting.versionCode, is(testPkgSetting.versionCode)); assertSame(origPkgSetting.volumeUuid, testPkgSetting.volumeUuid); assertThat(origPkgSetting.volumeUuid, is(testPkgSetting.volumeUuid)); diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java index 1cfbad93c2e54..938e4cc84e623 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java @@ -52,18 +52,10 @@ public class PackageUserStateTest { assertThat(testUserState.equals(testUserState), is(true)); assertThat(testUserState.equals(oldUserState), is(true)); - oldUserState = new PackageUserState(); - oldUserState.appLinkGeneration = 6; - assertThat(testUserState.equals(oldUserState), is(false)); - oldUserState = new PackageUserState(); oldUserState.ceDataInode = 4000L; assertThat(testUserState.equals(oldUserState), is(false)); - oldUserState = new PackageUserState(); - oldUserState.domainVerificationStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK; - assertThat(testUserState.equals(oldUserState), is(false)); - oldUserState = new PackageUserState(); oldUserState.enabled = COMPONENT_ENABLED_STATE_ENABLED; assertThat(testUserState.equals(oldUserState), is(false)); From 47dbabfc14420ff54646df05039657df43901554 Mon Sep 17 00:00:00 2001 From: Winson Date: Wed, 20 Jan 2021 12:26:37 -0800 Subject: [PATCH 21/23] Convert DomainVerificationService.Connection to synchronous set Unfortunately it's possible to call this during PackageManagerService initialization, so this needs to be attached before PMS gets created. Rather than using a deferred injection, this makes PMS call setConnection to directly set the callback. A better isolated might be worth exploring in the future, but overall it shouldn't really matter. Exempt-From-Owner-Approval: Already approved by owners on main branch Bug: 163565712 Test: com.android.server.pm.test.domain.verify Change-Id: Ib60f5560ecaa510474228aa1c38eff5d6041ab58 --- .../server/pm/PackageManagerService.java | 4 +- .../DomainVerificationManagerInternal.java | 38 +++++ .../verify/DomainVerificationService.java | 130 ++++++------------ .../java/com/android/server/SystemServer.java | 12 +- .../verify/DomainVerificationEnforcerTest.kt | 23 ++-- 5 files changed, 98 insertions(+), 109 deletions(-) diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index cbc66ac644364..f3cc7cf22387c 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -6456,6 +6456,9 @@ public class PackageManagerService extends IPackageManager.Stub mAppInstallDir = new File(Environment.getDataDirectory(), "app"); mAppLib32InstallDir = getAppLib32InstallDir(); + mDomainVerificationManager = injector.getDomainVerificationManagerInternal(); + mDomainVerificationManager.setConnection(mDomainVerificationConnection); + // Link up the watchers mPackages.registerObserver(mWatcher); mSharedLibraries.registerObserver(mWatcher); @@ -6482,7 +6485,6 @@ public class PackageManagerService extends IPackageManager.Stub mProcessLoggingHandler = new ProcessLoggingHandler(); Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT); mIntentFilterVerificationManager = injector.getIntentFilterVerificationManager(); - mDomainVerificationManager = injector.getDomainVerificationManagerInternal(); ArrayMap libConfig = systemConfig.getSharedLibraries(); diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java index 56994c864870b..f123aa22a0a75 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java @@ -25,10 +25,14 @@ import android.content.pm.IntentFilterVerificationInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.domain.verify.DomainVerificationManager; import android.content.pm.domain.verify.DomainVerificationSet; +import android.os.Binder; +import android.os.UserHandle; import android.util.IndentingPrintWriter; import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; +import com.android.internal.annotations.GuardedBy; +import com.android.server.pm.PackageManagerService; import com.android.server.pm.PackageSetting; import com.android.server.pm.domain.verify.models.DomainVerificationPkgState; import com.android.server.pm.domain.verify.proxy.DomainVerificationProxy; @@ -50,6 +54,8 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan @NonNull UUID generateNewId(); + void setConnection(@NonNull Connection connection); + @NonNull DomainVerificationProxy getProxy(); @@ -219,4 +225,36 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan void setDomainVerificationStatusInternal(int callingUid, @NonNull UUID domainSetId, @NonNull Set domains, int state) throws InvalidDomainSetException, NameNotFoundException; + + + interface Connection { + + /** + * Notify that a settings change has been made and that eventually + * {@link #writeSettings(TypedXmlSerializer)} should be invoked by the parent. + */ + void scheduleWriteSettings(); + + /** + * Delegate to {@link Binder#getCallingUid()} to allow mocking in tests. + */ + int getCallingUid(); + + /** + * Delegate to {@link UserHandle#getCallingUserId()} to allow mocking in tests. + */ + @UserIdInt + int getCallingUserId(); + + /** + * @see DomainVerificationProxy.BaseConnection#schedule(int, java.lang.Object) + */ + void schedule(int code, @Nullable Object object); + + @Nullable + PackageSetting getPackageSettingLocked(@NonNull String pkgName); + + @Nullable + AndroidPackage getPackageLocked(@NonNull String pkgName); + } } diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java index ace72090193aa..d316773031b1b 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java @@ -110,7 +110,7 @@ public class DomainVerificationService extends SystemService private final Object mLock = new Object(); @NonNull - private final Singleton mConnection; + private Connection mConnection; @NonNull private final SystemConfig mSystemConfig; @@ -143,9 +143,8 @@ public class DomainVerificationService extends SystemService private DomainVerificationProxy mProxy = new DomainVerificationProxyUnavailable(); public DomainVerificationService(@NonNull Context context, @NonNull SystemConfig systemConfig, - @NonNull PlatformCompat platformCompat, @NonNull Singleton connection) { + @NonNull PlatformCompat platformCompat) { super(context); - mConnection = connection; mSystemConfig = systemConfig; mPlatformCompat = platformCompat; mSettings = new DomainVerificationSettings(); @@ -161,6 +160,11 @@ public class DomainVerificationService extends SystemService publishBinderService(Context.DOMAIN_VERIFICATION_SERVICE, mStub); } + @Override + public void setConnection(@NonNull Connection connection) { + mConnection = connection; + } + @NonNull @Override public DomainVerificationProxy getProxy() { @@ -185,7 +189,7 @@ public class DomainVerificationService extends SystemService @NonNull @Override public List getValidVerificationPackageNames() { - mEnforcer.assertApprovedVerifier(mConnection.get().getCallingUid(), mProxy); + mEnforcer.assertApprovedVerifier(mConnection.getCallingUid(), mProxy); List packageNames = new ArrayList<>(); synchronized (mLock) { int size = mAttachedPkgStates.size(); @@ -216,14 +220,14 @@ public class DomainVerificationService extends SystemService @Override public DomainVerificationSet getDomainVerificationSet(@NonNull String packageName) throws NameNotFoundException { - mEnforcer.assertApprovedQuerent(mConnection.get().getCallingUid(), mProxy); + mEnforcer.assertApprovedQuerent(mConnection.getCallingUid(), mProxy); synchronized (mLock) { DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName); if (pkgState == null) { return null; } - AndroidPackage pkg = mConnection.get().getPackageLocked(packageName); + AndroidPackage pkg = mConnection.getPackageLocked(packageName); if (pkg == null) { throw DomainVerificationUtils.throwPackageUnavailable(packageName); } @@ -258,7 +262,7 @@ public class DomainVerificationService extends SystemService } } - setDomainVerificationStatusInternal(mConnection.get().getCallingUid(), domainSetId, domains, + setDomainVerificationStatusInternal(mConnection.getCallingUid(), domainSetId, domains, state); } @@ -282,13 +286,13 @@ public class DomainVerificationService extends SystemService } } - mConnection.get().scheduleWriteSettings(); + mConnection.scheduleWriteSettings(); } @Override public void setDomainVerificationStatusInternal(@Nullable String packageName, int state, @Nullable ArraySet domains) throws NameNotFoundException { - mEnforcer.assertInternal(mConnection.get().getCallingUid()); + mEnforcer.assertInternal(mConnection.getCallingUid()); switch (state) { case DomainVerificationState.STATE_NO_RESPONSE: @@ -309,7 +313,7 @@ public class DomainVerificationService extends SystemService for (int index = 0; index < size; index++) { DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index); String pkgName = pkgState.getPackageName(); - PackageSetting pkgSetting = mConnection.get().getPackageSettingLocked(pkgName); + PackageSetting pkgSetting = mConnection.getPackageSettingLocked(pkgName); if (pkgSetting == null || pkgSetting.getPkg() == null) { continue; } @@ -336,7 +340,7 @@ public class DomainVerificationService extends SystemService throw DomainVerificationUtils.throwPackageUnavailable(packageName); } - PackageSetting pkgSetting = mConnection.get().getPackageSettingLocked(packageName); + PackageSetting pkgSetting = mConnection.getPackageSettingLocked(packageName); if (pkgSetting == null || pkgSetting.getPkg() == null) { throw DomainVerificationUtils.throwPackageUnavailable(packageName); } @@ -352,7 +356,7 @@ public class DomainVerificationService extends SystemService } } - mConnection.get().scheduleWriteSettings(); + mConnection.scheduleWriteSettings(); } private void setDomainVerificationStatusInternal(@NonNull DomainVerificationPkgState pkgState, @@ -368,14 +372,13 @@ public class DomainVerificationService extends SystemService public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName, boolean allowed) throws NameNotFoundException { setDomainVerificationLinkHandlingAllowed(packageName, allowed, - mConnection.get().getCallingUserId()); + mConnection.getCallingUserId()); } public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName, boolean allowed, @UserIdInt int userId) throws NameNotFoundException { - Connection connection = mConnection.get(); - mEnforcer.assertApprovedUserSelector(connection.getCallingUid(), - connection.getCallingUserId(), userId); + mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(), + mConnection.getCallingUserId(), userId); synchronized (mLock) { DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName); if (pkgState == null) { @@ -386,13 +389,13 @@ public class DomainVerificationService extends SystemService .setDisallowLinkHandling(!allowed); } - mConnection.get().scheduleWriteSettings(); + mConnection.scheduleWriteSettings(); } @Override public void setDomainVerificationLinkHandlingAllowedInternal(@Nullable String packageName, boolean allowed, @UserIdInt int userId) throws NameNotFoundException { - mEnforcer.assertInternal(mConnection.get().getCallingUid()); + mEnforcer.assertInternal(mConnection.getCallingUid()); if (packageName == null) { synchronized (mLock) { int pkgStateSize = mAttachedPkgStates.size(); @@ -426,7 +429,7 @@ public class DomainVerificationService extends SystemService } } - mConnection.get().scheduleWriteSettings(); + mConnection.scheduleWriteSettings(); } @Override @@ -434,15 +437,14 @@ public class DomainVerificationService extends SystemService @NonNull Set domains, boolean enabled) throws InvalidDomainSetException, NameNotFoundException { setDomainVerificationUserSelection(domainSetId, domains, enabled, - mConnection.get().getCallingUserId()); + mConnection.getCallingUserId()); } public void setDomainVerificationUserSelection(@NonNull UUID domainSetId, @NonNull Set domains, boolean enabled, @UserIdInt int userId) throws InvalidDomainSetException, NameNotFoundException { - Connection connection = mConnection.get(); - mEnforcer.assertApprovedUserSelector(connection.getCallingUid(), - connection.getCallingUserId(), userId); + mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(), + mConnection.getCallingUserId(), userId); synchronized (mLock) { DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains, false /* forAutoVerify */); @@ -454,14 +456,14 @@ public class DomainVerificationService extends SystemService } } - mConnection.get().scheduleWriteSettings(); + mConnection.scheduleWriteSettings(); } @Override public void setDomainVerificationUserSelectionInternal(@UserIdInt int userId, @Nullable String packageName, boolean enabled, @NonNull ArraySet domains) throws NameNotFoundException { - mEnforcer.assertInternal(mConnection.get().getCallingUid()); + mEnforcer.assertInternal(mConnection.getCallingUid()); if (packageName == null) { synchronized (mLock) { @@ -471,7 +473,7 @@ public class DomainVerificationService extends SystemService for (int index = 0; index < size; index++) { DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index); String pkgName = pkgState.getPackageName(); - PackageSetting pkgSetting = mConnection.get().getPackageSettingLocked(pkgName); + PackageSetting pkgSetting = mConnection.getPackageSettingLocked(pkgName); if (pkgSetting == null || pkgSetting.getPkg() == null) { continue; } @@ -490,7 +492,7 @@ public class DomainVerificationService extends SystemService throw DomainVerificationUtils.throwPackageUnavailable(packageName); } - PackageSetting pkgSetting = mConnection.get().getPackageSettingLocked(packageName); + PackageSetting pkgSetting = mConnection.getPackageSettingLocked(packageName); if (pkgSetting == null || pkgSetting.getPkg() == null) { throw DomainVerificationUtils.throwPackageUnavailable(packageName); } @@ -500,7 +502,7 @@ public class DomainVerificationService extends SystemService } } - mConnection.get().scheduleWriteSettings(); + mConnection.scheduleWriteSettings(); } private void setDomainVerificationUserSelectionInternal(int userId, @@ -529,7 +531,7 @@ public class DomainVerificationService extends SystemService } } - mConnection.get().scheduleWriteSettings(); + mConnection.scheduleWriteSettings(); } @Nullable @@ -537,23 +539,22 @@ public class DomainVerificationService extends SystemService public DomainVerificationUserSelection getDomainVerificationUserSelection( @NonNull String packageName) throws NameNotFoundException { return getDomainVerificationUserSelection(packageName, - mConnection.get().getCallingUserId()); + mConnection.getCallingUserId()); } @Nullable @Override public DomainVerificationUserSelection getDomainVerificationUserSelection( @NonNull String packageName, @UserIdInt int userId) throws NameNotFoundException { - Connection connection = mConnection.get(); - mEnforcer.assertApprovedUserSelector(connection.getCallingUid(), - connection.getCallingUserId(), userId); + mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(), + mConnection.getCallingUserId(), userId); synchronized (mLock) { DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName); if (pkgState == null) { return null; } - AndroidPackage pkg = connection.getPackageLocked(packageName); + AndroidPackage pkg = mConnection.getPackageLocked(packageName); if (pkg == null) { throw DomainVerificationUtils.throwPackageUnavailable(packageName); } @@ -834,7 +835,7 @@ public class DomainVerificationService extends SystemService @Override public void setLegacyUserState(@NonNull String packageName, @UserIdInt int userId, int state) { - mEnforcer.callerIsLegacyUserSelector(mConnection.get().getCallingUid()); + mEnforcer.callerIsLegacyUserSelector(mConnection.getCallingUid()); mLegacySettings.add(packageName, userId, state); } @@ -854,7 +855,7 @@ public class DomainVerificationService extends SystemService mAttachedPkgStates.remove(packageName); } - mConnection.get().scheduleWriteSettings(); + mConnection.scheduleWriteSettings(); } @Override @@ -868,7 +869,7 @@ public class DomainVerificationService extends SystemService mSettings.removeUser(userId); } - mConnection.get().scheduleWriteSettings(); + mConnection.scheduleWriteSettings(); } @Override @@ -880,7 +881,7 @@ public class DomainVerificationService extends SystemService public void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName, @Nullable @UserIdInt Integer userId) throws NameNotFoundException { synchronized (mLock) { - mDebug.printState(writer, packageName, userId, mConnection.get(), mAttachedPkgStates); + mDebug.printState(writer, packageName, userId, mConnection, mAttachedPkgStates); } } @@ -925,7 +926,7 @@ public class DomainVerificationService extends SystemService } String pkgName = pkgState.getPackageName(); - PackageSetting pkgSetting = mConnection.get().getPackageSettingLocked(pkgName); + PackageSetting pkgSetting = mConnection.getPackageSettingLocked(pkgName); if (pkgSetting == null || pkgSetting.getPkg() == null) { throw DomainVerificationUtils.throwPackageUnavailable(pkgName); } @@ -949,7 +950,7 @@ public class DomainVerificationService extends SystemService @Override public void verifyPackages(@Nullable List packageNames, boolean reVerify) { - mEnforcer.assertInternal(mConnection.get().getCallingUid()); + mEnforcer.assertInternal(mConnection.getCallingUid()); Set packagesToBroadcast = new ArraySet<>(); if (packageNames == null) { @@ -1013,14 +1014,14 @@ public class DomainVerificationService extends SystemService @Override public void clearDomainVerificationState(@Nullable List packageNames) { - mEnforcer.assertInternal(mConnection.get().getCallingUid()); + mEnforcer.assertInternal(mConnection.getCallingUid()); synchronized (mLock) { if (packageNames == null) { int size = mAttachedPkgStates.size(); for (int index = 0; index < size; index++) { DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index); String pkgName = pkgState.getPackageName(); - PackageSetting pkgSetting = mConnection.get().getPackageSettingLocked(pkgName); + PackageSetting pkgSetting = mConnection.getPackageSettingLocked(pkgName); if (pkgSetting == null || pkgSetting.getPkg() == null) { continue; } @@ -1031,7 +1032,7 @@ public class DomainVerificationService extends SystemService for (int index = 0; index < size; index++) { String pkgName = packageNames.get(index); DomainVerificationPkgState pkgState = mAttachedPkgStates.get(pkgName); - PackageSetting pkgSetting = mConnection.get().getPackageSettingLocked(pkgName); + PackageSetting pkgSetting = mConnection.getPackageSettingLocked(pkgName); if (pkgSetting == null || pkgSetting.getPkg() == null) { continue; } @@ -1071,7 +1072,7 @@ public class DomainVerificationService extends SystemService @Override public void clearUserSelections(@Nullable List packageNames, @UserIdInt int userId) { - mEnforcer.assertInternal(mConnection.get().getCallingUid()); + mEnforcer.assertInternal(mConnection.getCallingUid()); synchronized (mLock) { if (packageNames == null) { int size = mAttachedPkgStates.size(); @@ -1226,45 +1227,4 @@ public class DomainVerificationService extends SystemService Slog.d(TAG + "Approval", packageName + " was " + approvalString + " for " + intent + " for user " + userId + ": " + reason); } - - public interface Connection { - - /** - * Notify that a settings change has been made and that eventually - * {@link #writeSettings(TypedXmlSerializer)} should be invoked by the parent. - */ - void scheduleWriteSettings(); - - /** - * Delegate to {@link Binder#getCallingUid()} to allow mocking in tests. - */ - int getCallingUid(); - - /** - * Delegate to {@link UserHandle#getCallingUserId()} to allow mocking in tests. - */ - @UserIdInt - int getCallingUserId(); - - /** - * @see DomainVerificationProxy.BaseConnection#schedule(int, java.lang.Object) - */ - void schedule(int code, @Nullable Object object); - - /** - * This can only be called when the internal {@link #mLock} is held. Otherwise it's possible - * to deadlock with {@link PackageManagerService}. - */ - @GuardedBy("mLock") - @Nullable - PackageSetting getPackageSettingLocked(@NonNull String pkgName); - - /** - * This can only be called when the internal {@link #mLock} is held. Otherwise it's possible - * to deadlock with {@link PackageManagerService}. - */ - @GuardedBy("mLock") - @Nullable - AndroidPackage getPackageLocked(@NonNull String pkgName); - } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index b09d8a67577fb..7b117d76b3282 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -87,7 +87,6 @@ import android.util.DisplayMetrics; import android.util.EventLog; import android.util.IndentingPrintWriter; import android.util.Pair; -import android.util.Singleton; import android.util.Slog; import android.util.TimeUtils; import android.view.contentcapture.ContentCaptureManager; @@ -1064,16 +1063,7 @@ public final class SystemServer implements Dumpable { t.traceBegin("StartDomainVerificationService"); DomainVerificationService domainVerificationService = new DomainVerificationService( - mSystemContext, SystemConfig.getInstance(), platformCompat, - new Singleton() { - @Override - protected DomainVerificationService.Connection create() { - // Deferred retrieval from PackageManagerService, since PMS is initialized after - // DVS. The alternative would be to expose this through the PackageManagerInternal - // local service, but making it visible to consumers of that interface isn't useful. - return mPackageManagerService.getDomainVerificationConnection(); - } - }); + mSystemContext, SystemConfig.getInstance(), platformCompat); mSystemServiceManager.startService(domainVerificationService); t.traceEnd(); diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationEnforcerTest.kt index c944cff8c04f1..fbd5ae8863369 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationEnforcerTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationEnforcerTest.kt @@ -30,6 +30,7 @@ import android.util.SparseArray import androidx.test.platform.app.InstrumentationRegistry import com.android.server.pm.PackageSetting import com.android.server.pm.domain.verify.DomainVerificationEnforcer +import com.android.server.pm.domain.verify.DomainVerificationManagerInternal import com.android.server.pm.domain.verify.DomainVerificationService import com.android.server.pm.domain.verify.proxy.DomainVerificationProxy import com.android.server.pm.parsing.pkg.AndroidPackage @@ -140,18 +141,16 @@ class DomainVerificationEnforcerTest { any() ) ) { true } - }, - object : Singleton() { - override fun create(): DomainVerificationService.Connection = - mockThrowOnUnmocked { - whenever(callingUid) { callingUidInt.get() } - whenever(callingUserId) { callingUserIdInt.get() } - whenever(getPackageSettingLocked(TEST_PKG)) { mockPkgSetting } - whenever(getPackageLocked(TEST_PKG)) { mockPkg } - whenever(schedule(anyInt(), any())) - whenever(scheduleWriteSettings()) - } - }) + }).apply { + setConnection(mockThrowOnUnmocked { + whenever(callingUid) { callingUidInt.get() } + whenever(callingUserId) { callingUserIdInt.get() } + whenever(getPackageSettingLocked(TEST_PKG)) { mockPkgSetting } + whenever(getPackageLocked(TEST_PKG)) { mockPkg } + whenever(schedule(anyInt(), any())) + whenever(scheduleWriteSettings()) + }) + } ) } From 3c7113a4010419da506d72e35f758662a1c85685 Mon Sep 17 00:00:00 2001 From: Winson Date: Tue, 19 Jan 2021 12:21:12 -0800 Subject: [PATCH 22/23] Remove legacy IntentFilterVerificationManager Completely deletes all the code for v1 of domain verification, delegating everything to the new DomainVerificationService. Exempt-From-Owner-Approval: Already approved by owners on main branch Bug: 171251883 Test: none, removal of effectively dead code Change-Id: Ib222ccd46019c1c28b402c0f68466077b62871c1 --- core/api/system-current.txt | 24 +- core/java/android/content/Intent.java | 6 +- .../android/content/pm/IPackageManager.aidl | 4 + .../android/content/pm/PackageManager.java | 40 ++ .../pm/parsing/ParsingPackageUtils.java | 4 + .../java/com/android/server/SystemConfig.java | 5 +- .../server/pm/PackageManagerService.java | 448 ++------------ .../java/com/android/server/pm/Settings.java | 11 - .../DomainVerificationManagerInternal.java | 1 + .../proxy/DomainVerificationProxyV1.java | 5 +- .../legacy/IntentFilterVerificationKey.java | 64 -- .../IntentFilterVerificationManager.java | 584 ------------------ .../IntentFilterVerificationParams.java | 42 -- .../IntentFilterVerificationResponse.java | 43 -- .../IntentFilterVerificationSettings.java | 394 ------------ .../legacy/IntentFilterVerificationState.java | 130 ---- .../verify/legacy/IntentVerifierProxy.java | 203 ------ .../verify/legacy/IntentVerifyUtils.java | 34 - .../verify/DomainVerificationEnforcerTest.kt | 1 - .../pm/PackageManagerSettingsTests.java | 5 +- 20 files changed, 110 insertions(+), 1938 deletions(-) delete mode 100644 services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationKey.java delete mode 100644 services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationManager.java delete mode 100644 services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationParams.java delete mode 100644 services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationResponse.java delete mode 100644 services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationSettings.java delete mode 100644 services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationState.java delete mode 100644 services/core/java/com/android/server/pm/intent/verify/legacy/IntentVerifierProxy.java delete mode 100644 services/core/java/com/android/server/pm/intent/verify/legacy/IntentVerifyUtils.java diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 1c19918f7add5..9d8accb4f676d 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -2153,7 +2153,7 @@ package android.content { field public static final String ACTION_INCIDENT_REPORT_READY = "android.intent.action.INCIDENT_REPORT_READY"; field public static final String ACTION_INSTALL_INSTANT_APP_PACKAGE = "android.intent.action.INSTALL_INSTANT_APP_PACKAGE"; field public static final String ACTION_INSTANT_APP_RESOLVER_SETTINGS = "android.intent.action.INSTANT_APP_RESOLVER_SETTINGS"; - field public static final String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION"; + field @Deprecated public static final String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION"; field public static final String ACTION_LOAD_DATA = "android.intent.action.LOAD_DATA"; field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_MANAGE_APP_PERMISSION = "android.intent.action.MANAGE_APP_PERMISSION"; field public static final String ACTION_MANAGE_APP_PERMISSIONS = "android.intent.action.MANAGE_APP_PERMISSIONS"; @@ -2486,8 +2486,8 @@ package android.content.pm { method @Nullable public abstract android.content.ComponentName getInstantAppInstallerComponent(); method @Nullable public abstract android.content.ComponentName getInstantAppResolverSettingsComponent(); method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_INSTANT_APPS) public abstract java.util.List getInstantApps(); - method @NonNull public abstract java.util.List getIntentFilterVerifications(@NonNull String); - method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract int getIntentVerificationStatusAsUser(@NonNull String, int); + method @Deprecated @NonNull public abstract java.util.List getIntentFilterVerifications(@NonNull String); + method @Deprecated @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract int getIntentVerificationStatusAsUser(@NonNull String, int); method @android.content.pm.PackageManager.PermissionFlags @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, android.Manifest.permission.GET_RUNTIME_PERMISSIONS}) public abstract int getPermissionFlags(@NonNull String, @NonNull String, @NonNull android.os.UserHandle); method @NonNull @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] getUnsuspendablePackages(@NonNull String[]); method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle); @@ -2511,9 +2511,9 @@ package android.content.pm { method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public void setSyntheticAppDetailsActivityEnabled(@NonNull String, boolean); method public void setSystemAppState(@NonNull String, int); method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public abstract void setUpdateAvailable(@NonNull String, boolean); - method @RequiresPermission(android.Manifest.permission.SET_PREFERRED_APPLICATIONS) public abstract boolean updateIntentVerificationStatusAsUser(@NonNull String, int, int); + method @Deprecated @RequiresPermission(android.Manifest.permission.SET_PREFERRED_APPLICATIONS) public abstract boolean updateIntentVerificationStatusAsUser(@NonNull String, int, int); method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS}) public abstract void updatePermissionFlags(@NonNull String, @NonNull String, @android.content.pm.PackageManager.PermissionFlags int, @android.content.pm.PackageManager.PermissionFlags int, @NonNull android.os.UserHandle); - method @RequiresPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT) public abstract void verifyIntentFilter(int, int, @NonNull java.util.List); + method @Deprecated @RequiresPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT) public abstract void verifyIntentFilter(int, int, @NonNull java.util.List); field public static final String ACTION_REQUEST_PERMISSIONS = "android.content.pm.action.REQUEST_PERMISSIONS"; field public static final String EXTRA_REQUEST_PERMISSIONS_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES"; field public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS"; @@ -2581,13 +2581,13 @@ package android.content.pm { field public static final int INSTALL_PARSE_FAILED_NO_CERTIFICATES = -103; // 0xffffff99 field public static final int INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION = -102; // 0xffffff9a field public static final int INSTALL_SUCCEEDED = 1; // 0x1 - field public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS = 2; // 0x2 - field public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK = 4; // 0x4 - field public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK = 1; // 0x1 - field public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER = 3; // 0x3 - field public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED = 0; // 0x0 - field public static final int INTENT_FILTER_VERIFICATION_FAILURE = -1; // 0xffffffff - field public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1; // 0x1 + field @Deprecated public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS = 2; // 0x2 + field @Deprecated public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK = 4; // 0x4 + field @Deprecated public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK = 1; // 0x1 + field @Deprecated public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER = 3; // 0x3 + field @Deprecated public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED = 0; // 0x0 + field @Deprecated public static final int INTENT_FILTER_VERIFICATION_FAILURE = -1; // 0xffffffff + field @Deprecated public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1; // 0x1 field @Deprecated public static final int MASK_PERMISSION_FLAGS = 255; // 0xff field public static final int MATCH_ANY_USER = 4194304; // 0x400000 field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000 diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index e6753427b908d..b6d5ac697939e 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -37,6 +37,7 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ShortcutInfo; import android.content.pm.SuspendDialogInfo; +import android.content.pm.domain.verify.DomainVerificationManager; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Rect; @@ -2841,10 +2842,13 @@ public class Intent implements Parcelable, Cloneable { *

* * @hide + * @deprecated Superseded by domain verification APIs. See {@link DomainVerificationManager}. */ + @Deprecated @SystemApi @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION"; + public static final String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = + "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION"; /** diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 34d100354051f..b8829bbf1ca5c 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -627,9 +627,13 @@ interface IPackageManager { void verifyPendingInstall(int id, int verificationCode); void extendVerificationTimeout(int id, int verificationCodeAtTimeout, long millisecondsToDelay); + /** @deprecated */ void verifyIntentFilter(int id, int verificationCode, in List failedDomains); + /** @deprecated */ int getIntentVerificationStatus(String packageName, int userId); + /** @deprecated */ boolean updateIntentVerificationStatus(String packageName, int status, int userId); + /** @deprecated */ ParceledListSlice getIntentFilterVerifications(String packageName); ParceledListSlice getAllIntentFilters(String packageName); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 0e7e6da7a4f1e..abe7b48059fef 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -47,6 +47,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; +import android.content.pm.verify.domain.DomainVerificationManager; import android.content.pm.dex.ArtManager; import android.content.res.Configuration; import android.content.res.Resources; @@ -93,6 +94,7 @@ import java.util.List; import java.util.Locale; import java.util.Objects; import java.util.Set; +import java.util.UUID; /** * Class for retrieving various kinds of information related to the application @@ -2201,8 +2203,10 @@ public abstract class PackageManager { * {@link PackageManager#verifyIntentFilter} to indicate that the calling * IntentFilter Verifier confirms that the IntentFilter is verified. * + * @deprecated Use {@link DomainVerificationManager} APIs. * @hide */ + @Deprecated @SystemApi public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1; @@ -2211,16 +2215,20 @@ public abstract class PackageManager { * {@link PackageManager#verifyIntentFilter} to indicate that the calling * IntentFilter Verifier confirms that the IntentFilter is NOT verified. * + * @deprecated Use {@link DomainVerificationManager} APIs. * @hide */ + @Deprecated @SystemApi public static final int INTENT_FILTER_VERIFICATION_FAILURE = -1; /** * Internal status code to indicate that an IntentFilter verification result is not specified. * + * @deprecated Use {@link DomainVerificationManager} APIs. * @hide */ + @Deprecated @SystemApi public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED = 0; @@ -2230,8 +2238,10 @@ public abstract class PackageManager { * will always be prompted the Intent Disambiguation Dialog if there are two * or more Intent resolved for the IntentFilter's domain(s). * + * @deprecated Use {@link DomainVerificationManager} APIs. * @hide */ + @Deprecated @SystemApi public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK = 1; @@ -2242,8 +2252,10 @@ public abstract class PackageManager { * or more resolution of the Intent. The default App for the domain(s) * specified in the IntentFilter will also ALWAYS be used. * + * @deprecated Use {@link DomainVerificationManager} APIs. * @hide */ + @Deprecated @SystemApi public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS = 2; @@ -2254,8 +2266,10 @@ public abstract class PackageManager { * Intent resolved. The default App for the domain(s) specified in the * IntentFilter will also NEVER be presented to the User. * + * @deprecated Use {@link DomainVerificationManager} APIs. * @hide */ + @Deprecated @SystemApi public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER = 3; @@ -2268,8 +2282,10 @@ public abstract class PackageManager { * more than one candidate app, then a disambiguation is *always* presented * even if there is another candidate app with the 'always' state. * + * @deprecated Use {@link DomainVerificationManager} APIs. * @hide */ + @Deprecated @SystemApi public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK = 4; @@ -3743,8 +3759,10 @@ public abstract class PackageManager { * Passed to an intent filter verifier and is used to call back to * {@link #verifyIntentFilter} * + * @deprecated Use {@link DomainVerificationManager} APIs. * @hide */ + @Deprecated public static final String EXTRA_INTENT_FILTER_VERIFICATION_ID = "android.content.pm.extra.INTENT_FILTER_VERIFICATION_ID"; @@ -3754,8 +3772,10 @@ public abstract class PackageManager { * * Usually this is "https" * + * @deprecated Use {@link DomainVerificationManager} APIs. * @hide */ + @Deprecated public static final String EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME = "android.content.pm.extra.INTENT_FILTER_VERIFICATION_URI_SCHEME"; @@ -3766,8 +3786,10 @@ public abstract class PackageManager { * * This is a space delimited list of hosts. * + * @deprecated Use {@link DomainVerificationManager} APIs. * @hide */ + @Deprecated public static final String EXTRA_INTENT_FILTER_VERIFICATION_HOSTS = "android.content.pm.extra.INTENT_FILTER_VERIFICATION_HOSTS"; @@ -3777,8 +3799,10 @@ public abstract class PackageManager { * from the hosts. Each host response will need to include the package name of APK containing * the intent filter. * + * @deprecated Use {@link DomainVerificationManager} APIs. * @hide */ + @Deprecated public static final String EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME = "android.content.pm.extra.INTENT_FILTER_VERIFICATION_PACKAGE_NAME"; @@ -6956,8 +6980,10 @@ public abstract class PackageManager { * @throws SecurityException if the caller does not have the * INTENT_FILTER_VERIFICATION_AGENT permission. * + * @deprecated Use {@link DomainVerificationManager} APIs. * @hide */ + @Deprecated @SuppressWarnings("HiddenAbstractMethod") @SystemApi @RequiresPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT) @@ -6982,8 +7008,10 @@ public abstract class PackageManager { * {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER} or * {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED} * + * @deprecated Use {@link DomainVerificationManager} APIs. * @hide */ + @Deprecated @SuppressWarnings("HiddenAbstractMethod") @SystemApi @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL) @@ -7008,8 +7036,18 @@ public abstract class PackageManager { * * @return true if the status has been set. False otherwise. * + * @deprecated This API represents a very dangerous behavior where Settings or a system app with + * the right permissions can force an application to be verified for all of its declared + * domains. This has been removed to prevent unintended usage, and no longer does anything, + * always returning false. If a caller truly wishes to grant every declared web + * domain to an application, use + * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, boolean)}, + * passing in all of the domains returned inside + * {@link DomainVerificationManager#getDomainVerificationUserSelection(String)}. + * * @hide */ + @Deprecated @SuppressWarnings("HiddenAbstractMethod") @SystemApi @RequiresPermission(android.Manifest.permission.SET_PREFERRED_APPLICATIONS) @@ -7026,8 +7064,10 @@ public abstract class PackageManager { * * @return a list of IntentFilterVerificationInfo for a specific package. * + * @deprecated Use {@link DomainVerificationManager} instead. * @hide */ + @Deprecated @SuppressWarnings("HiddenAbstractMethod") @NonNull @SystemApi diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java index 8fbf2879bc27a..31413b6156973 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java @@ -2493,6 +2493,10 @@ public class ParsingPackageUtils { /** * Check if one of the IntentFilter as both actions DEFAULT / VIEW and a HTTP/HTTPS data URI + * + * This is distinct from any of the functionality of app links domain verification, and cannot + * be converted to remain backwards compatible. It's possible the presence of this flag does + * not indicate a valid package for domain verification. */ private static boolean hasDomainURLs(ParsingPackage pkg) { final List activities = pkg.getActivities(); diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index e5bc47097751c..cb586d6606343 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -178,8 +178,9 @@ public class SystemConfig { // be delivered anonymously even to apps which target O+. final ArraySet mAllowImplicitBroadcasts = new ArraySet<>(); - // These are the package names of apps which should be in the 'always' - // URL-handling state upon factory reset. + // These are the package names of apps which should be automatically granted domain verification + // for all of their domains. The only way these apps can be overridden by the user is by + // explicitly disabling overall link handling support in app info. final ArraySet mLinkedApps = new ArraySet<>(); // These are the components that are enabled by default as VR mode listener services. diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index f3cc7cf22387c..ed8b7bd638561 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -382,9 +382,6 @@ import com.android.server.pm.domain.verify.DomainVerificationService; import com.android.server.pm.domain.verify.proxy.DomainVerificationProxy; import com.android.server.pm.domain.verify.proxy.DomainVerificationProxyV1; import com.android.server.pm.domain.verify.proxy.DomainVerificationProxyV2; -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.parsing.PackageCacher; import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.PackageParser2; @@ -1073,8 +1070,6 @@ 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 mDomainVerificationManagerInternalProducer; private final Singleton mHandlerProducer; @@ -1106,7 +1101,6 @@ public class PackageManagerService extends IPackageManager.Stub instantAppResolverConnectionProducer, Producer moduleInfoProviderProducer, Producer legacyPermissionManagerInternalProducer, - Producer intentFilterVerificationManagerProducer, Producer domainVerificationManagerInternalProducer, Producer handlerProducer, @@ -1147,8 +1141,6 @@ public class PackageManagerService extends IPackageManager.Stub mSystemWrapper = systemWrapper; mGetLocalServiceProducer = getLocalServiceProducer; mGetSystemServiceProducer = getSystemServiceProducer; - mIntentFilterVerificationManagerProducer = - new Singleton<>(intentFilterVerificationManagerProducer); mDomainVerificationManagerInternalProducer = new Singleton<>(domainVerificationManagerInternalProducer); mHandlerProducer = new Singleton<>(handlerProducer); @@ -1298,10 +1290,6 @@ public class PackageManagerService extends IPackageManager.Stub return mLegacyPermissionManagerInternalProducer.get(this, mPackageManager); } - public IntentFilterVerificationManager getIntentFilterVerificationManager() { - return mIntentFilterVerificationManagerProducer.get(this, mPackageManager); - } - public DomainVerificationManagerInternal getDomainVerificationManagerInternal() { return mDomainVerificationManagerInternalProducer.get(this, mPackageManager); } @@ -1485,9 +1473,6 @@ public class PackageManagerService extends IPackageManager.Stub boolean mResolverReplaced = false; - @NonNull - private final IntentFilterVerificationManager mIntentFilterVerificationManager; - @NonNull private final DomainVerificationManagerInternal mDomainVerificationManager; @@ -1598,8 +1583,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; - public static final int START_INTENT_FILTER_VERIFICATIONS = 17; - public static final int INTENT_FILTER_VERIFIED = 18; + // public static final int UNUSED = 17; + // public static final int UNUSED = 18; static final int WRITE_PACKAGE_LIST = 19; static final int INSTANT_APP_RESOLUTION_PHASE_TWO = 20; static final int ENABLE_ROLLBACK_STATUS = 21; @@ -1624,8 +1609,6 @@ public class PackageManagerService extends IPackageManager.Stub private static final long DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD = 2 * 60 * 60 * 1000L; /* two hours */ - private static final boolean USE_DOMAIN_VERIFICATION_V2 = true; - final UserManagerService mUserManager; // Stores a list of users whose package restrictions file needs to be updated @@ -1704,124 +1687,6 @@ 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 null; - } - - @NonNull - @Override - public DeviceIdleInternal getDeviceIdleInternal() { - return mInjector.getLocalService(DeviceIdleInternal.class); - } - }; - private final DomainVerificationConnection mDomainVerificationConnection = new DomainVerificationConnection(); @@ -2721,8 +2586,6 @@ public class PackageManagerService extends IPackageManager.Stub final ArrayList result = new ArrayList<>(); final ArrayList alwaysList = new ArrayList<>(); final ArrayList undefinedList = new ArrayList<>(); - final ArrayList alwaysAskList = new ArrayList<>(); - final ArrayList neverList = new ArrayList<>(); final ArrayList matchAllList = new ArrayList<>(); final int count = candidates.size(); // First, try to use linked apps. Partition the candidates into four lists: @@ -2739,55 +2602,14 @@ public class PackageManagerService extends IPackageManager.Stub continue; } - if (USE_DOMAIN_VERIFICATION_V2) { - boolean isAlways = mDomainVerificationManager - .isApprovedForDomain(ps, intent, userId); - if (isAlways) { - alwaysList.add(info); - } else { - undefinedList.add(info); - } - continue; - } - - // Try to get the status from User settings first - long packedStatus = 0; - //IntentVerifyUtils.getDomainVerificationStatus(ps, userId); - int status = (int)(packedStatus >> 32); - int linkGeneration = (int)(packedStatus & 0xFFFFFFFF); - if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) { - if (DEBUG_DOMAIN_VERIFICATION || debug) { - Slog.i(TAG, " + always: " + info.activityInfo.packageName - + " : linkgen=" + linkGeneration); - } - - if (!intent.hasCategory(CATEGORY_BROWSABLE) - || !intent.hasCategory(CATEGORY_DEFAULT)) { - undefinedList.add(info); - continue; - } - - // Use link-enabled generation as preferredOrder, i.e. - // prefer newly-enabled over earlier-enabled. - info.preferredOrder = linkGeneration; + boolean isAlways = mDomainVerificationManager + .isApprovedForDomain(ps, intent, userId); + if (isAlways) { alwaysList.add(info); - } else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { - if (DEBUG_DOMAIN_VERIFICATION || debug) { - Slog.i(TAG, " + never: " + info.activityInfo.packageName); - } - neverList.add(info); - } else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) { - if (DEBUG_DOMAIN_VERIFICATION || debug) { - Slog.i(TAG, " + always-ask: " + info.activityInfo.packageName); - } - alwaysAskList.add(info); - } else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED || - status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK) { - if (DEBUG_DOMAIN_VERIFICATION || debug) { - Slog.i(TAG, " + ask: " + info.activityInfo.packageName); - } + } else { undefinedList.add(info); } + continue; } } @@ -2801,30 +2623,12 @@ public class PackageManagerService extends IPackageManager.Stub // Add all undefined apps as we want them to appear in the disambiguation dialog. result.addAll(undefinedList); // Maybe add one for the other profile. - if (xpDomainInfo != null) { - if (USE_DOMAIN_VERIFICATION_V2) { - if (xpDomainInfo.wereAnyDomainsVerificationApproved) { - result.add(xpDomainInfo.resolveInfo); - } - } else if (xpDomainInfo.bestDomainVerificationStatus - != INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { - result.add(xpDomainInfo.resolveInfo); - } + if (xpDomainInfo != null && xpDomainInfo.wereAnyDomainsVerificationApproved) { + result.add(xpDomainInfo.resolveInfo); } includeBrowser = true; } - // The presence of any 'always ask' alternatives means we'll also offer browsers. - // If there were 'always' entries their preferred order has been set, so we also - // back that off to make the alternatives equivalent - if (alwaysAskList.size() > 0) { - for (ResolveInfo i : result) { - i.preferredOrder = 0; - } - result.addAll(alwaysAskList); - includeBrowser = true; - } - if (includeBrowser) { // Also add browsers (all of them or only the default one) if (DEBUG_DOMAIN_VERIFICATION) { @@ -2875,7 +2679,6 @@ public class PackageManagerService extends IPackageManager.Stub // has explicitly put into the INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER state if (result.size() == 0) { result.addAll(candidates); - result.removeAll(neverList); } } return result; @@ -2975,27 +2778,11 @@ public class PackageManagerService extends IPackageManager.Stub sourceUserId, parentUserId); } - if (USE_DOMAIN_VERIFICATION_V2) { - result.wereAnyDomainsVerificationApproved |= mDomainVerificationManager - .isApprovedForDomain(ps, intent, riTargetUser.targetUserId); - } else { - long verificationState = 0; - //IntentVerifyUtils.getDomainVerificationStatus(ps, parentUserId); - int status = (int) (verificationState >> 32); - result.bestDomainVerificationStatus = bestDomainVerificationStatus(status, - result.bestDomainVerificationStatus); - } + result.wereAnyDomainsVerificationApproved |= mDomainVerificationManager + .isApprovedForDomain(ps, intent, riTargetUser.targetUserId); } - if (result != null) { - if (USE_DOMAIN_VERIFICATION_V2) { - if (!result.wereAnyDomainsVerificationApproved) { - return null; - } - } else if (result.bestDomainVerificationStatus - == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { - // Don't consider matches with status NEVER across profiles. - return null; - } + if (result != null && !result.wereAnyDomainsVerificationApproved) { + return null; } return result; } @@ -3237,46 +3024,20 @@ public class PackageManagerService extends IPackageManager.Stub final String packageName = info.activityInfo.packageName; final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps.getInstantApp(userId)) { - if (USE_DOMAIN_VERIFICATION_V2) { - if (mDomainVerificationManager - .isApprovedForDomain(ps, intent, userId)) { - if (DEBUG_INSTANT) { - Slog.v(TAG, "Instant app approvd for intent; pkg: " - + packageName); - } - localInstantApp = info; - break; - } else { - if (DEBUG_INSTANT) { - Slog.v(TAG, "Instant app not approved for intent; pkg: " - + packageName); - } - blockResolution = true; - break; - } - } - - final long packedStatus = 0; - //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 - // chosen to never use it; skip resolution and don't acknowledge - // an instant application is even available + if (mDomainVerificationManager.isApprovedForDomain(ps, intent, userId)) { if (DEBUG_INSTANT) { - Slog.v(TAG, "Instant app marked to never run; pkg: " + packageName); - } - blockResolution = true; - break; - } else { - // we have a locally installed instant application; skip resolution - // but acknowledge there's an instant application available - if (DEBUG_INSTANT) { - Slog.v(TAG, "Found installed instant app; pkg: " + packageName); + Slog.v(TAG, "Instant app approvd for intent; pkg: " + + packageName); } localInstantApp = info; - break; + } else { + if (DEBUG_INSTANT) { + Slog.v(TAG, "Instant app not approved for intent; pkg: " + + packageName); + } + blockResolution = true; } + break; } } } @@ -4190,29 +3951,12 @@ public class PackageManagerService extends IPackageManager.Stub if (ps != null) { // only check domain verification status if the app is not a browser if (!info.handleAllWebDataURI) { - if (USE_DOMAIN_VERIFICATION_V2) { - if (mDomainVerificationManager - .isApprovedForDomain(ps, intent, userId)) { - if (DEBUG_INSTANT) { - Slog.v(TAG, "DENY instant app;" + " pkg: " + packageName - + ", approved"); - } - return false; - } - } else { - // Try to get the status from User settings first - final long packedStatus = 0; - //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) { - if (DEBUG_INSTANT) { - Slog.v(TAG, "DENY instant app;" - + " pkg: " + packageName + ", status: " + status); - } - return false; + if (mDomainVerificationManager.isApprovedForDomain(ps, intent, userId)) { + if (DEBUG_INSTANT) { + Slog.v(TAG, "DENY instant app;" + " pkg: " + packageName + + ", approved"); } + return false; } } if (ps.getInstantApp(userId)) { @@ -5352,15 +5096,6 @@ public class PackageManagerService extends IPackageManager.Stub params.handleIntegrityVerificationFinished(); break; } - case START_INTENT_FILTER_VERIFICATIONS: { - mIntentFilterVerificationManager.verifyIntentFiltersIfNeeded( - (IntentFilterVerificationParams) msg.obj); - break; - } - case INTENT_FILTER_VERIFIED: { - mIntentFilterVerificationManager.onFilterVerified(msg); - break; - } case INSTANT_APP_RESOLUTION_PHASE_TWO: { InstantAppResolver.doInstantAppResolutionPhaseTwo(mContext, mInstantAppResolverConnection, @@ -6018,7 +5753,6 @@ public class PackageManagerService extends IPackageManager.Stub (i, pm) -> new Settings(Environment.getDataDirectory(), RuntimePermissionsPersistence.createInstance(), i.getPermissionManagerServiceInternal(), - i.getIntentFilterVerificationManager(), domainVerificationService, lock), (i, pm) -> AppsFilter.create(pm.mPmInternal, i), (i, pm) -> (PlatformCompat) ServiceManager.getService("platform_compat"), @@ -6052,9 +5786,6 @@ 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) -> domainVerificationService, (i, pm) -> { HandlerThread thread = new ServiceThread(TAG, @@ -6233,7 +5964,6 @@ public class PackageManagerService extends IPackageManager.Stub mPermissionManager = injector.getPermissionManagerServiceInternal(); mSettings = injector.getSettings(); mUserManager = injector.getUserManagerService(); - mIntentFilterVerificationManager = injector.getIntentFilterVerificationManager(); mDomainVerificationManager = injector.getDomainVerificationManagerInternal(); mHandler = injector.getHandler(); @@ -6484,7 +6214,6 @@ public class PackageManagerService extends IPackageManager.Stub mHandler = injector.getHandler(); mProcessLoggingHandler = new ProcessLoggingHandler(); Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT); - mIntentFilterVerificationManager = injector.getIntentFilterVerificationManager(); ArrayMap libConfig = systemConfig.getSharedLibraries(); @@ -6967,7 +6696,6 @@ public class PackageManagerService extends IPackageManager.Stub if (!mOnlyCore && (mPromoteSystemApps || mFirstBoot)) { for (UserInfo user : mInjector.getUserManagerInternal().getUsers(true)) { mSettings.applyDefaultPreferredAppsLPw(user.id); - primeDomainVerificationsLPw(user.id); } } @@ -7086,11 +6814,6 @@ public class PackageManagerService extends IPackageManager.Stub mDomainVerificationManager.setProxy(domainVerificationProxy); - if (intentFilterVerifierComponent != null) { - mIntentFilterVerificationManager.setVerifierComponent( - intentFilterVerifierComponent); - } - mServicesExtensionPackageName = getRequiredServicesExtensionPackageLPr(); mSharedSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr( PackageManager.SYSTEM_SHARED_LIBRARY_SHARED, @@ -7800,11 +7523,6 @@ public class PackageManagerService extends IPackageManager.Stub return matches.get(0).getComponentInfo().getComponentName(); } - @GuardedBy("mLock") - private void primeDomainVerificationsLPw(int userId) { - mIntentFilterVerificationManager.primeDomainVerificationsLPw(userId, mPackages); - } - private boolean packageIsBrowser(String packageName, int userId) { List list = queryIntentActivitiesInternal(sBrowserIntent, null, PackageManager.MATCH_ALL, userId); @@ -9650,18 +9368,9 @@ public class PackageManagerService extends IPackageManager.Stub if (ri.activityInfo.applicationInfo.isInstantApp()) { final String packageName = ri.activityInfo.packageName; final PackageSetting ps = mSettings.getPackageLPr(packageName); - if (USE_DOMAIN_VERIFICATION_V2) { - if (ps != null && mDomainVerificationManager - .isApprovedForDomain(ps, intent, userId)) { - return ri; - } - } else { - final long packedStatus = 0; - //IntentVerifyUtils.getDomainVerificationStatus(ps, userId); - final int status = (int) (packedStatus >> 32); - if (status != INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) { - return ri; - } + if (ps != null && mDomainVerificationManager + .isApprovedForDomain(ps, intent, userId)) { + return ri; } } } @@ -10152,8 +9861,6 @@ public class PackageManagerService extends IPackageManager.Stub private static class CrossProfileDomainInfo { /* ResolveInfo for IntentForwarderActivity to send the intent to the other profile */ ResolveInfo resolveInfo; - /* Best domain verification status of the activities found in the other profile */ - int bestDomainVerificationStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER; boolean wereAnyDomainsVerificationApproved; } @@ -16439,28 +16146,31 @@ public class PackageManagerService extends IPackageManager.Stub return DEFAULT_INTEGRITY_VERIFY_ENABLE; } + @Deprecated @Override public void verifyIntentFilter(int id, int verificationCode, List failedDomains) { DomainVerificationProxyV1.queueLegacyVerifyResult(mContext, mDomainVerificationConnection, id, verificationCode, failedDomains, Binder.getCallingUid()); - mIntentFilterVerificationManager.queueVerifyResult(id, verificationCode, failedDomains); } + @Deprecated @Override public int getIntentVerificationStatus(String packageName, int userId) { return mDomainVerificationManager.getLegacyState(packageName, userId); } + @Deprecated @Override public boolean updateIntentVerificationStatus(String packageName, int status, int userId) { mDomainVerificationManager.setLegacyUserState(packageName, userId, status); return true; } + @Deprecated @Override public @NonNull ParceledListSlice getIntentFilterVerifications( String packageName) { - return mIntentFilterVerificationManager.getIntentFilterVerifications(packageName); + return ParceledListSlice.emptyList(); } @Override @@ -20143,14 +19853,6 @@ public class PackageManagerService extends IPackageManager.Stub "Failed to set up verity: " + e); } - if (!instantApp) { - mIntentFilterVerificationManager.startIntentFilterVerifications( - args.user.getIdentifier(), replace, parsedPackage); - } else { - if (DEBUG_DOMAIN_VERIFICATION) { - Slog.d(TAG, "Not verifying instant app install for app links: " + pkgName); - } - } final PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags, "installPackageLI"); boolean shouldCloseFreezerBeforeReturn = true; @@ -21153,8 +20855,6 @@ public class PackageManagerService extends IPackageManager.Stub if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) { final SparseBooleanArray changedUsers = new SparseBooleanArray(); synchronized (mLock) { - mIntentFilterVerificationManager.clearIntentFilterVerificationsLocked( - deletedPs.name, UserHandle.USER_ALL, true); mDomainVerificationManager.clearPackage(deletedPs.name); clearDefaultBrowserIfNeeded(packageName); mSettings.getKeySetManagerService().removeAppKeySetDataLPw(packageName); @@ -22286,10 +21986,7 @@ public class PackageManagerService extends IPackageManager.Stub } synchronized (mLock) { mSettings.applyDefaultPreferredAppsLPw(userId); - mIntentFilterVerificationManager.clearIntentFilterVerificationsLocked(userId, - mPackages); mDomainVerificationManager.clearUser(userId); - primeDomainVerificationsLPw(userId); final int numPackages = mPackages.size(); for (int i = 0; i < numPackages; i++) { final AndroidPackage pkg = mPackages.valueAt(i); @@ -22551,29 +22248,8 @@ public class PackageManagerService extends IPackageManager.Stub throw new SecurityException("Only the system may call getIntentFilterVerificationBackup()"); } - ByteArrayOutputStream dataStream = new ByteArrayOutputStream(); - try { - final TypedXmlSerializer serializer = Xml.newFastSerializer(); - serializer.setOutput(dataStream, StandardCharsets.UTF_8.name()); - serializer.startDocument(null, true); - serializer.startTag(null, TAG_INTENT_FILTER_VERIFICATION); - - synchronized (mLock) { - mIntentFilterVerificationManager.writeAllDomainVerificationsLPr(serializer, userId, - mSettings.mPackages); - } - - serializer.endTag(null, TAG_INTENT_FILTER_VERIFICATION); - serializer.endDocument(); - serializer.flush(); - } catch (Exception e) { - if (DEBUG_BACKUP) { - Slog.e(TAG, "Unable to write default apps for backup", e); - } - return null; - } - - return dataStream.toByteArray(); + // TODO(b/170746586) + return null; } @Override @@ -22581,23 +22257,7 @@ public class PackageManagerService extends IPackageManager.Stub if (Binder.getCallingUid() != Process.SYSTEM_UID) { throw new SecurityException("Only the system may call restorePreferredActivities()"); } - - try { - final TypedXmlPullParser parser = Xml.newFastPullParser(); - parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name()); - restoreFromXml(parser, userId, TAG_INTENT_FILTER_VERIFICATION, - (parser1, userId1) -> { - synchronized (mLock) { - mIntentFilterVerificationManager.readAllDomainVerificationsLPr(parser1, - userId1); - writeSettingsLPrTEMP(); - } - }); - } catch (Exception e) { - if (DEBUG_BACKUP) { - Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage()); - } - } + // TODO(b/170746586) } @Override @@ -24599,35 +24259,6 @@ public class PackageManagerService extends IPackageManager.Stub } } - private String dumpDomainString(String packageName) { - List iviList = getIntentFilterVerifications(packageName) - .getList(); - List filters = getAllIntentFilters(packageName).getList(); - - ArraySet result = new ArraySet<>(); - if (iviList.size() > 0) { - for (IntentFilterVerificationInfo ivi : iviList) { - result.addAll(ivi.getDomains()); - } - } - if (filters != null && filters.size() > 0) { - for (IntentFilter filter : filters) { - if (filter.hasCategory(Intent.CATEGORY_BROWSABLE) - && (filter.hasDataScheme(IntentFilter.SCHEME_HTTP) || - filter.hasDataScheme(IntentFilter.SCHEME_HTTPS))) { - result.addAll(filter.getHostsList()); - } - } - } - - StringBuilder sb = new StringBuilder(result.size() * 16); - for (String domain : result) { - if (sb.length() > 0) sb.append(" "); - sb.append(domain); - } - return sb.toString(); - } - // ------- apps on sdcard specific code ------- static final boolean DEBUG_SD_INSTALL = false; @@ -25823,7 +25454,6 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { scheduleWritePackageRestrictionsLocked(userId); scheduleWritePackageListLocked(userId); - primeDomainVerificationsLPw(userId); mAppsFilter.onUsersChanged(); } } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 43617cf9a0c9a..88144d449342e 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -108,7 +108,6 @@ import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.domain.verify.DomainVerificationLegacySettings; import com.android.server.pm.domain.verify.DomainVerificationManagerInternal; import com.android.server.pm.domain.verify.DomainVerificationPersistence; -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; @@ -511,8 +510,6 @@ public final class Settings implements Watchable, Snappable { private final LegacyPermissionDataProvider mPermissionDataProvider; - private final IntentFilterVerificationManager mIntentFilterVerificationManager; - private final DomainVerificationManagerInternal mDomainVerificationManager; /** @@ -541,7 +538,6 @@ public final class Settings implements Watchable, Snappable { mStoppedPackagesFilename = null; mBackupStoppedPackagesFilename = null; mKernelMappingFilename = null; - mIntentFilterVerificationManager = null; mDomainVerificationManager = null; mPackages.registerObserver(mObserver); mInstallerPackages.registerObserver(mObserver); @@ -563,7 +559,6 @@ public final class Settings implements Watchable, Snappable { Settings(File dataDir, RuntimePermissionsPersistence runtimePermissionsPersistence, LegacyPermissionDataProvider permissionDataProvider, - @NonNull IntentFilterVerificationManager intentFilterVerificationManager, @NonNull DomainVerificationManagerInternal domainVerificationManager, @NonNull Object lock) { mLock = lock; @@ -592,7 +587,6 @@ public final class Settings implements Watchable, Snappable { mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml"); mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml"); - mIntentFilterVerificationManager = intentFilterVerificationManager; mDomainVerificationManager = domainVerificationManager; mPackages.registerObserver(mObserver); @@ -635,7 +629,6 @@ public final class Settings implements Watchable, Snappable { mBackupStoppedPackagesFilename = null; mKernelMappingFilename = null; - mIntentFilterVerificationManager = r.mIntentFilterVerificationManager; mDomainVerificationManager = r.mDomainVerificationManager; mInstallerPackages.addAll(r.mInstallerPackages); @@ -2323,8 +2316,6 @@ public final class Settings implements Watchable, Snappable { } } - mIntentFilterVerificationManager.writeRestoredIntentFilterVerifications(serializer); - mDomainVerificationManager.writeSettings(serializer); mKeySetManagerService.writeKeySetManagerServiceLPr(serializer); @@ -2847,8 +2838,6 @@ public final class Settings implements Watchable, Snappable { if (nname != null && oname != null) { mRenamedPackages.put(nname, oname); } - } else if (tagName.equals("restored-ivi")) { - mIntentFilterVerificationManager.readRestoredIntentFilterVerifications(parser); } else if (tagName.equals("last-platform-version")) { // Upgrade from older XML schema final VersionInfo internal = findOrCreateVersion( diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java index f123aa22a0a75..d8e5f72ac1099 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java +++ b/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java @@ -41,6 +41,7 @@ import com.android.server.pm.parsing.pkg.AndroidPackage; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; +import java.util.List; import java.util.Set; import java.util.UUID; diff --git a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV1.java b/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV1.java index c156c3461daea..0d41f75be7b66 100644 --- a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV1.java +++ b/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV1.java @@ -76,11 +76,8 @@ public class DomainVerificationProxyV1 implements DomainVerificationProxy { @GuardedBy("mLock") private final ArrayMap> mRequests = new ArrayMap<>(); - // TODO(b/159952358): For now, IDs start at a really high number to avoid conflict with the - // legacy manager, which is still active in code. Should be set to 0 once - // IntentFilterVerificationManager is removed. @GuardedBy("mLock") - private int mVerificationToken = Integer.MAX_VALUE / 2; + private int mVerificationToken = 0; public DomainVerificationProxyV1(@NonNull Context context, @NonNull DomainVerificationManagerInternal manager, 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 deleted file mode 100644 index 1a15d8a75fc0e..0000000000000 --- a/services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationKey.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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 deleted file mode 100644 index ee4081028cc04..0000000000000 --- a/services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationManager.java +++ /dev/null @@ -1,584 +0,0 @@ -/* - * 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 deleted file mode 100644 index 699c3ef29d845..0000000000000 --- a/services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationParams.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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 deleted file mode 100644 index c513380b2e79b..0000000000000 --- a/services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationResponse.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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 deleted file mode 100644 index 3ff770d5fb3a5..0000000000000 --- a/services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationSettings.java +++ /dev/null @@ -1,394 +0,0 @@ -/* - * 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 = null;//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 0;//(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 null;//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); - } - 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 = null;//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 = null;//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 - // TODO: Removed, commented out to allow compile, awaiting removal of entire - // class - // 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 deleted file mode 100644 index 7026fbda89cc2..0000000000000 --- a/services/core/java/com/android/server/pm/intent/verify/legacy/IntentFilterVerificationState.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * 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 deleted file mode 100644 index b2213dc917116..0000000000000 --- a/services/core/java/com/android/server/pm/intent/verify/legacy/IntentVerifierProxy.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - * 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 deleted file mode 100644 index b64ac94ba07fd..0000000000000 --- a/services/core/java/com/android/server/pm/intent/verify/legacy/IntentVerifyUtils.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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)); - } -} diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationEnforcerTest.kt index fbd5ae8863369..8b6e085dc8e28 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationEnforcerTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationEnforcerTest.kt @@ -123,7 +123,6 @@ class DomainVerificationEnforcerTest { this[0] = PackageUserState() } } - whenever(intentFilterVerificationInfo) { null } } val makeService: (Context) -> Triple = 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 beeae7df6956e..d8036bba98e36 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -60,7 +60,6 @@ import androidx.test.runner.AndroidJUnit4; import com.android.permission.persistence.RuntimePermissionsPersistence; import com.android.server.LocalServices; import com.android.server.pm.domain.verify.DomainVerificationManagerInternal; -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; @@ -98,8 +97,6 @@ public class PackageManagerSettingsTests { @Mock LegacyPermissionDataProvider mPermissionDataProvider; @Mock - IntentFilterVerificationManager mIntentFilterVerificationManager; - @Mock DomainVerificationManagerInternal mDomainVerificationManager; @Before @@ -1200,7 +1197,7 @@ public class PackageManagerSettingsTests { private Settings makeSettings() { return new Settings(InstrumentationRegistry.getContext().getFilesDir(), mRuntimePermissionsPersistence, mPermissionDataProvider, - mIntentFilterVerificationManager, mDomainVerificationManager, new Object()); + mDomainVerificationManager, new Object()); } private void verifyKeySetMetaData(Settings settings) From ee612e99fee8f073ecb9707b7f628ddf646d2f37 Mon Sep 17 00:00:00 2001 From: Winson Date: Thu, 28 Jan 2021 09:49:44 -0800 Subject: [PATCH 23/23] Address domain verification API comments Changes package to pm.verify.domain, removes throwing InvalidDomainSet, cleans up permissions, and renames DomainVerificationSet -> DomainVerificationInfo. Bug: 163565712 CTS-Coverage-Bug: 179382047 Test: atest com.android.server.pm.test.verify.domain Change-Id: I5d60c227f0e529fe27c1844ada97716933a533b0 --- core/api/system-current.txt | 102 ++++++++--------- .../android/app/SystemServiceRegistry.java | 6 +- core/java/android/content/Intent.java | 2 +- .../android/content/pm/PackageManager.java | 8 +- .../domain/DomainVerificationInfo.aidl} | 4 +- .../domain/DomainVerificationInfo.java} | 105 ++++++++---------- .../domain}/DomainVerificationManager.java | 96 +++++++--------- .../DomainVerificationManagerImpl.java | 15 +-- .../domain}/DomainVerificationRequest.java | 16 +-- .../domain}/DomainVerificationState.java | 2 +- .../DomainVerificationUserSelection.aidl | 2 +- .../DomainVerificationUserSelection.java | 6 +- .../domain}/IDomainVerificationManager.aidl | 8 +- .../verify => verify/domain}/TEST_MAPPING | 2 +- .../server/pm/PackageManagerService.java | 10 +- .../server/pm/PackageManagerShellCommand.java | 2 +- .../android/server/pm/PackageSettingBase.java | 4 +- .../java/com/android/server/pm/Settings.java | 6 +- .../domain}/DomainVerificationCollector.java | 2 +- .../domain}/DomainVerificationDebug.java | 12 +- .../domain}/DomainVerificationEnforcer.java | 4 +- .../DomainVerificationLegacySettings.java | 2 +- .../DomainVerificationManagerInternal.java | 19 ++-- .../DomainVerificationManagerStub.java | 16 +-- .../DomainVerificationMessageCodes.java | 4 +- .../DomainVerificationPersistence.java | 10 +- .../domain}/DomainVerificationService.java | 32 +++--- .../domain}/DomainVerificationSettings.java | 10 +- .../domain}/DomainVerificationShell.java | 8 +- .../domain}/DomainVerificationUtils.java | 2 +- .../server/pm/verify/domain/TEST_MAPPING | 12 ++ .../models/DomainVerificationPkgState.java | 8 +- .../models/DomainVerificationStateMap.java | 2 +- .../models/DomainVerificationUserState.java | 4 +- .../proxy/DomainVerificationProxy.java | 8 +- .../DomainVerificationProxyCombined.java | 2 +- .../DomainVerificationProxyUnavailable.java | 2 +- .../proxy/DomainVerificationProxyV1.java | 20 ++-- .../proxy/DomainVerificationProxyV2.java | 10 +- .../java/com/android/server/SystemServer.java | 2 +- .../DomainVerificationCollectorTest.kt | 4 +- .../domain}/DomainVerificationCoreApiTest.kt | 18 +-- .../domain}/DomainVerificationEnforcerTest.kt | 16 +-- .../DomainVerificationLegacySettingsTest.kt | 8 +- .../DomainVerificationModelExtensions.kt | 16 +-- .../DomainVerificationPersistenceTest.kt | 12 +- .../domain}/DomainVerificationProxyTest.kt | 44 ++++---- .../src/com/android/server/pm/MockSystem.kt | 2 +- .../pm/PackageManagerSettingsTests.java | 2 +- .../src/com/android/server/pm/ScanTests.java | 2 +- 50 files changed, 344 insertions(+), 367 deletions(-) rename core/java/android/content/pm/{domain/verify/DomainVerificationSet.aidl => verify/domain/DomainVerificationInfo.aidl} (88%) rename core/java/android/content/pm/{domain/verify/DomainVerificationSet.java => verify/domain/DomainVerificationInfo.java} (79%) rename core/java/android/content/pm/{domain/verify => verify/domain}/DomainVerificationManager.java (77%) rename core/java/android/content/pm/{domain/verify => verify/domain}/DomainVerificationManagerImpl.java (92%) rename core/java/android/content/pm/{domain/verify => verify/domain}/DomainVerificationRequest.java (92%) rename core/java/android/content/pm/{domain/verify => verify/domain}/DomainVerificationState.java (98%) rename core/java/android/content/pm/{domain/verify => verify/domain}/DomainVerificationUserSelection.aidl (94%) rename core/java/android/content/pm/{domain/verify => verify/domain}/DomainVerificationUserSelection.java (98%) rename core/java/android/content/pm/{domain/verify => verify/domain}/IDomainVerificationManager.aidl (83%) rename core/java/android/content/pm/{domain/verify => verify/domain}/TEST_MAPPING (65%) rename services/core/java/com/android/server/pm/{domain/verify => verify/domain}/DomainVerificationCollector.java (99%) rename services/core/java/com/android/server/pm/{domain/verify => verify/domain}/DomainVerificationDebug.java (96%) rename services/core/java/com/android/server/pm/{domain/verify => verify/domain}/DomainVerificationEnforcer.java (97%) rename services/core/java/com/android/server/pm/{domain/verify => verify/domain}/DomainVerificationLegacySettings.java (99%) rename services/core/java/com/android/server/pm/{domain/verify => verify/domain}/DomainVerificationManagerInternal.java (93%) rename services/core/java/com/android/server/pm/{domain/verify => verify/domain}/DomainVerificationManagerStub.java (88%) rename services/core/java/com/android/server/pm/{domain/verify => verify/domain}/DomainVerificationMessageCodes.java (92%) rename services/core/java/com/android/server/pm/{domain/verify => verify/domain}/DomainVerificationPersistence.java (97%) rename services/core/java/com/android/server/pm/{domain/verify => verify/domain}/DomainVerificationService.java (97%) rename services/core/java/com/android/server/pm/{domain/verify => verify/domain}/DomainVerificationSettings.java (97%) rename services/core/java/com/android/server/pm/{domain/verify => verify/domain}/DomainVerificationShell.java (98%) rename services/core/java/com/android/server/pm/{domain/verify => verify/domain}/DomainVerificationUtils.java (98%) create mode 100644 services/core/java/com/android/server/pm/verify/domain/TEST_MAPPING rename services/core/java/com/android/server/pm/{domain/verify => verify/domain}/models/DomainVerificationPkgState.java (95%) rename services/core/java/com/android/server/pm/{domain/verify => verify/domain}/models/DomainVerificationStateMap.java (98%) rename services/core/java/com/android/server/pm/{domain/verify => verify/domain}/models/DomainVerificationUserState.java (93%) rename services/core/java/com/android/server/pm/{domain/verify => verify/domain}/proxy/DomainVerificationProxy.java (94%) rename services/core/java/com/android/server/pm/{domain/verify => verify/domain}/proxy/DomainVerificationProxyCombined.java (97%) rename services/core/java/com/android/server/pm/{domain/verify => verify/domain}/proxy/DomainVerificationProxyUnavailable.java (93%) rename services/core/java/com/android/server/pm/{domain/verify => verify/domain}/proxy/DomainVerificationProxyV1.java (94%) rename services/core/java/com/android/server/pm/{domain/verify => verify/domain}/proxy/DomainVerificationProxyV2.java (92%) rename services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/{domain/verify => verify/domain}/DomainVerificationCollectorTest.kt (99%) rename services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/{domain/verify => verify/domain}/DomainVerificationCoreApiTest.kt (91%) rename services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/{domain/verify => verify/domain}/DomainVerificationEnforcerTest.kt (97%) rename services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/{domain/verify => verify/domain}/DomainVerificationLegacySettingsTest.kt (94%) rename services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/{domain/verify => verify/domain}/DomainVerificationModelExtensions.kt (73%) rename services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/{domain/verify => verify/domain}/DomainVerificationPersistenceTest.kt (96%) rename services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/{domain/verify => verify/domain}/DomainVerificationProxyTest.kt (93%) diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 9d8accb4f676d..10423a63cb17f 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -2701,62 +2701,6 @@ package android.content.pm.dex { } -package android.content.pm.domain.verify { - - public interface DomainVerificationManager { - method @Nullable @RequiresPermission(allOf={android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, android.Manifest.permission.QUERY_ALL_PACKAGES, android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION}, conditional=true) public android.content.pm.domain.verify.DomainVerificationSet getDomainVerificationSet(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; - method @Nullable @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public android.content.pm.domain.verify.DomainVerificationUserSelection getDomainVerificationUserSelection(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; - method @NonNull @RequiresPermission(allOf={android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, android.Manifest.permission.QUERY_ALL_PACKAGES}) public java.util.List getValidVerificationPackageNames(); - method public static boolean isStateModifiable(int); - method public static boolean isStateVerified(int); - method @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public void setDomainVerificationLinkHandlingAllowed(@NonNull String, boolean) throws android.content.pm.PackageManager.NameNotFoundException; - method @RequiresPermission(allOf={android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, android.Manifest.permission.QUERY_ALL_PACKAGES}) public void setDomainVerificationStatus(@NonNull java.util.UUID, @NonNull java.util.Set, int) throws android.content.pm.domain.verify.DomainVerificationManager.InvalidDomainSetException, android.content.pm.PackageManager.NameNotFoundException; - method @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public void setDomainVerificationUserSelection(@NonNull java.util.UUID, @NonNull java.util.Set, boolean) throws android.content.pm.domain.verify.DomainVerificationManager.InvalidDomainSetException, android.content.pm.PackageManager.NameNotFoundException; - field public static final String EXTRA_VERIFICATION_REQUEST = "android.content.pm.domain.verify.extra.VERIFICATION_REQUEST"; - field public static final int STATE_FIRST_VERIFIER_DEFINED = 1024; // 0x400 - field public static final int STATE_NO_RESPONSE = 0; // 0x0 - field public static final int STATE_SUCCESS = 1; // 0x1 - } - - public static class DomainVerificationManager.InvalidDomainSetException extends android.util.AndroidException { - method @Nullable public java.util.UUID getDomainSetId(); - method @Nullable public String getPackageName(); - method public int getReason(); - field public static final int REASON_ID_INVALID = 2; // 0x2 - field public static final int REASON_ID_NULL = 1; // 0x1 - field public static final int REASON_SET_NULL_OR_EMPTY = 3; // 0x3 - field public static final int REASON_UNKNOWN_DOMAIN = 4; // 0x4 - } - - public final class DomainVerificationRequest implements android.os.Parcelable { - method public int describeContents(); - method @NonNull public java.util.Set getPackageNames(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator CREATOR; - } - - public final class DomainVerificationSet implements android.os.Parcelable { - method public int describeContents(); - method @NonNull public java.util.Map getHostToStateMap(); - method @NonNull public java.util.UUID getIdentifier(); - method @NonNull public String getPackageName(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator CREATOR; - } - - public final class DomainVerificationUserSelection implements android.os.Parcelable { - method public int describeContents(); - method @NonNull public java.util.Map getHostToUserSelectionMap(); - method @NonNull public java.util.UUID getIdentifier(); - method @NonNull public String getPackageName(); - method @NonNull public android.os.UserHandle getUser(); - method @NonNull public boolean isLinkHandlingAllowed(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator CREATOR; - } - -} - package android.content.pm.permission { @Deprecated public final class RuntimePermissionPresentationInfo implements android.os.Parcelable { @@ -2771,6 +2715,52 @@ package android.content.pm.permission { } +package android.content.pm.verify.domain { + + public final class DomainVerificationInfo implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.Map getHostToStateMap(); + method @NonNull public java.util.UUID getIdentifier(); + method @NonNull public String getPackageName(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + public interface DomainVerificationManager { + method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION}) public android.content.pm.verify.domain.DomainVerificationInfo getDomainVerificationInfo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; + method @Nullable @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public android.content.pm.verify.domain.DomainVerificationUserSelection getDomainVerificationUserSelection(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; + method @NonNull @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public java.util.List getValidVerificationPackageNames(); + method public static boolean isStateModifiable(int); + method public static boolean isStateVerified(int); + method @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public void setDomainVerificationLinkHandlingAllowed(@NonNull String, boolean) throws android.content.pm.PackageManager.NameNotFoundException; + method @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public void setDomainVerificationStatus(@NonNull java.util.UUID, @NonNull java.util.Set, int) throws android.content.pm.PackageManager.NameNotFoundException; + method @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public void setDomainVerificationUserSelection(@NonNull java.util.UUID, @NonNull java.util.Set, boolean) throws android.content.pm.PackageManager.NameNotFoundException; + field public static final String EXTRA_VERIFICATION_REQUEST = "android.content.pm.verify.domain.extra.VERIFICATION_REQUEST"; + field public static final int STATE_FIRST_VERIFIER_DEFINED = 1024; // 0x400 + field public static final int STATE_NO_RESPONSE = 0; // 0x0 + field public static final int STATE_SUCCESS = 1; // 0x1 + } + + public final class DomainVerificationRequest implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.Set getPackageNames(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + public final class DomainVerificationUserSelection implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.Map getHostToUserSelectionMap(); + method @NonNull public java.util.UUID getIdentifier(); + method @NonNull public String getPackageName(); + method @NonNull public android.os.UserHandle getUser(); + method @NonNull public boolean isLinkHandlingAllowed(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + +} + package android.content.rollback { public final class PackageRollbackInfo implements android.os.Parcelable { diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index a7f8331bd0a09..7404e53bd8b31 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -69,9 +69,9 @@ import android.content.pm.IShortcutService; import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.content.pm.ShortcutManager; -import android.content.pm.domain.verify.DomainVerificationManager; -import android.content.pm.domain.verify.DomainVerificationManagerImpl; -import android.content.pm.domain.verify.IDomainVerificationManager; +import android.content.pm.verify.domain.DomainVerificationManager; +import android.content.pm.verify.domain.DomainVerificationManagerImpl; +import android.content.pm.verify.domain.IDomainVerificationManager; import android.content.res.Resources; import android.content.rollback.RollbackManagerFrameworkInitializer; import android.debug.AdbManager; diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index b6d5ac697939e..30b24044a624d 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -37,7 +37,7 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ShortcutInfo; import android.content.pm.SuspendDialogInfo; -import android.content.pm.domain.verify.DomainVerificationManager; +import android.content.pm.verify.domain.DomainVerificationManager; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Rect; diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index abe7b48059fef..d09d83f0cd1dd 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3759,7 +3759,7 @@ public abstract class PackageManager { * Passed to an intent filter verifier and is used to call back to * {@link #verifyIntentFilter} * - * @deprecated Use {@link DomainVerificationManager} APIs. + * @deprecated Use DomainVerificationManager APIs. * @hide */ @Deprecated @@ -3772,7 +3772,7 @@ public abstract class PackageManager { * * Usually this is "https" * - * @deprecated Use {@link DomainVerificationManager} APIs. + * @deprecated Use DomainVerificationManager APIs. * @hide */ @Deprecated @@ -3786,7 +3786,7 @@ public abstract class PackageManager { * * This is a space delimited list of hosts. * - * @deprecated Use {@link DomainVerificationManager} APIs. + * @deprecated Use DomainVerificationManager APIs. * @hide */ @Deprecated @@ -3799,7 +3799,7 @@ public abstract class PackageManager { * from the hosts. Each host response will need to include the package name of APK containing * the intent filter. * - * @deprecated Use {@link DomainVerificationManager} APIs. + * @deprecated Use DomainVerificationManager APIs. * @hide */ @Deprecated diff --git a/core/java/android/content/pm/domain/verify/DomainVerificationSet.aidl b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.aidl similarity index 88% rename from core/java/android/content/pm/domain/verify/DomainVerificationSet.aidl rename to core/java/android/content/pm/verify/domain/DomainVerificationInfo.aidl index 0208907224e6c..c143cc517486b 100644 --- a/core/java/android/content/pm/domain/verify/DomainVerificationSet.aidl +++ b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.aidl @@ -14,6 +14,6 @@ * limitations under the License. */ -package android.content.pm.domain.verify; +package android.content.pm.verify.domain; -parcelable DomainVerificationSet; +parcelable DomainVerificationInfo; diff --git a/core/java/android/content/pm/domain/verify/DomainVerificationSet.java b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java similarity index 79% rename from core/java/android/content/pm/domain/verify/DomainVerificationSet.java rename to core/java/android/content/pm/verify/domain/DomainVerificationInfo.java index bc076505bae23..7afbe1fcb69fa 100644 --- a/core/java/android/content/pm/domain/verify/DomainVerificationSet.java +++ b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.content.pm.domain.verify; +package android.content.pm.verify.domain; import android.annotation.NonNull; import android.annotation.SystemApi; @@ -42,25 +42,22 @@ import java.util.UUID; @SuppressWarnings("DefaultAnnotationParam") @DataClass(genAidl = true, genHiddenConstructor = true, genParcelable = true, genToString = true, genEqualsHashCode = true) -public final class DomainVerificationSet implements Parcelable { +public final class DomainVerificationInfo implements Parcelable { /** - * A domain verification ID for use in later API calls. This represents the snapshot - * of the domains for a package on device, and will be invalidated whenever the - * package changes. + * A domain verification ID for use in later API calls. This represents the snapshot of the + * domains for a package on device, and will be invalidated whenever the package changes. *

- * An exception will be thrown at the next API call that receives the ID if it is no - * longer valid. + * An exception will be thrown at the next API call that receives the ID if it is no longer + * valid. *

- * The caller may also be notified with a broadcast whenever a package and ID is - * invalidated, at which point it can use the package name to evict existing - * requests with an invalid set ID. If the caller wants to manually check if any - * IDs have been invalidate, the {@link PackageManager#getChangedPackages(int)} - * API will allow tracking the packages changed since the last query of this - * method, prompting the caller to re-query. + * The caller may also be notified with a broadcast whenever a package and ID is invalidated, at + * which point it can use the package name to evict existing requests with an invalid set ID. If + * the caller wants to manually check if any IDs have been invalidate, the {@link + * PackageManager#getChangedPackages(int)} API will allow tracking the packages changed since + * the last query of this method, prompting the caller to re-query. *

- * This allows the caller to arbitrarily grant or revoke domain verification - * status, through + * This allows the caller to arbitrarily grant or revoke domain verification status, through * {@link DomainVerificationManager#setDomainVerificationStatus(UUID, Set, int)}. */ @NonNull @@ -98,7 +95,7 @@ public final class DomainVerificationSet implements Parcelable { // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/domain/verify/DomainVerificationSet.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -106,25 +103,22 @@ public final class DomainVerificationSet implements Parcelable { /** - * Creates a new DomainVerificationSet. + * Creates a new DomainVerificationInfo. * * @param identifier - * A domain verification ID for use in later API calls. This represents the snapshot - * of the domains for a package on device, and will be invalidated whenever the - * package changes. + * A domain verification ID for use in later API calls. This represents the snapshot of the + * domains for a package on device, and will be invalidated whenever the package changes. *

- * An exception will be thrown at the next API call that receives the ID if it is no - * longer valid. + * An exception will be thrown at the next API call that receives the ID if it is no longer + * valid. *

- * The caller may also be notified with a broadcast whenever a package and ID is - * invalidated, at which point it can use the package name to evict existing - * requests with an invalid set ID. If the caller wants to manually check if any - * IDs have been invalidate, the {@link PackageManager#getChangedPackages(int)} - * API will allow tracking the packages changed since the last query of this - * method, prompting the caller to re-query. + * The caller may also be notified with a broadcast whenever a package and ID is invalidated, at + * which point it can use the package name to evict existing requests with an invalid set ID. If + * the caller wants to manually check if any IDs have been invalidate, the {@link + * PackageManager#getChangedPackages(int)} API will allow tracking the packages changed since + * the last query of this method, prompting the caller to re-query. *

- * This allows the caller to arbitrarily grant or revoke domain verification - * status, through + * This allows the caller to arbitrarily grant or revoke domain verification status, through * {@link DomainVerificationManager#setDomainVerificationStatus(UUID, Set, int)}. * @param packageName * The package name that this data corresponds to. @@ -144,7 +138,7 @@ public final class DomainVerificationSet implements Parcelable { * @hide */ @DataClass.Generated.Member - public DomainVerificationSet( + public DomainVerificationInfo( @NonNull UUID identifier, @NonNull String packageName, @NonNull Map hostToStateMap) { @@ -162,22 +156,19 @@ public final class DomainVerificationSet implements Parcelable { } /** - * A domain verification ID for use in later API calls. This represents the snapshot - * of the domains for a package on device, and will be invalidated whenever the - * package changes. + * A domain verification ID for use in later API calls. This represents the snapshot of the + * domains for a package on device, and will be invalidated whenever the package changes. *

- * An exception will be thrown at the next API call that receives the ID if it is no - * longer valid. + * An exception will be thrown at the next API call that receives the ID if it is no longer + * valid. *

- * The caller may also be notified with a broadcast whenever a package and ID is - * invalidated, at which point it can use the package name to evict existing - * requests with an invalid set ID. If the caller wants to manually check if any - * IDs have been invalidate, the {@link PackageManager#getChangedPackages(int)} - * API will allow tracking the packages changed since the last query of this - * method, prompting the caller to re-query. + * The caller may also be notified with a broadcast whenever a package and ID is invalidated, at + * which point it can use the package name to evict existing requests with an invalid set ID. If + * the caller wants to manually check if any IDs have been invalidate, the {@link + * PackageManager#getChangedPackages(int)} API will allow tracking the packages changed since + * the last query of this method, prompting the caller to re-query. *

- * This allows the caller to arbitrarily grant or revoke domain verification - * status, through + * This allows the caller to arbitrarily grant or revoke domain verification status, through * {@link DomainVerificationManager#setDomainVerificationStatus(UUID, Set, int)}. */ @DataClass.Generated.Member @@ -218,7 +209,7 @@ public final class DomainVerificationSet implements Parcelable { // You can override field toString logic by defining methods like: // String fieldNameToString() { ... } - return "DomainVerificationSet { " + + return "DomainVerificationInfo { " + "identifier = " + mIdentifier + ", " + "packageName = " + mPackageName + ", " + "hostToStateMap = " + mHostToStateMap + @@ -229,13 +220,13 @@ public final class DomainVerificationSet implements Parcelable { @DataClass.Generated.Member public boolean equals(@android.annotation.Nullable Object o) { // You can override field equality logic by defining either of the methods like: - // boolean fieldNameEquals(DomainVerificationSet other) { ... } + // boolean fieldNameEquals(DomainVerificationInfo other) { ... } // boolean fieldNameEquals(FieldType otherValue) { ... } if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; @SuppressWarnings("unchecked") - DomainVerificationSet that = (DomainVerificationSet) o; + DomainVerificationInfo that = (DomainVerificationInfo) o; //noinspection PointlessBooleanExpression return true && java.util.Objects.equals(mIdentifier, that.mIdentifier) @@ -285,7 +276,7 @@ public final class DomainVerificationSet implements Parcelable { /** @hide */ @SuppressWarnings({"unchecked", "RedundantCast"}) @DataClass.Generated.Member - /* package-private */ DomainVerificationSet(@NonNull android.os.Parcel in) { + /* package-private */ DomainVerificationInfo(@NonNull android.os.Parcel in) { // You can override field unparcelling by defining methods like: // static FieldType unparcelFieldName(Parcel in) { ... } @@ -308,24 +299,24 @@ public final class DomainVerificationSet implements Parcelable { } @DataClass.Generated.Member - public static final @NonNull Parcelable.Creator CREATOR - = new Parcelable.Creator() { + public static final @NonNull Parcelable.Creator CREATOR + = new Parcelable.Creator() { @Override - public DomainVerificationSet[] newArray(int size) { - return new DomainVerificationSet[size]; + public DomainVerificationInfo[] newArray(int size) { + return new DomainVerificationInfo[size]; } @Override - public DomainVerificationSet createFromParcel(@NonNull android.os.Parcel in) { - return new DomainVerificationSet(in); + public DomainVerificationInfo createFromParcel(@NonNull android.os.Parcel in) { + return new DomainVerificationInfo(in); } }; @DataClass.Generated( - time = 1611795504275L, + time = 1611862790369L, codegenVersion = "1.0.22", - sourceFile = "frameworks/base/core/java/android/content/pm/domain/verify/DomainVerificationSet.java", - inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.util.Map mHostToStateMap\nclass DomainVerificationSet extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true)") + sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java", + inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.util.Map mHostToStateMap\nclass DomainVerificationInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/content/pm/domain/verify/DomainVerificationManager.java b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java similarity index 77% rename from core/java/android/content/pm/domain/verify/DomainVerificationManager.java rename to core/java/android/content/pm/verify/domain/DomainVerificationManager.java index ddae8f9d1295c..af12536fff99c 100644 --- a/core/java/android/content/pm/domain/verify/DomainVerificationManager.java +++ b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.content.pm.domain.verify; +package android.content.pm.verify.domain; import android.annotation.IntDef; import android.annotation.NonNull; @@ -26,7 +26,6 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager.NameNotFoundException; import android.os.UserHandle; -import android.util.AndroidException; import java.util.List; import java.util.Set; @@ -40,7 +39,7 @@ import java.util.UUID; * {@link android.Manifest.permission#DOMAIN_VERIFICATION_AGENT}) to update the approval status * of domains declared by applications in their AndroidManifest.xml, to allow them to open those * links inside the app when selected by the user. This is done through querying - * {@link #getDomainVerificationSet(String)} and calling + * {@link #getDomainVerificationInfo(String)} and calling * {@link #setDomainVerificationStatus(UUID, Set, int)}. * * Also allows the domain preference settings (holder of @@ -62,7 +61,7 @@ public interface DomainVerificationManager { * {@link Intent#ACTION_DOMAINS_NEED_VERIFICATION}. */ String EXTRA_VERIFICATION_REQUEST = - "android.content.pm.domain.verify.extra.VERIFICATION_REQUEST"; + "android.content.pm.verify.domain.extra.VERIFICATION_REQUEST"; /** * No response has been recorded by either the system or any verification agent. @@ -169,67 +168,53 @@ public interface DomainVerificationManager { } /** - * Used to iterate all {@link DomainVerificationSet} values to do cleanup or retries. This is + * Used to iterate all {@link DomainVerificationInfo} values to do cleanup or retries. This is * usually a heavy workload and should be done infrequently. * * @return the current snapshot of package names with valid autoVerify URLs. */ @NonNull - @RequiresPermission(allOf = { - android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, - android.Manifest.permission.QUERY_ALL_PACKAGES - }) + @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) List getValidVerificationPackageNames(); /** - * Retrieves the domain verification state for a given package. The caller must be the domain - * verification agent for the device with - * {@link android.Manifest.permission#DOMAIN_VERIFICATION_AGENT}, or hold - * {@link android.Manifest.permission#UPDATE_DOMAIN_VERIFICATION_USER_SELECTION}. - * Also requires that the caller have the - * {@link android.Manifest.permission#QUERY_ALL_PACKAGES} permission in addition to either of - * the requirements above. + * Retrieves the domain verification state for a given package. * * @return the data for the package, or null if it does not declare any autoVerify domains * @throws NameNotFoundException If the package is unavailable. This is an unrecoverable error * and should not be re-tried except on a time scheduled basis. */ @Nullable - @RequiresPermission(allOf = { + @RequiresPermission(anyOf = { android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, - android.Manifest.permission.QUERY_ALL_PACKAGES, android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION - }, conditional = true) - DomainVerificationSet getDomainVerificationSet(@NonNull String packageName) + }) + DomainVerificationInfo getDomainVerificationInfo(@NonNull String packageName) throws NameNotFoundException; /** * Change the verification status of the {@param domains} of the package associated with * {@param domainSetId}. * - * @param domainSetId See {@link DomainVerificationSet#getIdentifier()}. + * @param domainSetId See {@link DomainVerificationInfo#getIdentifier()}. * @param domains List of host names to change the state of. - * @param state See {@link DomainVerificationSet#getHostToStateMap()}. - * @throws InvalidDomainSetException If the ID is invalidated or the {@param domains} are - * invalid. This usually means the work being processed by the - * verification agent is outdated and a new request should - * be scheduled, if one has not already been done as part of - * the {@link Intent#ACTION_DOMAINS_NEED_VERIFICATION} - * broadcast. - * @throws NameNotFoundException If the ID is known to be good, but the package is - * unavailable. This may be because the package is - * installed on a volume that is no longer mounted. This - * error is unrecoverable until the package is available - * again, and should not be re-tried except on a time - * scheduled basis. + * @param state See {@link DomainVerificationInfo#getHostToStateMap()}. + * @throws IllegalArgumentException If the ID is invalidated or the {@param domains} are + * invalid. This usually means the work being processed by the + * verification agent is outdated and a new request should + * be scheduled, if one has not already been done as part of + * the {@link Intent#ACTION_DOMAINS_NEED_VERIFICATION} + * broadcast. + * @throws NameNotFoundException If the ID is known to be good, but the package is + * unavailable. This may be because the package is + * installed on a volume that is no longer mounted. This + * error is unrecoverable until the package is available + * again, and should not be re-tried except on a time + * scheduled basis. */ - @RequiresPermission(allOf = { - android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, - android.Manifest.permission.QUERY_ALL_PACKAGES - }) + @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set domains, - @DomainVerificationState.State int state) - throws InvalidDomainSetException, NameNotFoundException; + @DomainVerificationState.State int state) throws NameNotFoundException; /** * TODO(b/178525735): This documentation is incorrect in the context of UX changes. @@ -256,22 +241,21 @@ public interface DomainVerificationManager { * This will be combined with the verification status and other system state to determine which * application is launched to handle an app link. * - * @param domainSetId See {@link DomainVerificationSet#getIdentifier()}. + * @param domainSetId See {@link DomainVerificationInfo#getIdentifier()}. * @param domains The domains to toggle the state of. * @param enabled Whether or not the app should automatically open the domains specified. - * @throws InvalidDomainSetException If the ID is invalidated or the {@param domains} are - * invalid. - * @throws NameNotFoundException If the ID is known to be good, but the package is - * unavailable. This may be because the package is - * installed on a volume that is no longer mounted. This - * error is unrecoverable until the package is available - * again, and should not be re-tried except on a time - * scheduled basis. + * @throws IllegalArgumentException If the ID is invalidated or the {@param domains} are + * invalid. + * @throws NameNotFoundException If the ID is known to be good, but the package is + * unavailable. This may be because the package is + * installed on a volume that is no longer mounted. This + * error is unrecoverable until the package is available + * again, and should not be re-tried except on a time + * scheduled basis. */ @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) void setDomainVerificationUserSelection(@NonNull UUID domainSetId, - @NonNull Set domains, boolean enabled) - throws InvalidDomainSetException, NameNotFoundException; + @NonNull Set domains, boolean enabled) throws NameNotFoundException; /** * Retrieve the user selection data for the given {@param packageName} and the current user. @@ -292,13 +276,15 @@ public interface DomainVerificationManager { throws NameNotFoundException; /** - * Thrown if a {@link DomainVerificationSet#getIdentifier()}} or an associated set of domains + * Thrown if a {@link DomainVerificationInfo#getIdentifier()}} or an associated set of domains * provided by the caller is no longer valid. This may be recoverable, and the caller should * re-query the package name associated with the ID using - * {@link #getDomainVerificationSet(String)} in order to check. If that also fails, then the + * {@link #getDomainVerificationInfo(String)} in order to check. If that also fails, then the * package is no longer known to the device and thus all pending work for it should be dropped. + * + * @hide */ - class InvalidDomainSetException extends AndroidException { + class InvalidDomainSetException extends IllegalArgumentException { public static final int REASON_ID_NULL = 1; public static final int REASON_ID_INVALID = 2; @@ -315,7 +301,7 @@ public interface DomainVerificationManager { public @interface Reason { } - private static String buildMessage(@Nullable UUID domainSetId, @Nullable String packageName, + public static String buildMessage(@Nullable UUID domainSetId, @Nullable String packageName, @Reason int reason) { switch (reason) { case REASON_ID_NULL: diff --git a/core/java/android/content/pm/domain/verify/DomainVerificationManagerImpl.java b/core/java/android/content/pm/verify/domain/DomainVerificationManagerImpl.java similarity index 92% rename from core/java/android/content/pm/domain/verify/DomainVerificationManagerImpl.java rename to core/java/android/content/pm/verify/domain/DomainVerificationManagerImpl.java index 8ce0e9a79da99..5938def5c83c9 100644 --- a/core/java/android/content/pm/domain/verify/DomainVerificationManagerImpl.java +++ b/core/java/android/content/pm/verify/domain/DomainVerificationManagerImpl.java @@ -14,13 +14,14 @@ * limitations under the License. */ -package android.content.pm.domain.verify; +package android.content.pm.verify.domain; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.verify.domain.IDomainVerificationManager; import android.os.RemoteException; import android.os.ServiceSpecificException; @@ -67,10 +68,10 @@ public class DomainVerificationManagerImpl implements DomainVerificationManager @Nullable @Override - public DomainVerificationSet getDomainVerificationSet(@NonNull String packageName) + public DomainVerificationInfo getDomainVerificationInfo(@NonNull String packageName) throws NameNotFoundException { try { - return mDomainVerificationManager.getDomainVerificationSet(packageName); + return mDomainVerificationManager.getDomainVerificationInfo(packageName); } catch (Exception e) { Exception converted = rethrow(e, packageName); if (converted instanceof NameNotFoundException) { @@ -85,7 +86,7 @@ public class DomainVerificationManagerImpl implements DomainVerificationManager @Override public void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set domains, - int state) throws InvalidDomainSetException, NameNotFoundException { + int state) throws IllegalArgumentException, NameNotFoundException { try { mDomainVerificationManager.setDomainVerificationStatus(domainSetId.toString(), new ArrayList<>(domains), state); @@ -122,7 +123,7 @@ public class DomainVerificationManagerImpl implements DomainVerificationManager @Override public void setDomainVerificationUserSelection(@NonNull UUID domainSetId, @NonNull Set domains, boolean enabled) - throws InvalidDomainSetException, NameNotFoundException { + throws IllegalArgumentException, NameNotFoundException { try { mDomainVerificationManager.setDomainVerificationUserSelection(domainSetId.toString(), new ArrayList<>(domains), enabled, mContext.getUserId()); @@ -177,8 +178,8 @@ public class DomainVerificationManagerImpl implements DomainVerificationManager switch (managerErrorCode) { case ERROR_INVALID_DOMAIN_SET: int errorSpecificCode = packedErrorCode >> 16; - return new InvalidDomainSetException(domainSetId, packageName, - errorSpecificCode); + return new IllegalArgumentException(InvalidDomainSetException.buildMessage( + domainSetId, packageName, errorSpecificCode)); case ERROR_NAME_NOT_FOUND: return new NameNotFoundException(packageName); default: diff --git a/core/java/android/content/pm/domain/verify/DomainVerificationRequest.java b/core/java/android/content/pm/verify/domain/DomainVerificationRequest.java similarity index 92% rename from core/java/android/content/pm/domain/verify/DomainVerificationRequest.java rename to core/java/android/content/pm/verify/domain/DomainVerificationRequest.java index 46930ab528205..473abce26d81e 100644 --- a/core/java/android/content/pm/domain/verify/DomainVerificationRequest.java +++ b/core/java/android/content/pm/verify/domain/DomainVerificationRequest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.content.pm.domain.verify; +package android.content.pm.verify.domain; import android.annotation.NonNull; import android.annotation.SystemApi; @@ -32,7 +32,7 @@ import java.util.Set; *

* This contains the set of packages which have been invalidated and will require * re-verification. The exact domains can be retrieved with - * {@link DomainVerificationManager#getDomainVerificationSet(String)} + * {@link DomainVerificationManager#getDomainVerificationInfo(String)} * * @hide */ @@ -43,7 +43,7 @@ public final class DomainVerificationRequest implements Parcelable { /** * The package names of the apps that need to be verified. The receiver should call - * {@link DomainVerificationManager#getDomainVerificationSet(String)} with each of + * {@link DomainVerificationManager#getDomainVerificationInfo(String)} with each of * these values to get the actual set of domains that need to be acted on. */ @NonNull @@ -58,7 +58,7 @@ public final class DomainVerificationRequest implements Parcelable { // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/domain/verify/DomainVerificationRequest.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationRequest.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -70,7 +70,7 @@ public final class DomainVerificationRequest implements Parcelable { * * @param packageNames * The package names of the apps that need to be verified. The receiver should call - * {@link DomainVerificationManager#getDomainVerificationSet(String)} with each of + * {@link DomainVerificationManager#getDomainVerificationInfo(String)} with each of * these values to get the actual set of domains that need to be acted on. * @hide */ @@ -86,7 +86,7 @@ public final class DomainVerificationRequest implements Parcelable { /** * The package names of the apps that need to be verified. The receiver should call - * {@link DomainVerificationManager#getDomainVerificationSet(String)} with each of + * {@link DomainVerificationManager#getDomainVerificationInfo(String)} with each of * these values to get the actual set of domains that need to be acted on. */ @DataClass.Generated.Member @@ -176,9 +176,9 @@ public final class DomainVerificationRequest implements Parcelable { }; @DataClass.Generated( - time = 1611795646938L, + time = 1611862814990L, codegenVersion = "1.0.22", - sourceFile = "frameworks/base/core/java/android/content/pm/domain/verify/DomainVerificationRequest.java", + sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationRequest.java", inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForStringSet.class) java.util.Set mPackageNames\nclass DomainVerificationRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genAidl=false, genEqualsHashCode=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/content/pm/domain/verify/DomainVerificationState.java b/core/java/android/content/pm/verify/domain/DomainVerificationState.java similarity index 98% rename from core/java/android/content/pm/domain/verify/DomainVerificationState.java rename to core/java/android/content/pm/verify/domain/DomainVerificationState.java index 6e257b2fa9988..17593ef2aeb1c 100644 --- a/core/java/android/content/pm/domain/verify/DomainVerificationState.java +++ b/core/java/android/content/pm/verify/domain/DomainVerificationState.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.content.pm.domain.verify; +package android.content.pm.verify.domain; import android.annotation.IntDef; diff --git a/core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.aidl b/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.aidl similarity index 94% rename from core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.aidl rename to core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.aidl index edcdb76813e7c..ddb5ef85382a1 100644 --- a/core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.aidl +++ b/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.aidl @@ -14,6 +14,6 @@ * limitations under the License. */ -package android.content.pm.domain.verify; +package android.content.pm.verify.domain; parcelable DomainVerificationUserSelection; diff --git a/core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.java b/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java similarity index 98% rename from core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.java rename to core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java index 2e26ef3c97dad..8d16f75bf1b43 100644 --- a/core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.java +++ b/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.content.pm.domain.verify; +package android.content.pm.verify.domain; import android.annotation.NonNull; import android.annotation.SystemApi; @@ -62,7 +62,7 @@ import java.util.UUID; public final class DomainVerificationUserSelection implements Parcelable { /** - * @see DomainVerificationSet#getIdentifier + * @see DomainVerificationInfo#getIdentifier */ @NonNull @DataClass.ParcelWith(Parcelling.BuiltIn.ForUUID.class) @@ -155,7 +155,7 @@ public final class DomainVerificationUserSelection implements Parcelable { } /** - * @see DomainVerificationSet#getIdentifier + * @see DomainVerificationInfo#getIdentifier */ @DataClass.Generated.Member public @NonNull UUID getIdentifier() { diff --git a/core/java/android/content/pm/domain/verify/IDomainVerificationManager.aidl b/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl similarity index 83% rename from core/java/android/content/pm/domain/verify/IDomainVerificationManager.aidl rename to core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl index 3726480bec0d4..21dd623b46bcb 100644 --- a/core/java/android/content/pm/domain/verify/IDomainVerificationManager.aidl +++ b/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl @@ -14,10 +14,10 @@ * limitations under the License. */ -package android.content.pm.domain.verify; +package android.content.pm.verify.domain; -import android.content.pm.domain.verify.DomainVerificationSet; -import android.content.pm.domain.verify.DomainVerificationUserSelection; +import android.content.pm.verify.domain.DomainVerificationInfo; +import android.content.pm.verify.domain.DomainVerificationUserSelection; import java.util.List; /** @@ -29,7 +29,7 @@ interface IDomainVerificationManager { List getValidVerificationPackageNames(); @nullable - DomainVerificationSet getDomainVerificationSet(String packageName); + DomainVerificationInfo getDomainVerificationInfo(String packageName); @nullable DomainVerificationUserSelection getDomainVerificationUserSelection(String packageName, diff --git a/core/java/android/content/pm/domain/verify/TEST_MAPPING b/core/java/android/content/pm/verify/domain/TEST_MAPPING similarity index 65% rename from core/java/android/content/pm/domain/verify/TEST_MAPPING rename to core/java/android/content/pm/verify/domain/TEST_MAPPING index ffb1d9a600846..c6c979107e75c 100644 --- a/core/java/android/content/pm/domain/verify/TEST_MAPPING +++ b/core/java/android/content/pm/verify/domain/TEST_MAPPING @@ -4,7 +4,7 @@ "name": "PackageManagerServiceUnitTests", "options": [ { - "include-filter": "com.android.server.pm.test.domain.verify" + "include-filter": "com.android.server.pm.test.verify.domain" } ] } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index ed8b7bd638561..a702f5ea35828 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -377,11 +377,11 @@ 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.domain.verify.DomainVerificationManagerInternal; -import com.android.server.pm.domain.verify.DomainVerificationService; -import com.android.server.pm.domain.verify.proxy.DomainVerificationProxy; -import com.android.server.pm.domain.verify.proxy.DomainVerificationProxyV1; -import com.android.server.pm.domain.verify.proxy.DomainVerificationProxyV2; +import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; +import com.android.server.pm.verify.domain.DomainVerificationService; +import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy; +import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1; +import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV2; import com.android.server.pm.parsing.PackageCacher; import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.PackageParser2; diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index b6b6fb60d1815..b5765b50e7468 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -104,7 +104,7 @@ import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemConfig; import com.android.server.pm.PackageManagerShellCommandDataLoader.Metadata; -import com.android.server.pm.domain.verify.DomainVerificationShell; +import com.android.server.pm.verify.domain.DomainVerificationShell; import com.android.server.pm.permission.LegacyPermissionManagerInternal; import dalvik.system.DexFile; diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index d3005184e0878..8aa553d68b984 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -42,8 +42,8 @@ import android.util.SparseArray; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; -import com.android.server.pm.domain.verify.DomainVerificationManagerInternal; -import com.android.server.pm.domain.verify.DomainVerificationService; +import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; +import com.android.server.pm.verify.domain.DomainVerificationService; import com.android.server.pm.parsing.pkg.AndroidPackage; import java.io.File; diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 88144d449342e..fb033e6594b8d 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -105,9 +105,9 @@ 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.domain.verify.DomainVerificationLegacySettings; -import com.android.server.pm.domain.verify.DomainVerificationManagerInternal; -import com.android.server.pm.domain.verify.DomainVerificationPersistence; +import com.android.server.pm.verify.domain.DomainVerificationLegacySettings; +import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; +import com.android.server.pm.verify.domain.DomainVerificationPersistence; import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationCollector.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java similarity index 99% rename from services/core/java/com/android/server/pm/domain/verify/DomainVerificationCollector.java rename to services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java index 832714f2bfe61..36efb39909a68 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationCollector.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.domain.verify; +package com.android.server.pm.verify.domain; import android.annotation.NonNull; import android.compat.annotation.ChangeId; diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationDebug.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java similarity index 96% rename from services/core/java/com/android/server/pm/domain/verify/DomainVerificationDebug.java rename to services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java index 9bf65b43ae576..af9978b91e48f 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationDebug.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java @@ -14,15 +14,15 @@ * limitations under the License. */ -package com.android.server.pm.domain.verify; +package com.android.server.pm.verify.domain; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.Signature; -import android.content.pm.domain.verify.DomainVerificationManager; -import android.content.pm.domain.verify.DomainVerificationState; +import android.content.pm.verify.domain.DomainVerificationManager; +import android.content.pm.verify.domain.DomainVerificationState; import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; @@ -32,9 +32,9 @@ import android.util.SparseArray; import com.android.internal.util.CollectionUtils; import com.android.server.pm.PackageSetting; -import com.android.server.pm.domain.verify.models.DomainVerificationPkgState; -import com.android.server.pm.domain.verify.models.DomainVerificationStateMap; -import com.android.server.pm.domain.verify.models.DomainVerificationUserState; +import com.android.server.pm.verify.domain.models.DomainVerificationPkgState; +import com.android.server.pm.verify.domain.models.DomainVerificationStateMap; +import com.android.server.pm.verify.domain.models.DomainVerificationUserState; import com.android.server.pm.parsing.pkg.AndroidPackage; import java.util.Arrays; diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationEnforcer.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java similarity index 97% rename from services/core/java/com/android/server/pm/domain/verify/DomainVerificationEnforcer.java rename to services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java index cdcc5fcba7b5e..c521f828ade94 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationEnforcer.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.domain.verify; +package com.android.server.pm.verify.domain; import android.Manifest; import android.annotation.NonNull; @@ -23,7 +23,7 @@ import android.content.Context; import android.os.Binder; import android.os.Process; -import com.android.server.pm.domain.verify.proxy.DomainVerificationProxy; +import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy; public class DomainVerificationEnforcer { diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationLegacySettings.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationLegacySettings.java similarity index 99% rename from services/core/java/com/android/server/pm/domain/verify/DomainVerificationLegacySettings.java rename to services/core/java/com/android/server/pm/verify/domain/DomainVerificationLegacySettings.java index 09307d2a2db1e..c787356f342c9 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationLegacySettings.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationLegacySettings.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.domain.verify; +package com.android.server.pm.verify.domain; import android.annotation.NonNull; import android.annotation.Nullable; diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java similarity index 93% rename from services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java rename to services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java index d8e5f72ac1099..7ad275a6f351c 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerInternal.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.domain.verify; +package com.android.server.pm.verify.domain; import android.annotation.NonNull; import android.annotation.Nullable; @@ -23,25 +23,22 @@ import android.annotation.UserIdInt; import android.content.Intent; import android.content.pm.IntentFilterVerificationInfo; import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.domain.verify.DomainVerificationManager; -import android.content.pm.domain.verify.DomainVerificationSet; +import android.content.pm.verify.domain.DomainVerificationInfo; +import android.content.pm.verify.domain.DomainVerificationManager; import android.os.Binder; import android.os.UserHandle; import android.util.IndentingPrintWriter; import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; -import com.android.internal.annotations.GuardedBy; -import com.android.server.pm.PackageManagerService; import com.android.server.pm.PackageSetting; -import com.android.server.pm.domain.verify.models.DomainVerificationPkgState; -import com.android.server.pm.domain.verify.proxy.DomainVerificationProxy; +import com.android.server.pm.verify.domain.models.DomainVerificationPkgState; +import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy; import com.android.server.pm.parsing.pkg.AndroidPackage; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; -import java.util.List; import java.util.Set; import java.util.UUID; @@ -154,7 +151,7 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan * tag has already been entered. *

* This is only for restore, and will override package states, ignoring if their {@link - * DomainVerificationSet#getIdentifier()}s match. It's expected that any restored domains marked + * DomainVerificationInfo#getIdentifier()}s match. It's expected that any restored domains marked * as success verify against the server correctly, although the verification agent may decide to * re-verify them when it gets the chance. */ @@ -220,12 +217,12 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan * unavailable */ @Nullable - UUID getDomainVerificationSetId(@NonNull String packageName); + UUID getDomainVerificationInfoId(@NonNull String packageName); @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) void setDomainVerificationStatusInternal(int callingUid, @NonNull UUID domainSetId, @NonNull Set domains, int state) - throws InvalidDomainSetException, NameNotFoundException; + throws IllegalArgumentException, NameNotFoundException; interface Connection { diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerStub.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java similarity index 88% rename from services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerStub.java rename to services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java index 6147cdee23106..8aa63372b826d 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationManagerStub.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java @@ -14,17 +14,17 @@ * limitations under the License. */ -package com.android.server.pm.domain.verify; +package com.android.server.pm.verify.domain; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.domain.verify.DomainVerificationManager.InvalidDomainSetException; -import android.content.pm.domain.verify.DomainVerificationManagerImpl; -import android.content.pm.domain.verify.DomainVerificationSet; -import android.content.pm.domain.verify.DomainVerificationUserSelection; -import android.content.pm.domain.verify.IDomainVerificationManager; +import android.content.pm.verify.domain.DomainVerificationManager.InvalidDomainSetException; +import android.content.pm.verify.domain.DomainVerificationManagerImpl; +import android.content.pm.verify.domain.DomainVerificationInfo; +import android.content.pm.verify.domain.DomainVerificationUserSelection; +import android.content.pm.verify.domain.IDomainVerificationManager; import android.os.ServiceSpecificException; import android.util.ArraySet; @@ -52,9 +52,9 @@ class DomainVerificationManagerStub extends IDomainVerificationManager.Stub { @Nullable @Override - public DomainVerificationSet getDomainVerificationSet(String packageName) { + public DomainVerificationInfo getDomainVerificationInfo(String packageName) { try { - return mService.getDomainVerificationSet(packageName); + return mService.getDomainVerificationInfo(packageName); } catch (Exception e) { throw rethrow(e); } diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationMessageCodes.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationMessageCodes.java similarity index 92% rename from services/core/java/com/android/server/pm/domain/verify/DomainVerificationMessageCodes.java rename to services/core/java/com/android/server/pm/verify/domain/DomainVerificationMessageCodes.java index 7fb0067738ad9..7af78c6a98ca9 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationMessageCodes.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationMessageCodes.java @@ -14,12 +14,12 @@ * limitations under the License. */ -package com.android.server.pm.domain.verify; +package com.android.server.pm.verify.domain; import android.os.Handler; import com.android.server.pm.PackageManagerService; -import com.android.server.pm.domain.verify.proxy.DomainVerificationProxy; +import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy; /** * Codes that are sent through the {@link PackageManagerService} {@link Handler} and eventually diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationPersistence.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationPersistence.java similarity index 97% rename from services/core/java/com/android/server/pm/domain/verify/DomainVerificationPersistence.java rename to services/core/java/com/android/server/pm/verify/domain/DomainVerificationPersistence.java index 04adb003a7dad..679f948bb3de4 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationPersistence.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationPersistence.java @@ -14,11 +14,11 @@ * limitations under the License. */ -package com.android.server.pm.domain.verify; +package com.android.server.pm.verify.domain; import android.annotation.NonNull; import android.annotation.Nullable; -import android.content.pm.domain.verify.DomainVerificationState; +import android.content.pm.verify.domain.DomainVerificationState; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -27,9 +27,9 @@ import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; import com.android.server.pm.SettingsXml; -import com.android.server.pm.domain.verify.models.DomainVerificationPkgState; -import com.android.server.pm.domain.verify.models.DomainVerificationStateMap; -import com.android.server.pm.domain.verify.models.DomainVerificationUserState; +import com.android.server.pm.verify.domain.models.DomainVerificationPkgState; +import com.android.server.pm.verify.domain.models.DomainVerificationStateMap; +import com.android.server.pm.verify.domain.models.DomainVerificationUserState; import org.xmlpull.v1.XmlPullParserException; diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java similarity index 97% rename from services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java rename to services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java index d316773031b1b..4fd01d903261a 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationService.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.domain.verify; +package com.android.server.pm.verify.domain; import android.annotation.NonNull; import android.annotation.Nullable; @@ -26,18 +26,15 @@ import android.content.Intent; import android.content.pm.IntentFilterVerificationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.PackageUserState; -import android.content.pm.domain.verify.DomainVerificationManager; -import android.content.pm.domain.verify.DomainVerificationSet; -import android.content.pm.domain.verify.DomainVerificationState; -import android.content.pm.domain.verify.DomainVerificationUserSelection; -import android.content.pm.domain.verify.IDomainVerificationManager; -import android.os.Binder; +import android.content.pm.verify.domain.DomainVerificationInfo; +import android.content.pm.verify.domain.DomainVerificationManager; +import android.content.pm.verify.domain.DomainVerificationState; +import android.content.pm.verify.domain.DomainVerificationUserSelection; +import android.content.pm.verify.domain.IDomainVerificationManager; import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; import android.util.IndentingPrintWriter; -import android.util.Singleton; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; @@ -49,13 +46,12 @@ import com.android.internal.util.CollectionUtils; import com.android.server.SystemConfig; import com.android.server.SystemService; import com.android.server.compat.PlatformCompat; -import com.android.server.pm.PackageManagerService; import com.android.server.pm.PackageSetting; -import com.android.server.pm.domain.verify.models.DomainVerificationPkgState; -import com.android.server.pm.domain.verify.models.DomainVerificationStateMap; -import com.android.server.pm.domain.verify.models.DomainVerificationUserState; -import com.android.server.pm.domain.verify.proxy.DomainVerificationProxy; -import com.android.server.pm.domain.verify.proxy.DomainVerificationProxyUnavailable; +import com.android.server.pm.verify.domain.models.DomainVerificationPkgState; +import com.android.server.pm.verify.domain.models.DomainVerificationStateMap; +import com.android.server.pm.verify.domain.models.DomainVerificationUserState; +import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy; +import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyUnavailable; import com.android.server.pm.parsing.pkg.AndroidPackage; import org.xmlpull.v1.XmlPullParserException; @@ -205,7 +201,7 @@ public class DomainVerificationService extends SystemService @Nullable @Override - public UUID getDomainVerificationSetId(@NonNull String packageName) { + public UUID getDomainVerificationInfoId(@NonNull String packageName) { synchronized (mLock) { DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName); if (pkgState != null) { @@ -218,7 +214,7 @@ public class DomainVerificationService extends SystemService @Nullable @Override - public DomainVerificationSet getDomainVerificationSet(@NonNull String packageName) + public DomainVerificationInfo getDomainVerificationInfo(@NonNull String packageName) throws NameNotFoundException { mEnforcer.assertApprovedQuerent(mConnection.getCallingUid(), mProxy); synchronized (mLock) { @@ -247,7 +243,7 @@ public class DomainVerificationService extends SystemService } // TODO(b/159952358): Do not return if no values are editable (all ignored states)? - return new DomainVerificationSet(pkgState.getId(), packageName, hostToStateMap); + return new DomainVerificationInfo(pkgState.getId(), packageName, hostToStateMap); } } diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationSettings.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java similarity index 97% rename from services/core/java/com/android/server/pm/domain/verify/DomainVerificationSettings.java rename to services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java index 185fd62411570..073967e00134a 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationSettings.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java @@ -14,12 +14,12 @@ * limitations under the License. */ -package com.android.server.pm.domain.verify; +package com.android.server.pm.verify.domain; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; -import android.content.pm.domain.verify.DomainVerificationState; +import android.content.pm.verify.domain.DomainVerificationState; import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; @@ -30,9 +30,9 @@ import android.util.TypedXmlSerializer; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.server.pm.domain.verify.models.DomainVerificationPkgState; -import com.android.server.pm.domain.verify.models.DomainVerificationStateMap; -import com.android.server.pm.domain.verify.models.DomainVerificationUserState; +import com.android.server.pm.verify.domain.models.DomainVerificationPkgState; +import com.android.server.pm.verify.domain.models.DomainVerificationStateMap; +import com.android.server.pm.verify.domain.models.DomainVerificationUserState; import org.xmlpull.v1.XmlPullParserException; diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationShell.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java similarity index 98% rename from services/core/java/com/android/server/pm/domain/verify/DomainVerificationShell.java rename to services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java index c3efc67662549..7f9e75aa2926a 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationShell.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.domain.verify; +package com.android.server.pm.verify.domain; import android.annotation.NonNull; import android.annotation.Nullable; @@ -22,9 +22,9 @@ import android.annotation.UserIdInt; import android.app.ActivityManager; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.domain.verify.DomainVerificationManager; -import android.content.pm.domain.verify.DomainVerificationState; -import android.content.pm.domain.verify.DomainVerificationUserSelection; +import android.content.pm.verify.domain.DomainVerificationManager; +import android.content.pm.verify.domain.DomainVerificationState; +import android.content.pm.verify.domain.DomainVerificationUserSelection; import android.os.Binder; import android.os.UserHandle; import android.text.TextUtils; diff --git a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationUtils.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java similarity index 98% rename from services/core/java/com/android/server/pm/domain/verify/DomainVerificationUtils.java rename to services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java index f704478b92a59..474f822d6a730 100644 --- a/services/core/java/com/android/server/pm/domain/verify/DomainVerificationUtils.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.domain.verify; +package com.android.server.pm.verify.domain; import android.annotation.CheckResult; import android.annotation.NonNull; diff --git a/services/core/java/com/android/server/pm/verify/domain/TEST_MAPPING b/services/core/java/com/android/server/pm/verify/domain/TEST_MAPPING new file mode 100644 index 0000000000000..c6c979107e75c --- /dev/null +++ b/services/core/java/com/android/server/pm/verify/domain/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "presubmit": [ + { + "name": "PackageManagerServiceUnitTests", + "options": [ + { + "include-filter": "com.android.server.pm.test.verify.domain" + } + ] + } + ] +} diff --git a/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationPkgState.java b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java similarity index 95% rename from services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationPkgState.java rename to services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java index 1dc55494cae5a..48099aa5382bb 100644 --- a/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationPkgState.java +++ b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package com.android.server.pm.domain.verify.models; +package com.android.server.pm.verify.domain.models; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; -import android.content.pm.domain.verify.DomainVerificationManager; -import android.content.pm.domain.verify.DomainVerificationState; +import android.content.pm.verify.domain.DomainVerificationManager; +import android.content.pm.verify.domain.DomainVerificationState; import android.util.ArrayMap; import android.util.SparseArray; @@ -240,7 +240,7 @@ public class DomainVerificationPkgState { time = 1608234185474L, codegenVersion = "1.0.22", sourceFile = "frameworks/base/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationPkgState.java", - inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate @android.annotation.NonNull java.util.UUID mId\nprivate final boolean mHasAutoVerifyDomains\nprivate final @android.annotation.NonNull android.util.ArrayMap mStateMap\nprivate final @android.annotation.NonNull android.util.SparseArray mUserSelectionStates\npublic @android.annotation.Nullable com.android.server.pm.domain.verify.models.DomainVerificationUserState getUserSelectionState(int)\npublic @android.annotation.Nullable com.android.server.pm.domain.verify.models.DomainVerificationUserState getOrCreateUserSelectionState(int)\npublic void setId(java.util.UUID)\npublic void removeUser(int)\npublic void removeAllUsers()\nprivate int userSelectionStatesHashCode()\nprivate boolean userSelectionStatesEquals(android.util.SparseArray)\nclass DomainVerificationPkgState extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true)") + inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate @android.annotation.NonNull java.util.UUID mId\nprivate final boolean mHasAutoVerifyDomains\nprivate final @android.annotation.NonNull android.util.ArrayMap mStateMap\nprivate final @android.annotation.NonNull android.util.SparseArray mUserSelectionStates\npublic @android.annotation.Nullable com.android.server.pm.verify.domain.models.DomainVerificationUserState getUserSelectionState(int)\npublic @android.annotation.Nullable com.android.server.pm.verify.domain.models.DomainVerificationUserState getOrCreateUserSelectionState(int)\npublic void setId(java.util.UUID)\npublic void removeUser(int)\npublic void removeAllUsers()\nprivate int userSelectionStatesHashCode()\nprivate boolean userSelectionStatesEquals(android.util.SparseArray)\nclass DomainVerificationPkgState extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationStateMap.java b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationStateMap.java similarity index 98% rename from services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationStateMap.java rename to services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationStateMap.java index ece1ce8281ac5..88ccd83899c65 100644 --- a/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationStateMap.java +++ b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationStateMap.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.domain.verify.models; +package com.android.server.pm.verify.domain.models; import android.annotation.IntRange; import android.annotation.NonNull; diff --git a/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationUserState.java b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java similarity index 93% rename from services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationUserState.java rename to services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java index 43595408d17b0..8e8260899a488 100644 --- a/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationUserState.java +++ b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.domain.verify.models; +package com.android.server.pm.verify.domain.models; import android.annotation.NonNull; import android.annotation.UserIdInt; @@ -174,7 +174,7 @@ public class DomainVerificationUserState { time = 1608234273324L, codegenVersion = "1.0.22", sourceFile = "frameworks/base/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationUserState.java", - inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull android.util.ArraySet mEnabledHosts\nprivate boolean mDisallowLinkHandling\npublic com.android.server.pm.domain.verify.models.DomainVerificationUserState addHosts(android.util.ArraySet)\npublic com.android.server.pm.domain.verify.models.DomainVerificationUserState addHosts(java.util.Set)\npublic com.android.server.pm.domain.verify.models.DomainVerificationUserState removeHosts(android.util.ArraySet)\npublic com.android.server.pm.domain.verify.models.DomainVerificationUserState removeHosts(java.util.Set)\nclass DomainVerificationUserState extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genSetters=true, genEqualsHashCode=true, genToString=true)") + inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull android.util.ArraySet mEnabledHosts\nprivate boolean mDisallowLinkHandling\npublic com.android.server.pm.verify.domain.models.DomainVerificationUserState addHosts(android.util.ArraySet)\npublic com.android.server.pm.verify.domain.models.DomainVerificationUserState addHosts(java.util.Set)\npublic com.android.server.pm.verify.domain.models.DomainVerificationUserState removeHosts(android.util.ArraySet)\npublic com.android.server.pm.verify.domain.models.DomainVerificationUserState removeHosts(java.util.Set)\nclass DomainVerificationUserState extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genSetters=true, genEqualsHashCode=true, genToString=true)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxy.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java similarity index 94% rename from services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxy.java rename to services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java index c641caaa8c1bb..715d8fb0fc2db 100644 --- a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxy.java +++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.domain.verify.proxy; +package com.android.server.pm.verify.domain.proxy; import android.annotation.NonNull; import android.annotation.Nullable; @@ -23,9 +23,9 @@ import android.content.Context; import android.util.Slog; import com.android.server.DeviceIdleInternal; -import com.android.server.pm.domain.verify.DomainVerificationMessageCodes; -import com.android.server.pm.domain.verify.DomainVerificationCollector; -import com.android.server.pm.domain.verify.DomainVerificationManagerInternal; +import com.android.server.pm.verify.domain.DomainVerificationMessageCodes; +import com.android.server.pm.verify.domain.DomainVerificationCollector; +import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; import java.util.Objects; import java.util.Set; diff --git a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyCombined.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyCombined.java similarity index 97% rename from services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyCombined.java rename to services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyCombined.java index eb63d52c0d1f5..8571c08699bd1 100644 --- a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyCombined.java +++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyCombined.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.domain.verify.proxy; +package com.android.server.pm.verify.domain.proxy; import android.annotation.NonNull; diff --git a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyUnavailable.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyUnavailable.java similarity index 93% rename from services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyUnavailable.java rename to services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyUnavailable.java index f376f48874fa6..bd77983256c5d 100644 --- a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyUnavailable.java +++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyUnavailable.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.domain.verify.proxy; +package com.android.server.pm.verify.domain.proxy; /** Stub implementation for when the verification agent is unavailable */ public class DomainVerificationProxyUnavailable implements DomainVerificationProxy { diff --git a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV1.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java similarity index 94% rename from services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV1.java rename to services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java index 0d41f75be7b66..eab89e9878852 100644 --- a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV1.java +++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.domain.verify.proxy; +package com.android.server.pm.verify.domain.proxy; import android.Manifest; import android.annotation.NonNull; @@ -25,9 +25,9 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; -import android.content.pm.domain.verify.DomainVerificationManager; -import android.content.pm.domain.verify.DomainVerificationSet; -import android.content.pm.domain.verify.DomainVerificationState; +import android.content.pm.verify.domain.DomainVerificationInfo; +import android.content.pm.verify.domain.DomainVerificationManager; +import android.content.pm.verify.domain.DomainVerificationState; import android.os.Process; import android.os.UserHandle; import android.text.TextUtils; @@ -37,9 +37,9 @@ import android.util.Pair; import android.util.Slog; import com.android.internal.annotations.GuardedBy; -import com.android.server.pm.domain.verify.DomainVerificationCollector; -import com.android.server.pm.domain.verify.DomainVerificationManagerInternal; -import com.android.server.pm.domain.verify.DomainVerificationMessageCodes; +import com.android.server.pm.verify.domain.DomainVerificationCollector; +import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; +import com.android.server.pm.verify.domain.DomainVerificationMessageCodes; import com.android.server.pm.parsing.pkg.AndroidPackage; import java.util.Collections; @@ -129,7 +129,7 @@ public class DomainVerificationProxyV1 implements DomainVerificationProxy { packageNames.size()); synchronized (mLock) { for (String packageName : packageNames) { - UUID domainSetId = mManager.getDomainVerificationSetId(packageName); + UUID domainSetId = mManager.getDomainVerificationInfoId(packageName); if (domainSetId == null) { continue; } @@ -152,9 +152,9 @@ public class DomainVerificationProxyV1 implements DomainVerificationProxy { UUID domainSetId = pair.first; String packageName = pair.second; - DomainVerificationSet set; + DomainVerificationInfo set; try { - set = mManager.getDomainVerificationSet(packageName); + set = mManager.getDomainVerificationInfo(packageName); } catch (PackageManager.NameNotFoundException ignored) { return true; } diff --git a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV2.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java similarity index 92% rename from services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV2.java rename to services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java index 1595374dd1d3e..9fcbce2ad0556 100644 --- a/services/core/java/com/android/server/pm/domain/verify/proxy/DomainVerificationProxyV2.java +++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.domain.verify.proxy; +package com.android.server.pm.verify.domain.proxy; import android.annotation.NonNull; import android.annotation.Nullable; @@ -22,13 +22,13 @@ import android.app.BroadcastOptions; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.pm.domain.verify.DomainVerificationManager; -import android.content.pm.domain.verify.DomainVerificationRequest; +import android.content.pm.verify.domain.DomainVerificationManager; +import android.content.pm.verify.domain.DomainVerificationRequest; import android.os.Process; import android.os.UserHandle; import android.util.Slog; -import com.android.server.pm.domain.verify.DomainVerificationMessageCodes; +import com.android.server.pm.verify.domain.DomainVerificationMessageCodes; import java.util.Set; @@ -56,7 +56,7 @@ public class DomainVerificationProxyV2 implements DomainVerificationProxy { @Override public void sendBroadcastForPackages(@NonNull Set packageNames) { - mConnection.schedule(com.android.server.pm.domain.verify.DomainVerificationMessageCodes.SEND_REQUEST, packageNames); + mConnection.schedule(com.android.server.pm.verify.domain.DomainVerificationMessageCodes.SEND_REQUEST, packageNames); } @Override diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 7b117d76b3282..50cb00f1887f8 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -160,7 +160,7 @@ import com.android.server.pm.PackageManagerService; import com.android.server.pm.ShortcutService; import com.android.server.pm.UserManagerService; import com.android.server.pm.dex.SystemServerDexLoadReporter; -import com.android.server.pm.domain.verify.DomainVerificationService; +import com.android.server.pm.verify.domain.DomainVerificationService; import com.android.server.policy.PermissionPolicyService; import com.android.server.policy.PhoneWindowManager; import com.android.server.policy.role.RoleServicePlatformHelperImpl; diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationCollectorTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt similarity index 99% rename from services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationCollectorTest.kt rename to services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt index 414a5e4e0b51c..e99b07144853c 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationCollectorTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.test.domain.verify +package com.android.server.pm.test.verify.domain import android.content.Intent import android.content.pm.ApplicationInfo @@ -25,7 +25,7 @@ import android.os.PatternMatcher import android.util.ArraySet import com.android.server.SystemConfig import com.android.server.compat.PlatformCompat -import com.android.server.pm.domain.verify.DomainVerificationCollector +import com.android.server.pm.verify.domain.DomainVerificationCollector import com.android.server.pm.parsing.pkg.AndroidPackage import com.android.server.testutils.mockThrowOnUnmocked import com.android.server.testutils.whenever diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationCoreApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt similarity index 91% rename from services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationCoreApiTest.kt rename to services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt index 2ff7baf1f5708..deb3147644045 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationCoreApiTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt @@ -14,11 +14,11 @@ * limitations under the License. */ -package com.android.server.pm.test.domain.verify +package com.android.server.pm.test.verify.domain -import android.content.pm.domain.verify.DomainVerificationRequest -import android.content.pm.domain.verify.DomainVerificationSet -import android.content.pm.domain.verify.DomainVerificationUserSelection +import android.content.pm.verify.domain.DomainVerificationRequest +import android.content.pm.verify.domain.DomainVerificationInfo +import android.content.pm.verify.domain.DomainVerificationUserSelection import android.os.Parcel import android.os.Parcelable import android.os.UserHandle @@ -62,7 +62,7 @@ class DomainVerificationCoreApiTest { ), Parameter( initial = { - DomainVerificationSet( + DomainVerificationInfo( UUID.fromString("703f6d34-6241-4cfd-8176-2e1d23355811"), "com.test.pkg", mapOf( @@ -72,15 +72,15 @@ class DomainVerificationCoreApiTest { ) ) }, - unparcel = { DomainVerificationSet.CREATOR.createFromParcel(it) }, + unparcel = { DomainVerificationInfo.CREATOR.createFromParcel(it) }, assertion = { first, second -> - assertAll(first, second, + assertAll(first, second, { it.identifier }, { it.component1() }, IS_EQUAL_TO ) - assertAll(first, second, + assertAll(first, second, { it.packageName }, { it.component2() }, IS_EQUAL_TO ) - assertAll>(first, second, + assertAll>(first, second, { it.hostToStateMap }, { it.component3() }, IS_MAP_EQUAL_TO ) } diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt similarity index 97% rename from services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationEnforcerTest.kt rename to services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt index 8b6e085dc8e28..d863194d6889d 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationEnforcerTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt @@ -14,12 +14,12 @@ * limitations under the License. */ -package com.android.server.pm.test.domain.verify +package com.android.server.pm.test.verify.domain import android.content.Context import android.content.Intent import android.content.pm.PackageUserState -import android.content.pm.domain.verify.DomainVerificationManager +import android.content.pm.verify.domain.DomainVerificationManager import android.content.pm.parsing.component.ParsedActivity import android.content.pm.parsing.component.ParsedIntentInfo import android.os.Build @@ -29,10 +29,10 @@ import android.util.Singleton import android.util.SparseArray import androidx.test.platform.app.InstrumentationRegistry import com.android.server.pm.PackageSetting -import com.android.server.pm.domain.verify.DomainVerificationEnforcer -import com.android.server.pm.domain.verify.DomainVerificationManagerInternal -import com.android.server.pm.domain.verify.DomainVerificationService -import com.android.server.pm.domain.verify.proxy.DomainVerificationProxy +import com.android.server.pm.verify.domain.DomainVerificationEnforcer +import com.android.server.pm.verify.domain.DomainVerificationManagerInternal +import com.android.server.pm.verify.domain.DomainVerificationService +import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy import com.android.server.pm.parsing.pkg.AndroidPackage import com.android.server.testutils.mockThrowOnUnmocked import com.android.server.testutils.spyThrowOnUnmocked @@ -230,8 +230,8 @@ class DomainVerificationEnforcerTest { service(Type.VERIFIER, "getPackageNames") { _, _, _ -> validVerificationPackageNames }, - service(Type.QUERENT, "getSet") { _, _, _ -> - getDomainVerificationSet(TEST_PKG) + service(Type.QUERENT, "getInfo") { _, _, _ -> + getDomainVerificationInfo(TEST_PKG) }, service(Type.VERIFIER, "setStatus") { _, _, _ -> setDomainVerificationStatus( diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationLegacySettingsTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationLegacySettingsTest.kt similarity index 94% rename from services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationLegacySettingsTest.kt rename to services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationLegacySettingsTest.kt index 47601a499d628..9a3bd994eac0c 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationLegacySettingsTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationLegacySettingsTest.kt @@ -14,14 +14,14 @@ * limitations under the License. */ -package com.android.server.pm.test.domain.verify +package com.android.server.pm.test.verify.domain import android.content.pm.IntentFilterVerificationInfo import android.content.pm.PackageManager import android.util.ArraySet -import com.android.server.pm.domain.verify.DomainVerificationLegacySettings -import com.android.server.pm.test.domain.verify.DomainVerificationPersistenceTest.Companion.readXml -import com.android.server.pm.test.domain.verify.DomainVerificationPersistenceTest.Companion.writeXml +import com.android.server.pm.verify.domain.DomainVerificationLegacySettings +import com.android.server.pm.test.verify.domain.DomainVerificationPersistenceTest.Companion.readXml +import com.android.server.pm.test.verify.domain.DomainVerificationPersistenceTest.Companion.writeXml import com.google.common.truth.Truth.assertWithMessage import org.junit.Rule import org.junit.Test diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationModelExtensions.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt similarity index 73% rename from services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationModelExtensions.kt rename to services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt index 41344c9e1e25f..a76d8cee582cf 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationModelExtensions.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt @@ -14,21 +14,21 @@ * limitations under the License. */ -package com.android.server.pm.test.domain.verify +package com.android.server.pm.test.verify.domain -import android.content.pm.domain.verify.DomainVerificationRequest -import android.content.pm.domain.verify.DomainVerificationSet -import android.content.pm.domain.verify.DomainVerificationUserSelection -import com.android.server.pm.domain.verify.DomainVerificationPersistence +import android.content.pm.verify.domain.DomainVerificationRequest +import android.content.pm.verify.domain.DomainVerificationInfo +import android.content.pm.verify.domain.DomainVerificationUserSelection +import com.android.server.pm.verify.domain.DomainVerificationPersistence operator fun android.util.Pair.component1() = first operator fun android.util.Pair<*, S>.component2() = second operator fun DomainVerificationRequest.component1() = packageNames -operator fun DomainVerificationSet.component1() = identifier -operator fun DomainVerificationSet.component2() = packageName -operator fun DomainVerificationSet.component3() = hostToStateMap +operator fun DomainVerificationInfo.component1() = identifier +operator fun DomainVerificationInfo.component2() = packageName +operator fun DomainVerificationInfo.component3() = hostToStateMap operator fun DomainVerificationUserSelection.component1() = identifier operator fun DomainVerificationUserSelection.component2() = packageName diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationPersistenceTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt similarity index 96% rename from services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationPersistenceTest.kt rename to services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt index ada5c1b063fa0..a76152c9df7d8 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationPersistenceTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt @@ -14,17 +14,17 @@ * limitations under the License. */ -package com.android.server.pm.test.domain.verify +package com.android.server.pm.test.verify.domain -import android.content.pm.domain.verify.DomainVerificationManager +import android.content.pm.verify.domain.DomainVerificationManager import android.util.ArrayMap import android.util.TypedXmlPullParser import android.util.TypedXmlSerializer import android.util.Xml -import com.android.server.pm.domain.verify.DomainVerificationPersistence -import com.android.server.pm.domain.verify.models.DomainVerificationPkgState -import com.android.server.pm.domain.verify.models.DomainVerificationStateMap -import com.android.server.pm.domain.verify.models.DomainVerificationUserState +import com.android.server.pm.verify.domain.DomainVerificationPersistence +import com.android.server.pm.verify.domain.models.DomainVerificationPkgState +import com.android.server.pm.verify.domain.models.DomainVerificationStateMap +import com.android.server.pm.verify.domain.models.DomainVerificationUserState import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertWithMessage import org.junit.Rule diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationProxyTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationProxyTest.kt similarity index 93% rename from services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationProxyTest.kt rename to services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationProxyTest.kt index 7519ff013458e..db541f6729548 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationProxyTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationProxyTest.kt @@ -14,26 +14,26 @@ * limitations under the License. */ -package com.android.server.pm.test.domain.verify +package com.android.server.pm.test.verify.domain 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.domain.verify.DomainVerificationManager -import android.content.pm.domain.verify.DomainVerificationRequest -import android.content.pm.domain.verify.DomainVerificationSet -import android.content.pm.domain.verify.DomainVerificationState +import android.content.pm.verify.domain.DomainVerificationManager +import android.content.pm.verify.domain.DomainVerificationRequest +import android.content.pm.verify.domain.DomainVerificationInfo +import android.content.pm.verify.domain.DomainVerificationState import android.os.Bundle import android.os.UserHandle import android.util.ArraySet import com.android.server.DeviceIdleInternal -import com.android.server.pm.domain.verify.DomainVerificationCollector -import com.android.server.pm.domain.verify.DomainVerificationManagerInternal -import com.android.server.pm.domain.verify.proxy.DomainVerificationProxy -import com.android.server.pm.domain.verify.proxy.DomainVerificationProxyV1 -import com.android.server.pm.domain.verify.proxy.DomainVerificationProxyV2 +import com.android.server.pm.verify.domain.DomainVerificationCollector +import com.android.server.pm.verify.domain.DomainVerificationManagerInternal +import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy +import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1 +import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV2 import com.android.server.pm.parsing.pkg.AndroidPackage import com.android.server.testutils.mockThrowOnUnmocked import com.android.server.testutils.whenever @@ -95,23 +95,27 @@ class DomainVerificationProxyTest { ) } manager = mockThrowOnUnmocked { - whenever(getDomainVerificationSetId(any())) { + whenever(getDomainVerificationInfoId(any())) { when (val pkgName = arguments[0] as String) { TEST_PKG_NAME_TARGET_ONE -> TEST_UUID_ONE TEST_PKG_NAME_TARGET_TWO -> TEST_UUID_TWO else -> throw IllegalArgumentException("Unexpected package name $pkgName") } } - whenever(getDomainVerificationSet(anyString())) { + whenever(getDomainVerificationInfo(anyString())) { when (val pkgName = arguments[0] as String) { - TEST_PKG_NAME_TARGET_ONE -> DomainVerificationSet(TEST_UUID_ONE, pkgName, mapOf( - "example1.com" to DomainVerificationManager.STATE_NO_RESPONSE, - "example2.com" to DomainVerificationManager.STATE_NO_RESPONSE - )) - TEST_PKG_NAME_TARGET_TWO -> DomainVerificationSet(TEST_UUID_TWO, pkgName, mapOf( - "example3.com" to DomainVerificationManager.STATE_NO_RESPONSE, - "example4.com" to DomainVerificationManager.STATE_NO_RESPONSE - )) + TEST_PKG_NAME_TARGET_ONE -> DomainVerificationInfo( + TEST_UUID_ONE, pkgName, mapOf( + "example1.com" to DomainVerificationManager.STATE_NO_RESPONSE, + "example2.com" to DomainVerificationManager.STATE_NO_RESPONSE + ) + ) + TEST_PKG_NAME_TARGET_TWO -> DomainVerificationInfo( + TEST_UUID_TWO, pkgName, mapOf( + "example3.com" to DomainVerificationManager.STATE_NO_RESPONSE, + "example4.com" to DomainVerificationManager.STATE_NO_RESPONSE + ) + ) else -> throw IllegalArgumentException("Unexpected package name $pkgName") } } diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt index c78b24a153bf7..6e27b3a8166c3 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt @@ -59,12 +59,12 @@ import com.android.server.SystemServerInitThreadPool import com.android.server.compat.PlatformCompat import com.android.server.extendedtestutils.wheneverStatic import com.android.server.pm.dex.DexManager -import com.android.server.pm.domain.verify.DomainVerificationManagerInternal import com.android.server.pm.parsing.PackageParser2 import com.android.server.pm.parsing.pkg.AndroidPackage import com.android.server.pm.parsing.pkg.PackageImpl import com.android.server.pm.parsing.pkg.ParsedPackage import com.android.server.pm.permission.PermissionManagerServiceInternal +import com.android.server.pm.verify.domain.DomainVerificationManagerInternal import com.android.server.testutils.TestHandler import com.android.server.testutils.mock import com.android.server.testutils.nullable 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 d8036bba98e36..59458e8df1181 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -59,7 +59,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.permission.persistence.RuntimePermissionsPersistence; import com.android.server.LocalServices; -import com.android.server.pm.domain.verify.DomainVerificationManagerInternal; +import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.parsing.pkg.ParsedPackage; import com.android.server.pm.permission.LegacyPermissionDataProvider; diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java index 70abf820b594e..b5add849c2dc7 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java @@ -50,7 +50,7 @@ import android.platform.test.annotations.Presubmit; import android.util.Pair; import com.android.server.compat.PlatformCompat; -import com.android.server.pm.domain.verify.DomainVerificationManagerInternal; +import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.PackageImpl;