blob: 1b7c7ad94dc92700e7fe65b3e827eaa2d0c02bdc [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.pkg;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.VersionedPackage;
import android.content.pm.dex.DexMetadataHelper;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
import android.os.incremental.IncrementalManager;
import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.pm.parsing.PackageParserException;
import com.android.internal.pm.parsing.pkg.AndroidPackageHidden;
import com.android.internal.pm.parsing.pkg.PackageImpl;
import com.android.internal.pm.pkg.component.ParsedActivity;
import com.android.internal.pm.pkg.component.ParsedInstrumentation;
import com.android.internal.pm.pkg.component.ParsedProvider;
import com.android.internal.pm.pkg.component.ParsedService;
import com.android.internal.pm.pkg.parsing.ParsingPackageHidden;
import com.android.internal.util.ArrayUtils;
import com.android.server.SystemConfig;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/** @hide */
public class AndroidPackageUtils {
private AndroidPackageUtils() {
}
public static List<String> getAllCodePathsExcludingResourceOnly(
AndroidPackage aPkg) {
PackageImpl pkg = (PackageImpl) aPkg;
ArrayList<String> paths = new ArrayList<>();
if (pkg.isDeclaredHavingCode()) {
paths.add(pkg.getBaseApkPath());
}
String[] splitCodePaths = pkg.getSplitCodePaths();
if (!ArrayUtils.isEmpty(splitCodePaths)) {
for (int i = 0; i < splitCodePaths.length; i++) {
if ((pkg.getSplitFlags()[i] & ApplicationInfo.FLAG_HAS_CODE) != 0) {
paths.add(splitCodePaths[i]);
}
}
}
return paths;
}
/**
* @return a list of the base and split code paths.
*/
public static List<String> getAllCodePaths(AndroidPackage aPkg) {
PackageImpl pkg = (PackageImpl) aPkg;
ArrayList<String> paths = new ArrayList<>();
paths.add(pkg.getBaseApkPath());
String[] splitCodePaths = pkg.getSplitCodePaths();
if (!ArrayUtils.isEmpty(splitCodePaths)) {
Collections.addAll(paths, splitCodePaths);
}
return paths;
}
public static SharedLibraryInfo createSharedLibraryForSdk(AndroidPackage pkg) {
return new SharedLibraryInfo(null, pkg.getPackageName(),
AndroidPackageUtils.getAllCodePaths(pkg),
pkg.getSdkLibraryName(),
pkg.getSdkLibVersionMajor(),
SharedLibraryInfo.TYPE_SDK_PACKAGE,
new VersionedPackage(pkg.getManifestPackageName(),
pkg.getLongVersionCode()),
null, null, false /* isNative */);
}
public static SharedLibraryInfo createSharedLibraryForStatic(AndroidPackage pkg) {
return new SharedLibraryInfo(null, pkg.getPackageName(),
AndroidPackageUtils.getAllCodePaths(pkg),
pkg.getStaticSharedLibraryName(),
pkg.getStaticSharedLibraryVersion(),
SharedLibraryInfo.TYPE_STATIC,
new VersionedPackage(pkg.getManifestPackageName(),
pkg.getLongVersionCode()),
null, null, false /* isNative */);
}
public static SharedLibraryInfo createSharedLibraryForDynamic(AndroidPackage pkg, String name) {
return new SharedLibraryInfo(null, pkg.getPackageName(),
AndroidPackageUtils.getAllCodePaths(pkg), name,
SharedLibraryInfo.VERSION_UNDEFINED,
SharedLibraryInfo.TYPE_DYNAMIC, new VersionedPackage(pkg.getPackageName(),
pkg.getLongVersionCode()),
null, null, false /* isNative */);
}
/**
* Return the dex metadata files for the given package as a map
* [code path -> dex metadata path].
*
* NOTE: involves I/O checks.
*/
public static Map<String, String> getPackageDexMetadata(AndroidPackage pkg) {
return DexMetadataHelper.buildPackageApkToDexMetadataMap
(AndroidPackageUtils.getAllCodePaths(pkg));
}
/**
* Validate the dex metadata files installed for the given package.
*
* @throws PackageParserException in case of errors.
*/
public static void validatePackageDexMetadata(AndroidPackage pkg)
throws PackageParserException {
Collection<String> apkToDexMetadataList = getPackageDexMetadata(pkg).values();
String packageName = pkg.getPackageName();
long versionCode = pkg.getLongVersionCode();
final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
for (String dexMetadata : apkToDexMetadataList) {
final ParseResult result = DexMetadataHelper.validateDexMetadataFile(
input.reset(), dexMetadata, packageName, versionCode);
if (result.isError()) {
throw new PackageParserException(
result.getErrorCode(), result.getErrorMessage(), result.getException());
}
}
}
public static NativeLibraryHelper.Handle createNativeLibraryHandle(AndroidPackage pkg)
throws IOException {
return NativeLibraryHelper.Handle.create(
AndroidPackageUtils.getAllCodePaths(pkg),
pkg.isMultiArch(),
pkg.isExtractNativeLibrariesRequested(),
pkg.isDebuggable()
);
}
public static boolean canHaveOatDir(@NonNull PackageState packageState,
@NonNull AndroidPackage pkg) {
// The following app types CANNOT have oat directory
// - non-updated system apps,
// - incrementally installed apps.
if (packageState.isSystem() && !packageState.isUpdatedSystemApp()) {
return false;
}
if (IncrementalManager.isIncrementalPath(pkg.getPath())) {
return false;
}
return true;
}
public static boolean hasComponentClassName(AndroidPackage pkg, String className) {
List<ParsedActivity> activities = pkg.getActivities();
int activitiesSize = activities.size();
for (int index = 0; index < activitiesSize; index++) {
if (Objects.equals(className, activities.get(index).getName())) {
return true;
}
}
List<ParsedActivity> receivers = pkg.getReceivers();
int receiversSize = receivers.size();
for (int index = 0; index < receiversSize; index++) {
if (Objects.equals(className, receivers.get(index).getName())) {
return true;
}
}
List<ParsedProvider> providers = pkg.getProviders();
int providersSize = providers.size();
for (int index = 0; index < providersSize; index++) {
if (Objects.equals(className, providers.get(index).getName())) {
return true;
}
}
List<ParsedService> services = pkg.getServices();
int servicesSize = services.size();
for (int index = 0; index < servicesSize; index++) {
if (Objects.equals(className, services.get(index).getName())) {
return true;
}
}
List<ParsedInstrumentation> instrumentations = pkg.getInstrumentations();
int instrumentationsSize = instrumentations.size();
for (int index = 0; index < instrumentationsSize; index++) {
if (Objects.equals(className, instrumentations.get(index).getName())) {
return true;
}
}
if (pkg.getBackupAgentName() != null) {
if (Objects.equals(className, pkg.getBackupAgentName())) {
return true;
}
}
return false;
}
public static boolean isEncryptionAware(AndroidPackage pkg) {
return pkg.isDirectBootAware() || pkg.isPartiallyDirectBootAware();
}
public static boolean isLibrary(AndroidPackage pkg) {
// TODO(b/135203078): Can parsing just enforce these always match?
return pkg.getSdkLibraryName() != null || pkg.getStaticSharedLibraryName() != null
|| !pkg.getLibraryNames().isEmpty();
}
public static int getHiddenApiEnforcementPolicy(@NonNull AndroidPackage pkg,
@NonNull PackageStateInternal packageState) {
boolean isAllowedToUseHiddenApis;
if (pkg == null) {
isAllowedToUseHiddenApis = false;
} else if (pkg.isSignedWithPlatformKey()) {
isAllowedToUseHiddenApis = true;
} else if (packageState.isSystem()) {
isAllowedToUseHiddenApis = pkg.isNonSdkApiRequested()
|| SystemConfig.getInstance().getHiddenApiWhitelistedApps().contains(
pkg.getPackageName());
} else {
isAllowedToUseHiddenApis = false;
}
if (isAllowedToUseHiddenApis) {
return ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED;
}
// TODO(b/135203078): Handle maybeUpdateHiddenApiEnforcementPolicy. Right now it's done
// entirely through ApplicationInfo and shouldn't touch this specific class, but that
// may not always hold true.
// if (mHiddenApiPolicy != ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT) {
// return mHiddenApiPolicy;
// }
return ApplicationInfo.HIDDEN_API_ENFORCEMENT_ENABLED;
}
/**
* Returns false iff the provided flags include the {@link PackageManager#MATCH_SYSTEM_ONLY}
* flag and the provided package is not a system package. Otherwise returns {@code true}.
*/
public static boolean isMatchForSystemOnly(@NonNull PackageState packageState, long flags) {
if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) {
return packageState.isSystem();
}
return true;
}
/**
* Returns the primary ABI as parsed from the package. Used only during parsing and derivation.
* Otherwise prefer {@link PackageState#getPrimaryCpuAbi()}.
*/
public static String getRawPrimaryCpuAbi(AndroidPackage pkg) {
return ((AndroidPackageHidden) pkg).getPrimaryCpuAbi();
}
/**
* Returns the secondary ABI as parsed from the package. Used only during parsing and
* derivation. Otherwise prefer {@link PackageState#getSecondaryCpuAbi()}.
*/
public static String getRawSecondaryCpuAbi(@NonNull AndroidPackage pkg) {
return ((AndroidPackageHidden) pkg).getSecondaryCpuAbi();
}
@Deprecated
@NonNull
public static ApplicationInfo generateAppInfoWithoutState(AndroidPackage pkg) {
return ((AndroidPackageHidden) pkg).toAppInfoWithoutState();
}
/**
* Replacement of unnecessary legacy getRealPackage. Only returns a value if the package was
* actually renamed.
*/
@Nullable
public static String getRealPackageOrNull(@NonNull AndroidPackage pkg, boolean isSystem) {
if (pkg.getOriginalPackages().isEmpty() || !isSystem) {
return null;
}
return pkg.getManifestPackageName();
}
public static void fillVersionCodes(@NonNull AndroidPackage pkg, @NonNull PackageInfo info) {
info.versionCode = ((ParsingPackageHidden) pkg).getVersionCode();
info.versionCodeMajor = ((ParsingPackageHidden) pkg).getVersionCodeMajor();
}
}