summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java62
-rw-r--r--core/java/com/android/internal/pm/pkg/component/AconfigFlags.java23
-rw-r--r--core/java/com/android/internal/pm/pkg/component/ComponentParseUtils.java2
-rw-r--r--core/java/com/android/internal/pm/pkg/component/InstallConstraintsTagParser.java6
-rw-r--r--core/java/com/android/internal/pm/pkg/component/ParsedActivityUtils.java2
-rw-r--r--core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoUtils.java4
-rw-r--r--core/java/com/android/internal/pm/pkg/component/ParsedProviderUtils.java2
-rw-r--r--core/java/com/android/internal/pm/pkg/component/ParsedServiceUtils.java2
-rw-r--r--core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java3
-rw-r--r--core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java21
-rw-r--r--services/core/java/com/android/server/pm/parsing/PackageCacher.java31
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt22
12 files changed, 150 insertions, 30 deletions
diff --git a/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
index 032ac4283712..c182b4aeb264 100644
--- a/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
+++ b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
@@ -421,6 +421,8 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
@NonNull
private String[] mUsesStaticLibrariesSorted;
+ private Map<String, Boolean> mFeatureFlagState = new ArrayMap<>();
+
@NonNull
public static PackageImpl forParsing(@NonNull String packageName, @NonNull String baseCodePath,
@NonNull String codePath, @NonNull TypedArray manifestArray, boolean isCoreApp,
@@ -2819,6 +2821,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
queriesProviders = Collections.unmodifiableSet(queriesProviders);
mimeGroups = Collections.unmodifiableSet(mimeGroups);
mKnownActivityEmbeddingCerts = Collections.unmodifiableSet(mKnownActivityEmbeddingCerts);
+ mFeatureFlagState = Collections.unmodifiableMap(mFeatureFlagState);
}
@Override
@@ -3118,6 +3121,8 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
@Override
public void writeToParcel(Parcel dest, int flags) {
+ writeFeatureFlagState(dest);
+
sForBoolean.parcel(this.supportsSmallScreens, dest, flags);
sForBoolean.parcel(this.supportsNormalScreens, dest, flags);
sForBoolean.parcel(this.supportsLargeScreens, dest, flags);
@@ -3267,6 +3272,27 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
dest.writeBoolean(this.mAllowCrossUidActivitySwitchFromBelow);
}
+ private void writeFeatureFlagState(@NonNull Parcel dest) {
+ // Use a string array to encode flag state. One string per flag in the form `<flag>=<value>`
+ // where value is 0 (disabled), 1 (enabled) or ? (unknown flag or value).
+ int featureFlagCount = this.mFeatureFlagState.size();
+ String[] featureFlagStateAsArray = new String[featureFlagCount];
+ var entryIterator = this.mFeatureFlagState.entrySet().iterator();
+ for (int i = 0; i < featureFlagCount; i++) {
+ var entry = entryIterator.next();
+ Boolean flagValue = entry.getValue();
+ if (flagValue == null) {
+ featureFlagStateAsArray[i] = entry.getKey() + "=?";
+ } else if (flagValue.booleanValue()) {
+ featureFlagStateAsArray[i] = entry.getKey() + "=1";
+ } else {
+ featureFlagStateAsArray[i] = entry.getKey() + "=0";
+ }
+
+ }
+ dest.writeStringArray(featureFlagStateAsArray);
+ }
+
public PackageImpl(Parcel in) {
this(in, /* callback */ null);
}
@@ -3275,6 +3301,9 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
mCallback = callback;
// We use the boot classloader for all classes that we load.
final ClassLoader boot = Object.class.getClassLoader();
+
+ readFeatureFlagState(in);
+
this.supportsSmallScreens = sForBoolean.unparcel(in);
this.supportsNormalScreens = sForBoolean.unparcel(in);
this.supportsLargeScreens = sForBoolean.unparcel(in);
@@ -3440,6 +3469,27 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
// to mutate this instance before it's finalized.
}
+ private void readFeatureFlagState(@NonNull Parcel in) {
+ // See comment in writeFeatureFlagState() for encoding of flag state.
+ String[] featureFlagStateAsArray = in.createStringArray();
+ for (String s : featureFlagStateAsArray) {
+ int sepIndex = s.lastIndexOf('=');
+ if (sepIndex >= 0 && sepIndex == s.length() - 2) {
+ String flagPackageAndName = s.substring(0, sepIndex);
+ char c = s.charAt(sepIndex + 1);
+ Boolean flagValue = null;
+ if (c == '1') {
+ flagValue = Boolean.TRUE;
+ } else if (c == '0') {
+ flagValue = Boolean.FALSE;
+ } else if (c != '?') {
+ continue;
+ }
+ this.mFeatureFlagState.put(flagPackageAndName, flagValue);
+ }
+ }
+ }
+
@NonNull
public static final Creator<PackageImpl> CREATOR = new Creator<PackageImpl>() {
@Override
@@ -3660,6 +3710,18 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
return mBaseAppDataDeviceProtectedDirForSystemUser;
}
+ @Override
+ public PackageImpl addFeatureFlag(
+ @NonNull String flagPackageAndName,
+ @Nullable Boolean flagValue) {
+ mFeatureFlagState.put(flagPackageAndName, flagValue);
+ return this;
+ }
+
+ public Map<String, Boolean> getFeatureFlagState() {
+ return mFeatureFlagState;
+ }
+
/**
* Flags used for a internal bitset. These flags should never be persisted or exposed outside
* of this class. It is expected that PackageCacher explicitly clears itself whenever the
diff --git a/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java b/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java
index 8faaf9584e54..70b7953ed364 100644
--- a/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java
+++ b/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java
@@ -33,6 +33,7 @@ import android.util.Slog;
import android.util.Xml;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
import com.android.modules.utils.TypedXmlPullParser;
import org.xmlpull.v1.XmlPullParser;
@@ -199,7 +200,7 @@ public class AconfigFlags {
* @return the current value of the given Aconfig flag, or null if there is no such flag
*/
@Nullable
- private Boolean getFlagValue(@NonNull String flagPackageAndName) {
+ public Boolean getFlagValue(@NonNull String flagPackageAndName) {
Boolean value = mFlagValues.get(flagPackageAndName);
if (DEBUG) {
Slog.v(LOG_TAG, "Aconfig flag value for " + flagPackageAndName + " = " + value);
@@ -209,10 +210,13 @@ public class AconfigFlags {
/**
* Check if the element in {@code parser} should be skipped because of the feature flag.
+ * @param pkg The package being parsed
* @param parser XML parser object currently parsing an element
* @return true if the element is disabled because of its feature flag
*/
- public boolean skipCurrentElement(@NonNull XmlResourceParser parser) {
+ public boolean skipCurrentElement(
+ @NonNull ParsingPackage pkg,
+ @NonNull XmlResourceParser parser) {
if (!Flags.manifestFlagging()) {
return false;
}
@@ -227,18 +231,21 @@ public class AconfigFlags {
featureFlag = featureFlag.substring(1).strip();
}
final Boolean flagValue = getFlagValue(featureFlag);
+ boolean shouldSkip = false;
if (flagValue == null) {
Slog.w(LOG_TAG, "Skipping element " + parser.getName()
+ " due to unknown feature flag " + featureFlag);
- return true;
- }
- // Skip if flag==false && attr=="flag" OR flag==true && attr=="!flag" (negated)
- if (flagValue == negated) {
+ shouldSkip = true;
+ } else if (flagValue == negated) {
+ // Skip if flag==false && attr=="flag" OR flag==true && attr=="!flag" (negated)
Slog.i(LOG_TAG, "Skipping element " + parser.getName()
+ " behind feature flag " + featureFlag + " = " + flagValue);
- return true;
+ shouldSkip = true;
+ }
+ if (android.content.pm.Flags.includeFeatureFlagsInPackageCacher()) {
+ pkg.addFeatureFlag(featureFlag, flagValue);
}
- return false;
+ return shouldSkip;
}
/**
diff --git a/core/java/com/android/internal/pm/pkg/component/ComponentParseUtils.java b/core/java/com/android/internal/pm/pkg/component/ComponentParseUtils.java
index 8858f9492890..335dedd17f75 100644
--- a/core/java/com/android/internal/pm/pkg/component/ComponentParseUtils.java
+++ b/core/java/com/android/internal/pm/pkg/component/ComponentParseUtils.java
@@ -61,7 +61,7 @@ public class ComponentParseUtils {
if (type != XmlPullParser.START_TAG) {
continue;
}
- if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(parser)) {
+ if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(pkg, parser)) {
continue;
}
diff --git a/core/java/com/android/internal/pm/pkg/component/InstallConstraintsTagParser.java b/core/java/com/android/internal/pm/pkg/component/InstallConstraintsTagParser.java
index bb015812c225..5f48d16a7b87 100644
--- a/core/java/com/android/internal/pm/pkg/component/InstallConstraintsTagParser.java
+++ b/core/java/com/android/internal/pm/pkg/component/InstallConstraintsTagParser.java
@@ -54,7 +54,7 @@ public class InstallConstraintsTagParser {
return input.skip("install-constraints cannot be used by this package");
}
- ParseResult<Set<String>> prefixes = parseFingerprintPrefixes(input, res, parser);
+ ParseResult<Set<String>> prefixes = parseFingerprintPrefixes(input, pkg, res, parser);
if (prefixes.isSuccess()) {
if (validateFingerprintPrefixes(prefixes.getResult())) {
return input.success(pkg);
@@ -68,7 +68,7 @@ public class InstallConstraintsTagParser {
}
private static ParseResult<Set<String>> parseFingerprintPrefixes(
- ParseInput input, Resources res, XmlResourceParser parser)
+ ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)
throws XmlPullParserException, IOException {
Set<String> prefixes = new ArraySet<>();
int type;
@@ -81,7 +81,7 @@ public class InstallConstraintsTagParser {
}
return input.success(prefixes);
} else if (type == XmlPullParser.START_TAG) {
- if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(parser)) {
+ if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(pkg, parser)) {
continue;
}
if (parser.getName().equals(TAG_FINGERPRINT_PREFIX)) {
diff --git a/core/java/com/android/internal/pm/pkg/component/ParsedActivityUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedActivityUtils.java
index 55baa532b434..a35150b30846 100644
--- a/core/java/com/android/internal/pm/pkg/component/ParsedActivityUtils.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedActivityUtils.java
@@ -393,7 +393,7 @@ public class ParsedActivityUtils {
if (type != XmlPullParser.START_TAG) {
continue;
}
- if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(parser)) {
+ if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(pkg, parser)) {
continue;
}
diff --git a/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoUtils.java
index da48b23a2b81..39d7af6c4c4a 100644
--- a/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoUtils.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoUtils.java
@@ -99,7 +99,7 @@ public class ParsedIntentInfoUtils {
if (type != XmlPullParser.START_TAG) {
continue;
}
- if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(parser)) {
+ if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(pkg, parser)) {
continue;
}
@@ -200,7 +200,7 @@ public class ParsedIntentInfoUtils {
if (type != XmlPullParser.START_TAG) {
continue;
}
- if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(parser)) {
+ if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(pkg, parser)) {
continue;
}
diff --git a/core/java/com/android/internal/pm/pkg/component/ParsedProviderUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedProviderUtils.java
index 6af2a29822a2..3726b42e3e8e 100644
--- a/core/java/com/android/internal/pm/pkg/component/ParsedProviderUtils.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedProviderUtils.java
@@ -174,7 +174,7 @@ public class ParsedProviderUtils {
if (type != XmlPullParser.START_TAG) {
continue;
}
- if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(parser)) {
+ if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(pkg, parser)) {
continue;
}
diff --git a/core/java/com/android/internal/pm/pkg/component/ParsedServiceUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedServiceUtils.java
index c68ea2dc8516..9601813ce496 100644
--- a/core/java/com/android/internal/pm/pkg/component/ParsedServiceUtils.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedServiceUtils.java
@@ -138,7 +138,7 @@ public class ParsedServiceUtils {
if (type != XmlPullParser.START_TAG) {
continue;
}
- if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(parser)) {
+ if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(pkg, parser)) {
continue;
}
diff --git a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java
index 5d185af17d48..341beed14137 100644
--- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java
@@ -123,6 +123,9 @@ public interface ParsingPackage {
ParsingPackage addQueriesProvider(String authority);
+ /** Adds a feature flag (`android:featureFlag` attribute) encountered in the manifest. */
+ ParsingPackage addFeatureFlag(@NonNull String flagPackageAndName, @Nullable Boolean flagValue);
+
/** Sets a process name -> {@link ParsedProcess} map coming from the <processes> tag. */
ParsingPackage setProcesses(@NonNull Map<String, ParsedProcess> processes);
diff --git a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
index 787006eb214c..bb733f220068 100644
--- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
@@ -763,7 +763,7 @@ public class ParsingPackageUtils {
if (outerDepth + 1 < parser.getDepth() || type != XmlPullParser.START_TAG) {
continue;
}
- if (sAconfigFlags.skipCurrentElement(parser)) {
+ if (sAconfigFlags.skipCurrentElement(pkg, parser)) {
continue;
}
@@ -842,7 +842,7 @@ public class ParsingPackageUtils {
if (type != XmlPullParser.START_TAG) {
continue;
}
- if (sAconfigFlags.skipCurrentElement(parser)) {
+ if (sAconfigFlags.skipCurrentElement(pkg, parser)) {
continue;
}
@@ -988,7 +988,7 @@ public class ParsingPackageUtils {
if (type != XmlPullParser.START_TAG) {
continue;
}
- if (sAconfigFlags.skipCurrentElement(parser)) {
+ if (sAconfigFlags.skipCurrentElement(pkg, parser)) {
continue;
}
@@ -1610,7 +1610,7 @@ public class ParsingPackageUtils {
if (type != XmlPullParser.START_TAG) {
continue;
}
- if (sAconfigFlags.skipCurrentElement(parser)) {
+ if (sAconfigFlags.skipCurrentElement(pkg, parser)) {
continue;
}
@@ -1853,7 +1853,7 @@ public class ParsingPackageUtils {
if (type != XmlPullParser.START_TAG) {
continue;
}
- if (sAconfigFlags.skipCurrentElement(parser)) {
+ if (sAconfigFlags.skipCurrentElement(pkg, parser)) {
continue;
}
if (parser.getName().equals("intent")) {
@@ -2202,7 +2202,7 @@ public class ParsingPackageUtils {
if (type != XmlPullParser.START_TAG) {
continue;
}
- if (sAconfigFlags.skipCurrentElement(parser)) {
+ if (sAconfigFlags.skipCurrentElement(pkg, parser)) {
continue;
}
@@ -2620,7 +2620,7 @@ public class ParsingPackageUtils {
}
}
- ParseResult<String[]> certResult = parseAdditionalCertificates(input, res, parser);
+ ParseResult<String[]> certResult = parseAdditionalCertificates(input, pkg, res, parser);
if (certResult.isError()) {
return input.error(certResult);
}
@@ -2674,7 +2674,8 @@ public class ParsingPackageUtils {
// Fot apps targeting O-MR1 we require explicit enumeration of all certs.
String[] additionalCertSha256Digests = EmptyArray.STRING;
if (pkg.getTargetSdkVersion() >= Build.VERSION_CODES.O_MR1) {
- ParseResult<String[]> certResult = parseAdditionalCertificates(input, res, parser);
+ ParseResult<String[]> certResult =
+ parseAdditionalCertificates(input, pkg, res, parser);
if (certResult.isError()) {
return input.error(certResult);
}
@@ -2782,7 +2783,7 @@ public class ParsingPackageUtils {
}
private static ParseResult<String[]> parseAdditionalCertificates(ParseInput input,
- Resources resources, XmlResourceParser parser)
+ ParsingPackage pkg, Resources resources, XmlResourceParser parser)
throws XmlPullParserException, IOException {
String[] certSha256Digests = EmptyArray.STRING;
final int depth = parser.getDepth();
@@ -2793,7 +2794,7 @@ public class ParsingPackageUtils {
if (type != XmlPullParser.START_TAG) {
continue;
}
- if (sAconfigFlags.skipCurrentElement(parser)) {
+ if (sAconfigFlags.skipCurrentElement(pkg, parser)) {
continue;
}
diff --git a/services/core/java/com/android/server/pm/parsing/PackageCacher.java b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
index 2db454aa4c41..db65bf059319 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageCacher.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
@@ -33,6 +33,7 @@ import com.android.internal.pm.parsing.IPackageCacher;
import com.android.internal.pm.parsing.PackageParser2;
import com.android.internal.pm.parsing.pkg.PackageImpl;
import com.android.internal.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.pkg.component.AconfigFlags;
import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
import com.android.server.pm.ApexManager;
@@ -41,6 +42,8 @@ import libcore.io.IoUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
public class PackageCacher implements IPackageCacher {
@@ -57,6 +60,8 @@ public class PackageCacher implements IPackageCacher {
@Nullable
private final PackageParser2.Callback mCallback;
+ private static final AconfigFlags sAconfigFlags = ParsingPackageUtils.getAconfigFlags();
+
public PackageCacher(File cacheDir) {
this(cacheDir, null);
}
@@ -136,7 +141,7 @@ public class PackageCacher implements IPackageCacher {
* Given a {@code packageFile} and a {@code cacheFile} returns whether the
* cache file is up to date based on the mod-time of both files.
*/
- private static boolean isCacheUpToDate(File packageFile, File cacheFile) {
+ private static boolean isCacheFileUpToDate(File packageFile, File cacheFile) {
try {
// In case packageFile is located on one of /apex mount points it's mtime will always be
// 0. Instead, we can use mtime of the APEX file backing the corresponding mount point.
@@ -185,16 +190,36 @@ public class PackageCacher implements IPackageCacher {
try {
// If the cache is not up to date, return null.
- if (!isCacheUpToDate(packageFile, cacheFile)) {
+ if (!isCacheFileUpToDate(packageFile, cacheFile)) {
return null;
}
final byte[] bytes = IoUtils.readFileAsByteArray(cacheFile.getAbsolutePath());
- ParsedPackage parsed = fromCacheEntry(bytes);
+ final ParsedPackage parsed = fromCacheEntry(bytes);
if (!packageFile.getAbsolutePath().equals(parsed.getPath())) {
// Don't use this cache if the path doesn't match
return null;
}
+
+ if (!android.content.pm.Flags.includeFeatureFlagsInPackageCacher()) {
+ return parsed;
+ }
+
+ final Map<String, Boolean> featureFlagState =
+ ((PackageImpl) parsed).getFeatureFlagState();
+ if (!featureFlagState.isEmpty()) {
+ Slog.d(TAG, "Feature flags for package " + packageFile + ": " + featureFlagState);
+ for (var entry : featureFlagState.entrySet()) {
+ final String flagPackageAndName = entry.getKey();
+ if (!Objects.equals(sAconfigFlags.getFlagValue(flagPackageAndName),
+ entry.getValue())) {
+ Slog.i(TAG, "Feature flag " + flagPackageAndName + " changed for package "
+ + packageFile + "; cached result is invalid");
+ return null;
+ }
+ }
+ }
+
return parsed;
} catch (Throwable e) {
Slog.w(TAG, "Error reading package cache: ", e);
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
index d4b57f191ecd..f439770bd648 100644
--- 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
@@ -26,6 +26,8 @@ import android.content.pm.SigningDetails
import android.net.Uri
import android.os.Bundle
import android.os.Parcelable
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
import android.util.ArraySet
import android.util.SparseArray
import android.util.SparseIntArray
@@ -47,14 +49,19 @@ import com.android.internal.pm.pkg.component.ParsedUsesPermissionImpl
import com.android.server.pm.pkg.AndroidPackage
import com.android.server.testutils.mockThrowOnUnmocked
import com.android.server.testutils.whenever
+import org.junit.Rule
import java.security.KeyPairGenerator
import java.security.PublicKey
import java.util.UUID
import kotlin.contracts.ExperimentalContracts
@ExperimentalContracts
+@EnableFlags(android.content.pm.Flags.FLAG_INCLUDE_FEATURE_FLAGS_IN_PACKAGE_CACHER)
class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, PackageImpl::class) {
+ @get:Rule
+ val setFlagsRule: SetFlagsRule = SetFlagsRule()
+
companion object {
private val TEST_UUID = UUID.fromString("57554103-df3e-4475-ae7a-8feba49353ac")
}
@@ -93,6 +100,8 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
"getUsesOptionalLibrariesSorted",
"getUsesSdkLibrariesSorted",
"getUsesStaticLibrariesSorted",
+ "readFeatureFlagState",
+ "writeFeatureFlagState",
// Tested through setting minor/major manually
"setLongVersionCode",
"getLongVersionCode",
@@ -149,6 +158,10 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
"isSystem",
"isSystemExt",
"isVendor",
+
+ // Tested through addFeatureFlag
+ "addFeatureFlag",
+ "getFeatureFlagState",
)
override val baseParams = listOf(
@@ -613,6 +626,9 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
.setSplitClassLoaderName(1, "testSplitClassLoaderNameOne")
.addUsesSdkLibrary("testSdk", 2L, arrayOf("testCertDigest1"), true)
.addUsesStaticLibrary("testStatic", 3L, arrayOf("testCertDigest2"))
+ .addFeatureFlag("testFlag1", null)
+ .addFeatureFlag("testFlag2", true)
+ .addFeatureFlag("testFlag3", false)
override fun finalizeObject(parcelable: Parcelable) {
(parcelable as PackageImpl).hideAsParsed().hideAsFinal()
@@ -673,6 +689,12 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
.containsExactly("testCertDigest2")
expect.that(after.storageUuid).isEqualTo(TEST_UUID)
+
+ expect.that(after.featureFlagState).containsExactlyEntriesIn(mapOf(
+ "testFlag1" to null,
+ "testFlag2" to true,
+ "testFlag3" to false,
+ ))
}
private fun testKey() = KeyPairGenerator.getInstance("RSA")