NativeLibraryHelper.java revision 8d479b0c2ddb150182bcf510876a240cb869661b
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;
22
23import android.content.pm.PackageManager;
24import android.content.pm.PackageParser;
25import android.content.pm.PackageParser.Package;
26import android.content.pm.PackageParser.PackageLite;
27import android.content.pm.PackageParser.PackageParserException;
28import android.util.Slog;
29
30import dalvik.system.CloseGuard;
31
32import java.io.Closeable;
33import java.io.File;
34import java.io.IOException;
35import java.util.List;
36
37/**
38 * Native libraries helper.
39 *
40 * @hide
41 */
42public class NativeLibraryHelper {
43    private static final String TAG = "NativeHelper";
44
45    private static final boolean DEBUG_NATIVE = false;
46
47    /**
48     * A handle to an opened package, consisting of one or more APKs. Used as
49     * input to the various NativeLibraryHelper methods. Allows us to scan and
50     * parse the APKs exactly once instead of doing it multiple times.
51     *
52     * @hide
53     */
54    public static class Handle implements Closeable {
55        private final CloseGuard mGuard = CloseGuard.get();
56        private volatile boolean mClosed;
57
58        final long[] apkHandles;
59
60        public static Handle create(File packageFile) throws IOException {
61            try {
62                final PackageLite lite = PackageParser.parsePackageLite(packageFile, 0);
63                return create(lite);
64            } catch (PackageParserException e) {
65                throw new IOException("Failed to parse package: " + packageFile, e);
66            }
67        }
68
69        public static Handle create(Package pkg) throws IOException {
70            return create(pkg.getAllCodePaths());
71        }
72
73        public static Handle create(PackageLite lite) throws IOException {
74            return create(lite.getAllCodePaths());
75        }
76
77        private static Handle create(List<String> codePaths) throws IOException {
78            final int size = codePaths.size();
79            final long[] apkHandles = new long[size];
80            for (int i = 0; i < size; i++) {
81                final String path = codePaths.get(i);
82                apkHandles[i] = nativeOpenApk(path);
83                if (apkHandles[i] == 0) {
84                    // Unwind everything we've opened so far
85                    for (int j = 0; j < i; j++) {
86                        nativeClose(apkHandles[j]);
87                    }
88                    throw new IOException("Unable to open APK: " + path);
89                }
90            }
91
92            return new Handle(apkHandles);
93        }
94
95        Handle(long[] apkHandles) {
96            this.apkHandles = apkHandles;
97            mGuard.open("close");
98        }
99
100        @Override
101        public void close() {
102            for (long apkHandle : apkHandles) {
103                nativeClose(apkHandle);
104            }
105            mGuard.close();
106            mClosed = true;
107        }
108
109        @Override
110        protected void finalize() throws Throwable {
111            if (mGuard != null) {
112                mGuard.warnIfOpen();
113            }
114            try {
115                if (!mClosed) {
116                    close();
117                }
118            } finally {
119                super.finalize();
120            }
121        }
122    }
123
124    private static native long nativeOpenApk(String path);
125    private static native void nativeClose(long handle);
126
127    private static native long nativeSumNativeBinaries(long handle, String cpuAbi);
128
129    /**
130     * Sums the size of native binaries in an APK for a given ABI.
131     *
132     * @return size of all native binary files in bytes
133     */
134    public static long sumNativeBinariesLI(Handle handle, String abi) {
135        long sum = 0;
136        for (long apkHandle : handle.apkHandles) {
137            sum += nativeSumNativeBinaries(apkHandle, abi);
138        }
139        return sum;
140    }
141
142    private native static int nativeCopyNativeBinaries(long handle,
143            String sharedLibraryPath, String abiToCopy);
144
145    /**
146     * Copies native binaries to a shared library directory.
147     *
148     * @param handle APK file to scan for native libraries
149     * @param sharedLibraryDir directory for libraries to be copied to
150     * @return {@link PackageManager#INSTALL_SUCCEEDED} if successful or another
151     *         error code from that class if not
152     */
153    public static int copyNativeBinariesIfNeededLI(Handle handle, File sharedLibraryDir,
154            String abi) {
155        for (long apkHandle : handle.apkHandles) {
156            int res = nativeCopyNativeBinaries(apkHandle, sharedLibraryDir.getPath(), abi);
157            if (res != INSTALL_SUCCEEDED) {
158                return res;
159            }
160        }
161        return INSTALL_SUCCEEDED;
162    }
163
164    /**
165     * Checks if a given APK contains native code for any of the provided
166     * {@code supportedAbis}. Returns an index into {@code supportedAbis} if a matching
167     * ABI is found, {@link PackageManager#NO_NATIVE_LIBRARIES} if the
168     * APK doesn't contain any native code, and
169     * {@link PackageManager#INSTALL_FAILED_NO_MATCHING_ABIS} if none of the ABIs match.
170     */
171    public static int findSupportedAbi(Handle handle, String[] supportedAbis) {
172        int finalRes = NO_NATIVE_LIBRARIES;
173        for (long apkHandle : handle.apkHandles) {
174            final int res = nativeFindSupportedAbi(apkHandle, supportedAbis);
175            if (res == NO_NATIVE_LIBRARIES) {
176                // No native code, keep looking through all APKs.
177            } else if (res == INSTALL_FAILED_NO_MATCHING_ABIS) {
178                // Found some native code, but no ABI match; update our final
179                // result if we haven't found other valid code.
180                if (finalRes < 0) {
181                    finalRes = INSTALL_FAILED_NO_MATCHING_ABIS;
182                }
183            } else if (res >= 0) {
184                // Found valid native code, track the best ABI match
185                if (finalRes < 0 || res < finalRes) {
186                    finalRes = res;
187                }
188            } else {
189                // Unexpected error; bail
190                return res;
191            }
192        }
193        return finalRes;
194    }
195
196    private native static int nativeFindSupportedAbi(long handle, String[] supportedAbis);
197
198    // Convenience method to call removeNativeBinariesFromDirLI(File)
199    public static boolean removeNativeBinariesLI(String nativeLibraryPath) {
200        if (nativeLibraryPath == null) return false;
201        return removeNativeBinariesFromDirLI(new File(nativeLibraryPath));
202    }
203
204    // Remove the native binaries of a given package. This simply
205    // gets rid of the files in the 'lib' sub-directory.
206    public static boolean removeNativeBinariesFromDirLI(File nativeLibraryDir) {
207        if (DEBUG_NATIVE) {
208            Slog.w(TAG, "Deleting native binaries from: " + nativeLibraryDir.getPath());
209        }
210
211        boolean deletedFiles = false;
212
213        /*
214         * Just remove any file in the directory. Since the directory is owned
215         * by the 'system' UID, the application is not supposed to have written
216         * anything there.
217         */
218        if (nativeLibraryDir.exists()) {
219            final File[] binaries = nativeLibraryDir.listFiles();
220            if (binaries != null) {
221                for (int nn = 0; nn < binaries.length; nn++) {
222                    if (DEBUG_NATIVE) {
223                        Slog.d(TAG, "    Deleting " + binaries[nn].getName());
224                    }
225
226                    if (!binaries[nn].delete()) {
227                        Slog.w(TAG, "Could not delete native binary: " + binaries[nn].getPath());
228                    } else {
229                        deletedFiles = true;
230                    }
231                }
232            }
233            // Do not delete 'lib' directory itself, or this will prevent
234            // installation of future updates.
235        }
236
237        return deletedFiles;
238    }
239
240    // We don't care about the other return values for now.
241    private static final int BITCODE_PRESENT = 1;
242
243    public static boolean hasRenderscriptBitcode(Handle handle) throws IOException {
244        for (long apkHandle : handle.apkHandles) {
245            final int res = hasRenderscriptBitcode(apkHandle);
246            if (res < 0) {
247                throw new IOException("Error scanning APK, code: " + res);
248            } else if (res == BITCODE_PRESENT) {
249                return true;
250            }
251        }
252        return false;
253    }
254
255    private static native int hasRenderscriptBitcode(long apkHandle);
256}
257