OSDN Git Service

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