diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index bed03835707ff..29ce3971e83a3 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -55,6 +55,8 @@ import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.overlay.OverlayPaths; +import android.content.pm.parsing.ParsingPackageUtils; +import android.content.pm.split.SplitAssetLoader; import android.content.pm.parsing.result.ParseResult; import android.content.pm.parsing.result.ParseTypeImpl; import android.content.res.ApkAssets; @@ -7425,7 +7427,7 @@ public class PackageParser { mCompileSdkVersionCodename = dest.readString(); mUpgradeKeySets = (ArraySet) dest.readArraySet(boot); - mKeySetMapping = readKeySetMapping(dest); + mKeySetMapping = ParsingPackageUtils.readKeySetMapping(dest); cpuAbiOverride = dest.readString(); use32bitAbi = (dest.readInt() == 1); @@ -7551,73 +7553,13 @@ public class PackageParser { dest.writeInt(mCompileSdkVersion); dest.writeString(mCompileSdkVersionCodename); dest.writeArraySet(mUpgradeKeySets); - writeKeySetMapping(dest, mKeySetMapping); + ParsingPackageUtils.writeKeySetMapping(dest, mKeySetMapping); dest.writeString(cpuAbiOverride); dest.writeInt(use32bitAbi ? 1 : 0); dest.writeByteArray(restrictUpdateHash); dest.writeInt(visibleToInstantApps ? 1 : 0); } - /** - * Writes the keyset mapping to the provided package. {@code null} mappings are permitted. - */ - private static void writeKeySetMapping( - Parcel dest, ArrayMap> keySetMapping) { - if (keySetMapping == null) { - dest.writeInt(-1); - return; - } - - final int N = keySetMapping.size(); - dest.writeInt(N); - - for (int i = 0; i < N; i++) { - dest.writeString(keySetMapping.keyAt(i)); - ArraySet keys = keySetMapping.valueAt(i); - if (keys == null) { - dest.writeInt(-1); - continue; - } - - final int M = keys.size(); - dest.writeInt(M); - for (int j = 0; j < M; j++) { - dest.writeSerializable(keys.valueAt(j)); - } - } - } - - /** - * Reads a keyset mapping from the given parcel at the given data position. May return - * {@code null} if the serialized mapping was {@code null}. - */ - private static ArrayMap> readKeySetMapping(Parcel in) { - final int N = in.readInt(); - if (N == -1) { - return null; - } - - ArrayMap> keySetMapping = new ArrayMap<>(); - for (int i = 0; i < N; ++i) { - String key = in.readString(); - final int M = in.readInt(); - if (M == -1) { - keySetMapping.put(key, null); - continue; - } - - ArraySet keys = new ArraySet<>(M); - for (int j = 0; j < M; ++j) { - PublicKey pk = (PublicKey) in.readSerializable(); - keys.add(pk); - } - - keySetMapping.put(key, keys); - } - - return keySetMapping; - } - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public Package createFromParcel(Parcel in) { return new Package(in); diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java index 9fee7bb924900..f2a6a5c9382f1 100644 --- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java +++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java @@ -535,7 +535,7 @@ public class PackageInfoWithoutStateUtils { ai.setMaxAspectRatio(maxAspectRatio != null ? maxAspectRatio : 0f); Float minAspectRatio = a.getMinAspectRatio(); ai.setMinAspectRatio(minAspectRatio != null ? minAspectRatio : 0f); - ai.supportsSizeChanges = a.getSupportsSizeChanges(); + ai.supportsSizeChanges = a.isSupportsSizeChanges(); ai.requestedVrComponent = a.getRequestedVrComponent(); ai.rotationAnimation = a.getRotationAnimation(); ai.colorMode = a.getColorMode(); diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java index 72cc9299376a4..d6e1ac903c42f 100644 --- a/core/java/android/content/pm/parsing/ParsingPackage.java +++ b/core/java/android/content/pm/parsing/ParsingPackage.java @@ -360,7 +360,7 @@ public interface ParsingPackage extends ParsingPackageRead { ParsingPackage setCompileSdkVersion(int compileSdkVersion); - ParsingPackage setCompileSdkVersionCodename(String compileSdkVersionCodename); + ParsingPackage setCompileSdkVersionCodeName(String compileSdkVersionCodeName); ParsingPackage setAttributionsAreUserVisible(boolean attributionsAreUserVisible); diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java index 0db65467c6865..f0d95d97263a9 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java +++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java @@ -553,7 +553,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { setCompileSdkVersion(manifestArray.getInteger( R.styleable.AndroidManifest_compileSdkVersion, 0)); - setCompileSdkVersionCodename(manifestArray.getNonConfigurationString( + setCompileSdkVersionCodeName(manifestArray.getNonConfigurationString( R.styleable.AndroidManifest_compileSdkVersionCodename, 0)); setIsolatedSplitLoading(manifestArray.getBoolean( @@ -2686,8 +2686,8 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { } @Override - public ParsingPackage setCompileSdkVersionCodename(String compileSdkVersionCodename) { - this.compileSdkVersionCodeName = compileSdkVersionCodename; + public ParsingPackage setCompileSdkVersionCodeName(String compileSdkVersionCodeName) { + this.compileSdkVersionCodeName = compileSdkVersionCodeName; return this; } diff --git a/core/java/android/content/pm/parsing/component/ParsedActivity.java b/core/java/android/content/pm/parsing/component/ParsedActivity.java index 73ee13212e90d..adb6b76473eb0 100644 --- a/core/java/android/content/pm/parsing/component/ParsedActivity.java +++ b/core/java/android/content/pm/parsing/component/ParsedActivity.java @@ -23,6 +23,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityTaskManager; import android.content.ComponentName; @@ -423,6 +424,7 @@ public class ParsedActivity extends ParsedMainComponent { } } + @NonNull public static final Parcelable.Creator CREATOR = new Creator() { @Override public ParsedActivity createFromParcel(Parcel source) { @@ -513,10 +515,6 @@ public class ParsedActivity extends ParsedMainComponent { return minAspectRatio; } - public boolean getSupportsSizeChanges() { - return supportsSizeChanges; - } - @Nullable public String getRequestedVrComponent() { return requestedVrComponent; diff --git a/core/java/android/content/pm/parsing/component/ParsedComponent.java b/core/java/android/content/pm/parsing/component/ParsedComponent.java index 3c0f097285ca3..838adfd3215e0 100644 --- a/core/java/android/content/pm/parsing/component/ParsedComponent.java +++ b/core/java/android/content/pm/parsing/component/ParsedComponent.java @@ -43,8 +43,8 @@ import java.util.Map; /** @hide */ public abstract class ParsedComponent implements Parcelable { - private static ParsedIntentInfo.ListParceler sForIntentInfos = Parcelling.Cache.getOrCreate( - ParsedIntentInfo.ListParceler.class); + private static final ParsedIntentInfo.ListParceler sForIntentInfos = + Parcelling.Cache.getOrCreate(ParsedIntentInfo.ListParceler.class); @NonNull @DataClass.ParcelWith(ForInternedString.class) diff --git a/core/java/android/content/pm/parsing/component/ParsedInstrumentation.java b/core/java/android/content/pm/parsing/component/ParsedInstrumentation.java index 65ff4720063b6..41789204a76d4 100644 --- a/core/java/android/content/pm/parsing/component/ParsedInstrumentation.java +++ b/core/java/android/content/pm/parsing/component/ParsedInstrumentation.java @@ -18,6 +18,7 @@ package android.content.pm.parsing.component; import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.os.Parcel; @@ -94,6 +95,7 @@ public class ParsedInstrumentation extends ParsedComponent { this.functionalTest = in.readByte() != 0; } + @NonNull public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override diff --git a/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java b/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java index 01ee0f4828f97..59d4a95edb0aa 100644 --- a/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java +++ b/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java @@ -16,6 +16,7 @@ package android.content.pm.parsing.component; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.IntentFilter; import android.os.Parcel; @@ -58,6 +59,7 @@ public final class ParsedIntentInfo extends IntentFilter { item.writeIntentInfoToParcel(dest, parcelFlags); } + @NonNull @Override public ParsedIntentInfo unparcel(Parcel source) { return new ParsedIntentInfo(source); diff --git a/core/java/android/content/pm/parsing/component/ParsedPermission.java b/core/java/android/content/pm/parsing/component/ParsedPermission.java index 50bc3d98fb2a0..0f82941f92b12 100644 --- a/core/java/android/content/pm/parsing/component/ParsedPermission.java +++ b/core/java/android/content/pm/parsing/component/ParsedPermission.java @@ -16,6 +16,7 @@ package android.content.pm.parsing.component; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.PermissionInfo; import android.os.Parcel; @@ -167,6 +168,7 @@ public class ParsedPermission extends ParsedComponent { this.knownCerts = sForStringSet.unparcel(in); } + @NonNull public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override diff --git a/core/java/android/content/pm/parsing/component/ParsedProcess.java b/core/java/android/content/pm/parsing/component/ParsedProcess.java index c39d6b1d32505..fe102252931e5 100644 --- a/core/java/android/content/pm/parsing/component/ParsedProcess.java +++ b/core/java/android/content/pm/parsing/component/ParsedProcess.java @@ -19,7 +19,6 @@ package android.content.pm.parsing.component; import static java.util.Collections.emptySet; import android.annotation.NonNull; -import android.annotation.Nullable; import android.content.pm.ApplicationInfo; import android.os.Parcel; import android.os.Parcelable; diff --git a/core/java/android/content/pm/parsing/component/ParsedProvider.java b/core/java/android/content/pm/parsing/component/ParsedProvider.java index ebf85f71d2821..9a12b48cffb06 100644 --- a/core/java/android/content/pm/parsing/component/ParsedProvider.java +++ b/core/java/android/content/pm/parsing/component/ParsedProvider.java @@ -169,6 +169,7 @@ public class ParsedProvider extends ParsedMainComponent { this.pathPermissions = in.createTypedArray(PathPermission.CREATOR); } + @NonNull public static final Parcelable.Creator CREATOR = new Creator() { @Override public ParsedProvider createFromParcel(Parcel source) { diff --git a/core/java/android/content/pm/parsing/component/ParsedService.java b/core/java/android/content/pm/parsing/component/ParsedService.java index 471d3468a084a..5499a130a4ad1 100644 --- a/core/java/android/content/pm/parsing/component/ParsedService.java +++ b/core/java/android/content/pm/parsing/component/ParsedService.java @@ -18,6 +18,7 @@ package android.content.pm.parsing.component; import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.os.Parcel; @@ -83,6 +84,7 @@ public class ParsedService extends ParsedMainComponent { this.permission = sForInternedString.unparcel(in); } + @NonNull public static final Parcelable.Creator CREATOR = new Creator() { @Override public ParsedService createFromParcel(Parcel source) { diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java index 3eb4bde6c67d0..2fcc4b2fae59e 100644 --- a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java +++ b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java @@ -55,6 +55,7 @@ import java.util.UUID; */ public final class PackageImpl extends ParsingPackageImpl implements ParsedPackage, AndroidPackage { + @NonNull public static PackageImpl forParsing(@NonNull String packageName, @NonNull String baseCodePath, @NonNull String codePath, @NonNull TypedArray manifestArray, boolean isCoreApp) { return new PackageImpl(packageName, baseCodePath, codePath, manifestArray, isCoreApp); @@ -70,6 +71,7 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka * this case only cares about * volumeUuid, just fake it rather than having separate method paths. */ + @NonNull public static AndroidPackage buildFakeForDeletion(String packageName, String volumeUuid) { return ((ParsedPackage) PackageImpl.forTesting(packageName) .setVolumeUuid(volumeUuid) @@ -77,11 +79,13 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka .hideAsFinal(); } + @NonNull @VisibleForTesting public static ParsingPackage forTesting(String packageName) { return forTesting(packageName, ""); } + @NonNull @VisibleForTesting public static ParsingPackage forTesting(String packageName, String baseCodePath) { return new PackageImpl(packageName, baseCodePath, baseCodePath, null, false); @@ -568,6 +572,7 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka assignDerivedFields(); } + @NonNull public static final Creator CREATOR = new Creator() { @Override public PackageImpl createFromParcel(Parcel source) { diff --git a/services/tests/PackageManagerServiceTests/unit/Android.bp b/services/tests/PackageManagerServiceTests/unit/Android.bp index 988c02bfb3db5..1bcc3d1f70ad8 100644 --- a/services/tests/PackageManagerServiceTests/unit/Android.bp +++ b/services/tests/PackageManagerServiceTests/unit/Android.bp @@ -32,6 +32,7 @@ android_test { "androidx.test.runner", "junit", "kotlin-test", + "kotlin-reflect", "services.core", "servicestests-utils", "truth-prebuilt", diff --git a/services/tests/PackageManagerServiceTests/unit/TEST_MAPPING b/services/tests/PackageManagerServiceTests/unit/TEST_MAPPING new file mode 100644 index 0000000000000..cacfcf0dbebce --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "PackageManagerServiceUnitTests" + } + ] +} diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt new file mode 100644 index 0000000000000..a7644ec439da5 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt @@ -0,0 +1,572 @@ +/* + * 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.parsing.parcelling + +import android.content.Intent +import android.content.pm.ApplicationInfo +import android.content.pm.ConfigurationInfo +import android.content.pm.FeatureGroupInfo +import android.content.pm.FeatureInfo +import android.content.pm.PackageManager +import android.content.pm.SigningDetails +import android.content.pm.parsing.ParsingPackage +import android.content.pm.parsing.component.ParsedActivity +import android.content.pm.parsing.component.ParsedAttribution +import android.content.pm.parsing.component.ParsedComponent +import android.content.pm.parsing.component.ParsedInstrumentation +import android.content.pm.parsing.component.ParsedIntentInfo +import android.content.pm.parsing.component.ParsedPermission +import android.content.pm.parsing.component.ParsedPermissionGroup +import android.content.pm.parsing.component.ParsedProcess +import android.content.pm.parsing.component.ParsedProvider +import android.content.pm.parsing.component.ParsedService +import android.content.pm.parsing.component.ParsedUsesPermission +import android.net.Uri +import android.os.Bundle +import android.os.Parcelable +import android.util.ArraySet +import android.util.SparseArray +import android.util.SparseIntArray +import com.android.internal.R +import com.android.server.pm.parsing.pkg.AndroidPackage +import com.android.server.pm.parsing.pkg.PackageImpl +import com.android.server.testutils.mockThrowOnUnmocked +import com.android.server.testutils.whenever +import java.security.KeyPairGenerator +import java.security.PublicKey +import kotlin.contracts.ExperimentalContracts + +@ExperimentalContracts +class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, PackageImpl::class) { + + override val defaultImpl = PackageImpl.forTesting("com.example.test") + override val creator = PackageImpl.CREATOR + + override val excludedMethods = listOf( + // Internal methods + "toAppInfoToString", + "toAppInfoWithoutState", + "toAppInfoWithoutStateWithoutFlags", + "assignDerivedFields", + "buildFakeForDeletion", + "capPermissionPriorities", + "forParsing", + "forTesting", + "getBaseAppDataCredentialProtectedDirForSystemUser", + "getBaseAppDataDeviceProtectedDirForSystemUser", + "getBoolean", + "setBoolean", + "hideAsFinal", + "hideAsParsed", + "markNotActivitiesAsNotExportedIfSingleUser", + "sortActivities", + "sortReceivers", + "sortServices", + "setAllComponentsDirectBootAware", + // Tested through setting minor/major manually + "setLongVersionCode", + "getLongVersionCode", + // Tested through constructor + "getManifestPackageName", + "setManifestPackageName", + // Utility methods + "getStorageUuid", + // Removal not tested, irrelevant for parcelling concerns + "removeUsesOptionalLibrary", + "clearAdoptPermissions", + "clearOriginalPackages", + "clearProtectedBroadcasts", + "removePermission", + "removeUsesLibrary", + "removeUsesOptionalNativeLibrary", + // Tested manually + "getMimeGroups", + "getRequestedPermissions", + // Tested through asSplit + "asSplit", + "getSplitNames", + "getSplitCodePaths", + "getSplitRevisionCodes", + "getSplitFlags", + "getSplitClassLoaderNames", + "getSplitDependencies", + "setSplitCodePaths", + "setSplitClassLoaderName", + "setSplitHasCode", + ) + + override val baseParams = listOf( + AndroidPackage::getAppComponentFactory, + AndroidPackage::getAutoRevokePermissions, + AndroidPackage::getBackupAgentName, + AndroidPackage::getBanner, + AndroidPackage::getBaseApkPath, + AndroidPackage::getBaseRevisionCode, + AndroidPackage::getCategory, + AndroidPackage::getClassLoaderName, + AndroidPackage::getClassName, + AndroidPackage::getCompatibleWidthLimitDp, + AndroidPackage::getCompileSdkVersion, + AndroidPackage::getCompileSdkVersionCodeName, + AndroidPackage::getDataExtractionRules, + AndroidPackage::getDescriptionRes, + AndroidPackage::getFullBackupContent, + AndroidPackage::getGwpAsanMode, + AndroidPackage::getIconRes, + AndroidPackage::getInstallLocation, + AndroidPackage::getLabelRes, + AndroidPackage::getLargestWidthLimitDp, + AndroidPackage::getLogo, + AndroidPackage::getManageSpaceActivityName, + AndroidPackage::getMemtagMode, + AndroidPackage::getMinSdkVersion, + AndroidPackage::getNativeHeapZeroInitialized, + AndroidPackage::getNativeLibraryDir, + AndroidPackage::getNativeLibraryRootDir, + AndroidPackage::getNetworkSecurityConfigRes, + AndroidPackage::getNonLocalizedLabel, + AndroidPackage::getOverlayCategory, + AndroidPackage::getOverlayPriority, + AndroidPackage::getOverlayTarget, + AndroidPackage::getOverlayTargetName, + AndroidPackage::getPackageName, + AndroidPackage::getPath, + AndroidPackage::getPermission, + AndroidPackage::getPrimaryCpuAbi, + AndroidPackage::getProcessName, + AndroidPackage::getRealPackage, + AndroidPackage::getRequiredAccountType, + AndroidPackage::getRequiresSmallestWidthDp, + AndroidPackage::getResizeableActivity, + AndroidPackage::getRestrictedAccountType, + AndroidPackage::getRoundIconRes, + AndroidPackage::getSeInfo, + AndroidPackage::getSeInfoUser, + AndroidPackage::getSecondaryCpuAbi, + AndroidPackage::getSecondaryNativeLibraryDir, + AndroidPackage::getSharedUserId, + AndroidPackage::getSharedUserLabel, + AndroidPackage::getStaticSharedLibName, + AndroidPackage::getStaticSharedLibVersion, + AndroidPackage::getTargetSandboxVersion, + AndroidPackage::getTargetSdkVersion, + AndroidPackage::getTaskAffinity, + AndroidPackage::getTheme, + AndroidPackage::getUiOptions, + AndroidPackage::getUid, + AndroidPackage::getVersionName, + AndroidPackage::getZygotePreloadName, + AndroidPackage::isAllowAudioPlaybackCapture, + AndroidPackage::isAllowBackup, + AndroidPackage::isAllowClearUserData, + AndroidPackage::isAllowClearUserDataOnFailedRestore, + AndroidPackage::isAllowNativeHeapPointerTagging, + AndroidPackage::isAllowTaskReparenting, + AndroidPackage::isBackupInForeground, + AndroidPackage::isBaseHardwareAccelerated, + AndroidPackage::isCantSaveState, + AndroidPackage::isCoreApp, + AndroidPackage::isCrossProfile, + AndroidPackage::isDebuggable, + AndroidPackage::isDefaultToDeviceProtectedStorage, + AndroidPackage::isDirectBootAware, + AndroidPackage::isEnabled, + AndroidPackage::isExternalStorage, + AndroidPackage::isExtractNativeLibs, + AndroidPackage::isFactoryTest, + AndroidPackage::isForceQueryable, + AndroidPackage::isFullBackupOnly, + AndroidPackage::isGame, + AndroidPackage::isHasCode, + AndroidPackage::isHasDomainUrls, + AndroidPackage::isHasFragileUserData, + AndroidPackage::isIsolatedSplitLoading, + AndroidPackage::isKillAfterRestore, + AndroidPackage::isLargeHeap, + AndroidPackage::isMultiArch, + AndroidPackage::isNativeLibraryRootRequiresIsa, + AndroidPackage::isOdm, + AndroidPackage::isOem, + AndroidPackage::isOverlay, + AndroidPackage::isOverlayIsStatic, + AndroidPackage::isPartiallyDirectBootAware, + AndroidPackage::isPersistent, + AndroidPackage::isPrivileged, + AndroidPackage::isProduct, + AndroidPackage::isProfileableByShell, + AndroidPackage::isRequestLegacyExternalStorage, + AndroidPackage::isRequiredForAllUsers, + AndroidPackage::isResizeableActivityViaSdkVersion, + AndroidPackage::isRestoreAnyVersion, + AndroidPackage::isSignedWithPlatformKey, + AndroidPackage::isStaticSharedLibrary, + AndroidPackage::isStub, + AndroidPackage::isSupportsRtl, + AndroidPackage::isSystem, + AndroidPackage::isSystemExt, + AndroidPackage::isTestOnly, + AndroidPackage::isUse32BitAbi, + AndroidPackage::isUseEmbeddedDex, + AndroidPackage::isUsesCleartextTraffic, + AndroidPackage::isUsesNonSdkApi, + AndroidPackage::isVendor, + AndroidPackage::isVisibleToInstantApps, + AndroidPackage::isVmSafeMode, + AndroidPackage::getMaxAspectRatio, + AndroidPackage::getMinAspectRatio, + AndroidPackage::hasPreserveLegacyExternalStorage, + AndroidPackage::hasRequestForegroundServiceExemption, + AndroidPackage::hasRequestRawExternalStorageAccess, + ) + + override fun extraParams() = listOf( + getter(AndroidPackage::getVolumeUuid, "57554103-df3e-4475-ae7a-8feba49353ac"), + getter(AndroidPackage::isProfileable, true), + getter(AndroidPackage::getVersionCode, 3), + getter(AndroidPackage::getVersionCodeMajor, 9), + getter(AndroidPackage::getUpgradeKeySets, setOf("testUpgradeKeySet")), + getter(AndroidPackage::isAnyDensity, false, 0), + getter(AndroidPackage::isResizeable, false, 0), + getter(AndroidPackage::isSupportsSmallScreens, false, 0), + getter(AndroidPackage::isSupportsNormalScreens, false, 0), + getter(AndroidPackage::isSupportsLargeScreens, false, 0), + getter(AndroidPackage::isSupportsExtraLargeScreens, false, 0), + adder(AndroidPackage::getAdoptPermissions, "test.adopt.PERMISSION"), + adder(AndroidPackage::getOriginalPackages, "com.test.original"), + adder(AndroidPackage::getImplicitPermissions, "test.implicit.PERMISSION"), + adder(AndroidPackage::getLibraryNames, "testLibraryName"), + adder(AndroidPackage::getProtectedBroadcasts, "test.protected.BROADCAST"), + adder(AndroidPackage::getQueriesPackages, "com.test.package.queries"), + adder(AndroidPackage::getQueriesProviders, "com.test.package.queries.provider"), + adder(AndroidPackage::getUsesLibraries, "testUsesLibrary"), + adder(AndroidPackage::getUsesNativeLibraries, "testUsesNativeLibrary"), + adder(AndroidPackage::getUsesOptionalLibraries, "testUsesOptionalLibrary"), + adder(AndroidPackage::getUsesOptionalNativeLibraries, "testUsesOptionalNativeLibrary"), + adder(AndroidPackage::getUsesStaticLibraries, "testUsesStaticLibrary"), + getSetByValue( + AndroidPackage::getUsesStaticLibrariesVersions, + PackageImpl::addUsesStaticLibraryVersion, + (testCounter++).toLong(), + transformGet = { it?.singleOrNull() } + ), + getSetByValue( + AndroidPackage::areAttributionsUserVisible, + ParsingPackage::setAttributionsAreUserVisible, + true + ), + getSetByValue2( + AndroidPackage::getOverlayables, + PackageImpl::addOverlayable, + "testOverlayableName" to "testActorName", + transformGet = { "testOverlayableName" to it["testOverlayableName"] } + ), + getSetByValue( + AndroidPackage::getMetaData, + PackageImpl::setMetaData, + "testBundleKey" to "testBundleValue", + transformGet = { "testBundleKey" to it?.getString("testBundleKey") }, + transformSet = { Bundle().apply { putString(it.first, it.second) } } + ), + getSetByValue( + AndroidPackage::getAttributions, + PackageImpl::addAttribution, + Triple("testTag", 13, listOf("testInherit")), + transformGet = { it.singleOrNull()?.let { Triple(it.tag, it.label, it.inheritFrom) } }, + transformSet = { it?.let { ParsedAttribution(it.first, it.second, it.third) } } + ), + getSetByValue2( + AndroidPackage::getKeySetMapping, + PackageImpl::addKeySet, + "testKeySetName" to testKey(), + transformGet = { "testKeySetName" to it["testKeySetName"]?.singleOrNull() }, + ), + getSetByValue( + AndroidPackage::getPermissionGroups, + PackageImpl::addPermissionGroup, + "test.permission.GROUP", + transformGet = { it.singleOrNull()?.name }, + transformSet = { ParsedPermissionGroup().apply { setName(it) } } + ), + getSetByValue2( + AndroidPackage::getPreferredActivityFilters, + PackageImpl::addPreferredActivityFilter, + "TestClassName" to ParsedIntentInfo().apply { + addDataScheme("http") + addDataAuthority("test.pm.server.android.com", null) + }, + transformGet = { it.singleOrNull()?.let { it.first to it.second } }, + compare = { first, second -> + equalBy( + first, second, + { it.first }, + { it.second.schemesIterator().asSequence().singleOrNull() }, + { it.second.authoritiesIterator().asSequence().singleOrNull()?.host }, + ) + } + ), + getSetByValue( + AndroidPackage::getQueriesIntents, + PackageImpl::addQueriesIntent, + Intent(Intent.ACTION_VIEW, Uri.parse("https://test.pm.server.android.com")), + transformGet = { it.singleOrNull() }, + compare = { first, second -> first?.filterEquals(second) }, + ), + getSetByValue( + AndroidPackage::getRestrictUpdateHash, + PackageImpl::setRestrictUpdateHash, + byteArrayOf(0, 1, 2, 3, 4), + compare = ByteArray::contentEquals + ), + getSetByValue( + AndroidPackage::getSigningDetails, + PackageImpl::setSigningDetails, + testKey(), + transformGet = { it.publicKeys?.takeIf { it.size > 0 }?.valueAt(0) }, + transformSet = { + SigningDetails( + null, + SigningDetails.SignatureSchemeVersion.UNKNOWN, + ArraySet().apply { add(it) }, + null + ) + } + ), + getSetByValue( + AndroidPackage::getUsesStaticLibrariesCertDigests, + PackageImpl::addUsesStaticLibraryCertDigests, + arrayOf("testCertDigest"), + transformGet = { it?.singleOrNull() }, + compare = Array?::contentEquals + ), + getSetByValue( + AndroidPackage::getActivities, + PackageImpl::addActivity, + "TestActivityName", + transformGet = { it.singleOrNull()?.name.orEmpty() }, + transformSet = { ParsedActivity().apply { name = it }.withMimeGroups() } + ), + getSetByValue( + AndroidPackage::getReceivers, + PackageImpl::addReceiver, + "TestReceiverName", + transformGet = { it.singleOrNull()?.name.orEmpty() }, + transformSet = { ParsedActivity().apply { name = it }.withMimeGroups() } + ), + getSetByValue( + AndroidPackage::getServices, + PackageImpl::addService, + "TestServiceName", + transformGet = { it.singleOrNull()?.name.orEmpty() }, + transformSet = { ParsedService().apply { name = it }.withMimeGroups() } + ), + getSetByValue( + AndroidPackage::getProviders, + PackageImpl::addProvider, + "TestProviderName", + transformGet = { it.singleOrNull()?.name.orEmpty() }, + transformSet = { ParsedProvider().apply { name = it }.withMimeGroups() } + ), + getSetByValue( + AndroidPackage::getInstrumentations, + PackageImpl::addInstrumentation, + "TestInstrumentationName", + transformGet = { it.singleOrNull()?.name.orEmpty() }, + transformSet = { ParsedInstrumentation().apply { name = it } } + ), + getSetByValue( + AndroidPackage::getConfigPreferences, + PackageImpl::addConfigPreference, + testCounter++, + transformGet = { it.singleOrNull()?.reqGlEsVersion ?: -1 }, + transformSet = { ConfigurationInfo().apply { reqGlEsVersion = it } } + ), + getSetByValue( + AndroidPackage::getFeatureGroups, + PackageImpl::addFeatureGroup, + "test.feature.GROUP", + transformGet = { it.singleOrNull()?.features?.singleOrNull()?.name.orEmpty() }, + transformSet = { + FeatureGroupInfo().apply { + features = arrayOf(FeatureInfo().apply { name = it }) + } + } + ), + getSetByValue( + AndroidPackage::getPermissions, + PackageImpl::addPermission, + "test.PERMISSION", + transformGet = { it.singleOrNull()?.name.orEmpty() }, + transformSet = { ParsedPermission().apply { name = it } } + ), + getSetByValue( + AndroidPackage::getUsesPermissions, + PackageImpl::addUsesPermission, + "test.USES_PERMISSION", + transformGet = { + // Need to strip implicit permission, which calls addUsesPermission when added + it.filterNot { it.name == "test.implicit.PERMISSION" } + .singleOrNull()?.name.orEmpty() + }, + transformSet = { ParsedUsesPermission(it, 0) } + ), + getSetByValue( + AndroidPackage::getReqFeatures, + PackageImpl::addReqFeature, + "test.feature.INFO", + transformGet = { it.singleOrNull()?.name.orEmpty() }, + transformSet = { FeatureInfo().apply { name = it } } + ), + getSetByValue( + AndroidPackage::getMinExtensionVersions, + PackageImpl::setMinExtensionVersions, + SparseIntArray().apply { put(testCounter++, testCounter++) }, + compare = { first, second -> + equalBy( + first, second, + { it.size() }, + { it.keyAt(0) }, + { it.valueAt(0) }, + ) + } + ), + getSetByValue( + AndroidPackage::getProcesses, + PackageImpl::setProcesses, + mapOf("testProcess" to ParsedProcess().apply { name = "testProcessName" }), + compare = { first, second -> + equalBy( + first, second, + { it["testProcess"]?.name }, + ) + } + ), + getSetByValue( + AndroidPackage::getProperties, + PackageImpl::addProperty, + PackageManager.Property( + "testPropertyName", + "testPropertyValue", + "testPropertyClassName", + "testPropertyPackageName" + ), + transformGet = { it["testPropertyName"] }, + compare = { first, second -> + equalBy( + first, second, + PackageManager.Property::getName, + PackageManager.Property::getClassName, + PackageManager.Property::getPackageName, + PackageManager.Property::getString, + ) + } + ), + ) + + override fun initialObject() = PackageImpl.forParsing( + "com.example.test", + "/test/test/base.apk", + "/test/test", + mockThrowOnUnmocked { + whenever(getInteger(R.styleable.AndroidManifest_revisionCode, 0)) { 4 } + whenever(getBoolean(R.styleable.AndroidManifest_isolatedSplits, false)) { true } + + // Return invalid values here so that the getter/setter is tested properly + whenever(getInteger(R.styleable.AndroidManifest_versionCode, 0)) { -1 } + whenever(getInteger(R.styleable.AndroidManifest_versionCodeMajor, 0)) { -1 } + whenever( + getNonConfigurationString( + R.styleable.AndroidManifest_versionName, + 0 + ) + ) { "" } + whenever(getInteger(R.styleable.AndroidManifest_compileSdkVersion, 0)) { 31 } + whenever( + getNonConfigurationString( + R.styleable.AndroidManifest_compileSdkVersionCodename, + 0 + ) + ) { "" } + }, + true + ) + .asSplit( + arrayOf("testSplitNameZero", "testSplitNameOne"), + arrayOf("/test/testSplitZero.apk", "/test/testSplitOne.apk"), + intArrayOf(10, 11), + SparseArray().apply { + put(0, intArrayOf(-1)) + put(1, intArrayOf(0)) + } + ) + .setSplitHasCode(0, true) + .setSplitHasCode(1, false) + .setSplitClassLoaderName(0, "testSplitClassLoaderNameZero") + .setSplitClassLoaderName(1, "testSplitClassLoaderNameOne") + + override fun extraAssertions(before: Parcelable, after: Parcelable) { + super.extraAssertions(before, after) + after as PackageImpl + expect.that(after.manifestPackageName).isEqualTo("com.example.test") + expect.that(after.isCoreApp).isTrue() + expect.that(after.isIsolatedSplitLoading).isEqualTo(true) + expect.that(after.longVersionCode).isEqualTo(38654705667) + expect.that(after.requestedPermissions) + .containsExactlyElementsIn(after.usesPermissions.map { it.name }) + .inOrder() + + expect.that(after.mimeGroups).containsExactly( + "TestActivityName/mimeGroup", + "TestReceiverName/mimeGroup", + "TestServiceName/mimeGroup", + "TestProviderName/mimeGroup" + ) + + expect.that(after.splitNames).asList() + .containsExactly("testSplitNameZero", "testSplitNameOne") + .inOrder() + expect.that(after.splitCodePaths).asList() + .containsExactly("/test/testSplitZero.apk", "/test/testSplitOne.apk") + .inOrder() + expect.that(after.splitRevisionCodes).asList() + .containsExactly(10, 11) + .inOrder() + expect.that(after.splitFlags).asList() + .containsExactly(ApplicationInfo.FLAG_HAS_CODE, 0) + .inOrder() + expect.that(after.splitClassLoaderNames).asList() + .containsExactly("testSplitClassLoaderNameZero", "testSplitClassLoaderNameOne") + .inOrder() + + expect.that(after.splitDependencies).isNotNull() + after.splitDependencies?.let { + expect.that(it.size()).isEqualTo(2) + expect.that(it.get(0)).asList().containsExactly(-1) + expect.that(it.get(1)).asList().containsExactly(0) + } + } + + private fun testKey() = KeyPairGenerator.getInstance("RSA") + .generateKeyPair() + .public + + private fun T.withMimeGroups() = apply { + val componentName = name + addIntent(ParsedIntentInfo().apply { + addMimeGroup("$componentName/mimeGroup") + }) + } +} diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParcelableComponentTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParcelableComponentTest.kt new file mode 100644 index 0000000000000..e16a1871f8b8b --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParcelableComponentTest.kt @@ -0,0 +1,413 @@ +/* + * 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.parsing.parcelling + +import android.os.Parcel +import android.os.Parcelable +import com.android.server.pm.test.util.IgnoreableExpect +import com.google.common.truth.Expect +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TestRule +import java.util.Objects +import kotlin.contracts.ExperimentalContracts +import kotlin.reflect.KClass +import kotlin.reflect.KFunction +import kotlin.reflect.KFunction1 +import kotlin.reflect.KFunction2 +import kotlin.reflect.KFunction3 +import kotlin.reflect.KVisibility +import kotlin.reflect.full.allSuperclasses +import kotlin.reflect.full.createInstance +import kotlin.reflect.full.isSubclassOf +import kotlin.reflect.full.memberFunctions +import kotlin.reflect.full.memberProperties +import kotlin.reflect.full.staticProperties +import kotlin.reflect.jvm.jvmErasure + + +@ExperimentalContracts +abstract class ParcelableComponentTest( + private val getterType: KClass<*>, + private val setterType: KClass +) { + + companion object { + private val DEFAULT_EXCLUDED = listOf( + // Java + "toString", + "equals", + "hashCode", + // Parcelable + "getStability", + "describeContents", + "writeToParcel", + // @DataClass + "__metadata" + ) + } + + internal val ignoreableExpect = IgnoreableExpect() + + // Hides internal type + @get:Rule + val ignoreableAsTestRule: TestRule = ignoreableExpect + + val expect: Expect + get() = ignoreableExpect.expect + + protected var testCounter = 1 + + protected abstract val defaultImpl: Any + protected abstract val creator: Parcelable.Creator + + protected open val excludedMethods: Collection = emptyList() + + protected abstract val baseParams: Collection> + + private val getters = getterType.memberFunctions + .filterNot { DEFAULT_EXCLUDED.contains(it.name) } + + private val setters = setterType.memberFunctions + .filterNot { DEFAULT_EXCLUDED.contains(it.name) } + + constructor(kClass: KClass) : this(kClass, kClass) + + @Before + fun checkNoPublicFields() { + // Fields are not currently testable, and the idea is to enforce interface access for + // immutability purposes, so disallow any public fields from existing. + expect.that(getterType.memberProperties.filter { it.visibility == KVisibility.PUBLIC } + .filterNot { DEFAULT_EXCLUDED.contains(it.name) }) + .isEmpty() + } + + @Suppress("UNCHECKED_CAST") + private fun buildParams( + getFunction: KFunction1, + ): Param? { + return buildParams( + getFunction, + autoValue(getFunction) as ReturnType ?: return null + ) + } + + @Suppress("UNCHECKED_CAST") + private fun buildParams( + getFunction: KFunction1, + value: SetType, + ): Param? { + return getSetByValue( + getFunction, + findSetFunction(getFunction) ?: return null, + value + ) + } + + @Suppress("UNCHECKED_CAST") + private fun findSetFunction( + getFunction: KFunction1 + ): KFunction2? { + val getFunctionName = getFunction.name + val prefix = when { + getFunctionName.startsWith("get") -> "get" + getFunctionName.startsWith("is") -> "is" + getFunctionName.startsWith("has") -> "has" + else -> throw IllegalArgumentException("Unsupported method name $getFunctionName") + } + val setFunctionName = "set" + getFunctionName.removePrefix(prefix) + val setFunction = setters.filter { it.name == setFunctionName } + .minByOrNull { it.parameters.size } + + if (setFunction == null) { + expect.withMessage("$getFunctionName does not have corresponding $setFunctionName") + .fail() + return null + } + + return setFunction as KFunction2 + } + + @Suppress("UNCHECKED_CAST") + private fun findAddFunction( + getFunction: KFunction1 + ): KFunction2? { + val getFunctionName = getFunction.name + if (!getFunctionName.startsWith("get")) { + throw IllegalArgumentException("Unsupported method name $getFunctionName") + } + + val setFunctionName = "add" + getFunctionName.removePrefix("get").run { + // Remove plurality + when { + endsWith("ies") -> "${removeSuffix("ies")}y" + endsWith("s") -> removeSuffix("s") + else -> this + } + } + + val setFunction = setters.filter { it.name == setFunctionName } + .minByOrNull { it.parameters.size } + + if (setFunction == null) { + expect.withMessage("$getFunctionName does not have corresponding $setFunctionName") + .fail() + return null + } + + return setFunction as KFunction2 + } + + protected fun getter( + getFunction: KFunction1, + valueToSet: ReturnType + ) = buildParams(getFunction, valueToSet) + + protected fun getter( + getFunction: KFunction1, + expectedValue: CompareType, + valueToSet: SetType + ): Param? { + return getSetByValue( + getFunction, + findSetFunction(getFunction) ?: return null, + value = expectedValue, + transformSet = { valueToSet } + ) + } + + @Suppress("UNCHECKED_CAST") + protected fun adder( + getFunction: KFunction1, + value: ReturnType, + ): Param? { + return getSetByValue( + getFunction, + findAddFunction(getFunction) ?: return null, + value, + transformGet = { + // Primitive arrays don't implement Iterable, so cast manually + when (it) { + is BooleanArray -> it.singleOrNull() + is IntArray -> it.singleOrNull() + is LongArray -> it.singleOrNull() + is Iterable<*> -> it.singleOrNull() + else -> null + } + }, + ) + } + + /** + * Method to provide custom getter and setter logic for values which are not simple primitives + * or cannot be directly compared using [Objects.equals]. + * + * @param getFunction the getter function which will be called and marked as tested + * @param setFunction the setter function which will be called and marked as tested + * @param value the value for comparison through the parcel-unparcel cycle, which can be + * anything, like the [String] ID of an inner object + * @param transformGet the function to transform the result of [getFunction] into [value] + * @param transformSet the function to transform [value] into an input for [setFunction] + * @param compare the function that compares the pre/post-parcel [value] objects + */ + @Suppress("UNCHECKED_CAST") + protected fun getSetByValue( + getFunction: KFunction1, + setFunction: KFunction2, + value: CompareType, + transformGet: (ReturnType) -> CompareType = { it as CompareType }, + transformSet: (CompareType) -> SetType = { it as SetType }, + compare: (CompareType, CompareType) -> Boolean? = Objects::equals + ) = Param( + getFunction.name, + { transformGet(getFunction.call(it as ObjectType)) }, + setFunction.name, + { setFunction.call(it.first() as ObjectType, transformSet(it[1] as CompareType)) }, + { value }, + { first, second -> compare(first as CompareType, second as CompareType) == true } + ) + + /** + * Variant of [getSetByValue] that allows specifying a [setFunction] with 2 inputs. + */ + @Suppress("UNCHECKED_CAST") + protected fun + getSetByValue2( + getFunction: KFunction1, + setFunction: KFunction3, + value: CompareType, + transformGet: (ReturnType) -> CompareType = { it as CompareType }, + transformSet: (CompareType) -> Pair = + { it as Pair }, + compare: (CompareType, CompareType) -> Boolean = Objects::equals + ) = Param( + getFunction.name, + { transformGet(getFunction.call(it as ObjectType)) }, + setFunction.name, + { + val pair = transformSet(it[1] as CompareType) + setFunction.call(it.first() as ObjectType, pair.first, pair.second) + }, + { value }, + { first, second -> compare(first as CompareType, second as CompareType) } + ) + + protected fun autoValue(getFunction: KFunction<*>) = when (getFunction.returnType.jvmErasure) { + Boolean::class -> (getFunction.call(defaultImpl) as Boolean?)?.not() ?: true + CharSequence::class, + String::class -> getFunction.name + "TEST" + Int::class -> testCounter++ + Long::class -> (testCounter++).toLong() + Float::class -> (testCounter++).toFloat() + else -> { + expect.withMessage("${getFunction.name} needs to provide value").fail() + null + } + } + + /** + * Verifies two instances are equivalent via a series of properties. For use when a public API + * class has not implemented equals. + */ + @Suppress("UNCHECKED_CAST") + protected fun equalBy( + first: T?, + second: T?, + vararg properties: (T) -> Any? + ) = properties.all { property -> + first?.let { property(it) } == second?.let { property(it) } + } + + @Test + fun valueComparison() { + val params = baseParams.mapNotNull(::buildParams) + extraParams().filterNotNull() + val before = initialObject() + + params.forEach { it.setFunction(arrayOf(before, it.value())) } + + val parcel = Parcel.obtain() + writeToParcel(parcel, before) + + val dataSize = parcel.dataSize() + + parcel.setDataPosition(0) + + val after = creator.createFromParcel(parcel) + + expect.withMessage("Mismatched write and read data sizes") + .that(parcel.dataPosition()) + .isEqualTo(dataSize) + + parcel.recycle() + + runAssertions(params, before, after) + } + + @Test + open fun parcellingSize() { + val parcelOne = Parcel.obtain() + writeToParcel(parcelOne, initialObject()) + + val parcelTwo = Parcel.obtain() + initialObject().writeToParcel(parcelTwo, 0) + + val superDataSizes = setterType.allSuperclasses + .filter { it.isSubclassOf(Parcelable::class) } + .mapNotNull { it.memberFunctions.find { it.name == "writeToParcel" } } + .filter { it.isFinal } + .map { + val parcel = Parcel.obtain() + initialObject().writeToParcel(parcel, 0) + parcel.dataSize().also { parcel.recycle() } + } + + if ((superDataSizes + parcelOne.dataSize() + parcelTwo.dataSize()).distinct().size != 1) { + listOf(getterType, setterType).distinct().forEach { + val creatorProperties = it.staticProperties.filter { it.name == "CREATOR" } + if (creatorProperties.size > 1) { + expect.withMessage( + "Multiple matching CREATOR fields found for" + + it.qualifiedName + ) + .that(creatorProperties) + .hasSize(1) + } else { + val creator = creatorProperties.single().get() + if (creator !is Parcelable.Creator<*>) { + expect.that(creator).isInstanceOf(Parcelable.Creator::class.java) + return + } + + parcelTwo.setDataPosition(0) + val parcelable = creator.createFromParcel(parcelTwo) + if (parcelable::class.isSubclassOf(setterType)) { + expect.withMessage( + "${it.qualifiedName} which does not safely override writeToParcel " + + "cannot contain a subclass CREATOR field" + ) + .fail() + } + } + } + } + + parcelOne.recycle() + parcelTwo.recycle() + } + + private fun runAssertions(params: List, before: Parcelable, after: Parcelable) { + params.forEach { + val actual = it.getFunction(after) + val expected = it.value() + val equal = it.compare(actual, expected) + expect.withMessage("${it.getFunctionName} was $actual, expected $expected") + .that(equal) + .isTrue() + } + + extraAssertions(before, after) + + // TODO: Handle method overloads? + val expectedFunctions = (getters.map { it.name } + + setters.map { it.name } + - excludedMethods) + .distinct() + + val allTestedFunctions = params.flatMap { + listOfNotNull(it.getFunctionName, it.setFunctionName) + } + expect.that(allTestedFunctions).containsExactlyElementsIn(expectedFunctions) + } + + open fun extraParams(): Collection = emptyList() + + open fun initialObject(): Parcelable = setterType.createInstance() + + open fun extraAssertions(before: Parcelable, after: Parcelable) {} + + open fun writeToParcel(parcel: Parcel, value: Parcelable) = value.writeToParcel(parcel, 0) + + data class Param( + val getFunctionName: String, + val getFunction: (Any?) -> Any?, + val setFunctionName: String?, + val setFunction: (Array) -> Unit, + val value: () -> Any?, + val compare: (Any?, Any?) -> Boolean = Objects::equals + ) +} diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParcelableCreatorInvalidTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParcelableCreatorInvalidTest.kt new file mode 100644 index 0000000000000..d506190b8b094 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParcelableCreatorInvalidTest.kt @@ -0,0 +1,59 @@ +/* + * 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.parsing.parcelling + +import android.os.Parcel +import android.os.Parcelable +import com.android.server.pm.test.parsing.parcelling.java.TestSubWithCreator +import com.android.server.pm.test.parsing.parcelling.java.TestSuperClass +import org.junit.Test +import kotlin.contracts.ExperimentalContracts + +/** + * Verifies the failing side of [ParcelableCreatorValidTest]. The sole difference is the addition + * of [TestSubWithCreator.CREATOR]. + */ +@ExperimentalContracts +class ParcelableCreatorInvalidTest : + ParcelableComponentTest(TestSuperClass::class, TestSubWithCreator::class) { + + override val defaultImpl = TestSubWithCreator() + + override val creator = object : Parcelable.Creator { + override fun createFromParcel(source: Parcel) = TestSubWithCreator(source) + override fun newArray(size: Int) = Array(size) { null } + } + + override val excludedMethods = listOf("writeSubToParcel") + + override val baseParams = listOf(TestSuperClass::getSuperString) + + override fun writeToParcel(parcel: Parcel, value: Parcelable) { + (value as TestSubWithCreator).writeSubToParcel(parcel, 0) + } + + @Test + override fun parcellingSize() { + super.parcellingSize() + if (expect.hasFailures()) { + // This is a hack to ignore an expected failure result. Doing it this way, rather than + // adding a switch in the test itself, prevents it from accidentally passing through a + // programming error. + ignoreableExpect.ignore() + } + } +} diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParcelableCreatorValidTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParcelableCreatorValidTest.kt new file mode 100644 index 0000000000000..f1bc7b5a07f4e --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParcelableCreatorValidTest.kt @@ -0,0 +1,47 @@ +/* + * 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.parsing.parcelling + +import android.os.Parcel +import android.os.Parcelable +import com.android.server.pm.test.parsing.parcelling.java.TestSubWithoutCreator +import com.android.server.pm.test.parsing.parcelling.java.TestSuperClass +import kotlin.contracts.ExperimentalContracts + +/** + * Tests the [Parcelable] CREATOR verification by using a mock object with known differences to + * ensure that the method succeeds/fails. + */ +@ExperimentalContracts +class ParcelableCreatorValidTest : + ParcelableComponentTest(TestSuperClass::class, TestSubWithoutCreator::class) { + + override val defaultImpl = TestSubWithoutCreator() + + override val creator = object : Parcelable.Creator { + override fun createFromParcel(source: Parcel) = TestSubWithoutCreator(source) + override fun newArray(size: Int) = Array(size) { null } + } + + override val excludedMethods = listOf("writeSubToParcel") + + override val baseParams = listOf(TestSuperClass::getSuperString) + + override fun writeToParcel(parcel: Parcel, value: Parcelable) { + (value as TestSubWithoutCreator).writeSubToParcel(parcel, 0) + } +} diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt new file mode 100644 index 0000000000000..ece600bc446e5 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt @@ -0,0 +1,73 @@ +/* + * 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.parsing.parcelling + +import android.content.pm.ActivityInfo +import android.content.pm.parsing.component.ParsedActivity +import kotlin.contracts.ExperimentalContracts + +@ExperimentalContracts +class ParsedActivityTest : ParsedMainComponentTest(ParsedActivity::class) { + + override val defaultImpl = ParsedActivity() + override val creator = ParsedActivity.CREATOR + + override val mainComponentSubclassBaseParams = listOf( + ParsedActivity::getPermission, + ParsedActivity::getColorMode, + ParsedActivity::getConfigChanges, + ParsedActivity::getDocumentLaunchMode, + ParsedActivity::getLaunchMode, + ParsedActivity::getLockTaskLaunchMode, + ParsedActivity::getMaxAspectRatio, + ParsedActivity::getMaxRecents, + ParsedActivity::getMinAspectRatio, + ParsedActivity::getParentActivityName, + ParsedActivity::getPersistableMode, + ParsedActivity::getPrivateFlags, + ParsedActivity::getRequestedVrComponent, + ParsedActivity::getResizeMode, + ParsedActivity::getRotationAnimation, + ParsedActivity::getScreenOrientation, + ParsedActivity::getSoftInputMode, + ParsedActivity::getTargetActivity, + ParsedActivity::getTaskAffinity, + ParsedActivity::getTheme, + ParsedActivity::getUiOptions, + ParsedActivity::isSupportsSizeChanges, + ) + + override fun mainComponentSubclassExtraParams() = listOf( + getSetByValue( + ParsedActivity::getWindowLayout, + ParsedActivity::setWindowLayout, + ActivityInfo.WindowLayout(1, 1f, 2, 1f, 3, 4, 5), + compare = { first, second -> + equalBy( + first, second, + ActivityInfo.WindowLayout::width, + ActivityInfo.WindowLayout::widthFraction, + ActivityInfo.WindowLayout::height, + ActivityInfo.WindowLayout::heightFraction, + ActivityInfo.WindowLayout::gravity, + ActivityInfo.WindowLayout::minWidth, + ActivityInfo.WindowLayout::minHeight + ) + } + ) + ) +} diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt new file mode 100644 index 0000000000000..e739dc70cdbb3 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt @@ -0,0 +1,36 @@ +/* + * 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.parsing.parcelling + +import android.content.pm.parsing.component.ParsedAttribution +import kotlin.contracts.ExperimentalContracts + +@ExperimentalContracts +class ParsedAttributionTest : ParcelableComponentTest(ParsedAttribution::class) { + + override val defaultImpl = ParsedAttribution("", 0, emptyList()) + override val creator = ParsedAttribution.CREATOR + + override val baseParams = listOf( + ParsedAttribution::getTag, + ParsedAttribution::getLabel, + ) + + override fun extraParams() = listOf( + getter(ParsedAttribution::getInheritFrom, listOf("testInheritFrom")) + ) +} diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt new file mode 100644 index 0000000000000..0a22f6d301a9d --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt @@ -0,0 +1,95 @@ +/* + * 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.parsing.parcelling + +import android.content.pm.PackageManager +import android.content.pm.parsing.component.ParsedComponent +import android.content.pm.parsing.component.ParsedIntentInfo +import android.os.Bundle +import android.os.Parcelable +import kotlin.contracts.ExperimentalContracts +import kotlin.reflect.KClass +import kotlin.reflect.KFunction1 + +@ExperimentalContracts +abstract class ParsedComponentTest(kClass: KClass) : + ParcelableComponentTest(kClass) { + + final override val excludedMethods + get() = subclassExcludedMethods + listOf( + // Method aliases/utilities + "getClassName", + "getComponentName", + "setProperties" // Tested though addProperty + ) + + open val subclassExcludedMethods: Collection = emptyList() + + final override val baseParams + get() = subclassBaseParams + listOf( + ParsedComponent::getBanner, + ParsedComponent::getDescriptionRes, + ParsedComponent::getFlags, + ParsedComponent::getIcon, + ParsedComponent::getLabelRes, + ParsedComponent::getLogo, + ParsedComponent::getName, + ParsedComponent::getNonLocalizedLabel, + ParsedComponent::getPackageName, + ) + + abstract val subclassBaseParams: Collection> + + final override fun extraParams() = subclassExtraParams() + listOf( + getSetByValue( + ParsedComponent::getIntents, + ParsedComponent::addIntent, + "TestLabel", + transformGet = { it.singleOrNull()?.nonLocalizedLabel }, + transformSet = { ParsedIntentInfo().setNonLocalizedLabel(it) }, + ), + getSetByValue( + ParsedComponent::getProperties, + ParsedComponent::addProperty, + PackageManager.Property( + "testPropertyName", + "testPropertyValue", + "testPropertyClassName", + "testPropertyPackageName" + ), + transformGet = { it["testPropertyName"] }, + compare = { first, second -> + equalBy( + first, second, + PackageManager.Property::getName, + PackageManager.Property::getClassName, + PackageManager.Property::getPackageName, + PackageManager.Property::getString, + ) + } + ), + getSetByValue( + ParsedComponent::getMetaData, + ParsedComponent::setMetaData, + "testBundleKey" to "testBundleValue", + transformGet = { "testBundleKey" to it?.getString("testBundleKey") }, + transformSet = { Bundle().apply { putString(it.first, it.second) } } + ), + ) + + open fun subclassExtraParams(): Collection = emptyList() +} diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt new file mode 100644 index 0000000000000..b7a85cc3d186f --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt @@ -0,0 +1,34 @@ +/* + * 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.parsing.parcelling + +import android.content.pm.parsing.component.ParsedInstrumentation +import kotlin.contracts.ExperimentalContracts + +@ExperimentalContracts +class ParsedInstrumentationTest : ParsedComponentTest(ParsedInstrumentation::class) { + + override val defaultImpl = ParsedInstrumentation() + override val creator = ParsedInstrumentation.CREATOR + + override val subclassBaseParams = listOf( + ParsedInstrumentation::getTargetPackage, + ParsedInstrumentation::getTargetProcesses, + ParsedInstrumentation::isFunctionalTest, + ParsedInstrumentation::isHandleProfiling, + ) +} diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt new file mode 100644 index 0000000000000..e27bdf23cfc66 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt @@ -0,0 +1,152 @@ +/* + * 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.parsing.parcelling + +import android.content.pm.parsing.component.ParsedIntentInfo +import android.os.Parcel +import android.os.Parcelable +import android.os.PatternMatcher +import kotlin.contracts.ExperimentalContracts + +@ExperimentalContracts +class ParsedIntentInfoTest : ParcelableComponentTest(ParsedIntentInfo::class) { + + override val defaultImpl = ParsedIntentInfo() + + override val creator = object : Parcelable.Creator { + override fun createFromParcel(source: Parcel) = ParsedIntentInfo(source) + override fun newArray(size: Int) = Array(size) { null } + } + + override val excludedMethods = listOf( + // Used to parcel + "writeIntentInfoToParcel", + // All remaining IntentFilter methods, which are out of scope + "hasDataPath", + "hasDataSchemeSpecificPart", + "matchAction", + "matchData", + "actionsIterator", + "addAction", + "addCategory", + "addDataAuthority", + "addDataPath", + "addDataScheme", + "addDataSchemeSpecificPart", + "addDataType", + "addDynamicDataType", + "addMimeGroup", + "asPredicate", + "asPredicateWithTypeResolution", + "authoritiesIterator", + "categoriesIterator", + "clearDynamicDataTypes", + "countActions", + "countCategories", + "countDataAuthorities", + "countDataPaths", + "countDataSchemeSpecificParts", + "countDataSchemes", + "countDataTypes", + "countMimeGroups", + "countStaticDataTypes", + "dataTypes", + "debugCheck", + "dump", + "dumpDebug", + "getAction", + "getAutoVerify", + "getCategory", + "getDataAuthority", + "getDataPath", + "getDataScheme", + "getDataSchemeSpecificPart", + "getDataType", + "getHosts", + "getHostsList", + "getMimeGroup", + "getOrder", + "getPriority", + "getVisibilityToInstantApp", + "handleAllWebDataURI", + "handlesWebUris", + "hasAction", + "hasCategory", + "hasDataAuthority", + "hasDataScheme", + "hasDataType", + "hasExactDataType", + "hasExactDynamicDataType", + "hasExactStaticDataType", + "hasMimeGroup", + "isExplicitlyVisibleToInstantApp", + "isImplicitlyVisibleToInstantApp", + "isVerified", + "isVisibleToInstantApp", + "match", + "matchCategories", + "matchDataAuthority", + "mimeGroupsIterator", + "needsVerification", + "pathsIterator", + "readFromXml", + "schemeSpecificPartsIterator", + "schemesIterator", + "setAutoVerify", + "setOrder", + "setPriority", + "setVerified", + "setVisibilityToInstantApp", + "typesIterator", + "writeToXml", + ) + + override val baseParams = listOf( + ParsedIntentInfo::getIcon, + ParsedIntentInfo::getLabelRes, + ParsedIntentInfo::isHasDefault, + ParsedIntentInfo::getNonLocalizedLabel, + ) + + override fun initialObject() = ParsedIntentInfo().apply { + addAction("test.ACTION") + addDataAuthority("testAuthority", "404") + addCategory("test.CATEGORY") + addMimeGroup("testMime") + addDataPath("testPath", PatternMatcher.PATTERN_LITERAL) + } + + override fun extraAssertions(before: Parcelable, after: Parcelable) { + super.extraAssertions(before, after) + after as ParsedIntentInfo + expect.that(after.actionsIterator().asSequence().singleOrNull()) + .isEqualTo("test.ACTION") + + val authority = after.authoritiesIterator().asSequence().singleOrNull() + expect.that(authority?.host).isEqualTo("testAuthority") + expect.that(authority?.port).isEqualTo(404) + + expect.that(after.categoriesIterator().asSequence().singleOrNull()) + .isEqualTo("test.CATEGORY") + expect.that(after.mimeGroupsIterator().asSequence().singleOrNull()) + .isEqualTo("testMime") + expect.that(after.hasDataPath("testPath")).isTrue() + } + + override fun writeToParcel(parcel: Parcel, value: Parcelable) = + ParsedIntentInfo.PARCELER.parcel(value as ParsedIntentInfo, parcel, 0) +} diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt new file mode 100644 index 0000000000000..411cb0950d180 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt @@ -0,0 +1,53 @@ +/* + * 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.parsing.parcelling + +import android.content.pm.parsing.component.ParsedMainComponent +import android.content.pm.parsing.component.ParsedService +import android.os.Parcelable +import java.util.Arrays +import kotlin.contracts.ExperimentalContracts +import kotlin.reflect.KClass +import kotlin.reflect.KFunction1 + +@ExperimentalContracts +abstract class ParsedMainComponentTest(kClass: KClass) : + ParsedComponentTest(kClass) { + + final override val subclassBaseParams + get() = mainComponentSubclassBaseParams + listOf( + ParsedMainComponent::getOrder, + ParsedMainComponent::getProcessName, + ParsedMainComponent::getSplitName, + ParsedMainComponent::isDirectBootAware, + ParsedMainComponent::isEnabled, + ParsedMainComponent::isExported, + ) + + abstract val mainComponentSubclassBaseParams: Collection> + + final override fun subclassExtraParams() = mainComponentSubclassExtraParams() + listOf( + getSetByValue( + ParsedService::getAttributionTags, + ParsedService::setAttributionTags, + arrayOf("testAttributionTag"), + compare = Arrays::equals + ), + ) + + open fun mainComponentSubclassExtraParams(): Collection = emptyList() +} diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt new file mode 100644 index 0000000000000..53c862a519b15 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt @@ -0,0 +1,35 @@ +/* + * 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.parsing.parcelling + +import android.content.pm.parsing.component.ParsedPermissionGroup +import kotlin.contracts.ExperimentalContracts + +@ExperimentalContracts +class ParsedPermissionGroupTest : ParsedComponentTest(ParsedPermissionGroup::class) { + + override val defaultImpl = ParsedPermissionGroup() + override val creator = ParsedPermissionGroup.CREATOR + + override val subclassBaseParams = listOf( + ParsedPermissionGroup::getRequestDetailResourceId, + ParsedPermissionGroup::getBackgroundRequestDetailResourceId, + ParsedPermissionGroup::getBackgroundRequestResourceId, + ParsedPermissionGroup::getRequestRes, + ParsedPermissionGroup::getPriority, + ) +} diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt new file mode 100644 index 0000000000000..bb63e2e2a61de --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt @@ -0,0 +1,55 @@ +/* + * 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.parsing.parcelling + +import android.content.pm.parsing.component.ParsedPermission +import android.content.pm.parsing.component.ParsedPermissionGroup +import kotlin.contracts.ExperimentalContracts + +@ExperimentalContracts +class ParsedPermissionTest : ParsedComponentTest(ParsedPermission::class) { + + override val defaultImpl = ParsedPermission() + override val creator = ParsedPermission.CREATOR + + override val subclassExcludedMethods = listOf( + // Utility methods + "isAppOp", + "isRuntime", + "getProtection", + "getProtectionFlags", + "calculateFootprint", + "setKnownCert", // Tested through setKnownCerts + ) + override val subclassBaseParams = listOf( + ParsedPermission::getBackgroundPermission, + ParsedPermission::getGroup, + ParsedPermission::getRequestRes, + ParsedPermission::getProtectionLevel, + ParsedPermission::isTree, + ) + + override fun subclassExtraParams() = listOf( + getter(ParsedPermission::getKnownCerts, setOf("testCert")), + getSetByValue( + ParsedPermission::getParsedPermissionGroup, + ParsedPermission::setParsedPermissionGroup, + ParsedPermissionGroup().apply { name = "test.permission.group" }, + compare = { first, second -> equalBy(first, second, ParsedPermissionGroup::getName) } + ), + ) +} diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt new file mode 100644 index 0000000000000..34f46f2c0adba --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt @@ -0,0 +1,43 @@ +/* + * 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.parsing.parcelling + +import android.content.pm.parsing.component.ParsedProcess +import kotlin.contracts.ExperimentalContracts + +@ExperimentalContracts +class ParsedProcessTest : ParcelableComponentTest(ParsedProcess::class) { + + override val defaultImpl = ParsedProcess() + override val creator = ParsedProcess.CREATOR + + override val excludedMethods = listOf( + // Copying method + "addStateFrom", + ) + + override val baseParams = listOf( + ParsedProcess::getName, + ParsedProcess::getGwpAsanMode, + ParsedProcess::getMemtagMode, + ParsedProcess::getNativeHeapZeroInitialized, + ) + + override fun extraParams() = listOf( + getter(ParsedProcess::getDeniedPermissions, setOf("testDeniedPermission")) + ) +} diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt new file mode 100644 index 0000000000000..e6d5c0fd258f5 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt @@ -0,0 +1,78 @@ +/* + * 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.parsing.parcelling + +import android.content.pm.PathPermission +import android.content.pm.parsing.component.ParsedProvider +import android.os.PatternMatcher +import kotlin.contracts.ExperimentalContracts + +@ExperimentalContracts +class ParsedProviderTest : ParsedMainComponentTest(ParsedProvider::class) { + + override val defaultImpl = ParsedProvider() + override val creator = ParsedProvider.CREATOR + + override val mainComponentSubclassBaseParams = listOf( + ParsedProvider::getAuthority, + ParsedProvider::isSyncable, + ParsedProvider::getReadPermission, + ParsedProvider::getWritePermission, + ParsedProvider::isGrantUriPermissions, + ParsedProvider::isForceUriPermissions, + ParsedProvider::isMultiProcess, + ParsedProvider::getInitOrder, + ) + + override fun mainComponentSubclassExtraParams() = listOf( + getSetByValue( + ParsedProvider::getUriPermissionPatterns, + ParsedProvider::setUriPermissionPatterns, + PatternMatcher("testPattern", PatternMatcher.PATTERN_LITERAL), + transformGet = { it?.singleOrNull() }, + transformSet = { arrayOf(it) }, + compare = { first, second -> + equalBy( + first, second, + PatternMatcher::getPath, + PatternMatcher::getType + ) + } + ), + getSetByValue( + ParsedProvider::getPathPermissions, + ParsedProvider::setPathPermissions, + PathPermission( + "testPermissionPattern", + PatternMatcher.PATTERN_LITERAL, + "test.READ_PERMISSION", + "test.WRITE_PERMISSION" + ), + transformGet = { it?.singleOrNull() }, + transformSet = { arrayOf(it) }, + compare = { first, second -> + equalBy( + first, second, + PatternMatcher::getPath, + PatternMatcher::getType, + PathPermission::getReadPermission, + PathPermission::getWritePermission, + ) + } + ) + ) +} diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt new file mode 100644 index 0000000000000..553018493f8bd --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt @@ -0,0 +1,32 @@ +/* + * 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.parsing.parcelling + +import android.content.pm.parsing.component.ParsedService +import kotlin.contracts.ExperimentalContracts + +@ExperimentalContracts +class ParsedServiceTest : ParsedMainComponentTest(ParsedService::class) { + + override val defaultImpl = ParsedService() + override val creator = ParsedService.CREATOR + + override val mainComponentSubclassBaseParams = listOf( + ParsedService::getForegroundServiceType, + ParsedService::getPermission, + ) +} diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt new file mode 100644 index 0000000000000..1131c72152adb --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt @@ -0,0 +1,35 @@ +/* + * 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.parsing.parcelling + +import android.content.pm.parsing.component.ParsedUsesPermission +import android.os.Parcelable +import kotlin.contracts.ExperimentalContracts + +@ExperimentalContracts +class ParsedUsesPermissionTest : ParcelableComponentTest(ParsedUsesPermission::class) { + + override val defaultImpl = ParsedUsesPermission("", 0) + override val creator = ParsedUsesPermission.CREATOR + + override val baseParams = listOf( + ParsedUsesPermission::getName, + ParsedUsesPermission::getUsesPermissionFlags + ) + + override fun initialObject() = ParsedUsesPermission("", 0) +} diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/java/TestSubWithCreator.java b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/java/TestSubWithCreator.java new file mode 100644 index 0000000000000..581d2b2d72b80 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/java/TestSubWithCreator.java @@ -0,0 +1,56 @@ +/* + * 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.parsing.parcelling.java; + +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; + +public class TestSubWithCreator extends TestSuperClass { + + @NonNull + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public TestSubWithCreator createFromParcel(Parcel source) { + return new TestSubWithCreator(source); + } + + @Override + public TestSubWithCreator[] newArray(int size) { + return new TestSubWithCreator[size]; + } + }; + + @Nullable + private String subString; + + public TestSubWithCreator() { + } + + public TestSubWithCreator(@NonNull Parcel in) { + super(in); + subString = in.readString(); + } + + public void writeSubToParcel(@NonNull Parcel parcel, int flags) { + super.writeToParcel(parcel, flags); + parcel.writeString(subString); + } +} diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/java/TestSubWithoutCreator.java b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/java/TestSubWithoutCreator.java new file mode 100644 index 0000000000000..4264a11c03e37 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/java/TestSubWithoutCreator.java @@ -0,0 +1,41 @@ +/* + * 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.parsing.parcelling.java; + +import android.annotation.Nullable; +import android.os.Parcel; + +import androidx.annotation.NonNull; + +public class TestSubWithoutCreator extends TestSuperClass { + + @Nullable + private String subString; + + public TestSubWithoutCreator() { + } + + public TestSubWithoutCreator(@NonNull Parcel in) { + super(in); + subString = in.readString(); + } + + public void writeSubToParcel(@NonNull Parcel parcel, int flags) { + super.writeToParcel(parcel, flags); + parcel.writeString(subString); + } +} diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/java/TestSuperClass.java b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/java/TestSuperClass.java new file mode 100644 index 0000000000000..a009786de3528 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/java/TestSuperClass.java @@ -0,0 +1,118 @@ +/* + * 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.parsing.parcelling.java; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; + +@DataClass(genGetters = true, genSetters = true, genBuilder = false, genAidl = false, + genParcelable = true, genConstructor = false) +public class TestSuperClass implements Parcelable { + + @Nullable + private String superString; + + public TestSuperClass() { + } + + + + // Code below generated by codegen v1.0.23. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/java/TestSuperClass.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + @DataClass.Generated.Member + public @Nullable String getSuperString() { + return superString; + } + + @DataClass.Generated.Member + public @NonNull TestSuperClass setSuperString(@NonNull String value) { + superString = value; + return this; + } + + @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 (superString != null) flg |= 0x1; + dest.writeByte(flg); + if (superString != null) dest.writeString(superString); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + protected TestSuperClass(@NonNull android.os.Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + byte flg = in.readByte(); + String _superString = (flg & 0x1) == 0 ? null : in.readString(); + + this.superString = _superString; + + // 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 TestSuperClass[] newArray(int size) { + return new TestSuperClass[size]; + } + + @Override + public TestSuperClass createFromParcel(@NonNull android.os.Parcel in) { + return new TestSuperClass(in); + } + }; + + @DataClass.Generated( + time = 1624381019144L, + codegenVersion = "1.0.23", + sourceFile = "frameworks/base/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/java/TestSuperClass.java", + inputSignatures = "private @android.annotation.Nullable java.lang.String superString\nclass TestSuperClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genAidl=false, genParcelable=true, genConstructor=false)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/util/IgnoreableExpect.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/util/IgnoreableExpect.kt new file mode 100644 index 0000000000000..afb18f5be669f --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/util/IgnoreableExpect.kt @@ -0,0 +1,53 @@ +/* + * 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.util + +import com.google.common.truth.Expect +import org.junit.rules.TestRule +import org.junit.runner.Description +import org.junit.runners.model.Statement + +/** + * Wrapper for [Expect] which supports ignoring any failures. This should be used with caution, but + * it allows a base test to be written which doesn't switch success/failure in the test itself, + * preventing any logic errors from causing the test to accidentally succeed. + */ +internal class IgnoreableExpect : TestRule { + + val expect = Expect.create() + + private var ignore = false + + override fun apply(base: Statement?, description: Description?): Statement { + return object : Statement() { + override fun evaluate() { + ignore = false + try { + expect.apply(base, description).evaluate() + } catch (t: Throwable) { + if (!ignore) { + throw t + } + } + } + } + } + + fun ignore() { + ignore = true + } +} 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 e8c5fb3ecf5c5..38b98ca7a9c58 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java @@ -949,7 +949,7 @@ public class PackageParserTest { .addConfigPreference(new ConfigurationInfo()) .addReqFeature(new FeatureInfo()) .addFeatureGroup(new FeatureGroupInfo()) - .setCompileSdkVersionCodename("foo23") + .setCompileSdkVersionCodeName("foo23") .setCompileSdkVersion(100) .setOverlayCategory("foo24") .setOverlayIsStatic(true)