OSDN Git Service

Merge changes from topic "am-8d44d2e5-2b24-4867-a4fc-1c5b430ac3a0" into oc-dev am...
[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.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
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
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
98         public static Handle create(PackageLite lite) throws IOException {
99             return create(lite.getAllCodePaths(), lite.multiArch, lite.extractNativeLibs,
100                     lite.debuggable);
101         }
102
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
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
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
143         public void close() {
144             for (long apkHandle : apkHandles) {
145                 nativeClose(apkHandle);
146             }
147             mGuard.close();
148             mClosed = true;
149         }
150
151         @Override
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
166     private static native long nativeOpenApk(String path);
167     private static native long nativeOpenApkFd(FileDescriptor fd, String debugPath);
168     private static native void nativeClose(long handle);
169
170     private static native long nativeSumNativeBinaries(long handle, String cpuAbi,
171             boolean debuggable);
172
173     private native static int nativeCopyNativeBinaries(long handle, String sharedLibraryPath,
174             String abiToCopy, boolean extractNativeLibs, boolean hasNativeBridge,
175             boolean debuggable);
176
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      */
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      */
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
236     private native static int nativeFindSupportedAbi(long handle, String[] supportedAbis,
237             boolean debuggable);
238
239     // Convenience method to call removeNativeBinariesFromDirLI(File)
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      */
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      */
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
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
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
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
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
454     private static native int hasRenderscriptBitcode(long apkHandle);
455
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 }