Merge changes I7850ea5b,I9759b7dd
* changes: Prepare PackageState/PackageUserState for API Add runtime unmodifiable check for PackageState
This commit is contained in:
@@ -329,17 +329,15 @@ class ApexPackageInfo {
|
||||
ApexInfo ai = parsingApexInfo.get(parseResult.scanFile);
|
||||
|
||||
if (throwable == null) {
|
||||
// Calling hideAsFinal to assign derived fields for the app info flags.
|
||||
parseResult.parsedPackage.hideAsFinal();
|
||||
|
||||
// TODO: When ENABLE_FEATURE_SCAN_APEX is finalized, remove this and the entire
|
||||
// calling path code
|
||||
ScanPackageUtils.applyPolicy(parseResult.parsedPackage,
|
||||
PackageManagerService.SCAN_AS_SYSTEM,
|
||||
mPackageManager == null ? null : mPackageManager.getPlatformPackage(),
|
||||
false);
|
||||
results.add(new ApexManager.ScanResult(
|
||||
ai, parseResult.parsedPackage, parseResult.parsedPackage.getPackageName()));
|
||||
// Calling hideAsFinal to assign derived fields for the app info flags.
|
||||
AndroidPackage finalPkg = parseResult.parsedPackage.hideAsFinal();
|
||||
results.add(new ApexManager.ScanResult(ai, finalPkg, finalPkg.getPackageName()));
|
||||
} else if (throwable instanceof PackageManagerException) {
|
||||
throw new IllegalStateException("Unable to parse: " + ai.modulePath, throwable);
|
||||
} else {
|
||||
|
||||
@@ -3451,8 +3451,8 @@ final class InstallPackageHelper {
|
||||
|
||||
if (throwable == null) {
|
||||
try {
|
||||
AndroidPackage pkg = addForInitLI(
|
||||
parseResult.parsedPackage, newParseFlags, newScanFlags, null);
|
||||
addForInitLI(parseResult.parsedPackage, newParseFlags, newScanFlags, null);
|
||||
AndroidPackage pkg = parseResult.parsedPackage.hideAsFinal();
|
||||
if (ai.isFactory && !ai.isActive) {
|
||||
disableSystemPackageLPw(pkg);
|
||||
}
|
||||
|
||||
@@ -210,6 +210,7 @@ import com.android.server.pm.permission.LegacyPermissionManagerService;
|
||||
import com.android.server.pm.permission.PermissionManagerService;
|
||||
import com.android.server.pm.permission.PermissionManagerServiceInternal;
|
||||
import com.android.server.pm.pkg.AndroidPackage;
|
||||
import com.android.server.pm.pkg.PackageState;
|
||||
import com.android.server.pm.pkg.PackageStateInternal;
|
||||
import com.android.server.pm.pkg.PackageUserState;
|
||||
import com.android.server.pm.pkg.PackageUserStateInternal;
|
||||
@@ -222,6 +223,7 @@ import com.android.server.pm.pkg.mutate.PackageUserStateWrite;
|
||||
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
|
||||
import com.android.server.pm.resolution.ComponentResolver;
|
||||
import com.android.server.pm.resolution.ComponentResolverApi;
|
||||
import com.android.server.pm.snapshot.PackageDataSnapshot;
|
||||
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
|
||||
import com.android.server.pm.verify.domain.DomainVerificationService;
|
||||
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
|
||||
|
||||
@@ -1207,12 +1207,24 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
|
||||
return pkgState.getUsesLibraryInfos();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public PackageSetting addUsesLibraryInfo(@NonNull SharedLibraryInfo value) {
|
||||
pkgState.addUsesLibraryInfo(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public List<String> getUsesLibraryFiles() {
|
||||
return pkgState.getUsesLibraryFiles();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public PackageSetting addUsesLibraryFile(String value) {
|
||||
pkgState.addUsesLibraryFile(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHiddenUntilInstalled() {
|
||||
return pkgState.isHiddenUntilInstalled();
|
||||
|
||||
@@ -385,16 +385,16 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
|
||||
@Nullable
|
||||
@DataClass.ParcelWith(Parcelling.BuiltIn.ForBoolean.class)
|
||||
private Boolean requestRawExternalStorageAccess;
|
||||
// TODO(chiuwinson): Non-null
|
||||
@Nullable
|
||||
private ArraySet<String> mimeGroups;
|
||||
@NonNull
|
||||
@DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringSet.class)
|
||||
private Set<String> mimeGroups = emptySet();
|
||||
// Usually there's code to set enabled to true during parsing, but it's possible to install
|
||||
// an APK targeting <R that doesn't contain an <application> tag. That code would be skipped
|
||||
// and never assign this, so initialize this to true for those cases.
|
||||
private long mBooleans = Booleans.ENABLED;
|
||||
private long mBooleans2;
|
||||
@Nullable
|
||||
private Set<String> mKnownActivityEmbeddingCerts;
|
||||
@NonNull
|
||||
private Set<String> mKnownActivityEmbeddingCerts = emptySet();
|
||||
// Derived fields
|
||||
private long mLongVersionCode;
|
||||
private int mLocaleConfigRes;
|
||||
@@ -549,7 +549,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
|
||||
if (mimeGroups != null && mimeGroups.size() > 500) {
|
||||
throw new IllegalStateException("Max limit on number of MIME Groups reached");
|
||||
}
|
||||
mimeGroups = ArrayUtils.add(mimeGroups, filter.getMimeGroup(groupIndex));
|
||||
mimeGroups = CollectionUtils.add(mimeGroups, filter.getMimeGroup(groupIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -935,8 +935,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
|
||||
@NonNull
|
||||
@Override
|
||||
public Set<String> getKnownActivityEmbeddingCerts() {
|
||||
return mKnownActivityEmbeddingCerts == null ? Collections.emptySet()
|
||||
: mKnownActivityEmbeddingCerts;
|
||||
return mKnownActivityEmbeddingCerts;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1949,8 +1948,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParsingPackage setKnownActivityEmbeddingCerts(
|
||||
@Nullable Set<String> knownEmbeddingCerts) {
|
||||
public ParsingPackage setKnownActivityEmbeddingCerts(@NonNull Set<String> knownEmbeddingCerts) {
|
||||
mKnownActivityEmbeddingCerts = knownEmbeddingCerts;
|
||||
return this;
|
||||
}
|
||||
@@ -2516,7 +2514,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
|
||||
appInfo.setVersionCode(mLongVersionCode);
|
||||
appInfo.setAppClassNamesByProcess(buildAppClassNamesByProcess());
|
||||
appInfo.setLocaleConfigRes(mLocaleConfigRes);
|
||||
if (mKnownActivityEmbeddingCerts != null) {
|
||||
if (!mKnownActivityEmbeddingCerts.isEmpty()) {
|
||||
appInfo.setKnownActivityEmbeddingCerts(mKnownActivityEmbeddingCerts);
|
||||
}
|
||||
|
||||
@@ -2593,11 +2591,11 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
|
||||
|
||||
@Override
|
||||
public AndroidPackageInternal hideAsFinal() {
|
||||
// TODO(b/135203078): Lock as immutable
|
||||
if (mStorageUuid == null) {
|
||||
assignDerivedFields();
|
||||
}
|
||||
assignDerivedFields2();
|
||||
makeImmutable();
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -2613,6 +2611,48 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
|
||||
baseAppDataDir + Environment.DIR_USER_DE + systemUserSuffix);
|
||||
}
|
||||
|
||||
private void makeImmutable() {
|
||||
usesLibraries = Collections.unmodifiableList(usesLibraries);
|
||||
usesOptionalLibraries = Collections.unmodifiableList(usesOptionalLibraries);
|
||||
usesNativeLibraries = Collections.unmodifiableList(usesNativeLibraries);
|
||||
usesOptionalNativeLibraries = Collections.unmodifiableList(usesOptionalNativeLibraries);
|
||||
originalPackages = Collections.unmodifiableList(originalPackages);
|
||||
adoptPermissions = Collections.unmodifiableList(adoptPermissions);
|
||||
requestedPermissions = Collections.unmodifiableList(requestedPermissions);
|
||||
protectedBroadcasts = Collections.unmodifiableList(protectedBroadcasts);
|
||||
apexSystemServices = Collections.unmodifiableList(apexSystemServices);
|
||||
|
||||
activities = Collections.unmodifiableList(activities);
|
||||
receivers = Collections.unmodifiableList(receivers);
|
||||
services = Collections.unmodifiableList(services);
|
||||
providers = Collections.unmodifiableList(providers);
|
||||
permissions = Collections.unmodifiableList(permissions);
|
||||
permissionGroups = Collections.unmodifiableList(permissionGroups);
|
||||
instrumentations = Collections.unmodifiableList(instrumentations);
|
||||
|
||||
overlayables = Collections.unmodifiableMap(overlayables);
|
||||
libraryNames = Collections.unmodifiableList(libraryNames);
|
||||
usesStaticLibraries = Collections.unmodifiableList(usesStaticLibraries);
|
||||
usesSdkLibraries = Collections.unmodifiableList(usesSdkLibraries);
|
||||
configPreferences = Collections.unmodifiableList(configPreferences);
|
||||
reqFeatures = Collections.unmodifiableList(reqFeatures);
|
||||
featureGroups = Collections.unmodifiableList(featureGroups);
|
||||
usesPermissions = Collections.unmodifiableList(usesPermissions);
|
||||
usesSdkLibraries = Collections.unmodifiableList(usesSdkLibraries);
|
||||
implicitPermissions = Collections.unmodifiableList(implicitPermissions);
|
||||
upgradeKeySets = Collections.unmodifiableSet(upgradeKeySets);
|
||||
keySetMapping = Collections.unmodifiableMap(keySetMapping);
|
||||
attributions = Collections.unmodifiableList(attributions);
|
||||
preferredActivityFilters = Collections.unmodifiableList(preferredActivityFilters);
|
||||
processes = Collections.unmodifiableMap(processes);
|
||||
mProperties = Collections.unmodifiableMap(mProperties);
|
||||
queriesIntents = Collections.unmodifiableList(queriesIntents);
|
||||
queriesPackages = Collections.unmodifiableList(queriesPackages);
|
||||
queriesProviders = Collections.unmodifiableSet(queriesProviders);
|
||||
mimeGroups = Collections.unmodifiableSet(mimeGroups);
|
||||
mKnownActivityEmbeddingCerts = Collections.unmodifiableSet(mKnownActivityEmbeddingCerts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLongVersionCode() {
|
||||
return PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode);
|
||||
@@ -3041,7 +3081,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
|
||||
dest.writeIntArray(this.splitRevisionCodes);
|
||||
sForBoolean.parcel(this.resizeableActivity, dest, flags);
|
||||
dest.writeInt(this.autoRevokePermissions);
|
||||
dest.writeArraySet(this.mimeGroups);
|
||||
sForInternedStringSet.parcel(this.mimeGroups, dest, flags);
|
||||
dest.writeInt(this.gwpAsanMode);
|
||||
dest.writeSparseIntArray(this.minExtensionVersions);
|
||||
dest.writeMap(this.mProperties);
|
||||
@@ -3201,7 +3241,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
|
||||
this.resizeableActivity = sForBoolean.unparcel(in);
|
||||
|
||||
this.autoRevokePermissions = in.readInt();
|
||||
this.mimeGroups = (ArraySet<String>) in.readArraySet(boot);
|
||||
this.mimeGroups = sForInternedStringSet.unparcel(in);
|
||||
this.gwpAsanMode = in.readInt();
|
||||
this.minExtensionVersions = in.readSparseIntArray();
|
||||
this.mProperties = in.readHashMap(boot);
|
||||
@@ -3224,6 +3264,9 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
|
||||
|
||||
assignDerivedFields();
|
||||
assignDerivedFields2();
|
||||
|
||||
// Do not call makeImmutable here as cached parsing will need
|
||||
// to mutate this instance before it's finalized.
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,37 +21,20 @@ import android.annotation.Nullable;
|
||||
import android.annotation.Size;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.SharedLibraryInfo;
|
||||
import android.content.pm.SigningInfo;
|
||||
import android.processor.immutability.Immutable;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.server.pm.PackageSetting;
|
||||
import com.android.server.pm.Settings;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The API surface for a {@link PackageSetting}. Methods are expected to return immutable objects.
|
||||
* This may mean copying data on each invocation until related classes are refactored to be
|
||||
* immutable.
|
||||
* <p>
|
||||
* Note that until immutability or read-only caching is enabled, {@link PackageSetting} cannot be
|
||||
* returned directly, so {@link PackageStateImpl} is used to temporarily copy the data. This is a
|
||||
* relatively expensive operation since it has to create an object for every package, but it's much
|
||||
* lighter than the alternative of generating {@link PackageInfo} objects.
|
||||
* <p>
|
||||
* TODO: Documentation TODO: Currently missing, should be exposed as API?
|
||||
* <ul>
|
||||
* <li>keySetData</li>
|
||||
* <li>installSource</li>
|
||||
* <li>incrementalStates</li>
|
||||
* </ul>
|
||||
* The exposed system server process API for package data, shared with the internal
|
||||
* PackageManagerService implementation. All returned data is guaranteed immutable.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@@ -59,28 +42,61 @@ import java.util.Set;
|
||||
@Immutable
|
||||
public interface PackageState {
|
||||
|
||||
// Methods below this comment are not yet exposed as API
|
||||
|
||||
/*
|
||||
* Until immutability or read-only caching is enabled, {@link PackageSetting} cannot be
|
||||
* returned directly, so {@link PackageStateImpl} is used to temporarily copy the data.
|
||||
* This is a relatively expensive operation since it has to create an object for every package,
|
||||
* but it's much lighter than the alternative of generating {@link PackageInfo} objects.
|
||||
* <p>
|
||||
* TODO: Documentation
|
||||
* TODO: Currently missing, should be exposed as API?
|
||||
* - keySetData
|
||||
* - installSource
|
||||
* - incrementalStates
|
||||
*/
|
||||
|
||||
// Non-doc comment invisible to API consumers:
|
||||
// Guidelines:
|
||||
// - All return values should prefer non-null, immutable interfaces with only exposed getters
|
||||
// - Unless null itself communicates something important
|
||||
// - If the type is a Java collection type, it must be wrapped with unmodifiable
|
||||
// - All type names must be non-suffixed, with any internal types being refactored to suffix
|
||||
// with _Internal as necessary
|
||||
// - No exposure of raw values that are overridden during parsing, such as CPU ABI
|
||||
// - Mirroring another available system or public API is not enough justification to violate
|
||||
// these guidelines
|
||||
|
||||
/**
|
||||
* This can be null whenever a physical APK on device is missing. This can be the result of
|
||||
* removing an external storage device where the APK resides.
|
||||
* <p>
|
||||
* This will result in the system reading the {@link PackageSetting} from disk, but without
|
||||
* being able to parse the base APK's AndroidManifest.xml to read all of its metadata. The data
|
||||
* that is written and read in {@link Settings} includes a minimal set of metadata needed to
|
||||
* perform other checks in the system.
|
||||
* <p>
|
||||
* <p/>
|
||||
* This will result in the system reading the state from disk, but without being able to parse
|
||||
* the base APK's AndroidManifest.xml to read all of its metadata. The only available data that
|
||||
* is written and read is the minimal set required to perform other checks in the system.
|
||||
* <p/>
|
||||
* This is important in order to enforce uniqueness within the system, as the package, even if
|
||||
* on a removed storage device, is still considered installed. Another package of the same
|
||||
* application ID or declaring the same permissions or similar cannot be installed.
|
||||
* <p>
|
||||
* <p/>
|
||||
* Re-attaching the storage device to make the APK available should allow the user to use the
|
||||
* app once the device reboots or otherwise re-scans it.
|
||||
* <p/>
|
||||
* This can also occur in an device OTA situation where the package is no longer parseable on
|
||||
* an updated SDK version, causing it to be rejectd, but the state associated with it retained,
|
||||
* similarly to if the package had been uninstalled with the --keep-data option.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Nullable
|
||||
AndroidPackage getAndroidPackage();
|
||||
|
||||
/**
|
||||
* The non-user-specific UID, or the UID if the user ID is
|
||||
* {@link android.os.UserHandle#USER_SYSTEM}.
|
||||
* {@link android.os.UserHandle#SYSTEM}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
int getAppId();
|
||||
|
||||
@@ -89,6 +105,7 @@ public interface PackageState {
|
||||
* applied if the application itself does not declare a category.
|
||||
*
|
||||
* @see AndroidPackage#getCategory()
|
||||
* @hide
|
||||
*/
|
||||
int getCategoryOverride();
|
||||
|
||||
@@ -96,6 +113,8 @@ public interface PackageState {
|
||||
* The install time CPU override, if any. This value is written at install time
|
||||
* and doesn't change during the life of an install. If non-null,
|
||||
* {@link #getPrimaryCpuAbi()} will also contain the same value.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Nullable
|
||||
String getCpuAbiOverride();
|
||||
@@ -103,6 +122,8 @@ public interface PackageState {
|
||||
/**
|
||||
* In epoch milliseconds. The last modified time of the file directory which houses the app
|
||||
* APKs. Only updated on package update; does not track realtime modifications.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
long getLastModifiedTime();
|
||||
|
||||
@@ -110,6 +131,8 @@ public interface PackageState {
|
||||
* An aggregation across the framework of the last time an app was used for a particular reason.
|
||||
* Keys are indexes into the array represented by {@link PackageManager.NotifyReason}, values
|
||||
* are in epoch milliseconds.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Immutable.Ignore
|
||||
@Size(PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT)
|
||||
@@ -119,12 +142,15 @@ public interface PackageState {
|
||||
/**
|
||||
* In epoch milliseconds. The timestamp of the last time the package on device went through
|
||||
* an update package installation.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
long getLastUpdateTime();
|
||||
|
||||
/**
|
||||
* Cached here in case the physical code directory on device is unmounted.
|
||||
* @see AndroidPackage#getLongVersionCode()
|
||||
* @hide
|
||||
*/
|
||||
long getVersionCode();
|
||||
|
||||
@@ -132,37 +158,43 @@ public interface PackageState {
|
||||
* Maps mime group name to the set of Mime types in a group. Mime groups declared by app are
|
||||
* populated with empty sets at construction. Mime groups can not be created/removed at runtime,
|
||||
* thus keys in this map should not change.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
Map<String, Set<String>> getMimeGroups();
|
||||
|
||||
/**
|
||||
* @see AndroidPackage#getPackageName()
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
String getPackageName();
|
||||
|
||||
/**
|
||||
* TODO: Rename this to getCodePath
|
||||
* @see AndroidPackage#getPath()
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
File getPath();
|
||||
|
||||
/**
|
||||
* @see ApplicationInfo#primaryCpuAbi
|
||||
* @hide
|
||||
*/
|
||||
@Nullable
|
||||
String getPrimaryCpuAbi();
|
||||
|
||||
/**
|
||||
* @see ApplicationInfo#secondaryCpuAbi
|
||||
* @hide
|
||||
*/
|
||||
@Nullable
|
||||
String getSecondaryCpuAbi();
|
||||
|
||||
/**
|
||||
* Whether the package shares the same user ID as other packages
|
||||
* @hide
|
||||
*/
|
||||
boolean hasSharedUser();
|
||||
|
||||
@@ -172,13 +204,16 @@ public interface PackageState {
|
||||
*
|
||||
* @return the app ID of the shared user that this package is a part of, or -1 if it's not part
|
||||
* of a shared user.
|
||||
* @hide
|
||||
*/
|
||||
int getSharedUserAppId();
|
||||
|
||||
/** @hide */
|
||||
@Immutable.Ignore
|
||||
@NonNull
|
||||
SigningInfo getSigningInfo();
|
||||
|
||||
/** @hide */
|
||||
@Immutable.Ignore
|
||||
@NonNull
|
||||
SparseArray<? extends PackageUserState> getUserStates();
|
||||
@@ -186,6 +221,7 @@ public interface PackageState {
|
||||
/**
|
||||
* @return the result of {@link #getUserStates()}.get(userId) or
|
||||
* {@link PackageUserState#DEFAULT} if the state doesn't exist.
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
default PackageUserState getUserStateOrDefault(@UserIdInt int userId) {
|
||||
@@ -197,12 +233,14 @@ public interface PackageState {
|
||||
* The actual files resolved for each shared library.
|
||||
*
|
||||
* @see R.styleable#AndroidManifestUsesLibrary
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
List<String> getUsesLibraryFiles();
|
||||
|
||||
/**
|
||||
* @see R.styleable#AndroidManifestUsesLibrary
|
||||
* @hide
|
||||
*/
|
||||
@Immutable.Ignore
|
||||
@NonNull
|
||||
@@ -210,6 +248,7 @@ public interface PackageState {
|
||||
|
||||
/**
|
||||
* @see R.styleable#AndroidManifestUsesSdkLibrary
|
||||
* @hide
|
||||
*/
|
||||
@Immutable.Ignore
|
||||
@NonNull
|
||||
@@ -217,6 +256,7 @@ public interface PackageState {
|
||||
|
||||
/**
|
||||
* @see R.styleable#AndroidManifestUsesSdkLibrary_versionMajor
|
||||
* @hide
|
||||
*/
|
||||
@Immutable.Ignore
|
||||
@NonNull
|
||||
@@ -224,6 +264,7 @@ public interface PackageState {
|
||||
|
||||
/**
|
||||
* @see R.styleable#AndroidManifestUsesStaticLibrary
|
||||
* @hide
|
||||
*/
|
||||
@Immutable.Ignore
|
||||
@NonNull
|
||||
@@ -231,6 +272,7 @@ public interface PackageState {
|
||||
|
||||
/**
|
||||
* @see R.styleable#AndroidManifestUsesStaticLibrary_version
|
||||
* @hide
|
||||
*/
|
||||
@Immutable.Ignore
|
||||
@NonNull
|
||||
@@ -238,18 +280,22 @@ public interface PackageState {
|
||||
|
||||
/**
|
||||
* @see AndroidPackage#getVolumeUuid()
|
||||
* @hide
|
||||
*/
|
||||
@Nullable
|
||||
String getVolumeUuid();
|
||||
|
||||
/**
|
||||
* @see AndroidPackage#isExternalStorage()
|
||||
* @hide
|
||||
*/
|
||||
boolean isExternalStorage();
|
||||
|
||||
/**
|
||||
* Whether a package was installed --force-queryable such that it is always queryable by any
|
||||
* package, regardless of their manifest content.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
boolean isForceQueryableOverride();
|
||||
|
||||
@@ -258,67 +304,82 @@ public interface PackageState {
|
||||
*
|
||||
* @see PackageManager#MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS
|
||||
* @see PackageManager#setSystemAppState
|
||||
* @hide
|
||||
*/
|
||||
boolean isHiddenUntilInstalled();
|
||||
|
||||
/**
|
||||
* @see com.android.server.pm.permission.UserPermissionState
|
||||
* @hide
|
||||
*/
|
||||
boolean isInstallPermissionsFixed();
|
||||
|
||||
/**
|
||||
* @see AndroidPackage#isOdm()
|
||||
* @hide
|
||||
*/
|
||||
boolean isOdm();
|
||||
|
||||
/**
|
||||
* @see AndroidPackage#isOem()
|
||||
* @hide
|
||||
*/
|
||||
boolean isOem();
|
||||
|
||||
/**
|
||||
* @see AndroidPackage#isPrivileged()
|
||||
* @hide
|
||||
*/
|
||||
boolean isPrivileged();
|
||||
|
||||
/**
|
||||
* @see AndroidPackage#isProduct()
|
||||
* @hide
|
||||
*/
|
||||
boolean isProduct();
|
||||
|
||||
/**
|
||||
* @see ApplicationInfo#PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER
|
||||
* @hide
|
||||
*/
|
||||
boolean isRequiredForSystemUser();
|
||||
|
||||
/**
|
||||
* @see AndroidPackage#isSystem()
|
||||
* @hide
|
||||
*/
|
||||
boolean isSystem();
|
||||
|
||||
/**
|
||||
* @see AndroidPackage#isSystemExt()
|
||||
* @hide
|
||||
*/
|
||||
boolean isSystemExt();
|
||||
|
||||
/**
|
||||
* Whether or not an update is available. Ostensibly only for instant apps.
|
||||
* @hide
|
||||
*/
|
||||
boolean isUpdateAvailable();
|
||||
|
||||
/**
|
||||
* Whether this app is on the /data partition having been upgraded from a preinstalled app on a
|
||||
* system partition.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
boolean isUpdatedSystemApp();
|
||||
|
||||
/**
|
||||
* Whether this app is packaged in an updated apex.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
boolean isApkInUpdatedApex();
|
||||
|
||||
/**
|
||||
* @see AndroidPackage#isVendor()
|
||||
* @hide
|
||||
*/
|
||||
boolean isVendor();
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.android.server.pm.PackageSetting;
|
||||
import com.android.server.pm.Settings;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -170,7 +171,7 @@ public class PackageStateImpl implements PackageState {
|
||||
mLastModifiedTime = pkgState.getLastModifiedTime();
|
||||
mLastUpdateTime = pkgState.getLastUpdateTime();
|
||||
mLongVersionCode = pkgState.getVersionCode();
|
||||
mMimeGroups = pkgState.getMimeGroups();
|
||||
mMimeGroups = Collections.unmodifiableMap(pkgState.getMimeGroups());
|
||||
mPath = pkgState.getPath();
|
||||
mPrimaryCpuAbi = pkgState.getPrimaryCpuAbi();
|
||||
mSecondaryCpuAbi = pkgState.getSecondaryCpuAbi();
|
||||
@@ -180,8 +181,8 @@ public class PackageStateImpl implements PackageState {
|
||||
mUsesSdkLibrariesVersionsMajor = pkgState.getUsesSdkLibrariesVersionsMajor();
|
||||
mUsesStaticLibraries = pkgState.getUsesStaticLibraries();
|
||||
mUsesStaticLibrariesVersions = pkgState.getUsesStaticLibrariesVersions();
|
||||
mUsesLibraryInfos = pkgState.getUsesLibraryInfos();
|
||||
mUsesLibraryFiles = pkgState.getUsesLibraryFiles();
|
||||
mUsesLibraryInfos = Collections.unmodifiableList(pkgState.getUsesLibraryInfos());
|
||||
mUsesLibraryFiles = Collections.unmodifiableList(pkgState.getUsesLibraryFiles());
|
||||
setBoolean(Booleans.FORCE_QUERYABLE_OVERRIDE, pkgState.isForceQueryableOverride());
|
||||
setBoolean(Booleans.HIDDEN_UNTIL_INSTALLED, pkgState.isHiddenUntilInstalled());
|
||||
setBoolean(Booleans.INSTALL_PERMISSIONS_FIXED, pkgState.isInstallPermissionsFixed());
|
||||
@@ -195,8 +196,8 @@ public class PackageStateImpl implements PackageState {
|
||||
int userStatesSize = userStates.size();
|
||||
mUserStates = new SparseArray<>(userStatesSize);
|
||||
for (int index = 0; index < userStatesSize; index++) {
|
||||
mUserStates.put(mUserStates.keyAt(index),
|
||||
UserStateImpl.copy(mUserStates.valueAt(index)));
|
||||
mUserStates.put(userStates.keyAt(index),
|
||||
UserStateImpl.copy(userStates.valueAt(index)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Exposes internal types for internal usage of {@link PackageState}.
|
||||
* @hide
|
||||
*/
|
||||
public interface PackageStateInternal extends PackageState {
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.annotation.Nullable;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.SharedLibraryInfo;
|
||||
|
||||
import com.android.internal.util.CollectionUtils;
|
||||
import com.android.internal.util.DataClass;
|
||||
import com.android.server.pm.PackageSetting;
|
||||
|
||||
@@ -38,6 +39,7 @@ import java.util.stream.Collectors;
|
||||
*
|
||||
* These fields are also not copied into any cloned PackageSetting, to preserve the old behavior
|
||||
* where they would be lost implicitly by re-generating the package object.
|
||||
* @hide
|
||||
*/
|
||||
@DataClass(genSetters = true, genConstructor = false, genBuilder = false)
|
||||
@DataClass.Suppress({"setLastPackageUsageTimeInMills", "setPackageSetting"})
|
||||
@@ -69,6 +71,18 @@ public class PackageStateUnserialized {
|
||||
mPackageSetting = packageSetting;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public PackageStateUnserialized addUsesLibraryInfo(@NonNull SharedLibraryInfo value) {
|
||||
usesLibraryInfos = CollectionUtils.add(usesLibraryInfos, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public PackageStateUnserialized addUsesLibraryFile(@NonNull String value) {
|
||||
usesLibraryFiles = CollectionUtils.add(usesLibraryFiles, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
private long[] lazyInitLastPackageUsageTimeInMills() {
|
||||
return new long[PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT];
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.util.SparseArray;
|
||||
|
||||
import com.android.server.pm.pkg.component.ParsedMainComponent;
|
||||
|
||||
/** @hide */
|
||||
public class PackageStateUtils {
|
||||
|
||||
public static boolean isMatch(PackageState packageState, long flags) {
|
||||
|
||||
@@ -26,9 +26,8 @@ import android.util.ArraySet;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* The API surface for {@link PackageUserStateInternal}, for use by in-process mainline consumers.
|
||||
*
|
||||
* The parent of this class is {@link PackageState}, which handles non-user state, exposing this
|
||||
* interface for per-user state.
|
||||
*
|
||||
@@ -36,123 +35,164 @@ import java.util.Map;
|
||||
*/
|
||||
// TODO(b/173807334): Expose API
|
||||
//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
|
||||
@Immutable.Ignore(reason = "Exposed through PackageState pending refactor")
|
||||
@Immutable
|
||||
public interface PackageUserState {
|
||||
|
||||
/** @hide */
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
PackageUserState DEFAULT = PackageUserStateInternal.DEFAULT;
|
||||
|
||||
/**
|
||||
* Combination of {@link #getOverlayPaths()} and {@link #getSharedLibraryOverlayPaths()}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Immutable.Ignore
|
||||
@Nullable
|
||||
OverlayPaths getAllOverlayPaths();
|
||||
|
||||
/**
|
||||
* Credential encrypted /data partition inode.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
long getCeDataInode();
|
||||
|
||||
/**
|
||||
* Fully qualified class names of components explicitly disabled.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
ArraySet<String> getDisabledComponents();
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@PackageManager.DistractionRestriction
|
||||
int getDistractionFlags();
|
||||
|
||||
/**
|
||||
* Fully qualified class names of components explicitly enabled.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
ArraySet<String> getEnabledComponents();
|
||||
|
||||
/**
|
||||
* Retrieve the effective enabled state of the package itself.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@PackageManager.EnabledState
|
||||
int getEnabledState();
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @see PackageManager#setHarmfulAppWarning(String, CharSequence)
|
||||
*/
|
||||
@Nullable
|
||||
String getHarmfulAppWarning();
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@PackageManager.InstallReason
|
||||
int getInstallReason();
|
||||
|
||||
/**
|
||||
* Tracks the last calling package to set a specific enabled state for the package.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Nullable
|
||||
String getLastDisableAppCaller();
|
||||
|
||||
/** @hide */
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@Immutable.Ignore
|
||||
@Nullable
|
||||
OverlayPaths getOverlayPaths();
|
||||
|
||||
/** @hide */
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@Immutable.Ignore
|
||||
@NonNull
|
||||
Map<String, OverlayPaths> getSharedLibraryOverlayPaths();
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@PackageManager.UninstallReason
|
||||
int getUninstallReason();
|
||||
|
||||
/**
|
||||
* @return whether the given fully qualified class name is explicitly enabled
|
||||
* @hide
|
||||
*/
|
||||
boolean isComponentEnabled(@NonNull String componentName);
|
||||
|
||||
/**
|
||||
* @return {@link #isComponentEnabled(String)} but for explicitly disabled
|
||||
* @hide
|
||||
*/
|
||||
boolean isComponentDisabled(@NonNull String componentName);
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @see PackageManager#setApplicationHiddenSettingAsUser(String, boolean, UserHandle)
|
||||
*/
|
||||
boolean isHidden();
|
||||
|
||||
/**
|
||||
* @return whether the package is marked as installed for all users
|
||||
* @hide
|
||||
*/
|
||||
boolean isInstalled();
|
||||
|
||||
/**
|
||||
* @return whether the package is marked as an ephemeral app, which restricts permissions,
|
||||
* features, visibility
|
||||
* @hide
|
||||
*/
|
||||
boolean isInstantApp();
|
||||
|
||||
/**
|
||||
* @return whether the package has not been launched since being explicitly stopped
|
||||
* @hide
|
||||
*/
|
||||
boolean isNotLaunched();
|
||||
|
||||
/**
|
||||
* @return whether the package has been stopped, which can occur if it's force-stopped, data
|
||||
* cleared, or just been installed
|
||||
* @hide
|
||||
*/
|
||||
boolean isStopped();
|
||||
|
||||
/**
|
||||
* @return whether the package has been suspended, maybe by the device admin, disallowing its
|
||||
* launch
|
||||
* @hide
|
||||
*/
|
||||
boolean isSuspended();
|
||||
|
||||
/**
|
||||
* @return whether the package was installed as a virtual preload, which may be done as part
|
||||
* of device infrastructure auto installation outside of the initial device image
|
||||
* @hide
|
||||
*/
|
||||
boolean isVirtualPreload();
|
||||
|
||||
/**
|
||||
* The "package:type/entry" form of the theme resource ID previously set as the splash screen.
|
||||
*
|
||||
* @hide
|
||||
* @see android.window.SplashScreen#setSplashScreenTheme(int)
|
||||
* @see android.content.res.Resources#getResourceName(int)
|
||||
*/
|
||||
@@ -163,8 +203,10 @@ public interface PackageUserState {
|
||||
* In epoch milliseconds. The timestamp of the first install of the app of the particular user
|
||||
* on the device, surviving past app updates. Different users might have a different first
|
||||
* install time.
|
||||
*
|
||||
* <p/>
|
||||
* This does not survive full removal of the app (i.e., uninstalls for all users).
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
long getFirstInstallTime();
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import com.android.server.utils.WatchedArraySet;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/** @hide */
|
||||
class PackageUserStateDefault implements PackageUserStateInternal {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -41,6 +41,7 @@ import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/** @hide */
|
||||
@DataClass(genConstructor = false, genBuilder = false, genEqualsHashCode = true)
|
||||
@DataClass.Suppress({"mOverlayPathsLock", "mOverlayPaths", "mSharedLibraryOverlayPathsLock",
|
||||
"mSharedLibraryOverlayPaths", "setOverlayPaths", "setCachedOverlayPaths"})
|
||||
|
||||
@@ -29,6 +29,8 @@ import com.android.server.utils.WatchedArraySet;
|
||||
* Internal variant of {@link PackageUserState} that includes data not exposed as API. This is
|
||||
* still read-only and should be used inside system server code when possible over the
|
||||
* implementation.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public interface PackageUserStateInternal extends PackageUserState, FrameworkPackageUserState {
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.android.server.pm.pkg.component.ParsedProcess;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** @hide */
|
||||
public interface SharedUserApi {
|
||||
|
||||
@NonNull
|
||||
|
||||
@@ -35,6 +35,7 @@ android_test {
|
||||
"kotlin-reflect",
|
||||
"services.core",
|
||||
"servicestests-utils",
|
||||
"servicestests-core-utils",
|
||||
"truth-prebuilt",
|
||||
],
|
||||
platform_apis: true,
|
||||
|
||||
@@ -23,7 +23,6 @@ import android.content.pm.FeatureGroupInfo
|
||||
import android.content.pm.FeatureInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.SigningDetails
|
||||
import com.android.server.pm.pkg.parsing.ParsingPackage
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
@@ -65,6 +64,7 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
|
||||
"addMimeGroupsFromComponent",
|
||||
"assignDerivedFields",
|
||||
"assignDerivedFields2",
|
||||
"makeImmutable",
|
||||
"buildFakeForDeletion",
|
||||
"buildAppClassNamesByProcess",
|
||||
"capPermissionPriorities",
|
||||
@@ -219,6 +219,7 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
|
||||
AndroidPackage::isNativeLibraryRootRequiresIsa,
|
||||
AndroidPackage::isOdm,
|
||||
AndroidPackage::isOem,
|
||||
AndroidPackage::isOnBackInvokedCallbackEnabled,
|
||||
AndroidPackage::isOverlay,
|
||||
AndroidPackage::isOverlayIsStatic,
|
||||
AndroidPackage::isPartiallyDirectBootAware,
|
||||
@@ -279,7 +280,7 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
|
||||
adder(AndroidPackage::getUsesOptionalNativeLibraries, "testUsesOptionalNativeLibrary"),
|
||||
getSetByValue(
|
||||
AndroidPackage::areAttributionsUserVisible,
|
||||
ParsingPackage::setAttributionsAreUserVisible,
|
||||
PackageImpl::setAttributionsAreUserVisible,
|
||||
true
|
||||
),
|
||||
getSetByValue2(
|
||||
@@ -513,11 +514,6 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
|
||||
}
|
||||
),
|
||||
getter(AndroidPackage::getKnownActivityEmbeddingCerts, setOf("TESTEMBEDDINGCERT")),
|
||||
getSetByValue(
|
||||
AndroidPackage::isOnBackInvokedCallbackEnabled,
|
||||
ParsingPackage::setOnBackInvokedCallbackEnabled,
|
||||
true
|
||||
)
|
||||
)
|
||||
|
||||
override fun initialObject() = PackageImpl.forParsing(
|
||||
@@ -560,10 +556,13 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
|
||||
.setSplitHasCode(1, false)
|
||||
.setSplitClassLoaderName(0, "testSplitClassLoaderNameZero")
|
||||
.setSplitClassLoaderName(1, "testSplitClassLoaderNameOne")
|
||||
|
||||
.addUsesSdkLibrary("testSdk", 2L, arrayOf("testCertDigest1"))
|
||||
.addUsesStaticLibrary("testStatic", 3L, arrayOf("testCertDigest2"))
|
||||
|
||||
override fun finalizeObject(parcelable: Parcelable) {
|
||||
(parcelable as PackageImpl).hideAsParsed().hideAsFinal()
|
||||
}
|
||||
|
||||
override fun extraAssertions(before: Parcelable, after: Parcelable) {
|
||||
super.extraAssertions(before, after)
|
||||
after as PackageImpl
|
||||
|
||||
@@ -293,12 +293,21 @@ abstract class ParcelableComponentTest(
|
||||
first?.let { property(it) } == second?.let { property(it) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun valueComparison() {
|
||||
fun buildBefore(): Pair<List<Param>, Parcelable> {
|
||||
val params = baseParams.mapNotNull(::buildParams) + extraParams().filterNotNull()
|
||||
val before = initialObject()
|
||||
|
||||
params.forEach { it.setFunction(arrayOf(before, it.value())) }
|
||||
finalizeObject(before)
|
||||
return params to before
|
||||
}
|
||||
|
||||
protected open fun finalizeObject(parcelable: Parcelable) {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun valueComparison() {
|
||||
val (params, before) = buildBefore()
|
||||
|
||||
val parcel = Parcel.obtain()
|
||||
writeToParcel(parcel, before)
|
||||
@@ -307,6 +316,15 @@ abstract class ParcelableComponentTest(
|
||||
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val baseline = initialObject()
|
||||
finalizeObject(baseline)
|
||||
|
||||
val baselineParcel = Parcel.obtain()
|
||||
writeToParcel(baselineParcel, baseline)
|
||||
|
||||
// Check that something substantial actually changed in the test object
|
||||
expect.that(parcel.dataSize()).isGreaterThan(baselineParcel.dataSize())
|
||||
|
||||
val after = creator.createFromParcel(parcel)
|
||||
|
||||
expect.withMessage("Mismatched write and read data sizes")
|
||||
@@ -314,6 +332,7 @@ abstract class ParcelableComponentTest(
|
||||
.isEqualTo(dataSize)
|
||||
|
||||
parcel.recycle()
|
||||
baselineParcel.recycle()
|
||||
|
||||
runAssertions(params, before, after)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.pkg
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.pm.overlay.OverlayPaths
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.PathPermission
|
||||
import android.content.pm.SharedLibraryInfo
|
||||
import android.content.pm.VersionedPackage
|
||||
import android.os.PatternMatcher
|
||||
import android.util.ArraySet
|
||||
import com.android.server.pm.PackageSetting
|
||||
import com.android.server.pm.PackageSettingBuilder
|
||||
import com.android.server.pm.parsing.pkg.PackageImpl
|
||||
import com.android.server.pm.pkg.AndroidPackage
|
||||
import com.android.server.pm.pkg.PackageState
|
||||
import com.android.server.pm.pkg.PackageStateImpl
|
||||
import com.android.server.pm.pkg.PackageUserState
|
||||
import com.android.server.pm.pkg.PackageUserStateImpl
|
||||
import com.android.server.pm.pkg.component.ParsedActivity
|
||||
import com.android.server.pm.pkg.component.ParsedActivityImpl
|
||||
import com.android.server.pm.pkg.component.ParsedComponentImpl
|
||||
import com.android.server.pm.pkg.component.ParsedInstrumentation
|
||||
import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
|
||||
import com.android.server.pm.pkg.component.ParsedPermission
|
||||
import com.android.server.pm.pkg.component.ParsedPermissionGroup
|
||||
import com.android.server.pm.pkg.component.ParsedPermissionImpl
|
||||
import com.android.server.pm.pkg.component.ParsedProcess
|
||||
import com.android.server.pm.pkg.component.ParsedProcessImpl
|
||||
import com.android.server.pm.pkg.component.ParsedProvider
|
||||
import com.android.server.pm.pkg.component.ParsedProviderImpl
|
||||
import com.android.server.pm.pkg.component.ParsedService
|
||||
import com.android.server.pm.test.parsing.parcelling.AndroidPackageTest
|
||||
import com.google.common.truth.Expect
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KFunction
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.full.isSubtypeOf
|
||||
import kotlin.reflect.full.memberFunctions
|
||||
import kotlin.reflect.full.starProjectedType
|
||||
|
||||
class PackageStateTest {
|
||||
|
||||
companion object {
|
||||
private val IGNORED_TYPES = listOf(
|
||||
"java.io.File",
|
||||
"java.lang.Boolean",
|
||||
"java.lang.Byte",
|
||||
"java.lang.CharSequence",
|
||||
"java.lang.Character",
|
||||
"java.lang.Double",
|
||||
"java.lang.Float",
|
||||
"java.lang.Integer",
|
||||
"java.lang.Long",
|
||||
"java.lang.Short",
|
||||
"java.lang.String",
|
||||
"java.lang.Void",
|
||||
)
|
||||
// STOPSHIP: Remove these and fix the implementations
|
||||
private val IGNORED_FUNCTIONS = listOf(
|
||||
ParsedActivity::getIntents,
|
||||
ParsedActivity::getKnownActivityEmbeddingCerts,
|
||||
ParsedActivity::getProperties,
|
||||
ParsedInstrumentation::getIntents,
|
||||
ParsedInstrumentation::getIntents,
|
||||
ParsedInstrumentation::getProperties,
|
||||
ParsedInstrumentation::getProperties,
|
||||
ParsedPermission::getIntents,
|
||||
ParsedPermission::getProperties,
|
||||
ParsedPermissionGroup::getIntents,
|
||||
ParsedPermissionGroup::getProperties,
|
||||
ParsedProcess::getAppClassNamesByPackage,
|
||||
ParsedProvider::getIntents,
|
||||
ParsedProvider::getPathPermissions,
|
||||
ParsedProvider::getProperties,
|
||||
ParsedProvider::getUriPermissionPatterns,
|
||||
ParsedService::getIntents,
|
||||
ParsedService::getProperties,
|
||||
SharedLibraryInfo::getAllCodePaths,
|
||||
SharedLibraryInfo::getDependencies,
|
||||
Intent::getCategories,
|
||||
PackageUserState::getDisabledComponents,
|
||||
PackageUserState::getEnabledComponents,
|
||||
PackageUserState::getSharedLibraryOverlayPaths,
|
||||
OverlayPaths::getOverlayPaths,
|
||||
OverlayPaths::getResourceDirs,
|
||||
)
|
||||
}
|
||||
|
||||
@get:Rule
|
||||
val tempFolder = TemporaryFolder()
|
||||
|
||||
@get:Rule
|
||||
val expect = Expect.create()
|
||||
|
||||
private val collectionType = MutableCollection::class.starProjectedType
|
||||
private val mapType = Map::class.starProjectedType
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
@Test
|
||||
fun collectionImmutability() {
|
||||
val seenTypes = mutableSetOf<KType>()
|
||||
val (_, pkg) = AndroidPackageTest().buildBefore()
|
||||
val packageState = PackageSettingBuilder()
|
||||
.setPackage(pkg as AndroidPackage)
|
||||
.setCodePath(tempFolder.newFile().path)
|
||||
.build()
|
||||
|
||||
fillMissingData(packageState, pkg as PackageImpl)
|
||||
|
||||
visitType(seenTypes, emptyList(), PackageStateImpl.copy(packageState),
|
||||
PackageState::class.starProjectedType)
|
||||
visitType(seenTypes, emptyList(), pkg, AndroidPackage::class.starProjectedType)
|
||||
visitType(seenTypes, emptyList(), packageState.getUserStateOrDefault(0),
|
||||
PackageUserState::class.starProjectedType)
|
||||
|
||||
// Don't check empties for defaults since their collections will always be empty
|
||||
visitType(seenTypes, emptyList(), PackageUserState.DEFAULT,
|
||||
PackageUserState::class.starProjectedType, enforceNonEmpty = false)
|
||||
|
||||
// Check that some minimum number of functions were validated,
|
||||
// in case the type checking breaks somehow
|
||||
expect.that(seenTypes.size).isGreaterThan(10)
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill fields in [PackageState] and its children that are not filled by [AndroidPackageTest].
|
||||
* Real objects and real invocations of the live APIs are necessary to ensure that the test
|
||||
* mirrors real device behavior.
|
||||
*/
|
||||
private fun fillMissingData(pkgSetting: PackageSetting, pkg: PackageImpl) {
|
||||
pkgSetting.addUsesLibraryFile("usesLibraryFile")
|
||||
pkgSetting.addUsesLibraryInfo(SharedLibraryInfo(
|
||||
"path",
|
||||
"packageName",
|
||||
listOf(tempFolder.newFile().path),
|
||||
"name",
|
||||
1,
|
||||
0,
|
||||
VersionedPackage("versionedPackage0", 1),
|
||||
listOf(VersionedPackage("versionedPackage1", 2)),
|
||||
emptyList(),
|
||||
false
|
||||
))
|
||||
pkgSetting.addMimeTypes("mimeGroup", setOf("mimeType"))
|
||||
pkgSetting.getOrCreateUserState(0).apply {
|
||||
setEnabledComponents(ArraySet<String>().apply { add("com.test.EnabledComponent") })
|
||||
setDisabledComponents(ArraySet<String>().apply { add("com.test.DisabledComponent") })
|
||||
setSharedLibraryOverlayPaths("sharedLibrary",
|
||||
OverlayPaths.Builder().addApkPath("/test/overlay.apk").build())
|
||||
}
|
||||
|
||||
val property = PackageManager.Property("propertyName", 1, "com.test", null)
|
||||
listOf(
|
||||
pkg.activities,
|
||||
pkg.receivers,
|
||||
pkg.providers,
|
||||
pkg.services,
|
||||
pkg.instrumentations,
|
||||
pkg.permissions,
|
||||
pkg.permissionGroups
|
||||
).map { it.first() as ParsedComponentImpl }
|
||||
.forEach {
|
||||
it.addIntent(ParsedIntentInfoImpl())
|
||||
it.addProperty(property)
|
||||
}
|
||||
|
||||
(pkg.activities.first() as ParsedActivityImpl).knownActivityEmbeddingCerts =
|
||||
setOf("TESTEMBEDDINGCERT")
|
||||
|
||||
(pkg.permissions.first() as ParsedPermissionImpl).knownCerts = setOf("TESTEMBEDDINGCERT")
|
||||
|
||||
(pkg.providers.first() as ParsedProviderImpl).apply {
|
||||
addPathPermission(PathPermission("pattern", PatternMatcher.PATTERN_LITERAL,
|
||||
"readPermission", "writerPermission"))
|
||||
addUriPermissionPattern(PatternMatcher("*", PatternMatcher.PATTERN_LITERAL))
|
||||
}
|
||||
|
||||
(pkg.processes.values.first() as ParsedProcessImpl).apply {
|
||||
deniedPermissions = setOf("deniedPermission")
|
||||
putAppClassNameForPackage("package", "className")
|
||||
}
|
||||
}
|
||||
|
||||
private fun visitType(
|
||||
seenTypes: MutableSet<KType>,
|
||||
parentChain: List<String>,
|
||||
impl: Any,
|
||||
type: KType,
|
||||
enforceNonEmpty: Boolean = true
|
||||
) {
|
||||
if (!seenTypes.add(type)) return
|
||||
val kClass = type.classifier as KClass<*>
|
||||
val qualifiedName = kClass.qualifiedName!!
|
||||
if (IGNORED_TYPES.contains(qualifiedName)) return
|
||||
|
||||
val newChain = parentChain + kClass.simpleName!!
|
||||
val newChainText = newChain.joinToString()
|
||||
|
||||
val filteredFunctions = kClass.memberFunctions
|
||||
.filter {
|
||||
// Size 1 because the impl receiver counts as a parameter
|
||||
it.parameters.size == 1
|
||||
}
|
||||
.filterNot(IGNORED_FUNCTIONS::contains)
|
||||
|
||||
filteredFunctions.filter { it.returnType.isSubtypeOf(collectionType) }
|
||||
.forEach {
|
||||
val collection = it.call(impl)
|
||||
if (collection as? MutableCollection<*> == null) {
|
||||
expect.withMessage("Method $newChainText ${it.name} cannot return null")
|
||||
.fail()
|
||||
return@forEach
|
||||
}
|
||||
|
||||
val value = try {
|
||||
collection.stream().findFirst().get()!!
|
||||
} catch (e: Exception) {
|
||||
if (enforceNonEmpty) {
|
||||
expect.withMessage("Method $newChainText ${it.name} returns empty")
|
||||
.that(e)
|
||||
.isNull()
|
||||
return@forEach
|
||||
} else null
|
||||
}
|
||||
|
||||
if (value != null) {
|
||||
it.returnType.arguments.forEach {
|
||||
visitType(seenTypes, newChain, value, it.type!!)
|
||||
}
|
||||
}
|
||||
|
||||
// Must test clear last in case it works and actually clears the collection
|
||||
expectUnsupported(newChain, it) { collection.clear() }
|
||||
}
|
||||
filteredFunctions.filter { it.returnType.isSubtypeOf(mapType) }
|
||||
.forEach {
|
||||
val map = it.call(impl)
|
||||
if (map as? MutableMap<*, *> == null) {
|
||||
expect.withMessage("Method $newChainText ${it.name} cannot return null")
|
||||
.fail()
|
||||
return@forEach
|
||||
}
|
||||
|
||||
val entry = try {
|
||||
map.entries.stream().findFirst().get()!!
|
||||
} catch (e: Exception) {
|
||||
expect.withMessage("Method $newChainText ${it.name} returns empty")
|
||||
.that(e)
|
||||
.isNull()
|
||||
return@forEach
|
||||
}
|
||||
|
||||
visitType(seenTypes, newChain, entry.key!!, it.returnType.arguments[0].type!!)
|
||||
visitType(seenTypes, newChain, entry.value!!, it.returnType.arguments[1].type!!)
|
||||
|
||||
// Must test clear last in case it works and actually clears the map
|
||||
expectUnsupported(newChain, it) { map.clear() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun expectUnsupported(
|
||||
parentChain: List<String>,
|
||||
function: KFunction<*>,
|
||||
block: () -> Unit
|
||||
) {
|
||||
val exception = try {
|
||||
block()
|
||||
null
|
||||
} catch (e: UnsupportedOperationException) {
|
||||
e
|
||||
}
|
||||
|
||||
expect.withMessage("Method ${parentChain.joinToString()} $function doesn't throw")
|
||||
.that(exception)
|
||||
.isNotNull()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user