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