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.pm.ApplicationInfo;
29 import android.content.pm.PackageManager;
30 import android.content.pm.PackageParser;
31 import android.content.pm.PackageParser.Package;
32 import android.content.pm.PackageParser.PackageLite;
33 import android.content.pm.PackageParser.PackageParserException;
34 import android.os.Build;
35 import android.os.SELinux;
36 import android.os.SystemProperties;
37 import android.system.ErrnoException;
38 import android.system.Os;
39 import android.util.Slog;
40 
41 import dalvik.system.CloseGuard;
42 import dalvik.system.VMRuntime;
43 
44 import java.io.Closeable;
45 import java.io.File;
46 import java.io.FileDescriptor;
47 import java.io.IOException;
48 import java.util.List;
49 
50 /**
51  * Native libraries helper.
52  *
53  * @hide
54  */
55 public class NativeLibraryHelper {
56     private static final String TAG = "NativeHelper";
57     private static final boolean DEBUG_NATIVE = false;
58 
59     public static final String LIB_DIR_NAME = "lib";
60     public static final String LIB64_DIR_NAME = "lib64";
61 
62     // Special value for {@code PackageParser.Package#cpuAbiOverride} to indicate
63     // that the cpuAbiOverride must be clear.
64     public static final String CLEAR_ABI_OVERRIDE = "-";
65 
66     /**
67      * A handle to an opened package, consisting of one or more APKs. Used as
68      * input to the various NativeLibraryHelper methods. Allows us to scan and
69      * parse the APKs exactly once instead of doing it multiple times.
70      *
71      * @hide
72      */
73     public static class Handle implements Closeable {
74         private final CloseGuard mGuard = CloseGuard.get();
75         private volatile boolean mClosed;
76 
77         final long[] apkHandles;
78         final boolean multiArch;
79         final boolean extractNativeLibs;
80         final boolean debuggable;
81 
create(File packageFile)82         public static Handle create(File packageFile) throws IOException {
83             try {
84                 final PackageLite lite = PackageParser.parsePackageLite(packageFile, 0);
85                 return create(lite);
86             } catch (PackageParserException e) {
87                 throw new IOException("Failed to parse package: " + packageFile, e);
88             }
89         }
90 
create(Package pkg)91         public static Handle create(Package pkg) throws IOException {
92             return create(pkg.getAllCodePaths(),
93                     (pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) != 0,
94                     (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS) != 0,
95                     (pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
96         }
97 
create(PackageLite lite)98         public static Handle create(PackageLite lite) throws IOException {
99             return create(lite.getAllCodePaths(), lite.multiArch, lite.extractNativeLibs,
100                     lite.debuggable);
101         }
102 
create(List<String> codePaths, boolean multiArch, boolean extractNativeLibs, boolean debuggable)103         private static Handle create(List<String> codePaths, boolean multiArch,
104                 boolean extractNativeLibs, boolean debuggable) throws IOException {
105             final int size = codePaths.size();
106             final long[] apkHandles = new long[size];
107             for (int i = 0; i < size; i++) {
108                 final String path = codePaths.get(i);
109                 apkHandles[i] = nativeOpenApk(path);
110                 if (apkHandles[i] == 0) {
111                     // Unwind everything we've opened so far
112                     for (int j = 0; j < i; j++) {
113                         nativeClose(apkHandles[j]);
114                     }
115                     throw new IOException("Unable to open APK: " + path);
116                 }
117             }
118 
119             return new Handle(apkHandles, multiArch, extractNativeLibs, debuggable);
120         }
121 
createFd(PackageLite lite, FileDescriptor fd)122         public static Handle createFd(PackageLite lite, FileDescriptor fd) throws IOException {
123             final long[] apkHandles = new long[1];
124             final String path = lite.baseCodePath;
125             apkHandles[0] = nativeOpenApkFd(fd, path);
126             if (apkHandles[0] == 0) {
127                 throw new IOException("Unable to open APK " + path + " from fd " + fd);
128             }
129 
130             return new Handle(apkHandles, lite.multiArch, lite.extractNativeLibs, lite.debuggable);
131         }
132 
Handle(long[] apkHandles, boolean multiArch, boolean extractNativeLibs, boolean debuggable)133         Handle(long[] apkHandles, boolean multiArch, boolean extractNativeLibs,
134                 boolean debuggable) {
135             this.apkHandles = apkHandles;
136             this.multiArch = multiArch;
137             this.extractNativeLibs = extractNativeLibs;
138             this.debuggable = debuggable;
139             mGuard.open("close");
140         }
141 
142         @Override
close()143         public void close() {
144             for (long apkHandle : apkHandles) {
145                 nativeClose(apkHandle);
146             }
147             mGuard.close();
148             mClosed = true;
149         }
150 
151         @Override
finalize()152         protected void finalize() throws Throwable {
153             if (mGuard != null) {
154                 mGuard.warnIfOpen();
155             }
156             try {
157                 if (!mClosed) {
158                     close();
159                 }
160             } finally {
161                 super.finalize();
162             }
163         }
164     }
165 
nativeOpenApk(String path)166     private static native long nativeOpenApk(String path);
nativeOpenApkFd(FileDescriptor fd, String debugPath)167     private static native long nativeOpenApkFd(FileDescriptor fd, String debugPath);
nativeClose(long handle)168     private static native void nativeClose(long handle);
169 
nativeSumNativeBinaries(long handle, String cpuAbi, boolean debuggable)170     private static native long nativeSumNativeBinaries(long handle, String cpuAbi,
171             boolean debuggable);
172 
nativeCopyNativeBinaries(long handle, String sharedLibraryPath, String abiToCopy, boolean extractNativeLibs, boolean hasNativeBridge, boolean debuggable)173     private native static int nativeCopyNativeBinaries(long handle, String sharedLibraryPath,
174             String abiToCopy, boolean extractNativeLibs, boolean hasNativeBridge,
175             boolean debuggable);
176 
sumNativeBinaries(Handle handle, String abi)177     private static long sumNativeBinaries(Handle handle, String abi) {
178         long sum = 0;
179         for (long apkHandle : handle.apkHandles) {
180             sum += nativeSumNativeBinaries(apkHandle, abi, handle.debuggable);
181         }
182         return sum;
183     }
184 
185     /**
186      * Copies native binaries to a shared library directory.
187      *
188      * @param handle APK file to scan for native libraries
189      * @param sharedLibraryDir directory for libraries to be copied to
190      * @return {@link PackageManager#INSTALL_SUCCEEDED} if successful or another
191      *         error code from that class if not
192      */
copyNativeBinaries(Handle handle, File sharedLibraryDir, String abi)193     public static int copyNativeBinaries(Handle handle, File sharedLibraryDir, String abi) {
194         for (long apkHandle : handle.apkHandles) {
195             int res = nativeCopyNativeBinaries(apkHandle, sharedLibraryDir.getPath(), abi,
196                     handle.extractNativeLibs, HAS_NATIVE_BRIDGE, handle.debuggable);
197             if (res != INSTALL_SUCCEEDED) {
198                 return res;
199             }
200         }
201         return INSTALL_SUCCEEDED;
202     }
203 
204     /**
205      * Checks if a given APK contains native code for any of the provided
206      * {@code supportedAbis}. Returns an index into {@code supportedAbis} if a matching
207      * ABI is found, {@link PackageManager#NO_NATIVE_LIBRARIES} if the
208      * APK doesn't contain any native code, and
209      * {@link PackageManager#INSTALL_FAILED_NO_MATCHING_ABIS} if none of the ABIs match.
210      */
findSupportedAbi(Handle handle, String[] supportedAbis)211     public static int findSupportedAbi(Handle handle, String[] supportedAbis) {
212         int finalRes = NO_NATIVE_LIBRARIES;
213         for (long apkHandle : handle.apkHandles) {
214             final int res = nativeFindSupportedAbi(apkHandle, supportedAbis, handle.debuggable);
215             if (res == NO_NATIVE_LIBRARIES) {
216                 // No native code, keep looking through all APKs.
217             } else if (res == INSTALL_FAILED_NO_MATCHING_ABIS) {
218                 // Found some native code, but no ABI match; update our final
219                 // result if we haven't found other valid code.
220                 if (finalRes < 0) {
221                     finalRes = INSTALL_FAILED_NO_MATCHING_ABIS;
222                 }
223             } else if (res >= 0) {
224                 // Found valid native code, track the best ABI match
225                 if (finalRes < 0 || res < finalRes) {
226                     finalRes = res;
227                 }
228             } else {
229                 // Unexpected error; bail
230                 return res;
231             }
232         }
233         return finalRes;
234     }
235 
nativeFindSupportedAbi(long handle, String[] supportedAbis, boolean debuggable)236     private native static int nativeFindSupportedAbi(long handle, String[] supportedAbis,
237             boolean debuggable);
238 
239     // Convenience method to call removeNativeBinariesFromDirLI(File)
removeNativeBinariesLI(String nativeLibraryPath)240     public static void removeNativeBinariesLI(String nativeLibraryPath) {
241         if (nativeLibraryPath == null) return;
242         removeNativeBinariesFromDirLI(new File(nativeLibraryPath), false /* delete root dir */);
243     }
244 
245     /**
246      * Remove the native binaries of a given package. This deletes the files
247      */
removeNativeBinariesFromDirLI(File nativeLibraryRoot, boolean deleteRootDir)248     public static void removeNativeBinariesFromDirLI(File nativeLibraryRoot,
249             boolean deleteRootDir) {
250         if (DEBUG_NATIVE) {
251             Slog.w(TAG, "Deleting native binaries from: " + nativeLibraryRoot.getPath());
252         }
253 
254         /*
255          * Just remove any file in the directory. Since the directory is owned
256          * by the 'system' UID, the application is not supposed to have written
257          * anything there.
258          */
259         if (nativeLibraryRoot.exists()) {
260             final File[] files = nativeLibraryRoot.listFiles();
261             if (files != null) {
262                 for (int nn = 0; nn < files.length; nn++) {
263                     if (DEBUG_NATIVE) {
264                         Slog.d(TAG, "    Deleting " + files[nn].getName());
265                     }
266 
267                     if (files[nn].isDirectory()) {
268                         removeNativeBinariesFromDirLI(files[nn], true /* delete root dir */);
269                     } else if (!files[nn].delete()) {
270                         Slog.w(TAG, "Could not delete native binary: " + files[nn].getPath());
271                     }
272                 }
273             }
274             // Do not delete 'lib' directory itself, unless we're specifically
275             // asked to or this will prevent installation of future updates.
276             if (deleteRootDir) {
277                 if (!nativeLibraryRoot.delete()) {
278                     Slog.w(TAG, "Could not delete native binary directory: " +
279                             nativeLibraryRoot.getPath());
280                 }
281             }
282         }
283     }
284 
285     /**
286      * @hide
287      */
createNativeLibrarySubdir(File path)288     public static void createNativeLibrarySubdir(File path) throws IOException {
289         if (!path.isDirectory()) {
290             path.delete();
291 
292             if (!path.mkdir()) {
293                 throw new IOException("Cannot create " + path.getPath());
294             }
295 
296             try {
297                 Os.chmod(path.getPath(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
298             } catch (ErrnoException e) {
299                 throw new IOException("Cannot chmod native library directory "
300                         + path.getPath(), e);
301             }
302         } else if (!SELinux.restorecon(path)) {
303             throw new IOException("Cannot set SELinux context for " + path.getPath());
304         }
305     }
306 
sumNativeBinariesForSupportedAbi(Handle handle, String[] abiList)307     private static long sumNativeBinariesForSupportedAbi(Handle handle, String[] abiList) {
308         int abi = findSupportedAbi(handle, abiList);
309         if (abi >= 0) {
310             return sumNativeBinaries(handle, abiList[abi]);
311         } else {
312             return 0;
313         }
314     }
315 
copyNativeBinariesForSupportedAbi(Handle handle, File libraryRoot, String[] abiList, boolean useIsaSubdir)316     public static int copyNativeBinariesForSupportedAbi(Handle handle, File libraryRoot,
317             String[] abiList, boolean useIsaSubdir) throws IOException {
318         createNativeLibrarySubdir(libraryRoot);
319 
320         /*
321          * If this is an internal application or our nativeLibraryPath points to
322          * the app-lib directory, unpack the libraries if necessary.
323          */
324         int abi = findSupportedAbi(handle, abiList);
325         if (abi >= 0) {
326             /*
327              * If we have a matching instruction set, construct a subdir under the native
328              * library root that corresponds to this instruction set.
329              */
330             final String instructionSet = VMRuntime.getInstructionSet(abiList[abi]);
331             final File subDir;
332             if (useIsaSubdir) {
333                 final File isaSubdir = new File(libraryRoot, instructionSet);
334                 createNativeLibrarySubdir(isaSubdir);
335                 subDir = isaSubdir;
336             } else {
337                 subDir = libraryRoot;
338             }
339 
340             int copyRet = copyNativeBinaries(handle, subDir, abiList[abi]);
341             if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
342                 return copyRet;
343             }
344         }
345 
346         return abi;
347     }
348 
copyNativeBinariesWithOverride(Handle handle, File libraryRoot, String abiOverride)349     public static int copyNativeBinariesWithOverride(Handle handle, File libraryRoot,
350             String abiOverride) {
351         try {
352             if (handle.multiArch) {
353                 // Warn if we've set an abiOverride for multi-lib packages..
354                 // By definition, we need to copy both 32 and 64 bit libraries for
355                 // such packages.
356                 if (abiOverride != null && !CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
357                     Slog.w(TAG, "Ignoring abiOverride for multi arch application.");
358                 }
359 
360                 int copyRet = PackageManager.NO_NATIVE_LIBRARIES;
361                 if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
362                     copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot,
363                             Build.SUPPORTED_32_BIT_ABIS, true /* use isa specific subdirs */);
364                     if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
365                             copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
366                         Slog.w(TAG, "Failure copying 32 bit native libraries; copyRet=" +copyRet);
367                         return copyRet;
368                     }
369                 }
370 
371                 if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
372                     copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot,
373                             Build.SUPPORTED_64_BIT_ABIS, true /* use isa specific subdirs */);
374                     if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
375                             copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
376                         Slog.w(TAG, "Failure copying 64 bit native libraries; copyRet=" +copyRet);
377                         return copyRet;
378                     }
379                 }
380             } else {
381                 String cpuAbiOverride = null;
382                 if (CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
383                     cpuAbiOverride = null;
384                 } else if (abiOverride != null) {
385                     cpuAbiOverride = abiOverride;
386                 }
387 
388                 String[] abiList = (cpuAbiOverride != null) ?
389                         new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;
390                 if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null &&
391                         hasRenderscriptBitcode(handle)) {
392                     abiList = Build.SUPPORTED_32_BIT_ABIS;
393                 }
394 
395                 int copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot, abiList,
396                         true /* use isa specific subdirs */);
397                 if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
398                     Slog.w(TAG, "Failure copying native libraries [errorCode=" + copyRet + "]");
399                     return copyRet;
400                 }
401             }
402 
403             return PackageManager.INSTALL_SUCCEEDED;
404         } catch (IOException e) {
405             Slog.e(TAG, "Copying native libraries failed", e);
406             return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
407         }
408     }
409 
sumNativeBinariesWithOverride(Handle handle, String abiOverride)410     public static long sumNativeBinariesWithOverride(Handle handle, String abiOverride)
411             throws IOException {
412         long sum = 0;
413         if (handle.multiArch) {
414             // Warn if we've set an abiOverride for multi-lib packages..
415             // By definition, we need to copy both 32 and 64 bit libraries for
416             // such packages.
417             if (abiOverride != null && !CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
418                 Slog.w(TAG, "Ignoring abiOverride for multi arch application.");
419             }
420 
421             if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
422                 sum += sumNativeBinariesForSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS);
423             }
424 
425             if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
426                 sum += sumNativeBinariesForSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);
427             }
428         } else {
429             String cpuAbiOverride = null;
430             if (CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
431                 cpuAbiOverride = null;
432             } else if (abiOverride != null) {
433                 cpuAbiOverride = abiOverride;
434             }
435 
436             String[] abiList = (cpuAbiOverride != null) ?
437                     new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;
438             if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null &&
439                     hasRenderscriptBitcode(handle)) {
440                 abiList = Build.SUPPORTED_32_BIT_ABIS;
441             }
442 
443             sum += sumNativeBinariesForSupportedAbi(handle, abiList);
444         }
445         return sum;
446     }
447 
448     // We don't care about the other return values for now.
449     private static final int BITCODE_PRESENT = 1;
450 
451     private static final boolean HAS_NATIVE_BRIDGE =
452             !"0".equals(SystemProperties.get("ro.dalvik.vm.native.bridge", "0"));
453 
hasRenderscriptBitcode(long apkHandle)454     private static native int hasRenderscriptBitcode(long apkHandle);
455 
hasRenderscriptBitcode(Handle handle)456     public static boolean hasRenderscriptBitcode(Handle handle) throws IOException {
457         for (long apkHandle : handle.apkHandles) {
458             final int res = hasRenderscriptBitcode(apkHandle);
459             if (res < 0) {
460                 throw new IOException("Error scanning APK, code: " + res);
461             } else if (res == BITCODE_PRESENT) {
462                 return true;
463             }
464         }
465         return false;
466     }
467 }
468