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; 25 import android.content.pm.PackageParser.PackageParserException; 26 import android.content.pm.SharedLibraryInfo; 27 import android.content.pm.VersionedPackage; 28 import android.content.pm.dex.DexMetadataHelper; 29 import android.content.pm.parsing.ParsingPackageRead; 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.getBaseCodePath()); 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.getBaseCodePath()); 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); 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); 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 for (String dexMetadata : apkToDexMetadataList) { 129 DexMetadataHelper.validateDexMetadataFile(dexMetadata); 130 } 131 } 132 createNativeLibraryHandle(AndroidPackage pkg)133 public static NativeLibraryHelper.Handle createNativeLibraryHandle(AndroidPackage pkg) 134 throws IOException { 135 return NativeLibraryHelper.Handle.create( 136 AndroidPackageUtils.getAllCodePaths(pkg), 137 pkg.isMultiArch(), 138 pkg.isExtractNativeLibs(), 139 pkg.isDebuggable() 140 ); 141 } 142 canHaveOatDir(AndroidPackage pkg, boolean isUpdatedSystemApp)143 public static boolean canHaveOatDir(AndroidPackage pkg, boolean isUpdatedSystemApp) { 144 // The following app types CANNOT have oat directory 145 // - non-updated system apps, 146 // - incrementally installed apps. 147 if (pkg.isSystem() && !isUpdatedSystemApp) { 148 return false; 149 } 150 if (IncrementalManager.isIncrementalPath(pkg.getCodePath())) { 151 return false; 152 } 153 return true; 154 } 155 hasComponentClassName(AndroidPackage pkg, String className)156 public static boolean hasComponentClassName(AndroidPackage pkg, String className) { 157 List<ParsedActivity> activities = pkg.getActivities(); 158 int activitiesSize = activities.size(); 159 for (int index = 0; index < activitiesSize; index++) { 160 if (Objects.equals(className, activities.get(index).getName())) { 161 return true; 162 } 163 } 164 165 List<ParsedActivity> receivers = pkg.getReceivers(); 166 int receiversSize = receivers.size(); 167 for (int index = 0; index < receiversSize; index++) { 168 if (Objects.equals(className, receivers.get(index).getName())) { 169 return true; 170 } 171 } 172 173 List<ParsedProvider> providers = pkg.getProviders(); 174 int providersSize = providers.size(); 175 for (int index = 0; index < providersSize; index++) { 176 if (Objects.equals(className, providers.get(index).getName())) { 177 return true; 178 } 179 } 180 181 List<ParsedService> services = pkg.getServices(); 182 int servicesSize = services.size(); 183 for (int index = 0; index < servicesSize; index++) { 184 if (Objects.equals(className, services.get(index).getName())) { 185 return true; 186 } 187 } 188 189 List<ParsedInstrumentation> instrumentations = pkg.getInstrumentations(); 190 int instrumentationsSize = instrumentations.size(); 191 for (int index = 0; index < instrumentationsSize; index++) { 192 if (Objects.equals(className, instrumentations.get(index).getName())) { 193 return true; 194 } 195 } 196 197 return false; 198 } 199 isEncryptionAware(AndroidPackage pkg)200 public static boolean isEncryptionAware(AndroidPackage pkg) { 201 return pkg.isDirectBootAware() || pkg.isPartiallyDirectBootAware(); 202 } 203 isLibrary(AndroidPackage pkg)204 public static boolean isLibrary(AndroidPackage pkg) { 205 // TODO(b/135203078): Can parsing just enforce these always match? 206 return pkg.getStaticSharedLibName() != null || !pkg.getLibraryNames().isEmpty(); 207 } 208 getHiddenApiEnforcementPolicy(AndroidPackage pkg, @NonNull PackageSetting pkgSetting)209 public static int getHiddenApiEnforcementPolicy(AndroidPackage pkg, 210 @NonNull PackageSetting pkgSetting) { 211 boolean isAllowedToUseHiddenApis; 212 if (pkg.isSignedWithPlatformKey()) { 213 isAllowedToUseHiddenApis = true; 214 } else if (pkg.isSystem() || pkgSetting.getPkgState().isUpdatedSystemApp()) { 215 isAllowedToUseHiddenApis = pkg.isUsesNonSdkApi() 216 || SystemConfig.getInstance().getHiddenApiWhitelistedApps().contains( 217 pkg.getPackageName()); 218 } else { 219 isAllowedToUseHiddenApis = false; 220 } 221 222 if (isAllowedToUseHiddenApis) { 223 return ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED; 224 } 225 226 // TODO(b/135203078): Handle maybeUpdateHiddenApiEnforcementPolicy. Right now it's done 227 // entirely through ApplicationInfo and shouldn't touch this specific class, but that 228 // may not always hold true. 229 // if (mHiddenApiPolicy != ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT) { 230 // return mHiddenApiPolicy; 231 // } 232 return ApplicationInfo.HIDDEN_API_ENFORCEMENT_ENABLED; 233 } 234 getIcon(ParsingPackageRead pkg)235 public static int getIcon(ParsingPackageRead pkg) { 236 return (PackageParser.sUseRoundIcon && pkg.getRoundIconRes() != 0) 237 ? pkg.getRoundIconRes() : pkg.getIconRes(); 238 } 239 getLongVersionCode(AndroidPackage pkg)240 public static long getLongVersionCode(AndroidPackage pkg) { 241 return PackageInfo.composeLongVersionCode(pkg.getVersionCodeMajor(), pkg.getVersionCode()); 242 } 243 244 /** 245 * Returns false iff the provided flags include the {@link PackageManager#MATCH_SYSTEM_ONLY} 246 * flag and the provided package is not a system package. Otherwise returns {@code true}. 247 */ isMatchForSystemOnly(AndroidPackage pkg, int flags)248 public static boolean isMatchForSystemOnly(AndroidPackage pkg, int flags) { 249 if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) { 250 return pkg.isSystem(); 251 } 252 return true; 253 } 254 getPrimaryCpuAbi(AndroidPackage pkg, @Nullable PackageSetting pkgSetting)255 public static String getPrimaryCpuAbi(AndroidPackage pkg, @Nullable PackageSetting pkgSetting) { 256 if (pkgSetting == null || TextUtils.isEmpty(pkgSetting.primaryCpuAbiString)) { 257 return pkg.getPrimaryCpuAbi(); 258 } 259 260 return pkgSetting.primaryCpuAbiString; 261 } 262 getSecondaryCpuAbi(AndroidPackage pkg, @Nullable PackageSetting pkgSetting)263 public static String getSecondaryCpuAbi(AndroidPackage pkg, 264 @Nullable PackageSetting pkgSetting) { 265 if (pkgSetting == null || TextUtils.isEmpty(pkgSetting.secondaryCpuAbiString)) { 266 return pkg.getSecondaryCpuAbi(); 267 } 268 269 return pkgSetting.secondaryCpuAbiString; 270 } 271 272 /** 273 * Returns the primary ABI as parsed from the package. Used only during parsing and derivation. 274 * Otherwise prefer {@link #getPrimaryCpuAbi(AndroidPackage, PackageSetting)}. 275 * 276 * TODO(b/135203078): Actually hide the method 277 * Placed in the utility to hide the method on the interface. 278 */ getRawPrimaryCpuAbi(AndroidPackage pkg)279 public static String getRawPrimaryCpuAbi(AndroidPackage pkg) { 280 return pkg.getPrimaryCpuAbi(); 281 } 282 283 /** 284 * Returns the secondary ABI as parsed from the package. Used only during parsing and 285 * derivation. Otherwise prefer {@link #getSecondaryCpuAbi(AndroidPackage, PackageSetting)}. 286 * 287 * TODO(b/135203078): Actually hide the method 288 * Placed in the utility to hide the method on the interface. 289 */ getRawSecondaryCpuAbi(AndroidPackage pkg)290 public static String getRawSecondaryCpuAbi(AndroidPackage pkg) { 291 return pkg.getSecondaryCpuAbi(); 292 } 293 getSeInfo(AndroidPackage pkg, @Nullable PackageSetting pkgSetting)294 public static String getSeInfo(AndroidPackage pkg, @Nullable PackageSetting pkgSetting) { 295 if (pkgSetting != null) { 296 String overrideSeInfo = pkgSetting.getPkgState().getOverrideSeInfo(); 297 if (!TextUtils.isEmpty(overrideSeInfo)) { 298 return overrideSeInfo; 299 } 300 } 301 return pkg.getSeInfo(); 302 } 303 } 304