Merge changes I7850ea5b,I9759b7dd

* changes:
  Prepare PackageState/PackageUserState for API
  Add runtime unmodifiable check for PackageState
This commit is contained in:
Winson Chiu
2022-09-22 22:02:26 +00:00
committed by Android (Google) Code Review
20 changed files with 756 additions and 78 deletions

View File

@@ -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 {

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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();

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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)));
}
}

View File

@@ -31,6 +31,7 @@ import java.util.UUID;
/**
* Exposes internal types for internal usage of {@link PackageState}.
* @hide
*/
public interface PackageStateInternal extends PackageState {

View File

@@ -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];
}

View File

@@ -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) {

View File

@@ -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();
}

View File

@@ -30,6 +30,7 @@ import com.android.server.utils.WatchedArraySet;
import java.util.Collections;
import java.util.Map;
/** @hide */
class PackageUserStateDefault implements PackageUserStateInternal {
@Override

View File

@@ -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"})

View File

@@ -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 {

View File

@@ -27,6 +27,7 @@ import com.android.server.pm.pkg.component.ParsedProcess;
import java.util.List;
/** @hide */
public interface SharedUserApi {
@NonNull

View File

@@ -35,6 +35,7 @@ android_test {
"kotlin-reflect",
"services.core",
"servicestests-utils",
"servicestests-core-utils",
"truth-prebuilt",
],
platform_apis: true,

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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()
}
}