Patrick Baumann | f62817e | 2019-05-31 10:46:53 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2019 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; |
| 18 | |
| 19 | import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR; |
| 20 | import static android.content.pm.PackageParser.isApkFile; |
| 21 | import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; |
| 22 | |
| 23 | import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME; |
| 24 | import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME; |
| 25 | import static com.android.server.pm.InstructionSets.getPreferredInstructionSet; |
| 26 | import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet; |
| 27 | |
| 28 | import android.annotation.Nullable; |
| 29 | import android.content.pm.ApplicationInfo; |
| 30 | import android.content.pm.PackageManager; |
| 31 | import android.content.pm.PackageParser; |
| 32 | import android.os.Build; |
| 33 | import android.os.Environment; |
| 34 | import android.os.FileUtils; |
| 35 | import android.os.Trace; |
| 36 | import android.text.TextUtils; |
Patrick Baumann | 235e52e | 2019-07-25 16:28:45 -0700 | [diff] [blame] | 37 | import android.util.Pair; |
Patrick Baumann | f62817e | 2019-05-31 10:46:53 -0700 | [diff] [blame] | 38 | import android.util.Slog; |
| 39 | |
| 40 | import com.android.internal.content.NativeLibraryHelper; |
| 41 | import com.android.internal.util.ArrayUtils; |
| 42 | |
| 43 | import dalvik.system.VMRuntime; |
| 44 | |
| 45 | import libcore.io.IoUtils; |
| 46 | |
| 47 | import java.io.File; |
| 48 | import java.io.IOException; |
Patrick Baumann | f62817e | 2019-05-31 10:46:53 -0700 | [diff] [blame] | 49 | import java.util.Set; |
| 50 | |
| 51 | final class PackageAbiHelperImpl implements PackageAbiHelper { |
| 52 | |
Patrick Baumann | f62817e | 2019-05-31 10:46:53 -0700 | [diff] [blame] | 53 | private static String calculateBundledApkRoot(final String codePathString) { |
| 54 | final File codePath = new File(codePathString); |
| 55 | final File codeRoot; |
| 56 | if (FileUtils.contains(Environment.getRootDirectory(), codePath)) { |
| 57 | codeRoot = Environment.getRootDirectory(); |
| 58 | } else if (FileUtils.contains(Environment.getOemDirectory(), codePath)) { |
| 59 | codeRoot = Environment.getOemDirectory(); |
| 60 | } else if (FileUtils.contains(Environment.getVendorDirectory(), codePath)) { |
| 61 | codeRoot = Environment.getVendorDirectory(); |
| 62 | } else if (FileUtils.contains(Environment.getOdmDirectory(), codePath)) { |
| 63 | codeRoot = Environment.getOdmDirectory(); |
| 64 | } else if (FileUtils.contains(Environment.getProductDirectory(), codePath)) { |
| 65 | codeRoot = Environment.getProductDirectory(); |
| 66 | } else if (FileUtils.contains(Environment.getSystemExtDirectory(), codePath)) { |
| 67 | codeRoot = Environment.getSystemExtDirectory(); |
| 68 | } else if (FileUtils.contains(Environment.getOdmDirectory(), codePath)) { |
| 69 | codeRoot = Environment.getOdmDirectory(); |
Dario Freni | 7d038cd | 2019-10-09 15:43:38 +0100 | [diff] [blame] | 70 | } else if (FileUtils.contains(Environment.getApexDirectory(), codePath)) { |
| 71 | String fullPath = codePath.getAbsolutePath(); |
| 72 | String[] parts = fullPath.split(File.separator); |
| 73 | if (parts.length > 2) { |
| 74 | codeRoot = new File(parts[1] + File.separator + parts[2]); |
| 75 | } else { |
| 76 | Slog.w(PackageManagerService.TAG, "Can't canonicalize code path " + codePath); |
| 77 | codeRoot = Environment.getApexDirectory(); |
| 78 | } |
Patrick Baumann | f62817e | 2019-05-31 10:46:53 -0700 | [diff] [blame] | 79 | } else { |
| 80 | // Unrecognized code path; take its top real segment as the apk root: |
| 81 | // e.g. /something/app/blah.apk => /something |
| 82 | try { |
| 83 | File f = codePath.getCanonicalFile(); |
| 84 | File parent = f.getParentFile(); // non-null because codePath is a file |
| 85 | File tmp; |
| 86 | while ((tmp = parent.getParentFile()) != null) { |
| 87 | f = parent; |
| 88 | parent = tmp; |
| 89 | } |
| 90 | codeRoot = f; |
| 91 | Slog.w(PackageManagerService.TAG, "Unrecognized code path " |
| 92 | + codePath + " - using " + codeRoot); |
| 93 | } catch (IOException e) { |
| 94 | // Can't canonicalize the code path -- shenanigans? |
| 95 | Slog.w(PackageManagerService.TAG, "Can't canonicalize code path " + codePath); |
| 96 | return Environment.getRootDirectory().getPath(); |
| 97 | } |
| 98 | } |
| 99 | return codeRoot.getPath(); |
| 100 | } |
| 101 | |
| 102 | // Utility method that returns the relative package path with respect |
| 103 | // to the installation directory. Like say for /data/data/com.test-1.apk |
| 104 | // string com.test-1 is returned. |
| 105 | private static String deriveCodePathName(String codePath) { |
| 106 | if (codePath == null) { |
| 107 | return null; |
| 108 | } |
| 109 | final File codeFile = new File(codePath); |
| 110 | final String name = codeFile.getName(); |
| 111 | if (codeFile.isDirectory()) { |
| 112 | return name; |
| 113 | } else if (name.endsWith(".apk") || name.endsWith(".tmp")) { |
| 114 | final int lastDot = name.lastIndexOf('.'); |
| 115 | return name.substring(0, lastDot); |
| 116 | } else { |
| 117 | Slog.w(PackageManagerService.TAG, "Odd, " + codePath + " doesn't look like an APK"); |
| 118 | return null; |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | private static void maybeThrowExceptionForMultiArchCopy(String message, int copyRet) throws |
| 123 | PackageManagerException { |
| 124 | if (copyRet < 0) { |
| 125 | if (copyRet != PackageManager.NO_NATIVE_LIBRARIES |
| 126 | && copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) { |
| 127 | throw new PackageManagerException(copyRet, message); |
| 128 | } |
| 129 | } |
| 130 | } |
| 131 | |
Patrick Baumann | 235e52e | 2019-07-25 16:28:45 -0700 | [diff] [blame] | 132 | @Override |
| 133 | public NativeLibraryPaths getNativeLibraryPaths( |
| 134 | PackageParser.Package pkg, File appLib32InstallDir) { |
| 135 | return getNativeLibraryPaths(new Abis(pkg), appLib32InstallDir, pkg.codePath, |
| 136 | pkg.applicationInfo.sourceDir, pkg.applicationInfo.isSystemApp(), |
| 137 | pkg.applicationInfo.isUpdatedSystemApp()); |
| 138 | } |
| 139 | |
| 140 | private static NativeLibraryPaths getNativeLibraryPaths(final Abis abis, |
| 141 | final File appLib32InstallDir, final String codePath, final String sourceDir, |
| 142 | final boolean isSystemApp, final boolean isUpdatedSystemApp) { |
| 143 | final File codeFile = new File(codePath); |
| 144 | final boolean bundledApp = isSystemApp && !isUpdatedSystemApp; |
| 145 | |
| 146 | final String nativeLibraryRootDir; |
| 147 | final boolean nativeLibraryRootRequiresIsa; |
| 148 | final String nativeLibraryDir; |
| 149 | final String secondaryNativeLibraryDir; |
| 150 | |
| 151 | if (isApkFile(codeFile)) { |
| 152 | // Monolithic install |
| 153 | if (bundledApp) { |
| 154 | // If "/system/lib64/apkname" exists, assume that is the per-package |
| 155 | // native library directory to use; otherwise use "/system/lib/apkname". |
| 156 | final String apkRoot = calculateBundledApkRoot(sourceDir); |
| 157 | final boolean is64Bit = VMRuntime.is64BitInstructionSet( |
| 158 | getPrimaryInstructionSet(abis)); |
| 159 | |
| 160 | // This is a bundled system app so choose the path based on the ABI. |
| 161 | // if it's a 64 bit abi, use lib64 otherwise use lib32. Note that this |
| 162 | // is just the default path. |
| 163 | final String apkName = deriveCodePathName(codePath); |
| 164 | final String libDir = is64Bit ? LIB64_DIR_NAME : LIB_DIR_NAME; |
| 165 | nativeLibraryRootDir = Environment.buildPath(new File(apkRoot), libDir, |
| 166 | apkName).getAbsolutePath(); |
| 167 | |
| 168 | if (abis.secondary != null) { |
| 169 | final String secondaryLibDir = is64Bit ? LIB_DIR_NAME : LIB64_DIR_NAME; |
| 170 | secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot), |
| 171 | secondaryLibDir, apkName).getAbsolutePath(); |
| 172 | } else { |
| 173 | secondaryNativeLibraryDir = null; |
| 174 | } |
| 175 | } else { |
| 176 | final String apkName = deriveCodePathName(codePath); |
| 177 | nativeLibraryRootDir = new File(appLib32InstallDir, apkName) |
| 178 | .getAbsolutePath(); |
| 179 | secondaryNativeLibraryDir = null; |
| 180 | } |
| 181 | |
| 182 | nativeLibraryRootRequiresIsa = false; |
| 183 | nativeLibraryDir = nativeLibraryRootDir; |
| 184 | } else { |
| 185 | // Cluster install |
| 186 | nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath(); |
| 187 | nativeLibraryRootRequiresIsa = true; |
| 188 | |
| 189 | nativeLibraryDir = new File(nativeLibraryRootDir, |
| 190 | getPrimaryInstructionSet(abis)).getAbsolutePath(); |
| 191 | |
| 192 | if (abis.secondary != null) { |
| 193 | secondaryNativeLibraryDir = new File(nativeLibraryRootDir, |
| 194 | VMRuntime.getInstructionSet(abis.secondary)).getAbsolutePath(); |
| 195 | } else { |
| 196 | secondaryNativeLibraryDir = null; |
| 197 | } |
| 198 | } |
| 199 | return new NativeLibraryPaths(nativeLibraryRootDir, nativeLibraryRootRequiresIsa, |
| 200 | nativeLibraryDir, secondaryNativeLibraryDir); |
| 201 | } |
| 202 | |
| 203 | @Override |
| 204 | public Abis getBundledAppAbis(PackageParser.Package pkg) { |
| 205 | final String apkName = deriveCodePathName(pkg.applicationInfo.getCodePath()); |
| 206 | |
| 207 | // If "/system/lib64/apkname" exists, assume that is the per-package |
| 208 | // native library directory to use; otherwise use "/system/lib/apkname". |
| 209 | final String apkRoot = calculateBundledApkRoot(pkg.applicationInfo.sourceDir); |
| 210 | final Abis abis = getBundledAppAbi(pkg, apkRoot, apkName); |
| 211 | return abis; |
| 212 | } |
| 213 | |
| 214 | /** |
| 215 | * Deduces the ABI of a bundled app and sets the relevant fields on the |
| 216 | * parsed pkg object. |
| 217 | * |
| 218 | * @param apkRoot the root of the installed apk, something like {@code /system} or |
| 219 | * {@code /oem} under which system libraries are installed. |
| 220 | * @param apkName the name of the installed package. |
| 221 | */ |
| 222 | private Abis getBundledAppAbi(PackageParser.Package pkg, String apkRoot, String apkName) { |
| 223 | final File codeFile = new File(pkg.codePath); |
| 224 | |
| 225 | final boolean has64BitLibs; |
| 226 | final boolean has32BitLibs; |
| 227 | |
| 228 | final String primaryCpuAbi; |
| 229 | final String secondaryCpuAbi; |
| 230 | if (isApkFile(codeFile)) { |
| 231 | // Monolithic install |
| 232 | has64BitLibs = |
| 233 | (new File(apkRoot, new File(LIB64_DIR_NAME, apkName).getPath())).exists(); |
| 234 | has32BitLibs = (new File(apkRoot, new File(LIB_DIR_NAME, apkName).getPath())).exists(); |
| 235 | } else { |
| 236 | // Cluster install |
| 237 | final File rootDir = new File(codeFile, LIB_DIR_NAME); |
| 238 | if (!ArrayUtils.isEmpty(Build.SUPPORTED_64_BIT_ABIS) |
| 239 | && !TextUtils.isEmpty(Build.SUPPORTED_64_BIT_ABIS[0])) { |
| 240 | final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_64_BIT_ABIS[0]); |
| 241 | has64BitLibs = (new File(rootDir, isa)).exists(); |
| 242 | } else { |
| 243 | has64BitLibs = false; |
| 244 | } |
| 245 | if (!ArrayUtils.isEmpty(Build.SUPPORTED_32_BIT_ABIS) |
| 246 | && !TextUtils.isEmpty(Build.SUPPORTED_32_BIT_ABIS[0])) { |
| 247 | final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_32_BIT_ABIS[0]); |
| 248 | has32BitLibs = (new File(rootDir, isa)).exists(); |
| 249 | } else { |
| 250 | has32BitLibs = false; |
| 251 | } |
| 252 | } |
| 253 | |
| 254 | if (has64BitLibs && !has32BitLibs) { |
| 255 | // The package has 64 bit libs, but not 32 bit libs. Its primary |
| 256 | // ABI should be 64 bit. We can safely assume here that the bundled |
| 257 | // native libraries correspond to the most preferred ABI in the list. |
| 258 | |
| 259 | primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; |
| 260 | secondaryCpuAbi = null; |
| 261 | } else if (has32BitLibs && !has64BitLibs) { |
| 262 | // The package has 32 bit libs but not 64 bit libs. Its primary |
| 263 | // ABI should be 32 bit. |
| 264 | |
| 265 | primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; |
| 266 | secondaryCpuAbi = null; |
| 267 | } else if (has32BitLibs && has64BitLibs) { |
| 268 | // The application has both 64 and 32 bit bundled libraries. We check |
| 269 | // here that the app declares multiArch support, and warn if it doesn't. |
| 270 | // |
| 271 | // We will be lenient here and record both ABIs. The primary will be the |
| 272 | // ABI that's higher on the list, i.e, a device that's configured to prefer |
| 273 | // 64 bit apps will see a 64 bit primary ABI, |
| 274 | |
| 275 | if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) == 0) { |
| 276 | Slog.e(PackageManagerService.TAG, |
| 277 | "Package " + pkg + " has multiple bundled libs, but is not multiarch."); |
| 278 | } |
| 279 | |
| 280 | if (VMRuntime.is64BitInstructionSet(getPreferredInstructionSet())) { |
| 281 | primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; |
| 282 | secondaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; |
| 283 | } else { |
| 284 | primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; |
| 285 | secondaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; |
| 286 | } |
| 287 | } else { |
| 288 | primaryCpuAbi = null; |
| 289 | secondaryCpuAbi = null; |
| 290 | } |
| 291 | return new Abis(primaryCpuAbi, secondaryCpuAbi); |
| 292 | } |
| 293 | |
| 294 | @Override |
| 295 | public Pair<Abis, NativeLibraryPaths> derivePackageAbi( |
| 296 | PackageParser.Package pkg, String cpuAbiOverride, boolean extractLibs) |
| 297 | throws PackageManagerException { |
| 298 | // Give ourselves some initial paths; we'll come back for another |
| 299 | // pass once we've determined ABI below. |
| 300 | final NativeLibraryPaths initialLibraryPaths = getNativeLibraryPaths(new Abis(pkg), |
| 301 | PackageManagerService.sAppLib32InstallDir, pkg.codePath, |
| 302 | pkg.applicationInfo.sourceDir, pkg.applicationInfo.isSystemApp(), |
| 303 | pkg.applicationInfo.isUpdatedSystemApp()); |
| 304 | |
| 305 | // We shouldn't attempt to extract libs from system app when it was not updated. |
| 306 | if (PackageManagerService.isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) { |
| 307 | extractLibs = false; |
| 308 | } |
| 309 | |
| 310 | final String nativeLibraryRootStr = initialLibraryPaths.nativeLibraryRootDir; |
| 311 | final boolean useIsaSpecificSubdirs = initialLibraryPaths.nativeLibraryRootRequiresIsa; |
| 312 | |
| 313 | String primaryCpuAbi = null; |
| 314 | String secondaryCpuAbi = null; |
| 315 | |
| 316 | NativeLibraryHelper.Handle handle = null; |
| 317 | try { |
| 318 | handle = NativeLibraryHelper.Handle.create(pkg); |
| 319 | // TODO(multiArch): This can be null for apps that didn't go through the |
| 320 | // usual installation process. We can calculate it again, like we |
| 321 | // do during install time. |
| 322 | // |
| 323 | // TODO(multiArch): Why do we need to rescan ASEC apps again ? It seems totally |
| 324 | // unnecessary. |
| 325 | final File nativeLibraryRoot = new File(nativeLibraryRootStr); |
| 326 | |
| 327 | // Null out the abis so that they can be recalculated. |
| 328 | primaryCpuAbi = null; |
| 329 | secondaryCpuAbi = null; |
| 330 | if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) != 0) { |
| 331 | // Warn if we've set an abiOverride for multi-lib packages.. |
| 332 | // By definition, we need to copy both 32 and 64 bit libraries for |
| 333 | // such packages. |
| 334 | if (pkg.cpuAbiOverride != null |
| 335 | && !NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(pkg.cpuAbiOverride)) { |
| 336 | Slog.w(PackageManagerService.TAG, |
| 337 | "Ignoring abiOverride for multi arch application."); |
| 338 | } |
| 339 | |
| 340 | int abi32 = PackageManager.NO_NATIVE_LIBRARIES; |
| 341 | int abi64 = PackageManager.NO_NATIVE_LIBRARIES; |
| 342 | if (Build.SUPPORTED_32_BIT_ABIS.length > 0) { |
| 343 | if (extractLibs) { |
| 344 | Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries"); |
| 345 | abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle, |
| 346 | nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS, |
| 347 | useIsaSpecificSubdirs); |
| 348 | } else { |
| 349 | Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi"); |
| 350 | abi32 = NativeLibraryHelper.findSupportedAbi( |
| 351 | handle, Build.SUPPORTED_32_BIT_ABIS); |
| 352 | } |
| 353 | Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); |
| 354 | } |
| 355 | |
| 356 | // Shared library native code should be in the APK zip aligned |
| 357 | if (abi32 >= 0 && pkg.isLibrary() && extractLibs) { |
| 358 | throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, |
| 359 | "Shared library native lib extraction not supported"); |
| 360 | } |
| 361 | |
| 362 | maybeThrowExceptionForMultiArchCopy( |
| 363 | "Error unpackaging 32 bit native libs for multiarch app.", abi32); |
| 364 | |
| 365 | if (Build.SUPPORTED_64_BIT_ABIS.length > 0) { |
| 366 | if (extractLibs) { |
| 367 | Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries"); |
| 368 | abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle, |
| 369 | nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS, |
| 370 | useIsaSpecificSubdirs); |
| 371 | } else { |
| 372 | Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi"); |
| 373 | abi64 = NativeLibraryHelper.findSupportedAbi( |
| 374 | handle, Build.SUPPORTED_64_BIT_ABIS); |
| 375 | } |
| 376 | Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); |
| 377 | } |
| 378 | |
| 379 | maybeThrowExceptionForMultiArchCopy( |
| 380 | "Error unpackaging 64 bit native libs for multiarch app.", abi64); |
| 381 | |
| 382 | if (abi64 >= 0) { |
| 383 | // Shared library native libs should be in the APK zip aligned |
| 384 | if (extractLibs && pkg.isLibrary()) { |
| 385 | throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, |
| 386 | "Shared library native lib extraction not supported"); |
| 387 | } |
| 388 | primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[abi64]; |
| 389 | } |
| 390 | |
| 391 | if (abi32 >= 0) { |
| 392 | final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32]; |
| 393 | if (abi64 >= 0) { |
| 394 | if (pkg.use32bitAbi) { |
| 395 | secondaryCpuAbi = primaryCpuAbi; |
| 396 | primaryCpuAbi = abi; |
| 397 | } else { |
| 398 | secondaryCpuAbi = abi; |
| 399 | } |
| 400 | } else { |
| 401 | primaryCpuAbi = abi; |
| 402 | } |
| 403 | } |
| 404 | } else { |
| 405 | String[] abiList = (cpuAbiOverride != null) |
| 406 | ? new String[]{cpuAbiOverride} : Build.SUPPORTED_ABIS; |
| 407 | |
| 408 | // Enable gross and lame hacks for apps that are built with old |
| 409 | // SDK tools. We must scan their APKs for renderscript bitcode and |
| 410 | // not launch them if it's present. Don't bother checking on devices |
| 411 | // that don't have 64 bit support. |
| 412 | boolean needsRenderScriptOverride = false; |
| 413 | if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null |
| 414 | && NativeLibraryHelper.hasRenderscriptBitcode(handle)) { |
| 415 | abiList = Build.SUPPORTED_32_BIT_ABIS; |
| 416 | needsRenderScriptOverride = true; |
| 417 | } |
| 418 | |
| 419 | final int copyRet; |
| 420 | if (extractLibs) { |
| 421 | Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries"); |
| 422 | copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle, |
| 423 | nativeLibraryRoot, abiList, useIsaSpecificSubdirs); |
| 424 | } else { |
| 425 | Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi"); |
| 426 | copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList); |
| 427 | } |
| 428 | Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); |
| 429 | |
| 430 | if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) { |
| 431 | throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, |
| 432 | "Error unpackaging native libs for app, errorCode=" + copyRet); |
| 433 | } |
| 434 | |
| 435 | if (copyRet >= 0) { |
| 436 | // Shared libraries that have native libs must be multi-architecture |
| 437 | if (pkg.isLibrary()) { |
| 438 | throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, |
| 439 | "Shared library with native libs must be multiarch"); |
| 440 | } |
| 441 | primaryCpuAbi = abiList[copyRet]; |
| 442 | } else if (copyRet == PackageManager.NO_NATIVE_LIBRARIES |
| 443 | && cpuAbiOverride != null) { |
| 444 | primaryCpuAbi = cpuAbiOverride; |
| 445 | } else if (needsRenderScriptOverride) { |
| 446 | primaryCpuAbi = abiList[0]; |
| 447 | } |
| 448 | } |
| 449 | } catch (IOException ioe) { |
| 450 | Slog.e(PackageManagerService.TAG, "Unable to get canonical file " + ioe.toString()); |
| 451 | } finally { |
| 452 | IoUtils.closeQuietly(handle); |
| 453 | } |
| 454 | |
| 455 | // Now that we've calculated the ABIs and determined if it's an internal app, |
| 456 | // we will go ahead and populate the nativeLibraryPath. |
| 457 | |
| 458 | final Abis abis = new Abis(primaryCpuAbi, secondaryCpuAbi); |
| 459 | return new Pair<>(abis, |
| 460 | getNativeLibraryPaths(abis, PackageManagerService.sAppLib32InstallDir, |
| 461 | pkg.codePath, pkg.applicationInfo.sourceDir, |
| 462 | pkg.applicationInfo.isSystemApp(), |
| 463 | pkg.applicationInfo.isUpdatedSystemApp())); |
| 464 | } |
| 465 | |
| 466 | /** |
| 467 | * Adjusts ABIs for a set of packages belonging to a shared user so that they all match. |
| 468 | * i.e, so that all packages can be run inside a single process if required. |
| 469 | * |
| 470 | * Optionally, callers can pass in a parsed package via {@code newPackage} in which case |
| 471 | * this function will either try and make the ABI for all packages in |
| 472 | * {@code packagesForUser} match {@code scannedPackage} or will update the ABI of |
| 473 | * {@code scannedPackage} to match the ABI selected for {@code packagesForUser}. This |
| 474 | * variant is used when installing or updating a package that belongs to a shared user. |
| 475 | * |
| 476 | * NOTE: We currently only match for the primary CPU abi string. Matching the secondary |
| 477 | * adds unnecessary complexity. |
| 478 | */ |
| 479 | @Override |
| 480 | @Nullable |
| 481 | public String getAdjustedAbiForSharedUser( |
| 482 | Set<PackageSetting> packagesForUser, PackageParser.Package scannedPackage) { |
| 483 | String requiredInstructionSet = null; |
| 484 | if (scannedPackage != null && scannedPackage.applicationInfo.primaryCpuAbi != null) { |
| 485 | requiredInstructionSet = VMRuntime.getInstructionSet( |
| 486 | scannedPackage.applicationInfo.primaryCpuAbi); |
| 487 | } |
| 488 | |
| 489 | PackageSetting requirer = null; |
| 490 | for (PackageSetting ps : packagesForUser) { |
| 491 | // If packagesForUser contains scannedPackage, we skip it. This will happen |
| 492 | // when scannedPackage is an update of an existing package. Without this check, |
| 493 | // we will never be able to change the ABI of any package belonging to a shared |
| 494 | // user, even if it's compatible with other packages. |
| 495 | if (scannedPackage != null && scannedPackage.packageName.equals(ps.name)) { |
| 496 | continue; |
| 497 | } |
| 498 | if (ps.primaryCpuAbiString == null) { |
| 499 | continue; |
| 500 | } |
| 501 | |
| 502 | final String instructionSet = |
| 503 | VMRuntime.getInstructionSet(ps.primaryCpuAbiString); |
| 504 | if (requiredInstructionSet != null && !requiredInstructionSet.equals(instructionSet)) { |
| 505 | // We have a mismatch between instruction sets (say arm vs arm64) warn about |
| 506 | // this but there's not much we can do. |
| 507 | String errorMessage = "Instruction set mismatch, " |
| 508 | + ((requirer == null) ? "[caller]" : requirer) |
| 509 | + " requires " + requiredInstructionSet + " whereas " + ps |
| 510 | + " requires " + instructionSet; |
| 511 | Slog.w(PackageManagerService.TAG, errorMessage); |
| 512 | } |
| 513 | |
| 514 | if (requiredInstructionSet == null) { |
| 515 | requiredInstructionSet = instructionSet; |
| 516 | requirer = ps; |
| 517 | } |
| 518 | } |
| 519 | |
| 520 | if (requiredInstructionSet == null) { |
| 521 | return null; |
| 522 | } |
| 523 | final String adjustedAbi; |
| 524 | if (requirer != null) { |
| 525 | // requirer != null implies that either scannedPackage was null or that |
| 526 | // scannedPackage did not require an ABI, in which case we have to adjust |
| 527 | // scannedPackage to match the ABI of the set (which is the same as |
| 528 | // requirer's ABI) |
| 529 | adjustedAbi = requirer.primaryCpuAbiString; |
| 530 | } else { |
| 531 | // requirer == null implies that we're updating all ABIs in the set to |
| 532 | // match scannedPackage. |
| 533 | adjustedAbi = scannedPackage.applicationInfo.primaryCpuAbi; |
| 534 | } |
| 535 | return adjustedAbi; |
| 536 | } |
Patrick Baumann | f62817e | 2019-05-31 10:46:53 -0700 | [diff] [blame] | 537 | } |