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