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