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_CPU_ABI_INCOMPATIBLE;
20 import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
21 import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
22 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
23 import static android.os.incremental.IncrementalManager.isIncrementalPath;
24 
25 import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME;
26 import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
27 import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
28 import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
29 
30 import android.annotation.NonNull;
31 import android.annotation.Nullable;
32 import android.content.pm.Flags;
33 import android.content.pm.PackageManager;
34 import android.os.Build;
35 import android.os.Environment;
36 import android.os.FileUtils;
37 import android.os.SystemProperties;
38 import android.os.Trace;
39 import android.text.TextUtils;
40 import android.util.ArraySet;
41 import android.util.Pair;
42 import android.util.Slog;
43 
44 import com.android.internal.content.NativeLibraryHelper;
45 import com.android.internal.util.ArrayUtils;
46 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
47 import com.android.server.pm.pkg.AndroidPackage;
48 import com.android.server.pm.pkg.PackageStateInternal;
49 
50 import dalvik.system.VMRuntime;
51 
52 import libcore.io.IoUtils;
53 
54 import java.io.File;
55 import java.io.IOException;
56 import java.util.ArrayList;
57 import java.util.List;
58 
59 final class PackageAbiHelperImpl implements PackageAbiHelper {
60 
61     @Nullable
62     private static String[] sNativelySupported32BitAbis = null;
63     @Nullable
64     private static String[] sNativelySupported64BitAbis = null;
65 
calculateBundledApkRoot(final String codePathString)66     private static String calculateBundledApkRoot(final String codePathString) {
67         final File codePath = new File(codePathString);
68         final File codeRoot;
69         if (FileUtils.contains(Environment.getRootDirectory(), codePath)) {
70             codeRoot = Environment.getRootDirectory();
71         } else if (FileUtils.contains(Environment.getOemDirectory(), codePath)) {
72             codeRoot = Environment.getOemDirectory();
73         } else if (FileUtils.contains(Environment.getVendorDirectory(), codePath)) {
74             codeRoot = Environment.getVendorDirectory();
75         } else if (FileUtils.contains(Environment.getOdmDirectory(), codePath)) {
76             codeRoot = Environment.getOdmDirectory();
77         } else if (FileUtils.contains(Environment.getProductDirectory(), codePath)) {
78             codeRoot = Environment.getProductDirectory();
79         } else if (FileUtils.contains(Environment.getSystemExtDirectory(), codePath)) {
80             codeRoot = Environment.getSystemExtDirectory();
81         } else if (FileUtils.contains(Environment.getOdmDirectory(), codePath)) {
82             codeRoot = Environment.getOdmDirectory();
83         } else if (FileUtils.contains(Environment.getApexDirectory(), codePath)) {
84             String fullPath = codePath.getAbsolutePath();
85             String[] parts = fullPath.split(File.separator);
86             if (parts.length > 2) {
87                 codeRoot = new File(parts[1] + File.separator + parts[2]);
88             } else {
89                 Slog.w(PackageManagerService.TAG, "Can't canonicalize code path " + codePath);
90                 codeRoot = Environment.getApexDirectory();
91             }
92         } else {
93             // Unrecognized code path; take its top real segment as the apk root:
94             // e.g. /something/app/blah.apk => /something
95             try {
96                 File f = codePath.getCanonicalFile();
97                 File parent = f.getParentFile();    // non-null because codePath is a file
98                 File tmp;
99                 while ((tmp = parent.getParentFile()) != null) {
100                     f = parent;
101                     parent = tmp;
102                 }
103                 codeRoot = f;
104                 Slog.w(PackageManagerService.TAG, "Unrecognized code path "
105                         + codePath + " - using " + codeRoot);
106             } catch (IOException e) {
107                 // Can't canonicalize the code path -- shenanigans?
108                 Slog.w(PackageManagerService.TAG, "Can't canonicalize code path " + codePath);
109                 return Environment.getRootDirectory().getPath();
110             }
111         }
112         return codeRoot.getPath();
113     }
114 
115     // Utility method that returns the relative package path with respect
116     // to the installation directory. Like say for /data/data/com.test-1.apk
117     // string com.test-1 is returned.
deriveCodePathName(String codePath)118     private static String deriveCodePathName(String codePath) {
119         if (codePath == null) {
120             return null;
121         }
122         final File codeFile = new File(codePath);
123         final String name = codeFile.getName();
124         if (codeFile.isDirectory()) {
125             return name;
126         } else if (name.endsWith(".apk") || name.endsWith(".tmp")) {
127             final int lastDot = name.lastIndexOf('.');
128             return name.substring(0, lastDot);
129         } else {
130             Slog.w(PackageManagerService.TAG, "Odd, " + codePath + " doesn't look like an APK");
131             return null;
132         }
133     }
134 
maybeThrowExceptionForMultiArchCopy(String message, int copyRet, boolean forceMatch)135     private static void maybeThrowExceptionForMultiArchCopy(String message, int copyRet,
136             boolean forceMatch) throws PackageManagerException {
137         if (copyRet < 0) {
138             if (copyRet != PackageManager.NO_NATIVE_LIBRARIES
139                     && copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
140                 throw new PackageManagerException(copyRet, message);
141             }
142 
143             if (forceMatch && copyRet == PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
144                 throw new PackageManagerException(
145                         PackageManager.INSTALL_FAILED_MULTI_ARCH_NOT_MATCH_ALL_NATIVE_ABIS,
146                         "The multiArch app's native libs don't support all the natively"
147                                 + " supported ABIs of the device.");
148             }
149         }
150     }
151 
152     @Override
deriveNativeLibraryPaths(AndroidPackage pkg, boolean isSystemApp, boolean isUpdatedSystemApp, File appLib32InstallDir)153     public NativeLibraryPaths deriveNativeLibraryPaths(AndroidPackage pkg, boolean isSystemApp,
154             boolean isUpdatedSystemApp, File appLib32InstallDir) {
155         // Trying to derive the paths, thus need the raw ABI info from the parsed package, and the
156         // current state in PackageSetting is irrelevant.
157         return deriveNativeLibraryPaths(new Abis(AndroidPackageUtils.getRawPrimaryCpuAbi(pkg),
158                 AndroidPackageUtils.getRawSecondaryCpuAbi(pkg)), appLib32InstallDir, pkg.getPath(),
159                 pkg.getBaseApkPath(), isSystemApp, isUpdatedSystemApp);
160     }
161 
deriveNativeLibraryPaths(final Abis abis, final File appLib32InstallDir, final String codePath, final String sourceDir, final boolean isSystemApp, final boolean isUpdatedSystemApp)162     private static NativeLibraryPaths deriveNativeLibraryPaths(final Abis abis,
163             final File appLib32InstallDir, final String codePath, final String sourceDir,
164             final boolean isSystemApp, final boolean isUpdatedSystemApp) {
165         final File codeFile = new File(codePath);
166         final boolean bundledApp = isSystemApp && !isUpdatedSystemApp;
167 
168         final String nativeLibraryRootDir;
169         final boolean nativeLibraryRootRequiresIsa;
170         final String nativeLibraryDir;
171         final String secondaryNativeLibraryDir;
172 
173         if (isApkFile(codeFile)) {
174             // Monolithic install
175             if (bundledApp) {
176                 // If "/system/lib64/apkname" exists, assume that is the per-package
177                 // native library directory to use; otherwise use "/system/lib/apkname".
178                 final String apkRoot = calculateBundledApkRoot(sourceDir);
179                 final boolean is64Bit = VMRuntime.is64BitInstructionSet(
180                         getPrimaryInstructionSet(abis));
181 
182                 // This is a bundled system app so choose the path based on the ABI.
183                 // if it's a 64 bit abi, use lib64 otherwise use lib32. Note that this
184                 // is just the default path.
185                 final String apkName = deriveCodePathName(codePath);
186                 final String libDir = is64Bit ? LIB64_DIR_NAME : LIB_DIR_NAME;
187                 nativeLibraryRootDir = Environment.buildPath(new File(apkRoot), libDir,
188                         apkName).getAbsolutePath();
189 
190                 if (abis.secondary != null) {
191                     final String secondaryLibDir = is64Bit ? LIB_DIR_NAME : LIB64_DIR_NAME;
192                     secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot),
193                             secondaryLibDir, apkName).getAbsolutePath();
194                 } else {
195                     secondaryNativeLibraryDir = null;
196                 }
197             } else {
198                 final String apkName = deriveCodePathName(codePath);
199                 nativeLibraryRootDir = new File(appLib32InstallDir, apkName)
200                         .getAbsolutePath();
201                 secondaryNativeLibraryDir = null;
202             }
203 
204             nativeLibraryRootRequiresIsa = false;
205             nativeLibraryDir = nativeLibraryRootDir;
206         } else {
207             // Cluster install
208             nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath();
209             nativeLibraryRootRequiresIsa = true;
210 
211             nativeLibraryDir = new File(nativeLibraryRootDir,
212                     getPrimaryInstructionSet(abis)).getAbsolutePath();
213 
214             if (abis.secondary != null) {
215                 secondaryNativeLibraryDir = new File(nativeLibraryRootDir,
216                         VMRuntime.getInstructionSet(abis.secondary)).getAbsolutePath();
217             } else {
218                 secondaryNativeLibraryDir = null;
219             }
220         }
221         return new NativeLibraryPaths(nativeLibraryRootDir, nativeLibraryRootRequiresIsa,
222                 nativeLibraryDir, secondaryNativeLibraryDir);
223     }
224 
225     @Override
getBundledAppAbis(AndroidPackage pkg)226     public Abis getBundledAppAbis(AndroidPackage pkg) {
227         final String apkName = deriveCodePathName(pkg.getPath());
228 
229         // If "/system/lib64/apkname" exists, assume that is the per-package
230         // native library directory to use; otherwise use "/system/lib/apkname".
231         final String apkRoot = calculateBundledApkRoot(pkg.getBaseApkPath());
232         final Abis abis = getBundledAppAbi(pkg, apkRoot, apkName);
233         return abis;
234     }
235 
236     /**
237      * Deduces the ABI of a bundled app and sets the relevant fields on the
238      * parsed pkg object.
239      *
240      * @param apkRoot the root of the installed apk, something like {@code /system} or
241      *                {@code /oem} under which system libraries are installed.
242      * @param apkName the name of the installed package.
243      */
getBundledAppAbi(AndroidPackage pkg, String apkRoot, String apkName)244     private Abis getBundledAppAbi(AndroidPackage pkg, String apkRoot, String apkName) {
245         final File codeFile = new File(pkg.getPath());
246 
247         final boolean has64BitLibs;
248         final boolean has32BitLibs;
249 
250         final String primaryCpuAbi;
251         final String secondaryCpuAbi;
252         if (isApkFile(codeFile)) {
253             // Monolithic install
254             has64BitLibs =
255                     (new File(apkRoot, new File(LIB64_DIR_NAME, apkName).getPath())).exists();
256             has32BitLibs = (new File(apkRoot, new File(LIB_DIR_NAME, apkName).getPath())).exists();
257         } else {
258             // Cluster install
259             final File rootDir = new File(codeFile, LIB_DIR_NAME);
260             if (!ArrayUtils.isEmpty(Build.SUPPORTED_64_BIT_ABIS)
261                     && !TextUtils.isEmpty(Build.SUPPORTED_64_BIT_ABIS[0])) {
262                 final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_64_BIT_ABIS[0]);
263                 has64BitLibs = (new File(rootDir, isa)).exists();
264             } else {
265                 has64BitLibs = false;
266             }
267             if (!ArrayUtils.isEmpty(Build.SUPPORTED_32_BIT_ABIS)
268                     && !TextUtils.isEmpty(Build.SUPPORTED_32_BIT_ABIS[0])) {
269                 final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_32_BIT_ABIS[0]);
270                 has32BitLibs = (new File(rootDir, isa)).exists();
271             } else {
272                 has32BitLibs = false;
273             }
274         }
275 
276         if (has64BitLibs && !has32BitLibs) {
277             // The package has 64 bit libs, but not 32 bit libs. Its primary
278             // ABI should be 64 bit. We can safely assume here that the bundled
279             // native libraries correspond to the most preferred ABI in the list.
280 
281             primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0];
282             secondaryCpuAbi = null;
283         } else if (has32BitLibs && !has64BitLibs) {
284             // The package has 32 bit libs but not 64 bit libs. Its primary
285             // ABI should be 32 bit.
286 
287             primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];
288             secondaryCpuAbi = null;
289         } else if (has32BitLibs && has64BitLibs) {
290             // The application has both 64 and 32 bit bundled libraries. We check
291             // here that the app declares multiArch support, and warn if it doesn't.
292             //
293             // We will be lenient here and record both ABIs. The primary will be the
294             // ABI that's higher on the list, i.e, a device that's configured to prefer
295             // 64 bit apps will see a 64 bit primary ABI,
296 
297             if (!pkg.isMultiArch()) {
298                 Slog.e(PackageManagerService.TAG,
299                         "Package " + pkg + " has multiple bundled libs, but is not multiarch.");
300             }
301 
302             if (VMRuntime.is64BitInstructionSet(getPreferredInstructionSet())) {
303                 primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0];
304                 secondaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];
305             } else {
306                 primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];
307                 secondaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0];
308             }
309         } else {
310             primaryCpuAbi = null;
311             secondaryCpuAbi = null;
312         }
313         return new Abis(primaryCpuAbi, secondaryCpuAbi);
314     }
315 
316     @NonNull
getNativelySupportedAbis(@onNull String[] supportedAbis)317     private static String[] getNativelySupportedAbis(@NonNull String[] supportedAbis) {
318         List<String> nativelySupportedAbis = new ArrayList<>();
319         for (int i = 0; i < supportedAbis.length; i++) {
320             final String currentAbi = supportedAbis[i];
321             // In presence of a native bridge this means the Abi is emulated.
322             final String currentIsa = VMRuntime.getInstructionSet(currentAbi);
323             if (TextUtils.isEmpty(SystemProperties.get("ro.dalvik.vm.isa." + currentIsa))) {
324                 nativelySupportedAbis.add(currentAbi);
325             }
326         }
327         return nativelySupportedAbis.toArray(new String[0]);
328     }
329 
getNativelySupported32BitAbis()330     private static String[] getNativelySupported32BitAbis() {
331         if (sNativelySupported32BitAbis != null) {
332             return sNativelySupported32BitAbis;
333         }
334 
335         sNativelySupported32BitAbis = getNativelySupportedAbis(Build.SUPPORTED_32_BIT_ABIS);
336         return sNativelySupported32BitAbis;
337     }
338 
getNativelySupported64BitAbis()339     private static String[] getNativelySupported64BitAbis() {
340         if (sNativelySupported64BitAbis != null) {
341             return sNativelySupported64BitAbis;
342         }
343 
344         sNativelySupported64BitAbis = getNativelySupportedAbis(Build.SUPPORTED_64_BIT_ABIS);
345         return sNativelySupported64BitAbis;
346     }
347 
348     @Override
349     @SuppressWarnings("AndroidFrameworkCompatChange") // the check is before the apk is installed
derivePackageAbi(AndroidPackage pkg, boolean isSystemApp, boolean isUpdatedSystemApp, String cpuAbiOverride, File appLib32InstallDir)350     public Pair<Abis, NativeLibraryPaths> derivePackageAbi(AndroidPackage pkg, boolean isSystemApp,
351             boolean isUpdatedSystemApp, String cpuAbiOverride, File appLib32InstallDir)
352             throws PackageManagerException {
353         // Give ourselves some initial paths; we'll come back for another
354         // pass once we've determined ABI below.
355         String pkgRawPrimaryCpuAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(pkg);
356         String pkgRawSecondaryCpuAbi = AndroidPackageUtils.getRawSecondaryCpuAbi(pkg);
357         final NativeLibraryPaths initialLibraryPaths = deriveNativeLibraryPaths(
358                 new Abis(pkgRawPrimaryCpuAbi, pkgRawSecondaryCpuAbi),
359                 appLib32InstallDir, pkg.getPath(),
360                 pkg.getBaseApkPath(), isSystemApp,
361                 isUpdatedSystemApp);
362 
363         final boolean extractLibs = shouldExtractLibs(pkg, isSystemApp, isUpdatedSystemApp);
364 
365         final String nativeLibraryRootStr = initialLibraryPaths.nativeLibraryRootDir;
366         final boolean useIsaSpecificSubdirs = initialLibraryPaths.nativeLibraryRootRequiresIsa;
367         final boolean onIncremental = isIncrementalPath(pkg.getPath());
368 
369         String primaryCpuAbi = null;
370         String secondaryCpuAbi = null;
371 
372         NativeLibraryHelper.Handle handle = null;
373         try {
374             handle = AndroidPackageUtils.createNativeLibraryHandle(pkg);
375             // TODO(multiArch): This can be null for apps that didn't go through the
376             // usual installation process. We can calculate it again, like we
377             // do during install time.
378             //
379             // TODO(multiArch): Why do we need to rescan ASEC apps again ? It seems totally
380             // unnecessary.
381             final File nativeLibraryRoot = new File(nativeLibraryRootStr);
382 
383             // Null out the abis so that they can be recalculated.
384             primaryCpuAbi = null;
385             secondaryCpuAbi = null;
386             if (pkg.isMultiArch()) {
387                 // Force the match for these cases
388                 // 1. pkg.getTargetSdkVersion >= Build.VERSION_CODES.VANILLA_ICE_CREAM
389                 // 2. cpuAbiOverride is null. If it is non-null, it is set via shell for testing
390                 final boolean forceMatch = Flags.forceMultiArchNativeLibsMatch()
391                         && pkg.getTargetSdkVersion() >= Build.VERSION_CODES.VANILLA_ICE_CREAM
392                         && cpuAbiOverride == null;
393 
394                 String[] supported32BitAbis = forceMatch ? getNativelySupported32BitAbis()
395                         : Build.SUPPORTED_32_BIT_ABIS;
396                 String[] supported64BitAbis = forceMatch ? getNativelySupported64BitAbis()
397                         : Build.SUPPORTED_64_BIT_ABIS;
398 
399                 final boolean systemSupports32BitAbi = supported32BitAbis.length > 0;
400                 final boolean systemSupports64BitAbi = supported64BitAbis.length > 0;
401 
402                 int abi32 = PackageManager.NO_NATIVE_LIBRARIES;
403                 int abi64 = PackageManager.NO_NATIVE_LIBRARIES;
404                 if (systemSupports32BitAbi) {
405                     if (extractLibs) {
406                         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
407                         abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
408                                 nativeLibraryRoot, supported32BitAbis,
409                                 useIsaSpecificSubdirs, onIncremental);
410                     } else {
411                         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
412                         abi32 = NativeLibraryHelper.findSupportedAbi(
413                                 handle, supported32BitAbis);
414                     }
415                     Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
416                 }
417 
418                 // Shared library native code should be in the APK zip aligned
419                 if (abi32 >= 0 && AndroidPackageUtils.isLibrary(pkg) && extractLibs) {
420                     throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
421                             "Shared library native lib extraction not supported");
422                 }
423 
424                 maybeThrowExceptionForMultiArchCopy(
425                         "Error unpackaging 32 bit native libs for multiarch app.", abi32,
426                         forceMatch && systemSupports32BitAbi);
427 
428                 if (systemSupports64BitAbi) {
429                     if (extractLibs) {
430                         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
431                         abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
432                                 nativeLibraryRoot, supported64BitAbis,
433                                 useIsaSpecificSubdirs, onIncremental);
434                     } else {
435                         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
436                         abi64 = NativeLibraryHelper.findSupportedAbi(
437                                 handle, supported64BitAbis);
438                     }
439                     Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
440                 }
441 
442                 maybeThrowExceptionForMultiArchCopy(
443                         "Error unpackaging 64 bit native libs for multiarch app.", abi64,
444                         forceMatch && systemSupports64BitAbi);
445 
446                 if (abi64 >= 0) {
447                     // Shared library native libs should be in the APK zip aligned
448                     if (extractLibs && AndroidPackageUtils.isLibrary(pkg)) {
449                         throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
450                                 "Shared library native lib extraction not supported");
451                     }
452                     primaryCpuAbi = supported64BitAbis[abi64];
453                 }
454 
455                 if (abi32 >= 0) {
456                     final String abi = supported32BitAbis[abi32];
457                     if (abi64 >= 0) {
458                         if (pkg.is32BitAbiPreferred()) {
459                             secondaryCpuAbi = primaryCpuAbi;
460                             primaryCpuAbi = abi;
461                         } else {
462                             secondaryCpuAbi = abi;
463                         }
464                     } else {
465                         primaryCpuAbi = abi;
466                     }
467                 }
468             } else {
469                 String[] abiList = (cpuAbiOverride != null)
470                         ? new String[]{cpuAbiOverride} : Build.SUPPORTED_ABIS;
471 
472                 // If an app that contains RenderScript has target API level < 21, it needs to run
473                 // with 32-bit ABI, and its APK file will contain a ".bc" file.
474                 // If an app that contains RenderScript has target API level >= 21, it can run with
475                 // either 32-bit or 64-bit ABI, and its APK file will not contain a ".bc" file.
476                 // Therefore, on a device that supports both 32-bit and 64-bit ABIs, we scan the app
477                 // APK to see if it has a ".bc" file. If so, we will run it with 32-bit ABI.
478                 // However, if the device only supports 64-bit ABI but does not support 32-bit ABI,
479                 // we will fail the installation for such an app because it won't be able to run.
480                 boolean needsRenderScriptOverride = false;
481                 // No need to check if the device only supports 32-bit
482                 if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null
483                         && NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
484                     if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
485                         abiList = Build.SUPPORTED_32_BIT_ABIS;
486                         needsRenderScriptOverride = true;
487                     } else {
488                         throw new PackageManagerException(
489                                 INSTALL_FAILED_CPU_ABI_INCOMPATIBLE,
490                                 "Apps that contain RenderScript with target API level < 21 are not "
491                                         + "supported on 64-bit only platforms");
492                     }
493                 }
494 
495                 final int copyRet;
496                 if (extractLibs) {
497                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
498                     copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
499                             nativeLibraryRoot, abiList, useIsaSpecificSubdirs, onIncremental);
500                 } else {
501                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
502                     copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList);
503                 }
504                 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
505 
506                 if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
507                     throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
508                             "Error unpackaging native libs for app, errorCode=" + copyRet);
509                 }
510 
511                 if (copyRet >= 0) {
512                     // Shared libraries that have native libs must be multi-architecture
513                     if (AndroidPackageUtils.isLibrary(pkg)) {
514                         throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
515                                 "Shared library with native libs must be multiarch");
516                     }
517                     primaryCpuAbi = abiList[copyRet];
518                 } else if (copyRet == PackageManager.NO_NATIVE_LIBRARIES
519                         && cpuAbiOverride != null) {
520                     primaryCpuAbi = cpuAbiOverride;
521                 } else if (needsRenderScriptOverride) {
522                     primaryCpuAbi = abiList[0];
523                 }
524             }
525         } catch (IOException ioe) {
526             Slog.e(PackageManagerService.TAG, "Unable to get canonical file " + ioe.toString());
527         } finally {
528             IoUtils.closeQuietly(handle);
529         }
530 
531         // Now that we've calculated the ABIs and determined if it's an internal app,
532         // we will go ahead and populate the nativeLibraryPath.
533 
534         final Abis abis = new Abis(primaryCpuAbi, secondaryCpuAbi);
535         return new Pair<>(abis,
536                 deriveNativeLibraryPaths(abis, appLib32InstallDir,
537                         pkg.getPath(), pkg.getBaseApkPath(), isSystemApp,
538                         isUpdatedSystemApp));
539     }
540 
shouldExtractLibs(AndroidPackage pkg, boolean isSystemApp, boolean isUpdatedSystemApp)541     private boolean shouldExtractLibs(AndroidPackage pkg, boolean isSystemApp,
542             boolean isUpdatedSystemApp) {
543         // We shouldn't extract libs if the package is a library or if extractNativeLibs=false
544         boolean extractLibs = !AndroidPackageUtils.isLibrary(pkg)
545                 && pkg.isExtractNativeLibrariesRequested();
546         // We shouldn't attempt to extract libs from system app when it was not updated.
547         if (isSystemApp && !isUpdatedSystemApp) {
548             extractLibs = false;
549         }
550         return extractLibs;
551     }
552 
553     /**
554      * Adjusts ABIs for a set of packages belonging to a shared user so that they all match.
555      * i.e, so that all packages can be run inside a single process if required.
556      *
557      * Optionally, callers can pass in a parsed package via {@code newPackage} in which case
558      * this function will either try and make the ABI for all packages in
559      * {@code packagesForUser} match {@code scannedPackage} or will update the ABI of
560      * {@code scannedPackage} to match the ABI selected for {@code packagesForUser}. This
561      * variant is used when installing or updating a package that belongs to a shared user.
562      *
563      * NOTE: We currently only match for the primary CPU abi string. Matching the secondary
564      * adds unnecessary complexity.
565      */
566     @Override
567     @Nullable
getAdjustedAbiForSharedUser( ArraySet<? extends PackageStateInternal> packagesForUser, AndroidPackage scannedPackage)568     public String getAdjustedAbiForSharedUser(
569             ArraySet<? extends PackageStateInternal> packagesForUser,
570             AndroidPackage scannedPackage) {
571         String requiredInstructionSet = null;
572         if (scannedPackage != null) {
573             String pkgRawPrimaryCpuAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(scannedPackage);
574             if (pkgRawPrimaryCpuAbi != null) {
575                 requiredInstructionSet = VMRuntime.getInstructionSet(pkgRawPrimaryCpuAbi);
576             }
577         }
578 
579         PackageStateInternal requirer = null;
580         for (PackageStateInternal ps : packagesForUser) {
581             // If packagesForUser contains scannedPackage, we skip it. This will happen
582             // when scannedPackage is an update of an existing package. Without this check,
583             // we will never be able to change the ABI of any package belonging to a shared
584             // user, even if it's compatible with other packages.
585             if (scannedPackage != null && scannedPackage.getPackageName().equals(
586                     ps.getPackageName())) {
587                 continue;
588             }
589             if (ps.getPrimaryCpuAbiLegacy() == null) {
590                 continue;
591             }
592 
593             final String instructionSet =
594                     VMRuntime.getInstructionSet(ps.getPrimaryCpuAbiLegacy());
595             if (requiredInstructionSet != null && !requiredInstructionSet.equals(instructionSet)) {
596                 // We have a mismatch between instruction sets (say arm vs arm64) warn about
597                 // this but there's not much we can do.
598                 String errorMessage = "Instruction set mismatch, "
599                         + ((requirer == null) ? "[caller]" : requirer)
600                         + " requires " + requiredInstructionSet + " whereas " + ps
601                         + " requires " + instructionSet;
602                 Slog.w(PackageManagerService.TAG, errorMessage);
603             }
604 
605             if (requiredInstructionSet == null) {
606                 requiredInstructionSet = instructionSet;
607                 requirer = ps;
608             }
609         }
610 
611         if (requiredInstructionSet == null) {
612             return null;
613         }
614         final String adjustedAbi;
615         if (requirer != null) {
616             // requirer != null implies that either scannedPackage was null or that
617             // scannedPackage did not require an ABI, in which case we have to adjust
618             // scannedPackage to match the ABI of the set (which is the same as
619             // requirer's ABI)
620             adjustedAbi = requirer.getPrimaryCpuAbiLegacy();
621         } else {
622             // requirer == null implies that we're updating all ABIs in the set to
623             // match scannedPackage.
624             adjustedAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(scannedPackage);
625         }
626         return adjustedAbi;
627     }
628 }
629