1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.pm.parsing.pkg; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.content.pm.ApplicationInfo; 22 import android.content.pm.PackageInfo; 23 import android.content.pm.PackageManager; 24 import android.content.pm.PackageParser.PackageParserException; 25 import android.content.pm.SharedLibraryInfo; 26 import android.content.pm.VersionedPackage; 27 import android.content.pm.dex.DexMetadataHelper; 28 import android.content.pm.parsing.ParsingPackageRead; 29 import android.content.pm.parsing.ParsingPackageUtils; 30 import android.content.pm.parsing.component.ParsedActivity; 31 import android.content.pm.parsing.component.ParsedInstrumentation; 32 import android.content.pm.parsing.component.ParsedProvider; 33 import android.content.pm.parsing.component.ParsedService; 34 import android.os.incremental.IncrementalManager; 35 import android.text.TextUtils; 36 37 import com.android.internal.content.NativeLibraryHelper; 38 import com.android.internal.util.ArrayUtils; 39 import com.android.server.SystemConfig; 40 import com.android.server.pm.PackageSetting; 41 42 import java.io.IOException; 43 import java.util.ArrayList; 44 import java.util.Collection; 45 import java.util.Collections; 46 import java.util.List; 47 import java.util.Map; 48 import java.util.Objects; 49 50 /** @hide */ 51 public class AndroidPackageUtils { 52 AndroidPackageUtils()53 private AndroidPackageUtils() { 54 } 55 getAllCodePathsExcludingResourceOnly( AndroidPackage aPkg)56 public static List<String> getAllCodePathsExcludingResourceOnly( 57 AndroidPackage aPkg) { 58 PackageImpl pkg = (PackageImpl) aPkg; 59 ArrayList<String> paths = new ArrayList<>(); 60 if (pkg.isHasCode()) { 61 paths.add(pkg.getBaseApkPath()); 62 } 63 String[] splitCodePaths = pkg.getSplitCodePaths(); 64 if (!ArrayUtils.isEmpty(splitCodePaths)) { 65 for (int i = 0; i < splitCodePaths.length; i++) { 66 if ((pkg.getSplitFlags()[i] & ApplicationInfo.FLAG_HAS_CODE) != 0) { 67 paths.add(splitCodePaths[i]); 68 } 69 } 70 } 71 return paths; 72 } 73 74 /** 75 * @return a list of the base and split code paths. 76 */ getAllCodePaths(AndroidPackage aPkg)77 public static List<String> getAllCodePaths(AndroidPackage aPkg) { 78 PackageImpl pkg = (PackageImpl) aPkg; 79 ArrayList<String> paths = new ArrayList<>(); 80 paths.add(pkg.getBaseApkPath()); 81 82 String[] splitCodePaths = pkg.getSplitCodePaths(); 83 if (!ArrayUtils.isEmpty(splitCodePaths)) { 84 Collections.addAll(paths, splitCodePaths); 85 } 86 return paths; 87 } 88 createSharedLibraryForStatic(AndroidPackage pkg)89 public static SharedLibraryInfo createSharedLibraryForStatic(AndroidPackage pkg) { 90 return new SharedLibraryInfo(null, pkg.getPackageName(), 91 AndroidPackageUtils.getAllCodePaths(pkg), 92 pkg.getStaticSharedLibName(), 93 pkg.getStaticSharedLibVersion(), 94 SharedLibraryInfo.TYPE_STATIC, 95 new VersionedPackage(pkg.getManifestPackageName(), 96 pkg.getLongVersionCode()), 97 null, null, false /* isNative */); 98 } 99 createSharedLibraryForDynamic(AndroidPackage pkg, String name)100 public static SharedLibraryInfo createSharedLibraryForDynamic(AndroidPackage pkg, String name) { 101 return new SharedLibraryInfo(null, pkg.getPackageName(), 102 AndroidPackageUtils.getAllCodePaths(pkg), name, 103 SharedLibraryInfo.VERSION_UNDEFINED, 104 SharedLibraryInfo.TYPE_DYNAMIC, new VersionedPackage(pkg.getPackageName(), 105 pkg.getLongVersionCode()), 106 null, null, false /* isNative */); 107 } 108 109 /** 110 * Return the dex metadata files for the given package as a map 111 * [code path -> dex metadata path]. 112 * 113 * NOTE: involves I/O checks. 114 */ getPackageDexMetadata(AndroidPackage pkg)115 public static Map<String, String> getPackageDexMetadata(AndroidPackage pkg) { 116 return DexMetadataHelper.buildPackageApkToDexMetadataMap 117 (AndroidPackageUtils.getAllCodePaths(pkg)); 118 } 119 120 /** 121 * Validate the dex metadata files installed for the given package. 122 * 123 * @throws PackageParserException in case of errors. 124 */ validatePackageDexMetadata(AndroidPackage pkg)125 public static void validatePackageDexMetadata(AndroidPackage pkg) 126 throws PackageParserException { 127 Collection<String> apkToDexMetadataList = getPackageDexMetadata(pkg).values(); 128 String packageName = pkg.getPackageName(); 129 long versionCode = pkg.toAppInfoWithoutState().longVersionCode; 130 for (String dexMetadata : apkToDexMetadataList) { 131 DexMetadataHelper.validateDexMetadataFile(dexMetadata, packageName, versionCode); 132 } 133 } 134 createNativeLibraryHandle(AndroidPackage pkg)135 public static NativeLibraryHelper.Handle createNativeLibraryHandle(AndroidPackage pkg) 136 throws IOException { 137 return NativeLibraryHelper.Handle.create( 138 AndroidPackageUtils.getAllCodePaths(pkg), 139 pkg.isMultiArch(), 140 pkg.isExtractNativeLibs(), 141 pkg.isDebuggable() 142 ); 143 } 144 canHaveOatDir(AndroidPackage pkg, boolean isUpdatedSystemApp)145 public static boolean canHaveOatDir(AndroidPackage pkg, boolean isUpdatedSystemApp) { 146 // The following app types CANNOT have oat directory 147 // - non-updated system apps, 148 // - incrementally installed apps. 149 if (pkg.isSystem() && !isUpdatedSystemApp) { 150 return false; 151 } 152 if (IncrementalManager.isIncrementalPath(pkg.getPath())) { 153 return false; 154 } 155 return true; 156 } 157 hasComponentClassName(AndroidPackage pkg, String className)158 public static boolean hasComponentClassName(AndroidPackage pkg, String className) { 159 List<ParsedActivity> activities = pkg.getActivities(); 160 int activitiesSize = activities.size(); 161 for (int index = 0; index < activitiesSize; index++) { 162 if (Objects.equals(className, activities.get(index).getName())) { 163 return true; 164 } 165 } 166 167 List<ParsedActivity> receivers = pkg.getReceivers(); 168 int receiversSize = receivers.size(); 169 for (int index = 0; index < receiversSize; index++) { 170 if (Objects.equals(className, receivers.get(index).getName())) { 171 return true; 172 } 173 } 174 175 List<ParsedProvider> providers = pkg.getProviders(); 176 int providersSize = providers.size(); 177 for (int index = 0; index < providersSize; index++) { 178 if (Objects.equals(className, providers.get(index).getName())) { 179 return true; 180 } 181 } 182 183 List<ParsedService> services = pkg.getServices(); 184 int servicesSize = services.size(); 185 for (int index = 0; index < servicesSize; index++) { 186 if (Objects.equals(className, services.get(index).getName())) { 187 return true; 188 } 189 } 190 191 List<ParsedInstrumentation> instrumentations = pkg.getInstrumentations(); 192 int instrumentationsSize = instrumentations.size(); 193 for (int index = 0; index < instrumentationsSize; index++) { 194 if (Objects.equals(className, instrumentations.get(index).getName())) { 195 return true; 196 } 197 } 198 199 return false; 200 } 201 isEncryptionAware(AndroidPackage pkg)202 public static boolean isEncryptionAware(AndroidPackage pkg) { 203 return pkg.isDirectBootAware() || pkg.isPartiallyDirectBootAware(); 204 } 205 isLibrary(AndroidPackage pkg)206 public static boolean isLibrary(AndroidPackage pkg) { 207 // TODO(b/135203078): Can parsing just enforce these always match? 208 return pkg.getStaticSharedLibName() != null || !pkg.getLibraryNames().isEmpty(); 209 } 210 getHiddenApiEnforcementPolicy(AndroidPackage pkg, @NonNull PackageSetting pkgSetting)211 public static int getHiddenApiEnforcementPolicy(AndroidPackage pkg, 212 @NonNull PackageSetting pkgSetting) { 213 boolean isAllowedToUseHiddenApis; 214 if (pkg.isSignedWithPlatformKey()) { 215 isAllowedToUseHiddenApis = true; 216 } else if (pkg.isSystem() || pkgSetting.getPkgState().isUpdatedSystemApp()) { 217 isAllowedToUseHiddenApis = pkg.isUsesNonSdkApi() 218 || SystemConfig.getInstance().getHiddenApiWhitelistedApps().contains( 219 pkg.getPackageName()); 220 } else { 221 isAllowedToUseHiddenApis = false; 222 } 223 224 if (isAllowedToUseHiddenApis) { 225 return ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED; 226 } 227 228 // TODO(b/135203078): Handle maybeUpdateHiddenApiEnforcementPolicy. Right now it's done 229 // entirely through ApplicationInfo and shouldn't touch this specific class, but that 230 // may not always hold true. 231 // if (mHiddenApiPolicy != ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT) { 232 // return mHiddenApiPolicy; 233 // } 234 return ApplicationInfo.HIDDEN_API_ENFORCEMENT_ENABLED; 235 } 236 getIcon(ParsingPackageRead pkg)237 public static int getIcon(ParsingPackageRead pkg) { 238 return (ParsingPackageUtils.sUseRoundIcon && pkg.getRoundIconRes() != 0) 239 ? pkg.getRoundIconRes() : pkg.getIconRes(); 240 } 241 getLongVersionCode(AndroidPackage pkg)242 public static long getLongVersionCode(AndroidPackage pkg) { 243 return PackageInfo.composeLongVersionCode(pkg.getVersionCodeMajor(), pkg.getVersionCode()); 244 } 245 246 /** 247 * Returns false iff the provided flags include the {@link PackageManager#MATCH_SYSTEM_ONLY} 248 * flag and the provided package is not a system package. Otherwise returns {@code true}. 249 */ isMatchForSystemOnly(AndroidPackage pkg, int flags)250 public static boolean isMatchForSystemOnly(AndroidPackage pkg, int flags) { 251 if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) { 252 return pkg.isSystem(); 253 } 254 return true; 255 } 256 getPrimaryCpuAbi(AndroidPackage pkg, @Nullable PackageSetting pkgSetting)257 public static String getPrimaryCpuAbi(AndroidPackage pkg, @Nullable PackageSetting pkgSetting) { 258 if (pkgSetting == null || TextUtils.isEmpty(pkgSetting.primaryCpuAbiString)) { 259 return pkg.getPrimaryCpuAbi(); 260 } 261 262 return pkgSetting.primaryCpuAbiString; 263 } 264 getSecondaryCpuAbi(AndroidPackage pkg, @Nullable PackageSetting pkgSetting)265 public static String getSecondaryCpuAbi(AndroidPackage pkg, 266 @Nullable PackageSetting pkgSetting) { 267 if (pkgSetting == null || TextUtils.isEmpty(pkgSetting.secondaryCpuAbiString)) { 268 return pkg.getSecondaryCpuAbi(); 269 } 270 271 return pkgSetting.secondaryCpuAbiString; 272 } 273 274 /** 275 * Returns the primary ABI as parsed from the package. Used only during parsing and derivation. 276 * Otherwise prefer {@link #getPrimaryCpuAbi(AndroidPackage, PackageSetting)}. 277 * 278 * TODO(b/135203078): Actually hide the method 279 * Placed in the utility to hide the method on the interface. 280 */ getRawPrimaryCpuAbi(AndroidPackage pkg)281 public static String getRawPrimaryCpuAbi(AndroidPackage pkg) { 282 return pkg.getPrimaryCpuAbi(); 283 } 284 285 /** 286 * Returns the secondary ABI as parsed from the package. Used only during parsing and 287 * derivation. Otherwise prefer {@link #getSecondaryCpuAbi(AndroidPackage, PackageSetting)}. 288 * 289 * TODO(b/135203078): Actually hide the method 290 * Placed in the utility to hide the method on the interface. 291 */ getRawSecondaryCpuAbi(AndroidPackage pkg)292 public static String getRawSecondaryCpuAbi(AndroidPackage pkg) { 293 return pkg.getSecondaryCpuAbi(); 294 } 295 getSeInfo(AndroidPackage pkg, @Nullable PackageSetting pkgSetting)296 public static String getSeInfo(AndroidPackage pkg, @Nullable PackageSetting pkgSetting) { 297 if (pkgSetting != null) { 298 String overrideSeInfo = pkgSetting.getPkgState().getOverrideSeInfo(); 299 if (!TextUtils.isEmpty(overrideSeInfo)) { 300 return overrideSeInfo; 301 } 302 } 303 return pkg.getSeInfo(); 304 } 305 } 306