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 debuggable)173     private native static int nativeCopyNativeBinaries(long handle, String sharedLibraryPath,
174             String abiToCopy, boolean extractNativeLibs, boolean debuggable);
175 
sumNativeBinaries(Handle handle, String abi)176     private static long sumNativeBinaries(Handle handle, String abi) {
177         long sum = 0;
178         for (long apkHandle : handle.apkHandles) {
179             sum += nativeSumNativeBinaries(apkHandle, abi, handle.debuggable);
180         }
181         return sum;
182     }
183 
184     /**
185      * Copies native binaries to a shared library directory.
186      *
187      * @param handle APK file to scan for native libraries
188      * @param sharedLibraryDir directory for libraries to be copied to
189      * @return {@link PackageManager#INSTALL_SUCCEEDED} if successful or another
190      *         error code from that class if not
191      */
copyNativeBinaries(Handle handle, File sharedLibraryDir, String abi)192     public static int copyNativeBinaries(Handle handle, File sharedLibraryDir, String abi) {
193         for (long apkHandle : handle.apkHandles) {
194             int res = nativeCopyNativeBinaries(apkHandle, sharedLibraryDir.getPath(), abi,
195                     handle.extractNativeLibs, handle.debuggable);
196             if (res != INSTALL_SUCCEEDED) {
197                 return res;
198             }
199         }
200         return INSTALL_SUCCEEDED;
201     }
202 
203     /**
204      * Checks if a given APK contains native code for any of the provided
205      * {@code supportedAbis}. Returns an index into {@code supportedAbis} if a matching
206      * ABI is found, {@link PackageManager#NO_NATIVE_LIBRARIES} if the
207      * APK doesn't contain any native code, and
208      * {@link PackageManager#INSTALL_FAILED_NO_MATCHING_ABIS} if none of the ABIs match.
209      */
findSupportedAbi(Handle handle, String[] supportedAbis)210     public static int findSupportedAbi(Handle handle, String[] supportedAbis) {
211         int finalRes = NO_NATIVE_LIBRARIES;
212         for (long apkHandle : handle.apkHandles) {
213             final int res = nativeFindSupportedAbi(apkHandle, supportedAbis, handle.debuggable);
214             if (res == NO_NATIVE_LIBRARIES) {
215                 // No native code, keep looking through all APKs.
216             } else if (res == INSTALL_FAILED_NO_MATCHING_ABIS) {
217                 // Found some native code, but no ABI match; update our final
218                 // result if we haven't found other valid code.
219                 if (finalRes < 0) {
220                     finalRes = INSTALL_FAILED_NO_MATCHING_ABIS;
221                 }
222             } else if (res >= 0) {
223                 // Found valid native code, track the best ABI match
224                 if (finalRes < 0 || res < finalRes) {
225                     finalRes = res;
226                 }
227             } else {
228                 // Unexpected error; bail
229                 return res;
230             }
231         }
232         return finalRes;
233     }
234 
nativeFindSupportedAbi(long handle, String[] supportedAbis, boolean debuggable)235     private native static int nativeFindSupportedAbi(long handle, String[] supportedAbis,
236             boolean debuggable);
237 
238     // Convenience method to call removeNativeBinariesFromDirLI(File)
removeNativeBinariesLI(String nativeLibraryPath)239     public static void removeNativeBinariesLI(String nativeLibraryPath) {
240         if (nativeLibraryPath == null) return;
241         removeNativeBinariesFromDirLI(new File(nativeLibraryPath), false /* delete root dir */);
242     }
243 
244     /**
245      * Remove the native binaries of a given package. This deletes the files
246      */
removeNativeBinariesFromDirLI(File nativeLibraryRoot, boolean deleteRootDir)247     public static void removeNativeBinariesFromDirLI(File nativeLibraryRoot,
248             boolean deleteRootDir) {
249         if (DEBUG_NATIVE) {
250             Slog.w(TAG, "Deleting native binaries from: " + nativeLibraryRoot.getPath());
251         }
252 
253         /*
254          * Just remove any file in the directory. Since the directory is owned
255          * by the 'system' UID, the application is not supposed to have written
256          * anything there.
257          */
258         if (nativeLibraryRoot.exists()) {
259             final File[] files = nativeLibraryRoot.listFiles();
260             if (files != null) {
261                 for (int nn = 0; nn < files.length; nn++) {
262                     if (DEBUG_NATIVE) {
263                         Slog.d(TAG, "    Deleting " + files[nn].getName());
264                     }
265 
266                     if (files[nn].isDirectory()) {
267                         removeNativeBinariesFromDirLI(files[nn], true /* delete root dir */);
268                     } else if (!files[nn].delete()) {
269                         Slog.w(TAG, "Could not delete native binary: " + files[nn].getPath());
270                     }
271                 }
272             }
273             // Do not delete 'lib' directory itself, unless we're specifically
274             // asked to or this will prevent installation of future updates.
275             if (deleteRootDir) {
276                 if (!nativeLibraryRoot.delete()) {
277                     Slog.w(TAG, "Could not delete native binary directory: " +
278                             nativeLibraryRoot.getPath());
279                 }
280             }
281         }
282     }
283 
284     /**
285      * @hide
286      */
createNativeLibrarySubdir(File path)287     public static void createNativeLibrarySubdir(File path) throws IOException {
288         if (!path.isDirectory()) {
289             path.delete();
290 
291             if (!path.mkdir()) {
292                 throw new IOException("Cannot create " + path.getPath());
293             }
294 
295             try {
296                 Os.chmod(path.getPath(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
297             } catch (ErrnoException e) {
298                 throw new IOException("Cannot chmod native library directory "
299                         + path.getPath(), e);
300             }
301         } else if (!SELinux.restorecon(path)) {
302             throw new IOException("Cannot set SELinux context for " + path.getPath());
303         }
304     }
305 
sumNativeBinariesForSupportedAbi(Handle handle, String[] abiList)306     private static long sumNativeBinariesForSupportedAbi(Handle handle, String[] abiList) {
307         int abi = findSupportedAbi(handle, abiList);
308         if (abi >= 0) {
309             return sumNativeBinaries(handle, abiList[abi]);
310         } else {
311             return 0;
312         }
313     }
314 
copyNativeBinariesForSupportedAbi(Handle handle, File libraryRoot, String[] abiList, boolean useIsaSubdir)315     public static int copyNativeBinariesForSupportedAbi(Handle handle, File libraryRoot,
316             String[] abiList, boolean useIsaSubdir) throws IOException {
317         createNativeLibrarySubdir(libraryRoot);
318 
319         /*
320          * If this is an internal application or our nativeLibraryPath points to
321          * the app-lib directory, unpack the libraries if necessary.
322          */
323         int abi = findSupportedAbi(handle, abiList);
324         if (abi >= 0) {
325             /*
326              * If we have a matching instruction set, construct a subdir under the native
327              * library root that corresponds to this instruction set.
328              */
329             final String instructionSet = VMRuntime.getInstructionSet(abiList[abi]);
330             final File subDir;
331             if (useIsaSubdir) {
332                 final File isaSubdir = new File(libraryRoot, instructionSet);
333                 createNativeLibrarySubdir(isaSubdir);
334                 subDir = isaSubdir;
335             } else {
336                 subDir = libraryRoot;
337             }
338 
339             int copyRet = copyNativeBinaries(handle, subDir, abiList[abi]);
340             if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
341                 return copyRet;
342             }
343         }
344 
345         return abi;
346     }
347 
copyNativeBinariesWithOverride(Handle handle, File libraryRoot, String abiOverride)348     public static int copyNativeBinariesWithOverride(Handle handle, File libraryRoot,
349             String abiOverride) {
350         try {
351             if (handle.multiArch) {
352                 // Warn if we've set an abiOverride for multi-lib packages..
353                 // By definition, we need to copy both 32 and 64 bit libraries for
354                 // such packages.
355                 if (abiOverride != null && !CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
356                     Slog.w(TAG, "Ignoring abiOverride for multi arch application.");
357                 }
358 
359                 int copyRet = PackageManager.NO_NATIVE_LIBRARIES;
360                 if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
361                     copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot,
362                             Build.SUPPORTED_32_BIT_ABIS, true /* use isa specific subdirs */);
363                     if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
364                             copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
365                         Slog.w(TAG, "Failure copying 32 bit native libraries; copyRet=" +copyRet);
366                         return copyRet;
367                     }
368                 }
369 
370                 if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
371                     copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot,
372                             Build.SUPPORTED_64_BIT_ABIS, true /* use isa specific subdirs */);
373                     if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
374                             copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
375                         Slog.w(TAG, "Failure copying 64 bit native libraries; copyRet=" +copyRet);
376                         return copyRet;
377                     }
378                 }
379             } else {
380                 String cpuAbiOverride = null;
381                 if (CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
382                     cpuAbiOverride = null;
383                 } else if (abiOverride != null) {
384                     cpuAbiOverride = abiOverride;
385                 }
386 
387                 String[] abiList = (cpuAbiOverride != null) ?
388                         new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;
389                 if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null &&
390                         hasRenderscriptBitcode(handle)) {
391                     abiList = Build.SUPPORTED_32_BIT_ABIS;
392                 }
393 
394                 int copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot, abiList,
395                         true /* use isa specific subdirs */);
396                 if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
397                     Slog.w(TAG, "Failure copying native libraries [errorCode=" + copyRet + "]");
398                     return copyRet;
399                 }
400             }
401 
402             return PackageManager.INSTALL_SUCCEEDED;
403         } catch (IOException e) {
404             Slog.e(TAG, "Copying native libraries failed", e);
405             return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
406         }
407     }
408 
sumNativeBinariesWithOverride(Handle handle, String abiOverride)409     public static long sumNativeBinariesWithOverride(Handle handle, String abiOverride)
410             throws IOException {
411         long sum = 0;
412         if (handle.multiArch) {
413             // Warn if we've set an abiOverride for multi-lib packages..
414             // By definition, we need to copy both 32 and 64 bit libraries for
415             // such packages.
416             if (abiOverride != null && !CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
417                 Slog.w(TAG, "Ignoring abiOverride for multi arch application.");
418             }
419 
420             if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
421                 sum += sumNativeBinariesForSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS);
422             }
423 
424             if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
425                 sum += sumNativeBinariesForSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);
426             }
427         } else {
428             String cpuAbiOverride = null;
429             if (CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
430                 cpuAbiOverride = null;
431             } else if (abiOverride != null) {
432                 cpuAbiOverride = abiOverride;
433             }
434 
435             String[] abiList = (cpuAbiOverride != null) ?
436                     new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;
437             if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null &&
438                     hasRenderscriptBitcode(handle)) {
439                 abiList = Build.SUPPORTED_32_BIT_ABIS;
440             }
441 
442             sum += sumNativeBinariesForSupportedAbi(handle, abiList);
443         }
444         return sum;
445     }
446 
447     // We don't care about the other return values for now.
448     private static final int BITCODE_PRESENT = 1;
449 
hasRenderscriptBitcode(long apkHandle)450     private static native int hasRenderscriptBitcode(long apkHandle);
451 
hasRenderscriptBitcode(Handle handle)452     public static boolean hasRenderscriptBitcode(Handle handle) throws IOException {
453         for (long apkHandle : handle.apkHandles) {
454             final int res = hasRenderscriptBitcode(apkHandle);
455             if (res < 0) {
456                 throw new IOException("Error scanning APK, code: " + res);
457             } else if (res == BITCODE_PRESENT) {
458                 return true;
459             }
460         }
461         return false;
462     }
463 }
464