• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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.internal.content;
18 
19 import static android.content.pm.PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS;
20 import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
21 import static android.content.pm.PackageManager.NO_NATIVE_LIBRARIES;
22 import static android.system.OsConstants.S_IRGRP;
23 import static android.system.OsConstants.S_IROTH;
24 import static android.system.OsConstants.S_IRWXU;
25 import static android.system.OsConstants.S_IXGRP;
26 import static android.system.OsConstants.S_IXOTH;
27 
28 import android.content.Context;
29 import android.content.pm.ApplicationInfo;
30 import android.content.pm.PackageManager;
31 import android.content.pm.parsing.ApkLiteParseUtils;
32 import android.content.pm.parsing.PackageLite;
33 import android.content.pm.parsing.result.ParseResult;
34 import android.content.pm.parsing.result.ParseTypeImpl;
35 import android.os.Build;
36 import android.os.IBinder;
37 import android.os.SELinux;
38 import android.os.ServiceManager;
39 import android.os.incremental.IIncrementalService;
40 import android.os.incremental.IncrementalManager;
41 import android.os.incremental.IncrementalStorage;
42 import android.system.ErrnoException;
43 import android.system.Os;
44 import android.util.Slog;
45 
46 import dalvik.system.CloseGuard;
47 import dalvik.system.VMRuntime;
48 
49 import java.io.Closeable;
50 import java.io.File;
51 import java.io.FileDescriptor;
52 import java.io.IOException;
53 import java.nio.file.Path;
54 import java.util.List;
55 
56 /**
57  * Native libraries helper.
58  *
59  * @hide
60  */
61 public class NativeLibraryHelper {
62     private static final String TAG = "NativeHelper";
63     private static final boolean DEBUG_NATIVE = false;
64 
65     public static final String LIB_DIR_NAME = "lib";
66     public static final String LIB64_DIR_NAME = "lib64";
67 
68     // Special value for indicating that the cpuAbiOverride must be clear.
69     public static final String CLEAR_ABI_OVERRIDE = "-";
70 
71     /**
72      * A handle to an opened package, consisting of one or more APKs. Used as
73      * input to the various NativeLibraryHelper methods. Allows us to scan and
74      * parse the APKs exactly once instead of doing it multiple times.
75      *
76      * @hide
77      */
78     public static class Handle implements Closeable {
79         private final CloseGuard mGuard = CloseGuard.get();
80         private volatile boolean mClosed;
81 
82         final String[] apkPaths;
83         final long[] apkHandles;
84         final boolean multiArch;
85         final boolean extractNativeLibs;
86         final boolean debuggable;
87 
88         final boolean pageSizeCompatDisabled;
89 
create(File packageFile)90         public static Handle create(File packageFile) throws IOException {
91             final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
92             final ParseResult<PackageLite> ret = ApkLiteParseUtils.parsePackageLite(input.reset(),
93                     packageFile, /* flags */ 0);
94             if (ret.isError()) {
95                 throw new IOException("Failed to parse package: " + packageFile,
96                         ret.getException());
97             }
98             return create(ret.getResult());
99         }
100 
create(PackageLite lite)101         public static Handle create(PackageLite lite) throws IOException {
102             boolean isPageSizeCompatDisabled = lite.getPageSizeCompat()
103                     == ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_DISABLED;
104             return create(lite.getAllApkPaths(), lite.isMultiArch(), lite.isExtractNativeLibs(),
105                     lite.isDebuggable(), isPageSizeCompatDisabled);
106         }
107 
create(List<String> codePaths, boolean multiArch, boolean extractNativeLibs, boolean debuggable, boolean isPageSizeCompatDisabled)108         public static Handle create(List<String> codePaths, boolean multiArch,
109                 boolean extractNativeLibs, boolean debuggable, boolean isPageSizeCompatDisabled)
110                 throws IOException {
111             final int size = codePaths.size();
112             final String[] apkPaths = new String[size];
113             final long[] apkHandles = new long[size];
114             for (int i = 0; i < size; i++) {
115                 final String path = codePaths.get(i);
116                 apkPaths[i] = path;
117                 apkHandles[i] = nativeOpenApk(path);
118                 if (apkHandles[i] == 0) {
119                     // Unwind everything we've opened so far
120                     for (int j = 0; j < i; j++) {
121                         nativeClose(apkHandles[j]);
122                     }
123                     throw new IOException("Unable to open APK: " + path);
124                 }
125             }
126 
127             return new Handle(apkPaths, apkHandles, multiArch, extractNativeLibs, debuggable,
128                     isPageSizeCompatDisabled);
129         }
130 
createFd(PackageLite lite, FileDescriptor fd)131         public static Handle createFd(PackageLite lite, FileDescriptor fd) throws IOException {
132             final long[] apkHandles = new long[1];
133             final String path = lite.getBaseApkPath();
134             apkHandles[0] = nativeOpenApkFd(fd, path);
135             if (apkHandles[0] == 0) {
136                 throw new IOException("Unable to open APK " + path + " from fd " + fd);
137             }
138 
139             boolean isPageSizeCompatDisabled = lite.getPageSizeCompat()
140                     == ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_DISABLED;
141 
142             return new Handle(new String[]{path}, apkHandles, lite.isMultiArch(),
143                     lite.isExtractNativeLibs(), lite.isDebuggable(), isPageSizeCompatDisabled);
144         }
145 
Handle(String[] apkPaths, long[] apkHandles, boolean multiArch, boolean extractNativeLibs, boolean debuggable, boolean isPageSizeCompatDisabled)146         Handle(String[] apkPaths, long[] apkHandles, boolean multiArch,
147                 boolean extractNativeLibs, boolean debuggable, boolean isPageSizeCompatDisabled) {
148             this.apkPaths = apkPaths;
149             this.apkHandles = apkHandles;
150             this.multiArch = multiArch;
151             this.extractNativeLibs = extractNativeLibs;
152             this.debuggable = debuggable;
153             this.pageSizeCompatDisabled = isPageSizeCompatDisabled;
154             mGuard.open("close");
155         }
156 
157         @Override
close()158         public void close() {
159             for (long apkHandle : apkHandles) {
160                 nativeClose(apkHandle);
161             }
162             mGuard.close();
163             mClosed = true;
164         }
165 
166         @Override
finalize()167         protected void finalize() throws Throwable {
168             if (mGuard != null) {
169                 mGuard.warnIfOpen();
170             }
171             try {
172                 if (!mClosed) {
173                     close();
174                 }
175             } finally {
176                 super.finalize();
177             }
178         }
179     }
180 
nativeOpenApk(String path)181     private static native long nativeOpenApk(String path);
nativeOpenApkFd(FileDescriptor fd, String debugPath)182     private static native long nativeOpenApkFd(FileDescriptor fd, String debugPath);
nativeClose(long handle)183     private static native void nativeClose(long handle);
184 
nativeSumNativeBinaries(long handle, String cpuAbi)185     private static native long nativeSumNativeBinaries(long handle, String cpuAbi);
186 
nativeCopyNativeBinaries(long handle, String sharedLibraryPath, String abiToCopy, boolean extractNativeLibs, boolean debuggable, boolean pageSizeCompatDisabled)187     private native static int nativeCopyNativeBinaries(long handle, String sharedLibraryPath,
188             String abiToCopy, boolean extractNativeLibs, boolean debuggable,
189             boolean pageSizeCompatDisabled);
190 
nativeCheckAlignment( long handle, String sharedLibraryPath, String abi, boolean extractNativeLibs, boolean debuggable)191     private static native int nativeCheckAlignment(
192             long handle,
193             String sharedLibraryPath,
194             String abi,
195             boolean extractNativeLibs,
196             boolean debuggable);
197 
sumNativeBinaries(Handle handle, String abi)198     private static long sumNativeBinaries(Handle handle, String abi) {
199         long sum = 0;
200         for (long apkHandle : handle.apkHandles) {
201             sum += nativeSumNativeBinaries(apkHandle, abi);
202         }
203         return sum;
204     }
205 
206     /**
207      * Copies native binaries to a shared library directory.
208      *
209      * @param handle APK file to scan for native libraries
210      * @param sharedLibraryDir directory for libraries to be copied to
211      * @return {@link PackageManager#INSTALL_SUCCEEDED} if successful or another
212      *         error code from that class if not
213      */
copyNativeBinaries(Handle handle, File sharedLibraryDir, String abi)214     public static int copyNativeBinaries(Handle handle, File sharedLibraryDir, String abi) {
215         for (long apkHandle : handle.apkHandles) {
216             int res = nativeCopyNativeBinaries(apkHandle, sharedLibraryDir.getPath(), abi,
217                     handle.extractNativeLibs, handle.debuggable, handle.pageSizeCompatDisabled);
218             if (res != INSTALL_SUCCEEDED) {
219                 return res;
220             }
221         }
222         return INSTALL_SUCCEEDED;
223     }
224 
225     /**
226      * Checks if a given APK contains native code for any of the provided
227      * {@code supportedAbis}. Returns an index into {@code supportedAbis} if a matching
228      * ABI is found, {@link PackageManager#NO_NATIVE_LIBRARIES} if the
229      * APK doesn't contain any native code, and
230      * {@link PackageManager#INSTALL_FAILED_NO_MATCHING_ABIS} if none of the ABIs match.
231      */
findSupportedAbi(Handle handle, String[] supportedAbis)232     public static int findSupportedAbi(Handle handle, String[] supportedAbis) {
233         int finalRes = NO_NATIVE_LIBRARIES;
234         for (long apkHandle : handle.apkHandles) {
235             final int res = nativeFindSupportedAbi(apkHandle, supportedAbis);
236             if (res == NO_NATIVE_LIBRARIES) {
237                 // No native code, keep looking through all APKs.
238             } else if (res == INSTALL_FAILED_NO_MATCHING_ABIS) {
239                 // Found some native code, but no ABI match; update our final
240                 // result if we haven't found other valid code.
241                 if (finalRes < 0) {
242                     finalRes = INSTALL_FAILED_NO_MATCHING_ABIS;
243                 }
244             } else if (res >= 0) {
245                 // Found valid native code, track the best ABI match
246                 if (finalRes < 0 || res < finalRes) {
247                     finalRes = res;
248                 }
249             } else {
250                 // Unexpected error; bail
251                 return res;
252             }
253         }
254         return finalRes;
255     }
256 
nativeFindSupportedAbi(long handle, String[] supportedAbis)257     private native static int nativeFindSupportedAbi(long handle, String[] supportedAbis);
258 
259     // Convenience method to call removeNativeBinariesFromDirLI(File)
removeNativeBinariesLI(String nativeLibraryPath)260     public static void removeNativeBinariesLI(String nativeLibraryPath) {
261         if (nativeLibraryPath == null) return;
262         removeNativeBinariesFromDirLI(new File(nativeLibraryPath), false /* delete root dir */);
263     }
264 
265     /**
266      * Remove the native binaries of a given package. This deletes the files
267      */
removeNativeBinariesFromDirLI(File nativeLibraryRoot, boolean deleteRootDir)268     public static void removeNativeBinariesFromDirLI(File nativeLibraryRoot,
269             boolean deleteRootDir) {
270         if (DEBUG_NATIVE) {
271             Slog.w(TAG, "Deleting native binaries from: " + nativeLibraryRoot.getPath());
272         }
273 
274         /*
275          * Just remove any file in the directory. Since the directory is owned
276          * by the 'system' UID, the application is not supposed to have written
277          * anything there.
278          */
279         if (nativeLibraryRoot.exists()) {
280             final File[] files = nativeLibraryRoot.listFiles();
281             if (files != null) {
282                 for (int nn = 0; nn < files.length; nn++) {
283                     if (DEBUG_NATIVE) {
284                         Slog.d(TAG, "    Deleting " + files[nn].getName());
285                     }
286 
287                     if (files[nn].isDirectory()) {
288                         removeNativeBinariesFromDirLI(files[nn], true /* delete root dir */);
289                     } else if (!files[nn].delete()) {
290                         Slog.w(TAG, "Could not delete native binary: " + files[nn].getPath());
291                     }
292                 }
293             }
294             // Do not delete 'lib' directory itself, unless we're specifically
295             // asked to or this will prevent installation of future updates.
296             if (deleteRootDir) {
297                 if (!nativeLibraryRoot.delete()) {
298                     Slog.w(TAG, "Could not delete native binary directory: " +
299                             nativeLibraryRoot.getPath());
300                 }
301             }
302         }
303     }
304 
305     /**
306      * @hide
307      */
createNativeLibrarySubdir(File path)308     public static void createNativeLibrarySubdir(File path) throws IOException {
309         if (!path.isDirectory()) {
310             path.delete();
311 
312             if (!path.mkdir()) {
313                 throw new IOException("Cannot create " + path.getPath());
314             }
315 
316             try {
317                 Os.chmod(path.getPath(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
318             } catch (ErrnoException e) {
319                 throw new IOException("Cannot chmod native library directory "
320                         + path.getPath(), e);
321             }
322         } else if (!SELinux.restorecon(path)) {
323             throw new IOException("Cannot set SELinux context for " + path.getPath());
324         }
325     }
326 
sumNativeBinariesForSupportedAbi(Handle handle, String[] abiList)327     private static long sumNativeBinariesForSupportedAbi(Handle handle, String[] abiList) {
328         int abi = findSupportedAbi(handle, abiList);
329         if (abi >= 0) {
330             return sumNativeBinaries(handle, abiList[abi]);
331         } else {
332             return 0;
333         }
334     }
335 
copyNativeBinariesForSupportedAbi(Handle handle, File libraryRoot, String[] abiList, boolean useIsaSubdir, boolean isIncremental)336     public static int copyNativeBinariesForSupportedAbi(Handle handle, File libraryRoot,
337             String[] abiList, boolean useIsaSubdir, boolean isIncremental) throws IOException {
338         /*
339          * If this is an internal application or our nativeLibraryPath points to
340          * the app-lib directory, unpack the libraries if necessary.
341          */
342         int abi = findSupportedAbi(handle, abiList);
343         if (abi < 0) {
344             return abi;
345         }
346 
347         /*
348          * If we have a matching instruction set, construct a subdir under the native
349          * library root that corresponds to this instruction set.
350          */
351         final String supportedAbi = abiList[abi];
352         final String instructionSet = VMRuntime.getInstructionSet(supportedAbi);
353         final File subDir;
354         if (useIsaSubdir) {
355             subDir = new File(libraryRoot, instructionSet);
356         } else {
357             subDir = libraryRoot;
358         }
359 
360         if (isIncremental) {
361             int res =
362                     incrementalConfigureNativeBinariesForSupportedAbi(handle, subDir, supportedAbi);
363             if (res != PackageManager.INSTALL_SUCCEEDED) {
364                 // TODO(b/133435829): the caller of this function expects that we return the index
365                 // to the supported ABI. However, any non-negative integer can be a valid index.
366                 // We should fix this function and make sure it doesn't accidentally return an error
367                 // code that can also be a valid index.
368                 return res;
369             }
370             return abi;
371         }
372 
373         // For non-incremental, use regular extraction and copy
374         createNativeLibrarySubdir(libraryRoot);
375         if (subDir != libraryRoot) {
376             createNativeLibrarySubdir(subDir);
377         }
378 
379         // Even if extractNativeLibs is false, we still need to check if the native libs in the APK
380         // are valid. This is done in the native code.
381         int copyRet = copyNativeBinaries(handle, subDir, supportedAbi);
382         if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
383             return copyRet;
384         }
385 
386         return abi;
387     }
388 
copyNativeBinariesWithOverride(Handle handle, File libraryRoot, String abiOverride, boolean isIncremental)389     public static int copyNativeBinariesWithOverride(Handle handle, File libraryRoot,
390             String abiOverride, boolean isIncremental) {
391         try {
392             if (handle.multiArch) {
393                 // Warn if we've set an abiOverride for multi-lib packages..
394                 // By definition, we need to copy both 32 and 64 bit libraries for
395                 // such packages.
396                 if (abiOverride != null && !CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
397                     Slog.w(TAG, "Ignoring abiOverride for multi arch application.");
398                 }
399 
400                 int copyRet = PackageManager.NO_NATIVE_LIBRARIES;
401                 if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
402                     copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot,
403                             Build.SUPPORTED_32_BIT_ABIS, true /* use isa specific subdirs */,
404                             isIncremental);
405                     if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
406                             copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
407                         Slog.w(TAG, "Failure copying 32 bit native libraries; copyRet=" +copyRet);
408                         return copyRet;
409                     }
410                 }
411 
412                 if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
413                     copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot,
414                             Build.SUPPORTED_64_BIT_ABIS, true /* use isa specific subdirs */,
415                             isIncremental);
416                     if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
417                             copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
418                         Slog.w(TAG, "Failure copying 64 bit native libraries; copyRet=" +copyRet);
419                         return copyRet;
420                     }
421                 }
422             } else {
423                 String cpuAbiOverride = null;
424                 if (CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
425                     cpuAbiOverride = null;
426                 } else if (abiOverride != null) {
427                     cpuAbiOverride = abiOverride;
428                 }
429 
430                 String[] abiList = (cpuAbiOverride != null) ?
431                         new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;
432                 if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null &&
433                         hasRenderscriptBitcode(handle)) {
434                     abiList = Build.SUPPORTED_32_BIT_ABIS;
435                 }
436 
437                 int copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot, abiList,
438                         true /* use isa specific subdirs */, isIncremental);
439                 if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
440                     Slog.w(TAG, "Failure copying native libraries [errorCode=" + copyRet + "]");
441                     return copyRet;
442                 }
443             }
444 
445             return PackageManager.INSTALL_SUCCEEDED;
446         } catch (IOException e) {
447             Slog.e(TAG, "Copying native libraries failed", e);
448             return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
449         }
450     }
451 
452     /**
453      * Checks alignment of APK and native libraries for 16KB device
454      *
455      * @param handle APK file to scan for native libraries
456      * @param libraryRoot directory for libraries
457      * @param abiOverride abiOverride for package
458      * @return {@link Modes from ApplicationInfo.PageSizeAppCompat} if successful or error code
459      *     which suggests undefined mode
460      */
461     @ApplicationInfo.PageSizeAppCompatFlags
checkAlignmentForCompatMode( Handle handle, String libraryRoot, boolean nativeLibraryRootRequiresIsa, String abiOverride)462     public static int checkAlignmentForCompatMode(
463             Handle handle,
464             String libraryRoot,
465             boolean nativeLibraryRootRequiresIsa,
466             String abiOverride) {
467         // Keep the code below in sync with copyNativeBinariesForSupportedAbi
468         int abi = findSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);
469         if (abi < 0) {
470             return ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
471         }
472 
473         final String supportedAbi = Build.SUPPORTED_64_BIT_ABIS[abi];
474         final String instructionSet = VMRuntime.getInstructionSet(supportedAbi);
475         String subDir = libraryRoot;
476         if (nativeLibraryRootRequiresIsa) {
477             subDir += "/" + instructionSet;
478         }
479 
480         int mode = ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED;
481         for (long apkHandle : handle.apkHandles) {
482             int res =
483                     nativeCheckAlignment(
484                             apkHandle,
485                             subDir,
486                             Build.SUPPORTED_64_BIT_ABIS[abi],
487                             handle.extractNativeLibs,
488                             handle.debuggable);
489             if (res == ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_ERROR) {
490                 return res;
491             }
492             mode |= res;
493         }
494         return mode;
495     }
496 
sumNativeBinariesWithOverride(Handle handle, String abiOverride)497     public static long sumNativeBinariesWithOverride(Handle handle, String abiOverride)
498             throws IOException {
499         long sum = 0;
500         if (handle.multiArch) {
501             // Warn if we've set an abiOverride for multi-lib packages..
502             // By definition, we need to copy both 32 and 64 bit libraries for
503             // such packages.
504             if (abiOverride != null && !CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
505                 Slog.w(TAG, "Ignoring abiOverride for multi arch application.");
506             }
507 
508             if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
509                 sum += sumNativeBinariesForSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS);
510             }
511 
512             if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
513                 sum += sumNativeBinariesForSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);
514             }
515         } else {
516             String cpuAbiOverride = null;
517             if (CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
518                 cpuAbiOverride = null;
519             } else if (abiOverride != null) {
520                 cpuAbiOverride = abiOverride;
521             }
522 
523             String[] abiList = (cpuAbiOverride != null) ?
524                     new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;
525             if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null &&
526                     hasRenderscriptBitcode(handle)) {
527                 abiList = Build.SUPPORTED_32_BIT_ABIS;
528             }
529 
530             sum += sumNativeBinariesForSupportedAbi(handle, abiList);
531         }
532         return sum;
533     }
534 
535     /**
536      * Configure the native library files managed by Incremental Service. Makes sure Incremental
537      * Service will create native library directories and set up native library binary files in the
538      * same structure as they are in non-incremental installations.
539      *
540      * @param handle The Handle object that contains all apk paths.
541      * @param libSubDir The target directory to put the native library files, e.g., lib/ or lib/arm
542      * @param abi The abi that is supported by the current device.
543      * @return Integer code if installation succeeds or fails.
544      */
incrementalConfigureNativeBinariesForSupportedAbi(Handle handle, File libSubDir, String abi)545     private static int incrementalConfigureNativeBinariesForSupportedAbi(Handle handle,
546             File libSubDir, String abi) {
547         final String[] apkPaths = handle.apkPaths;
548         if (apkPaths == null || apkPaths.length == 0) {
549             Slog.e(TAG, "No apks to extract native libraries from.");
550             return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
551         }
552 
553         final IBinder incrementalService = ServiceManager.getService(Context.INCREMENTAL_SERVICE);
554         if (incrementalService == null) {
555             //TODO(b/133435829): add incremental specific error codes
556             return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
557         }
558         final IncrementalManager incrementalManager = new IncrementalManager(
559                 IIncrementalService.Stub.asInterface(incrementalService));
560         final File apkParent = new File(apkPaths[0]).getParentFile();
561         IncrementalStorage incrementalStorage =
562                 incrementalManager.openStorage(apkParent.getAbsolutePath());
563         if (incrementalStorage == null) {
564             Slog.e(TAG, "Failed to find incremental storage");
565             return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
566         }
567 
568         String libRelativeDir = getRelativePath(apkParent, libSubDir);
569         if (libRelativeDir == null) {
570             return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
571         }
572 
573         for (int i = 0; i < apkPaths.length; i++) {
574             if (!incrementalStorage.configureNativeBinaries(apkPaths[i], libRelativeDir, abi,
575                     handle.extractNativeLibs)) {
576                 return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
577             }
578         }
579         return PackageManager.INSTALL_SUCCEEDED;
580     }
581 
getRelativePath(File base, File target)582     private static String getRelativePath(File base, File target) {
583         try {
584             final Path basePath = base.toPath();
585             final Path targetPath = target.toPath();
586             final Path relativePath = basePath.relativize(targetPath);
587             if (relativePath.toString().isEmpty()) {
588                 return "";
589             }
590             return relativePath.toString();
591         } catch (IllegalArgumentException ex) {
592             Slog.e(TAG, "Failed to find relative path between: " + base.getAbsolutePath()
593                     + " and: " + target.getAbsolutePath());
594             return null;
595         }
596     }
597 
598     // We don't care about the other return values for now.
599     private static final int BITCODE_PRESENT = 1;
600 
hasRenderscriptBitcode(long apkHandle)601     private static native int hasRenderscriptBitcode(long apkHandle);
602 
hasRenderscriptBitcode(Handle handle)603     public static boolean hasRenderscriptBitcode(Handle handle) throws IOException {
604         for (long apkHandle : handle.apkHandles) {
605             final int res = hasRenderscriptBitcode(apkHandle);
606             if (res < 0) {
607                 throw new IOException("Error scanning APK, code: " + res);
608             } else if (res == BITCODE_PRESENT) {
609                 return true;
610             }
611         }
612         return false;
613     }
614 }
615