blob: d0fe9647618a9dfce381cea093144003ad3ce1ae [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.pm.parsing;
import android.annotation.CheckResult;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.Attribution;
import android.content.pm.ComponentInfo;
import android.content.pm.ConfigurationInfo;
import android.content.pm.FallbackCategoryProvider;
import android.content.pm.FeatureGroupInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.Flags;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.PathPermission;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.pm.ProcessInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.Signature;
import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
import android.content.pm.overlay.OverlayPaths;
import android.os.Environment;
import android.os.PatternMatcher;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Pair;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.pm.parsing.pkg.AndroidPackageLegacyUtils;
import com.android.internal.pm.parsing.pkg.PackageImpl;
import com.android.internal.pm.pkg.component.ComponentParseUtils;
import com.android.internal.pm.pkg.component.ParsedActivity;
import com.android.internal.pm.pkg.component.ParsedAttribution;
import com.android.internal.pm.pkg.component.ParsedComponent;
import com.android.internal.pm.pkg.component.ParsedInstrumentation;
import com.android.internal.pm.pkg.component.ParsedMainComponent;
import com.android.internal.pm.pkg.component.ParsedPermission;
import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
import com.android.internal.pm.pkg.component.ParsedProcess;
import com.android.internal.pm.pkg.component.ParsedProvider;
import com.android.internal.pm.pkg.component.ParsedService;
import com.android.internal.pm.pkg.component.ParsedUsesPermission;
import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
import com.android.internal.pm.pkg.parsing.ParsingUtils;
import com.android.internal.util.ArrayUtils;
import com.android.server.SystemConfig;
import com.android.server.pm.PackageArchiver;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageStateUnserialized;
import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.pkg.PackageUserStateInternal;
import com.android.server.pm.pkg.PackageUserStateUtils;
import com.android.server.pm.pkg.SELinuxUtil;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Methods that use a {@link PackageStateInternal} use it to override information provided from the
* raw package, or to provide information that would otherwise be missing. Null can be passed if
* none of the state values should be applied.
*
* @hide
**/
public class PackageInfoUtils {
private static final String TAG = ParsingUtils.TAG;
private static final String SYSTEM_DATA_PATH =
Environment.getDataDirectoryPath() + File.separator + "system";
/**
* @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
*/
@Nullable
public static PackageInfo generate(AndroidPackage pkg, int[] gids,
@PackageManager.PackageInfoFlagsBits long flags, long firstInstallTime,
long lastUpdateTime, Set<String> installedPermissions, Set<String> grantedPermissions,
PackageUserStateInternal state, @UserIdInt int userId,
@NonNull PackageStateInternal pkgSetting) {
return generateWithComponents(pkg, gids, flags, firstInstallTime, lastUpdateTime,
installedPermissions, grantedPermissions, state, userId, pkgSetting);
}
/**
* @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
*/
private static PackageInfo generateWithComponents(AndroidPackage pkg, int[] gids,
@PackageManager.PackageInfoFlagsBits long flags, long firstInstallTime,
long lastUpdateTime, Set<String> installedPermissions, Set<String> grantedPermissions,
PackageUserStateInternal state, @UserIdInt int userId,
@NonNull PackageStateInternal pkgSetting) {
ApplicationInfo applicationInfo = generateApplicationInfo(pkg, flags, state, userId,
pkgSetting);
if (applicationInfo == null) {
return null;
}
PackageInfo info = new PackageInfo();
info.packageName = pkg.getPackageName();
info.splitNames = pkg.getSplitNames();
AndroidPackageUtils.fillVersionCodes(pkg, info);
info.baseRevisionCode = pkg.getBaseRevisionCode();
info.splitRevisionCodes = pkg.getSplitRevisionCodes();
info.versionName = pkg.getVersionName();
info.sharedUserId = pkg.getSharedUserId();
info.sharedUserLabel = pkg.getSharedUserLabelResourceId();
info.applicationInfo = applicationInfo;
info.installLocation = pkg.getInstallLocation();
if ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
|| (info.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
info.requiredForAllUsers = pkg.isRequiredForAllUsers();
}
info.restrictedAccountType = pkg.getRestrictedAccountType();
info.requiredAccountType = pkg.getRequiredAccountType();
info.overlayTarget = pkg.getOverlayTarget();
info.targetOverlayableName = pkg.getOverlayTargetOverlayableName();
info.overlayCategory = pkg.getOverlayCategory();
info.overlayPriority = pkg.getOverlayPriority();
info.mOverlayIsStatic = pkg.isOverlayIsStatic();
info.compileSdkVersion = pkg.getCompileSdkVersion();
info.compileSdkVersionCodename = pkg.getCompileSdkVersionCodeName();
info.firstInstallTime = firstInstallTime;
info.lastUpdateTime = lastUpdateTime;
if (state.getArchiveState() != null) {
info.setArchiveTimeMillis(state.getArchiveState().getArchiveTimeMillis());
}
if ((flags & PackageManager.GET_GIDS) != 0) {
info.gids = gids;
}
if ((flags & PackageManager.GET_CONFIGURATIONS) != 0) {
int size = pkg.getConfigPreferences().size();
if (size > 0) {
info.configPreferences = new ConfigurationInfo[size];
pkg.getConfigPreferences().toArray(info.configPreferences);
}
size = pkg.getRequestedFeatures().size();
if (size > 0) {
info.reqFeatures = new FeatureInfo[size];
pkg.getRequestedFeatures().toArray(info.reqFeatures);
}
size = pkg.getFeatureGroups().size();
if (size > 0) {
info.featureGroups = new FeatureGroupInfo[size];
pkg.getFeatureGroups().toArray(info.featureGroups);
}
}
if ((flags & PackageManager.GET_PERMISSIONS) != 0) {
int size = ArrayUtils.size(pkg.getPermissions());
if (size > 0) {
info.permissions = new PermissionInfo[size];
for (int i = 0; i < size; i++) {
final var permission = pkg.getPermissions().get(i);
final var permissionInfo = generatePermissionInfo(permission, flags);
if (installedPermissions.contains(permission.getName())) {
permissionInfo.flags |= PermissionInfo.FLAG_INSTALLED;
}
info.permissions[i] = permissionInfo;
}
}
final List<ParsedUsesPermission> usesPermissions = pkg.getUsesPermissions();
size = usesPermissions.size();
if (size > 0) {
info.requestedPermissions = new String[size];
info.requestedPermissionsFlags = new int[size];
for (int i = 0; i < size; i++) {
final ParsedUsesPermission usesPermission = usesPermissions.get(i);
info.requestedPermissions[i] = usesPermission.getName();
// The notion of required permissions is deprecated but for compatibility.
info.requestedPermissionsFlags[i] |=
PackageInfo.REQUESTED_PERMISSION_REQUIRED;
if (grantedPermissions != null
&& grantedPermissions.contains(usesPermission.getName())) {
info.requestedPermissionsFlags[i] |=
PackageInfo.REQUESTED_PERMISSION_GRANTED;
}
if ((usesPermission.getUsesPermissionFlags()
& ParsedUsesPermission.FLAG_NEVER_FOR_LOCATION) != 0) {
info.requestedPermissionsFlags[i] |=
PackageInfo.REQUESTED_PERMISSION_NEVER_FOR_LOCATION;
}
if (pkg.getImplicitPermissions().contains(info.requestedPermissions[i])) {
info.requestedPermissionsFlags[i] |=
PackageInfo.REQUESTED_PERMISSION_IMPLICIT;
}
}
}
}
if ((flags & PackageManager.GET_ATTRIBUTIONS_LONG) != 0) {
int size = ArrayUtils.size(pkg.getAttributions());
if (size > 0) {
info.attributions = new Attribution[size];
for (int i = 0; i < size; i++) {
ParsedAttribution parsedAttribution = pkg.getAttributions().get(i);
if (parsedAttribution != null) {
info.attributions[i] = new Attribution(parsedAttribution.getTag(),
parsedAttribution.getLabel());
}
}
}
if (pkg.isAttributionsUserVisible()) {
info.applicationInfo.privateFlagsExt
|= ApplicationInfo.PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE;
} else {
info.applicationInfo.privateFlagsExt
&= ~ApplicationInfo.PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE;
}
} else {
info.applicationInfo.privateFlagsExt
&= ~ApplicationInfo.PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE;
}
final SigningDetails signingDetails = pkg.getSigningDetails();
info.signatures = getDeprecatedSignatures(signingDetails, flags);
// replacement for GET_SIGNATURES
if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) {
if (signingDetails != SigningDetails.UNKNOWN) {
// only return a valid SigningInfo if there is signing information to report
info.signingInfo = new SigningInfo(signingDetails);
} else {
info.signingInfo = null;
}
}
info.isStub = pkg.isStub();
info.coreApp = pkg.isCoreApp();
info.isApex = pkg.isApex();
if (!pkgSetting.hasSharedUser()) {
// It is possible that this shared UID app has left
info.sharedUserId = null;
info.sharedUserLabel = 0;
}
if ((flags & PackageManager.GET_ACTIVITIES) != 0) {
final int N = pkg.getActivities().size();
if (N > 0) {
// Allow to match activities of quarantined packages.
long aflags = flags | PackageManager.MATCH_QUARANTINED_COMPONENTS;
int num = 0;
final ActivityInfo[] res = new ActivityInfo[N];
for (int i = 0; i < N; i++) {
final ParsedActivity a = pkg.getActivities().get(i);
if (PackageUserStateUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(),
a.isEnabled(), a.isDirectBootAware(), a.getName(), aflags)) {
if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(
a.getName())) {
continue;
}
res[num++] = generateActivityInfo(pkg, a, aflags, state,
applicationInfo, userId, pkgSetting);
}
}
info.activities = ArrayUtils.trimToSize(res, num);
}
}
if ((flags & PackageManager.GET_RECEIVERS) != 0) {
final int size = pkg.getReceivers().size();
if (size > 0) {
int num = 0;
final ActivityInfo[] res = new ActivityInfo[size];
for (int i = 0; i < size; i++) {
final ParsedActivity a = pkg.getReceivers().get(i);
if (PackageUserStateUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(),
a.isEnabled(), a.isDirectBootAware(), a.getName(), flags)) {
res[num++] = generateActivityInfo(pkg, a, flags, state, applicationInfo,
userId, pkgSetting);
}
}
info.receivers = ArrayUtils.trimToSize(res, num);
}
}
if ((flags & PackageManager.GET_SERVICES) != 0) {
final int size = pkg.getServices().size();
if (size > 0) {
int num = 0;
final ServiceInfo[] res = new ServiceInfo[size];
for (int i = 0; i < size; i++) {
final ParsedService s = pkg.getServices().get(i);
if (PackageUserStateUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(),
s.isEnabled(), s.isDirectBootAware(), s.getName(), flags)) {
res[num++] = generateServiceInfo(pkg, s, flags, state, applicationInfo,
userId, pkgSetting);
}
}
info.services = ArrayUtils.trimToSize(res, num);
}
}
if ((flags & PackageManager.GET_PROVIDERS) != 0) {
final int size = pkg.getProviders().size();
if (size > 0) {
int num = 0;
final ProviderInfo[] res = new ProviderInfo[size];
for (int i = 0; i < size; i++) {
final ParsedProvider pr = pkg.getProviders()
.get(i);
if (PackageUserStateUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(),
pr.isEnabled(), pr.isDirectBootAware(), pr.getName(), flags)) {
res[num++] = generateProviderInfo(pkg, pr, flags, state, applicationInfo,
userId, pkgSetting);
}
}
info.providers = ArrayUtils.trimToSize(res, num);
}
}
if ((flags & PackageManager.GET_INSTRUMENTATION) != 0) {
int N = pkg.getInstrumentations().size();
if (N > 0) {
info.instrumentation = new InstrumentationInfo[N];
for (int i = 0; i < N; i++) {
info.instrumentation[i] = generateInstrumentationInfo(
pkg.getInstrumentations().get(i), pkg, flags, state,
userId, pkgSetting);
}
}
}
return info;
}
/**
* Retrieve the deprecated {@link PackageInfo.signatures} field of signing certificates
*/
public static Signature[] getDeprecatedSignatures(SigningDetails signingDetails, long flags) {
if ((flags & PackageManager.GET_SIGNATURES) == 0) {
return null;
}
if (signingDetails.hasPastSigningCertificates()) {
// Package has included signing certificate rotation information. Return the oldest
// cert so that programmatic checks keep working even if unaware of key rotation.
Signature[] signatures = new Signature[1];
signatures[0] = signingDetails.getPastSigningCertificates()[0];
return signatures;
} else if (signingDetails.hasSignatures()) {
// otherwise keep old behavior
int numberOfSigs = signingDetails.getSignatures().length;
Signature[] signatures = new Signature[numberOfSigs];
System.arraycopy(signingDetails.getSignatures(), 0, signatures, 0,
numberOfSigs);
return signatures;
}
return null;
}
private static void updateApplicationInfo(ApplicationInfo ai, long flags,
PackageUserState state) {
if ((flags & PackageManager.GET_META_DATA) == 0) {
ai.metaData = null;
}
if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) == 0) {
ai.sharedLibraryFiles = null;
ai.sharedLibraryInfos = null;
}
// CompatibilityMode is global state.
if (!ParsingPackageUtils.sCompatibilityModeEnabled) {
ai.disableCompatibilityMode();
}
ai.flags |= flag(state.isStopped(), ApplicationInfo.FLAG_STOPPED)
| flag(state.isInstalled(), ApplicationInfo.FLAG_INSTALLED)
| flag(state.isSuspended(), ApplicationInfo.FLAG_SUSPENDED);
ai.privateFlags |= flag(state.isInstantApp(), ApplicationInfo.PRIVATE_FLAG_INSTANT)
| flag(state.isVirtualPreload(), ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD)
| flag(state.isHidden(), ApplicationInfo.PRIVATE_FLAG_HIDDEN);
if (state.getEnabledState() == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
ai.enabled = true;
} else if (state.getEnabledState()
== PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
ai.enabled = (flags & PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS) != 0;
} else if (state.getEnabledState() == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
|| state.getEnabledState()
== PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
ai.enabled = false;
}
ai.enabledSetting = state.getEnabledState();
if (ai.category == ApplicationInfo.CATEGORY_UNDEFINED) {
ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName);
}
ai.seInfoUser = SELinuxUtil.getSeinfoUser(state);
final OverlayPaths overlayPaths = state.getAllOverlayPaths();
if (overlayPaths != null) {
ai.resourceDirs = overlayPaths.getResourceDirs().toArray(new String[0]);
ai.overlayPaths = overlayPaths.getOverlayPaths().toArray(new String[0]);
}
ai.isArchived = PackageArchiver.isArchived(state);
if (ai.isArchived) {
ai.nonLocalizedLabel = state.getArchiveState().getActivityInfos().get(0).getTitle();
}
}
@Nullable
public static ApplicationInfo generateDelegateApplicationInfo(@Nullable ApplicationInfo ai,
@PackageManager.ApplicationInfoFlagsBits long flags,
@NonNull PackageUserState state, int userId) {
if (ai == null || !checkUseInstalledOrHidden(flags, state, ai)) {
return null;
}
ai = new ApplicationInfo(ai);
ai.initForUser(userId);
ai.icon = (ParsingPackageUtils.sUseRoundIcon && ai.roundIconRes != 0) ? ai.roundIconRes
: ai.iconRes;
updateApplicationInfo(ai, flags, state);
return ai;
}
/**
* @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
*/
@VisibleForTesting
@Nullable
public static ApplicationInfo generateApplicationInfo(AndroidPackage pkg,
@PackageManager.ApplicationInfoFlagsBits long flags,
@NonNull PackageUserStateInternal state, @UserIdInt int userId,
@NonNull PackageStateInternal pkgSetting) {
if (pkg == null) {
return null;
}
if (!checkUseInstalledOrHidden(pkg, pkgSetting, state, flags)
|| !AndroidPackageUtils.isMatchForSystemOnly(pkgSetting, flags)) {
return null;
}
// Make shallow copy so we can store the metadata/libraries safely
ApplicationInfo info = AndroidPackageUtils.generateAppInfoWithoutState(pkg);
updateApplicationInfo(info, flags, state);
initForUser(info, pkg, userId, state);
// TODO(b/135203078): Remove PackageParser1/toAppInfoWithoutState and clean all this up
PackageStateUnserialized pkgState = pkgSetting.getTransientState();
info.hiddenUntilInstalled = pkgState.isHiddenUntilInstalled();
List<String> usesLibraryFiles = pkgState.getUsesLibraryFiles();
var usesLibraries = pkgState.getUsesLibraryInfos();
var usesLibraryInfos = new ArrayList<SharedLibraryInfo>();
for (int index = 0; index < usesLibraries.size(); index++) {
usesLibraryInfos.add(usesLibraries.get(index).getInfo());
}
info.sharedLibraryFiles = usesLibraryFiles.isEmpty()
? null : usesLibraryFiles.toArray(new String[0]);
if (!Flags.sdkLibIndependence()) {
info.sharedLibraryInfos = usesLibraryInfos.isEmpty() ? null : usesLibraryInfos;
info.optionalSharedLibraryInfos = null;
} else {
// sharedLibraryInfos contains all shared libraries that the app depends on (including
// the optional sdk libraries)
info.sharedLibraryInfos = usesLibraryInfos.isEmpty() ? null : usesLibraryInfos;
String[] libsNames = pkgSetting.getUsesSdkLibraries();
boolean[] libsOptional = pkgSetting.getUsesSdkLibrariesOptional();
List<SharedLibraryInfo> optionalSdkLibraries = null;
if (!ArrayUtils.isEmpty(libsOptional) && !ArrayUtils.isEmpty(libsNames)
&& libsNames.length == libsOptional.length) {
for (SharedLibraryInfo info1 : usesLibraryInfos) {
if (info1.getType() == SharedLibraryInfo.TYPE_SDK_PACKAGE) {
int index = ArrayUtils.indexOf(libsNames, info1.getName());
if (index >= 0 && libsOptional[index]) {
if (optionalSdkLibraries == null) {
optionalSdkLibraries = new ArrayList<>();
}
optionalSdkLibraries.add(info1);
}
}
}
}
info.optionalSharedLibraryInfos = optionalSdkLibraries;
}
if (info.category == ApplicationInfo.CATEGORY_UNDEFINED) {
info.category = pkgSetting.getCategoryOverride();
}
info.seInfo = pkgSetting.getSeInfo();
info.primaryCpuAbi = pkgSetting.getPrimaryCpuAbi();
info.secondaryCpuAbi = pkgSetting.getSecondaryCpuAbi();
info.flags |= appInfoFlags(info.flags, pkgSetting);
info.privateFlags |= appInfoPrivateFlags(info.privateFlags, pkgSetting);
info.privateFlagsExt |= appInfoPrivateFlagsExt(info.privateFlagsExt, pkgSetting);
return info;
}
/**
* @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
*/
@VisibleForTesting
@Nullable
public static ActivityInfo generateActivityInfo(AndroidPackage pkg, ParsedActivity a,
@PackageManager.ComponentInfoFlagsBits long flags,
@NonNull PackageUserStateInternal state, @UserIdInt int userId,
@NonNull PackageStateInternal pkgSetting) {
return generateActivityInfo(pkg, a, flags, state, null, userId, pkgSetting);
}
/**
* @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
*/
@VisibleForTesting
@Nullable
public static ActivityInfo generateActivityInfo(AndroidPackage pkg, ParsedActivity a,
@PackageManager.ComponentInfoFlagsBits long flags,
@NonNull PackageUserStateInternal state, @Nullable ApplicationInfo applicationInfo,
@UserIdInt int userId, @NonNull PackageStateInternal pkgSetting) {
if (a == null) return null;
if (!checkUseInstalledOrHidden(pkg, pkgSetting, state, flags)) {
return null;
}
if (applicationInfo == null) {
applicationInfo = generateApplicationInfo(pkg, flags, state, userId, pkgSetting);
}
if (applicationInfo == null) {
return null;
}
// Make shallow copies so we can store the metadata safely
ActivityInfo ai = new ActivityInfo();
ai.targetActivity = a.getTargetActivity();
ai.processName = a.getProcessName();
ai.exported = a.isExported();
ai.theme = a.getTheme();
ai.uiOptions = a.getUiOptions();
ai.parentActivityName = a.getParentActivityName();
ai.permission = a.getPermission();
ai.taskAffinity = a.getTaskAffinity();
ai.flags = a.getFlags();
ai.privateFlags = a.getPrivateFlags();
ai.launchMode = a.getLaunchMode();
ai.documentLaunchMode = a.getDocumentLaunchMode();
ai.maxRecents = a.getMaxRecents();
ai.configChanges = a.getConfigChanges();
ai.softInputMode = a.getSoftInputMode();
ai.persistableMode = a.getPersistableMode();
ai.lockTaskLaunchMode = a.getLockTaskLaunchMode();
ai.screenOrientation = a.getScreenOrientation();
ai.resizeMode = a.getResizeMode();
ai.setMaxAspectRatio(a.getMaxAspectRatio());
ai.setMinAspectRatio(a.getMinAspectRatio());
ai.supportsSizeChanges = a.isSupportsSizeChanges();
ai.requestedVrComponent = a.getRequestedVrComponent();
ai.rotationAnimation = a.getRotationAnimation();
ai.colorMode = a.getColorMode();
ai.windowLayout = a.getWindowLayout();
ai.attributionTags = a.getAttributionTags();
if ((flags & PackageManager.GET_META_DATA) != 0) {
var metaData = a.getMetaData();
// Backwards compatibility, coerce to null if empty
ai.metaData = metaData.isEmpty() ? null : metaData;
} else {
ai.metaData = null;
}
ai.applicationInfo = applicationInfo;
ai.requiredDisplayCategory = a.getRequiredDisplayCategory();
ai.setKnownActivityEmbeddingCerts(a.getKnownActivityEmbeddingCerts());
assignFieldsComponentInfoParsedMainComponent(ai, a, pkgSetting, userId);
return ai;
}
@Nullable
public static ActivityInfo generateDelegateActivityInfo(@Nullable ActivityInfo a,
@PackageManager.ComponentInfoFlagsBits long flags,
@NonNull PackageUserState state, int userId) {
if (a == null || !checkUseInstalledOrHidden(flags, state, a.applicationInfo)) {
return null;
}
// This is used to return the ResolverActivity or instantAppInstallerActivity;
// we will just always make a copy.
final ActivityInfo ai = new ActivityInfo(a);
ai.applicationInfo =
generateDelegateApplicationInfo(ai.applicationInfo, flags, state, userId);
return ai;
}
/**
* @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
*/
@Nullable
public static ServiceInfo generateServiceInfo(AndroidPackage pkg, ParsedService s,
@PackageManager.ComponentInfoFlagsBits long flags, PackageUserStateInternal state,
@UserIdInt int userId, @NonNull PackageStateInternal pkgSetting) {
return generateServiceInfo(pkg, s, flags, state, null, userId, pkgSetting);
}
/**
* @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
*/
@VisibleForTesting
@Nullable
public static ServiceInfo generateServiceInfo(AndroidPackage pkg, ParsedService s,
@PackageManager.ComponentInfoFlagsBits long flags, PackageUserStateInternal state,
@Nullable ApplicationInfo applicationInfo, int userId,
@NonNull PackageStateInternal pkgSetting) {
if (s == null) return null;
if (!checkUseInstalledOrHidden(pkg, pkgSetting, state, flags)) {
return null;
}
if (applicationInfo == null) {
applicationInfo = generateApplicationInfo(pkg, flags, state, userId, pkgSetting);
}
if (applicationInfo == null) {
return null;
}
// Make shallow copies so we can store the metadata safely
ServiceInfo si = new ServiceInfo();
si.exported = s.isExported();
si.flags = s.getFlags();
si.permission = s.getPermission();
si.processName = s.getProcessName();
si.mForegroundServiceType = s.getForegroundServiceType();
si.applicationInfo = applicationInfo;
if ((flags & PackageManager.GET_META_DATA) != 0) {
var metaData = s.getMetaData();
// Backwards compatibility, coerce to null if empty
si.metaData = metaData.isEmpty() ? null : metaData;
}
assignFieldsComponentInfoParsedMainComponent(si, s, pkgSetting, userId);
return si;
}
/**
* @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
*/
@VisibleForTesting
@Nullable
public static ProviderInfo generateProviderInfo(AndroidPackage pkg, ParsedProvider p,
@PackageManager.ComponentInfoFlagsBits long flags, PackageUserStateInternal state,
@NonNull ApplicationInfo applicationInfo, int userId,
@NonNull PackageStateInternal pkgSetting) {
if (p == null) return null;
if (!checkUseInstalledOrHidden(pkg, pkgSetting, state, flags)) {
return null;
}
if (applicationInfo == null || !pkg.getPackageName().equals(applicationInfo.packageName)) {
Slog.wtf(TAG, "AppInfo's package name is different. Expected=" + pkg.getPackageName()
+ " actual=" + (applicationInfo == null ? "(null AppInfo)"
: applicationInfo.packageName));
applicationInfo = generateApplicationInfo(pkg, flags, state, userId, pkgSetting);
}
if (applicationInfo == null) {
return null;
}
// Make shallow copies so we can store the metadata safely
ProviderInfo pi = new ProviderInfo();
pi.exported = p.isExported();
pi.flags = p.getFlags();
pi.processName = p.getProcessName();
pi.authority = p.getAuthority();
pi.isSyncable = p.isSyncable();
pi.readPermission = p.getReadPermission();
pi.writePermission = p.getWritePermission();
pi.grantUriPermissions = p.isGrantUriPermissions();
pi.forceUriPermissions = p.isForceUriPermissions();
pi.multiprocess = p.isMultiProcess();
pi.initOrder = p.getInitOrder();
pi.uriPermissionPatterns = p.getUriPermissionPatterns().toArray(new PatternMatcher[0]);
pi.pathPermissions = p.getPathPermissions().toArray(new PathPermission[0]);
if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) {
pi.uriPermissionPatterns = null;
}
if ((flags & PackageManager.GET_META_DATA) != 0) {
var metaData = p.getMetaData();
// Backwards compatibility, coerce to null if empty
pi.metaData = metaData.isEmpty() ? null : metaData;
}
pi.applicationInfo = applicationInfo;
assignFieldsComponentInfoParsedMainComponent(pi, p, pkgSetting, userId);
return pi;
}
/**
* @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
*/
@Nullable
public static InstrumentationInfo generateInstrumentationInfo(ParsedInstrumentation i,
AndroidPackage pkg, @PackageManager.ComponentInfoFlagsBits long flags,
PackageUserStateInternal state, int userId, @NonNull PackageStateInternal pkgSetting) {
if (i == null) return null;
if (!checkUseInstalledOrHidden(pkg, pkgSetting, state, flags)) {
return null;
}
InstrumentationInfo info = new InstrumentationInfo();
info.targetPackage = i.getTargetPackage();
info.targetProcesses = i.getTargetProcesses();
info.handleProfiling = i.isHandleProfiling();
info.functionalTest = i.isFunctionalTest();
info.sourceDir = pkg.getBaseApkPath();
info.publicSourceDir = pkg.getBaseApkPath();
info.splitNames = pkg.getSplitNames();
info.splitSourceDirs = pkg.getSplitCodePaths().length == 0 ? null : pkg.getSplitCodePaths();
info.splitPublicSourceDirs = pkg.getSplitCodePaths().length == 0
? null : pkg.getSplitCodePaths();
info.splitDependencies = pkg.getSplitDependencies().size() == 0
? null : pkg.getSplitDependencies();
initForUser(info, pkg, userId, state);
info.primaryCpuAbi = pkgSetting.getPrimaryCpuAbi();
info.secondaryCpuAbi = pkgSetting.getSecondaryCpuAbi();
info.nativeLibraryDir = pkg.getNativeLibraryDir();
info.secondaryNativeLibraryDir = pkg.getSecondaryNativeLibraryDir();
assignFieldsPackageItemInfoParsedComponent(info, i, pkgSetting, userId);
if ((flags & PackageManager.GET_META_DATA) == 0) {
info.metaData = null;
} else {
var metaData = i.getMetaData();
// Backwards compatibility, coerce to null if empty
info.metaData = metaData.isEmpty() ? null : metaData;
}
return info;
}
// TODO(b/135203078): Determine if permission methods need to pass in a non-null
// PackageStateInternal os that checkUseInstalledOrHidden filter can apply
@Nullable
public static PermissionInfo generatePermissionInfo(ParsedPermission p,
@PackageManager.ComponentInfoFlagsBits long flags) {
// TODO(b/135203078): Remove null checks and make all usages @NonNull
if (p == null) return null;
PermissionInfo pi = new PermissionInfo(p.getBackgroundPermission());
assignFieldsPackageItemInfoParsedComponent(pi, p);
pi.group = p.getGroup();
pi.requestRes = p.getRequestRes();
pi.protectionLevel = p.getProtectionLevel();
pi.descriptionRes = p.getDescriptionRes();
pi.flags = p.getFlags();
pi.knownCerts = p.getKnownCerts();
if ((flags & PackageManager.GET_META_DATA) == 0) {
pi.metaData = null;
} else {
var metaData = p.getMetaData();
// Backwards compatibility, coerce to null if empty
pi.metaData = metaData.isEmpty() ? null : metaData;
}
return pi;
}
@Nullable
public static PermissionGroupInfo generatePermissionGroupInfo(ParsedPermissionGroup pg,
@PackageManager.ComponentInfoFlagsBits long flags) {
if (pg == null) return null;
PermissionGroupInfo pgi = new PermissionGroupInfo(
pg.getRequestDetailRes(),
pg.getBackgroundRequestRes(),
pg.getBackgroundRequestDetailRes()
);
assignFieldsPackageItemInfoParsedComponent(pgi, pg);
pgi.descriptionRes = pg.getDescriptionRes();
pgi.priority = pg.getPriority();
pgi.requestRes = pg.getRequestRes();
pgi.flags = pg.getFlags();
if ((flags & PackageManager.GET_META_DATA) == 0) {
pgi.metaData = null;
} else {
var metaData = pg.getMetaData();
// Backwards compatibility, coerce to null if empty
pgi.metaData = metaData.isEmpty() ? null : metaData;
}
return pgi;
}
@Nullable
public static ArrayMap<String, ProcessInfo> generateProcessInfo(
Map<String, ParsedProcess> procs, @PackageManager.ComponentInfoFlagsBits long flags) {
if (procs == null) {
return null;
}
final int numProcs = procs.size();
ArrayMap<String, ProcessInfo> retProcs = new ArrayMap<>(numProcs);
for (String key : procs.keySet()) {
ParsedProcess proc = procs.get(key);
retProcs.put(proc.getName(),
new ProcessInfo(proc.getName(), new ArraySet<>(proc.getDeniedPermissions()),
proc.getGwpAsanMode(), proc.getMemtagMode(),
proc.getNativeHeapZeroInitialized()));
}
return retProcs;
}
/**
* Returns true if the package is installed and not hidden, or if the caller explicitly wanted
* all uninstalled and hidden packages as well.
*/
public static boolean checkUseInstalledOrHidden(AndroidPackage pkg,
@NonNull PackageStateInternal pkgSetting, PackageUserStateInternal state,
@PackageManager.PackageInfoFlagsBits long flags) {
// Returns false if the package is hidden system app until installed.
if ((flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) == 0
&& !state.isInstalled()
&& pkgSetting.getTransientState().isHiddenUntilInstalled()) {
return false;
}
// If available for the target user, or trying to match uninstalled packages and it's
// a system app.
return PackageUserStateUtils.isAvailable(state, flags)
|| (pkgSetting.isSystem() && matchUninstalledOrHidden(flags));
}
private static boolean checkUseInstalledOrHidden(long flags,
@NonNull PackageUserState state, @Nullable ApplicationInfo appInfo) {
// Returns false if the package is hidden system app until installed.
if ((flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) == 0
&& !state.isInstalled()
&& appInfo != null && appInfo.hiddenUntilInstalled) {
return false;
}
// If available for the target user, or trying to match uninstalled packages and it's
// a system app.
return PackageUserStateUtils.isAvailable(state, flags)
|| (appInfo != null && appInfo.isSystemApp() && matchUninstalledOrHidden(flags));
}
private static boolean matchUninstalledOrHidden(long flags) {
return (flags
& (PackageManager.MATCH_KNOWN_PACKAGES
| PackageManager.MATCH_ARCHIVED_PACKAGES
| PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS))
!= 0;
}
private static void assignFieldsComponentInfoParsedMainComponent(
@NonNull ComponentInfo info, @NonNull ParsedMainComponent component) {
assignFieldsPackageItemInfoParsedComponent(info, component);
info.descriptionRes = component.getDescriptionRes();
info.directBootAware = component.isDirectBootAware();
info.enabled = component.isEnabled();
info.splitName = component.getSplitName();
info.attributionTags = component.getAttributionTags();
}
private static void assignFieldsPackageItemInfoParsedComponent(
@NonNull PackageItemInfo packageItemInfo, @NonNull ParsedComponent component) {
packageItemInfo.nonLocalizedLabel = ComponentParseUtils.getNonLocalizedLabel(component);
packageItemInfo.icon = ComponentParseUtils.getIcon(component);
packageItemInfo.banner = component.getBanner();
packageItemInfo.labelRes = component.getLabelRes();
packageItemInfo.logo = component.getLogo();
packageItemInfo.name = component.getName();
packageItemInfo.packageName = component.getPackageName();
}
private static void assignFieldsComponentInfoParsedMainComponent(
@NonNull ComponentInfo info, @NonNull ParsedMainComponent component,
@NonNull PackageStateInternal pkgSetting, @UserIdInt int userId) {
assignFieldsComponentInfoParsedMainComponent(info, component);
Pair<CharSequence, Integer> labelAndIcon =
ParsedComponentStateUtils.getNonLocalizedLabelAndIcon(component, pkgSetting,
userId);
info.nonLocalizedLabel = labelAndIcon.first;
info.icon = labelAndIcon.second;
}
private static void assignFieldsPackageItemInfoParsedComponent(
@NonNull PackageItemInfo info, @NonNull ParsedComponent component,
@NonNull PackageStateInternal pkgSetting, @UserIdInt int userId) {
assignFieldsPackageItemInfoParsedComponent(info, component);
Pair<CharSequence, Integer> labelAndIcon =
ParsedComponentStateUtils.getNonLocalizedLabelAndIcon(component, pkgSetting,
userId);
info.nonLocalizedLabel = labelAndIcon.first;
info.icon = labelAndIcon.second;
}
@CheckResult
private static int flag(boolean hasFlag, int flag) {
return hasFlag ? flag : 0;
}
/**
* @see ApplicationInfo#flags
*/
public static int appInfoFlags(AndroidPackage pkg, @Nullable PackageStateInternal pkgSetting) {
// @formatter:off
int pkgWithoutStateFlags = flag(pkg.isExternalStorage(), ApplicationInfo.FLAG_EXTERNAL_STORAGE)
| flag(pkg.isHardwareAccelerated(), ApplicationInfo.FLAG_HARDWARE_ACCELERATED)
| flag(pkg.isBackupAllowed(), ApplicationInfo.FLAG_ALLOW_BACKUP)
| flag(pkg.isKillAfterRestoreAllowed(), ApplicationInfo.FLAG_KILL_AFTER_RESTORE)
| flag(pkg.isRestoreAnyVersion(), ApplicationInfo.FLAG_RESTORE_ANY_VERSION)
| flag(pkg.isFullBackupOnly(), ApplicationInfo.FLAG_FULL_BACKUP_ONLY)
| flag(pkg.isPersistent(), ApplicationInfo.FLAG_PERSISTENT)
| flag(pkg.isDebuggable(), ApplicationInfo.FLAG_DEBUGGABLE)
| flag(pkg.isVmSafeMode(), ApplicationInfo.FLAG_VM_SAFE_MODE)
| flag(pkg.isDeclaredHavingCode(), ApplicationInfo.FLAG_HAS_CODE)
| flag(pkg.isTaskReparentingAllowed(), ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING)
| flag(pkg.isClearUserDataAllowed(), ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA)
| flag(pkg.isLargeHeap(), ApplicationInfo.FLAG_LARGE_HEAP)
| flag(pkg.isCleartextTrafficAllowed(), ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC)
| flag(pkg.isRtlSupported(), ApplicationInfo.FLAG_SUPPORTS_RTL)
| flag(pkg.isTestOnly(), ApplicationInfo.FLAG_TEST_ONLY)
| flag(pkg.isMultiArch(), ApplicationInfo.FLAG_MULTIARCH)
| flag(pkg.isExtractNativeLibrariesRequested(), ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS)
| flag(pkg.isGame(), ApplicationInfo.FLAG_IS_GAME)
| flag(pkg.isSmallScreensSupported(), ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS)
| flag(pkg.isNormalScreensSupported(), ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS)
| flag(pkg.isLargeScreensSupported(), ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS)
| flag(pkg.isExtraLargeScreensSupported(), ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS)
| flag(pkg.isResizeable(), ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS)
| flag(pkg.isAnyDensity(), ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES)
| flag(AndroidPackageLegacyUtils.isSystem(pkg), ApplicationInfo.FLAG_SYSTEM)
| flag(pkg.isFactoryTest(), ApplicationInfo.FLAG_FACTORY_TEST);
return appInfoFlags(pkgWithoutStateFlags, pkgSetting);
// @formatter:on
}
/** @see ApplicationInfo#flags */
public static int appInfoFlags(int pkgWithoutStateFlags,
@NonNull PackageStateInternal pkgSetting) {
// @formatter:off
int flags = pkgWithoutStateFlags;
if (pkgSetting != null) {
flags |= flag(pkgSetting.isUpdatedSystemApp(), ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
}
return flags;
// @formatter:on
}
/** @see ApplicationInfo#privateFlags */
public static int appInfoPrivateFlags(AndroidPackage pkg,
@Nullable PackageStateInternal pkgSetting) {
// @formatter:off
int pkgWithoutStateFlags = flag(pkg.isStaticSharedLibrary(), ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY)
| flag(pkg.isResourceOverlay(), ApplicationInfo.PRIVATE_FLAG_IS_RESOURCE_OVERLAY)
| flag(pkg.isIsolatedSplitLoading(), ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING)
| flag(pkg.isHasDomainUrls(), ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS)
| flag(pkg.isProfileableByShell(), ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL)
| flag(pkg.isBackupInForeground(), ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND)
| flag(pkg.isUseEmbeddedDex(), ApplicationInfo.PRIVATE_FLAG_USE_EMBEDDED_DEX)
| flag(pkg.isDefaultToDeviceProtectedStorage(), ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE)
| flag(pkg.isDirectBootAware(), ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE)
| flag(pkg.isPartiallyDirectBootAware(), ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE)
| flag(pkg.isClearUserDataOnFailedRestoreAllowed(), ApplicationInfo.PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE)
| flag(pkg.isAllowAudioPlaybackCapture(), ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE)
| flag(pkg.isRequestLegacyExternalStorage(), ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE)
| flag(pkg.isNonSdkApiRequested(), ApplicationInfo.PRIVATE_FLAG_USES_NON_SDK_API)
| flag(pkg.isUserDataFragile(), ApplicationInfo.PRIVATE_FLAG_HAS_FRAGILE_USER_DATA)
| flag(pkg.isSaveStateDisallowed(), ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE)
| flag(pkg.isResizeableActivityViaSdkVersion(), ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION)
| flag(pkg.isAllowNativeHeapPointerTagging(), ApplicationInfo.PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING)
| flag(AndroidPackageLegacyUtils.isSystemExt(pkg), ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT)
| flag(AndroidPackageLegacyUtils.isPrivileged(pkg), ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)
| flag(AndroidPackageLegacyUtils.isOem(pkg), ApplicationInfo.PRIVATE_FLAG_OEM)
| flag(AndroidPackageLegacyUtils.isVendor(pkg), ApplicationInfo.PRIVATE_FLAG_VENDOR)
| flag(AndroidPackageLegacyUtils.isProduct(pkg), ApplicationInfo.PRIVATE_FLAG_PRODUCT)
| flag(AndroidPackageLegacyUtils.isOdm(pkg), ApplicationInfo.PRIVATE_FLAG_ODM)
| flag(pkg.isSignedWithPlatformKey(), ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY);
Boolean resizeableActivity = pkg.getResizeableActivity();
if (resizeableActivity != null) {
if (resizeableActivity) {
pkgWithoutStateFlags |= ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE;
} else {
pkgWithoutStateFlags |= ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE;
}
}
return appInfoPrivateFlags(pkgWithoutStateFlags, pkgSetting);
// @formatter:on
}
/** @see ApplicationInfo#privateFlags */
public static int appInfoPrivateFlags(int pkgWithoutStateFlags,
@Nullable PackageStateInternal pkgSetting) {
// @formatter:off
// TODO: Add state specific flags
return pkgWithoutStateFlags;
// @formatter:on
}
/** @see ApplicationInfo#privateFlagsExt */
public static int appInfoPrivateFlagsExt(AndroidPackage pkg,
@Nullable PackageStateInternal pkgSetting) {
var isAllowlistedForHiddenApis = SystemConfig.getInstance().getHiddenApiWhitelistedApps()
.contains(pkg.getPackageName());
// @formatter:off
int pkgWithoutStateFlags = flag(pkg.isProfileable(), ApplicationInfo.PRIVATE_FLAG_EXT_PROFILEABLE)
| flag(pkg.hasRequestForegroundServiceExemption(), ApplicationInfo.PRIVATE_FLAG_EXT_REQUEST_FOREGROUND_SERVICE_EXEMPTION)
| flag(pkg.isAttributionsUserVisible(), ApplicationInfo.PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE)
| flag(pkg.isOnBackInvokedCallbackEnabled(), ApplicationInfo.PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK)
| flag(isAllowlistedForHiddenApis, ApplicationInfo.PRIVATE_FLAG_EXT_ALLOWLISTED_FOR_HIDDEN_APIS);
return appInfoPrivateFlagsExt(pkgWithoutStateFlags, pkgSetting);
// @formatter:on
}
/** @see ApplicationInfo#privateFlagsExt */
private static int appInfoPrivateFlagsExt(int pkgWithoutStateFlags,
@Nullable PackageStateInternal pkgSetting) {
// @formatter:off
int flags = pkgWithoutStateFlags;
if (pkgSetting != null) {
flags |= flag(pkgSetting.getCpuAbiOverride() != null, ApplicationInfo.PRIVATE_FLAG_EXT_CPU_OVERRIDE);
}
return flags;
// @formatter:on
}
private static void initForUser(ApplicationInfo output, AndroidPackage input,
@UserIdInt int userId, PackageUserStateInternal state) {
PackageImpl pkg = ((PackageImpl) input);
String packageName = input.getPackageName();
output.uid = UserHandle.getUid(userId, UserHandle.getAppId(input.getUid()));
if ("android".equals(packageName)) {
output.dataDir = SYSTEM_DATA_PATH;
return;
}
if (!state.isInstalled() && !state.dataExists()
&& android.content.pm.Flags.nullableDataDir()) {
// The data dir has been deleted
output.dataDir = null;
return;
}
// For performance reasons, all these paths are built as strings
if (userId == UserHandle.USER_SYSTEM) {
output.credentialProtectedDataDir =
pkg.getBaseAppDataCredentialProtectedDirForSystemUser() + packageName;
output.deviceProtectedDataDir =
pkg.getBaseAppDataDeviceProtectedDirForSystemUser() + packageName;
} else {
// Convert /data/user/0/ -> /data/user/1/com.example.app
String userIdString = String.valueOf(userId);
int credentialLength = pkg.getBaseAppDataCredentialProtectedDirForSystemUser().length();
output.credentialProtectedDataDir =
new StringBuilder(pkg.getBaseAppDataCredentialProtectedDirForSystemUser())
.replace(credentialLength - 2, credentialLength - 1, userIdString)
.append(packageName)
.toString();
int deviceLength = pkg.getBaseAppDataDeviceProtectedDirForSystemUser().length();
output.deviceProtectedDataDir =
new StringBuilder(pkg.getBaseAppDataDeviceProtectedDirForSystemUser())
.replace(deviceLength - 2, deviceLength - 1, userIdString)
.append(packageName)
.toString();
}
if (input.isDefaultToDeviceProtectedStorage()
&& PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
output.dataDir = output.deviceProtectedDataDir;
} else {
output.dataDir = output.credentialProtectedDataDir;
}
}
// This duplicates the ApplicationInfo variant because it uses field assignment and the classes
// don't inherit from each other, unfortunately. Consolidating logic would introduce overhead.
private static void initForUser(InstrumentationInfo output, AndroidPackage input,
@UserIdInt int userId, PackageUserStateInternal state) {
PackageImpl pkg = ((PackageImpl) input);
String packageName = input.getPackageName();
if ("android".equals(packageName)) {
output.dataDir = SYSTEM_DATA_PATH;
return;
}
if (!state.isInstalled() && !state.dataExists()
&& android.content.pm.Flags.nullableDataDir()) {
// The data dir has been deleted
output.dataDir = null;
return;
}
// For performance reasons, all these paths are built as strings
if (userId == UserHandle.USER_SYSTEM) {
output.credentialProtectedDataDir =
pkg.getBaseAppDataCredentialProtectedDirForSystemUser() + packageName;
output.deviceProtectedDataDir =
pkg.getBaseAppDataDeviceProtectedDirForSystemUser() + packageName;
} else {
// Convert /data/user/0/ -> /data/user/1/com.example.app
String userIdString = String.valueOf(userId);
int credentialLength = pkg.getBaseAppDataCredentialProtectedDirForSystemUser().length();
output.credentialProtectedDataDir =
new StringBuilder(pkg.getBaseAppDataCredentialProtectedDirForSystemUser())
.replace(credentialLength - 2, credentialLength - 1, userIdString)
.append(packageName)
.toString();
int deviceLength = pkg.getBaseAppDataDeviceProtectedDirForSystemUser().length();
output.deviceProtectedDataDir =
new StringBuilder(pkg.getBaseAppDataDeviceProtectedDirForSystemUser())
.replace(deviceLength - 2, deviceLength - 1, userIdString)
.append(packageName)
.toString();
}
if (input.isDefaultToDeviceProtectedStorage()
&& PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
output.dataDir = output.deviceProtectedDataDir;
} else {
output.dataDir = output.credentialProtectedDataDir;
}
}
/**
* Returns the data dir of the app for the target user. Return null if the app isn't installed
* on the target user and doesn't have a data dir on the target user.
*/
@Nullable
public static File getDataDir(PackageStateInternal ps, int userId) {
if ("android".equals(ps.getPackageName())) {
return Environment.getDataSystemDirectory();
}
if (!ps.getUserStateOrDefault(userId).isInstalled()
&& !ps.getUserStateOrDefault(userId).dataExists()
&& android.content.pm.Flags.nullableDataDir()) {
// The app has been uninstalled for the user and the data dir has been deleted
return null;
}
if (ps.isDefaultToDeviceProtectedStorage()
&& PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
return Environment.getDataUserDePackageDirectory(ps.getVolumeUuid(), userId,
ps.getPackageName());
} else {
return Environment.getDataUserCePackageDirectory(ps.getVolumeUuid(), userId,
ps.getPackageName());
}
}
/**
* Wraps {@link PackageInfoUtils#generateApplicationInfo} with a cache.
*/
public static class CachedApplicationInfoGenerator {
// Map from a package name to the corresponding app info.
private final ArrayMap<String, ApplicationInfo> mCache = new ArrayMap<>();
/**
* {@link PackageInfoUtils#generateApplicationInfo} with a cache.
*/
@Nullable
public ApplicationInfo generate(AndroidPackage pkg,
@PackageManager.ApplicationInfoFlagsBits long flags, PackageUserStateInternal state,
int userId, @NonNull PackageStateInternal pkgSetting) {
ApplicationInfo appInfo = mCache.get(pkg.getPackageName());
if (appInfo != null) {
return appInfo;
}
appInfo = PackageInfoUtils.generateApplicationInfo(
pkg, flags, state, userId, pkgSetting);
mCache.put(pkg.getPackageName(), appInfo);
return appInfo;
}
}
}