NativeLibraryHelper.java revision d6dd6b8a613c9c91eeebde713f12f18e6cc43c1f
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
17package com.android.internal.content;
18
19import static android.content.pm.PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS;
20import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
21import static android.content.pm.PackageManager.NO_NATIVE_LIBRARIES;
22import static android.system.OsConstants.S_IRGRP;
23import static android.system.OsConstants.S_IROTH;
24import static android.system.OsConstants.S_IRWXU;
25import static android.system.OsConstants.S_IXGRP;
26import static android.system.OsConstants.S_IXOTH;
27
28import android.content.pm.ApplicationInfo;
29import android.content.pm.PackageManager;
30import android.content.pm.PackageParser;
31import android.content.pm.PackageParser.Package;
32import android.content.pm.PackageParser.PackageLite;
33import android.content.pm.PackageParser.PackageParserException;
34import android.os.Build;
35import android.os.SELinux;
36import android.os.SystemProperties;
37import android.system.ErrnoException;
38import android.system.Os;
39import android.util.Slog;
40
41import dalvik.system.CloseGuard;
42import dalvik.system.VMRuntime;
43
44import java.io.Closeable;
45import java.io.File;
46import java.io.IOException;
47import java.util.List;
48
49/**
50 * Native libraries helper.
51 *
52 * @hide
53 */
54public 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}
452