OSDN Git Service

Merge cherrypicks of [4025538, 4025981, 4025580, 4024762, 4024763, 4025174, 4026015...
[android-x86/frameworks-base.git] / core / java / com / android / internal / content / NativeLibraryHelper.java
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
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
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
97         public static Handle create(PackageLite lite) throws IOException {
98             return create(lite.getAllCodePaths(), lite.multiArch, lite.extractNativeLibs,
99                     lite.debuggable);
100         }
101
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
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
131         public void close() {
132             for (long apkHandle : apkHandles) {
133                 nativeClose(apkHandle);
134             }
135             mGuard.close();
136             mClosed = true;
137         }
138
139         @Override
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
154     private static native long nativeOpenApk(String path);
155     private static native void nativeClose(long handle);
156
157     private static native long nativeSumNativeBinaries(long handle, String cpuAbi,
158             boolean debuggable);
159
160     private native static int nativeCopyNativeBinaries(long handle, String sharedLibraryPath,
161             String abiToCopy, boolean extractNativeLibs, boolean hasNativeBridge,
162             boolean debuggable);
163
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      */
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      */
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
223     private native static int nativeFindSupportedAbi(long handle, String[] supportedAbis,
224             boolean debuggable);
225
226     // Convenience method to call removeNativeBinariesFromDirLI(File)
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      */
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
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
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
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
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
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
438     private static native int hasRenderscriptBitcode(long apkHandle);
439
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 }