PackageHelper.java revision 742e790294b3441b79f715fe447069b63c6065db
1/*
2 * Copyright (C) 2009 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 android.content.Context;
20import android.content.pm.PackageInfo;
21import android.content.pm.PackageManager;
22import android.os.Environment;
23import android.os.FileUtils;
24import android.os.IBinder;
25import android.os.RemoteException;
26import android.os.ServiceManager;
27import android.os.storage.IMountService;
28import android.os.storage.StorageManager;
29import android.os.storage.StorageResultCode;
30import android.util.Log;
31
32import libcore.io.IoUtils;
33
34import java.io.File;
35import java.io.FileOutputStream;
36import java.io.IOException;
37import java.io.InputStream;
38import java.util.Collections;
39import java.util.zip.ZipEntry;
40import java.util.zip.ZipFile;
41import java.util.zip.ZipOutputStream;
42
43/**
44 * Constants used internally between the PackageManager
45 * and media container service transports.
46 * Some utility methods to invoke MountService api.
47 */
48public class PackageHelper {
49    public static final int RECOMMEND_INSTALL_INTERNAL = 1;
50    public static final int RECOMMEND_INSTALL_EXTERNAL = 2;
51    public static final int RECOMMEND_FAILED_INSUFFICIENT_STORAGE = -1;
52    public static final int RECOMMEND_FAILED_INVALID_APK = -2;
53    public static final int RECOMMEND_FAILED_INVALID_LOCATION = -3;
54    public static final int RECOMMEND_FAILED_ALREADY_EXISTS = -4;
55    public static final int RECOMMEND_MEDIA_UNAVAILABLE = -5;
56    public static final int RECOMMEND_FAILED_INVALID_URI = -6;
57    public static final int RECOMMEND_FAILED_VERSION_DOWNGRADE = -7;
58
59    private static final boolean localLOGV = false;
60    private static final String TAG = "PackageHelper";
61    // App installation location settings values
62    public static final int APP_INSTALL_AUTO = 0;
63    public static final int APP_INSTALL_INTERNAL = 1;
64    public static final int APP_INSTALL_EXTERNAL = 2;
65
66    public static IMountService getMountService() throws RemoteException {
67        IBinder service = ServiceManager.getService("mount");
68        if (service != null) {
69            return IMountService.Stub.asInterface(service);
70        } else {
71            Log.e(TAG, "Can't get mount service");
72            throw new RemoteException("Could not contact mount service");
73        }
74    }
75
76    public static String createSdDir(int sizeMb, String cid, String sdEncKey, int uid,
77            boolean isExternal) {
78        // Create mount point via MountService
79        try {
80            IMountService mountService = getMountService();
81
82            if (localLOGV)
83                Log.i(TAG, "Size of container " + sizeMb + " MB");
84
85            int rc = mountService.createSecureContainer(cid, sizeMb, "ext4", sdEncKey, uid,
86                    isExternal);
87            if (rc != StorageResultCode.OperationSucceeded) {
88                Log.e(TAG, "Failed to create secure container " + cid);
89                return null;
90            }
91            String cachePath = mountService.getSecureContainerPath(cid);
92            if (localLOGV) Log.i(TAG, "Created secure container " + cid +
93                    " at " + cachePath);
94                return cachePath;
95        } catch (RemoteException e) {
96            Log.e(TAG, "MountService running?");
97        }
98        return null;
99    }
100
101   public static String mountSdDir(String cid, String key, int ownerUid) {
102    try {
103        int rc = getMountService().mountSecureContainer(cid, key, ownerUid);
104        if (rc != StorageResultCode.OperationSucceeded) {
105            Log.i(TAG, "Failed to mount container " + cid + " rc : " + rc);
106            return null;
107        }
108        return getMountService().getSecureContainerPath(cid);
109    } catch (RemoteException e) {
110        Log.e(TAG, "MountService running?");
111    }
112    return null;
113   }
114
115   public static boolean unMountSdDir(String cid) {
116    try {
117        int rc = getMountService().unmountSecureContainer(cid, true);
118        if (rc != StorageResultCode.OperationSucceeded) {
119            Log.e(TAG, "Failed to unmount " + cid + " with rc " + rc);
120            return false;
121        }
122        return true;
123    } catch (RemoteException e) {
124        Log.e(TAG, "MountService running?");
125    }
126        return false;
127   }
128
129   public static boolean renameSdDir(String oldId, String newId) {
130       try {
131           int rc = getMountService().renameSecureContainer(oldId, newId);
132           if (rc != StorageResultCode.OperationSucceeded) {
133               Log.e(TAG, "Failed to rename " + oldId + " to " +
134                       newId + "with rc " + rc);
135               return false;
136           }
137           return true;
138       } catch (RemoteException e) {
139           Log.i(TAG, "Failed ot rename  " + oldId + " to " + newId +
140                   " with exception : " + e);
141       }
142       return false;
143   }
144
145   public static String getSdDir(String cid) {
146       try {
147            return getMountService().getSecureContainerPath(cid);
148        } catch (RemoteException e) {
149            Log.e(TAG, "Failed to get container path for " + cid +
150                " with exception " + e);
151        }
152        return null;
153   }
154
155   public static String getSdFilesystem(String cid) {
156       try {
157            return getMountService().getSecureContainerFilesystemPath(cid);
158        } catch (RemoteException e) {
159            Log.e(TAG, "Failed to get container path for " + cid +
160                " with exception " + e);
161        }
162        return null;
163   }
164
165    public static boolean finalizeSdDir(String cid) {
166        try {
167            int rc = getMountService().finalizeSecureContainer(cid);
168            if (rc != StorageResultCode.OperationSucceeded) {
169                Log.i(TAG, "Failed to finalize container " + cid);
170                return false;
171            }
172            return true;
173        } catch (RemoteException e) {
174            Log.e(TAG, "Failed to finalize container " + cid +
175                    " with exception " + e);
176        }
177        return false;
178    }
179
180    public static boolean destroySdDir(String cid) {
181        try {
182            if (localLOGV) Log.i(TAG, "Forcibly destroying container " + cid);
183            int rc = getMountService().destroySecureContainer(cid, true);
184            if (rc != StorageResultCode.OperationSucceeded) {
185                Log.i(TAG, "Failed to destroy container " + cid);
186                return false;
187            }
188            return true;
189        } catch (RemoteException e) {
190            Log.e(TAG, "Failed to destroy container " + cid +
191                    " with exception " + e);
192        }
193        return false;
194    }
195
196    public static String[] getSecureContainerList() {
197        try {
198            return getMountService().getSecureContainerList();
199        } catch (RemoteException e) {
200            Log.e(TAG, "Failed to get secure container list with exception" +
201                    e);
202        }
203        return null;
204    }
205
206   public static boolean isContainerMounted(String cid) {
207       try {
208           return getMountService().isSecureContainerMounted(cid);
209       } catch (RemoteException e) {
210           Log.e(TAG, "Failed to find out if container " + cid + " mounted");
211       }
212       return false;
213   }
214
215    /**
216     * Extract public files for the single given APK.
217     */
218    public static int extractPublicFiles(String apkPath, File publicZipFile)
219            throws IOException {
220        final FileOutputStream fstr;
221        final ZipOutputStream publicZipOutStream;
222
223        if (publicZipFile == null) {
224            fstr = null;
225            publicZipOutStream = null;
226        } else {
227            fstr = new FileOutputStream(publicZipFile);
228            publicZipOutStream = new ZipOutputStream(fstr);
229        }
230
231        int size = 0;
232
233        try {
234            final ZipFile privateZip = new ZipFile(apkPath);
235            try {
236                // Copy manifest, resources.arsc and res directory to public zip
237                for (final ZipEntry zipEntry : Collections.list(privateZip.entries())) {
238                    final String zipEntryName = zipEntry.getName();
239                    if ("AndroidManifest.xml".equals(zipEntryName)
240                            || "resources.arsc".equals(zipEntryName)
241                            || zipEntryName.startsWith("res/")) {
242                        size += zipEntry.getSize();
243                        if (publicZipFile != null) {
244                            copyZipEntry(zipEntry, privateZip, publicZipOutStream);
245                        }
246                    }
247                }
248            } finally {
249                try { privateZip.close(); } catch (IOException e) {}
250            }
251
252            if (publicZipFile != null) {
253                publicZipOutStream.finish();
254                publicZipOutStream.flush();
255                FileUtils.sync(fstr);
256                publicZipOutStream.close();
257                FileUtils.setPermissions(publicZipFile.getAbsolutePath(), FileUtils.S_IRUSR
258                        | FileUtils.S_IWUSR | FileUtils.S_IRGRP | FileUtils.S_IROTH, -1, -1);
259            }
260        } finally {
261            IoUtils.closeQuietly(publicZipOutStream);
262        }
263
264        return size;
265    }
266
267    private static void copyZipEntry(ZipEntry zipEntry, ZipFile inZipFile,
268            ZipOutputStream outZipStream) throws IOException {
269        byte[] buffer = new byte[4096];
270        int num;
271
272        ZipEntry newEntry;
273        if (zipEntry.getMethod() == ZipEntry.STORED) {
274            // Preserve the STORED method of the input entry.
275            newEntry = new ZipEntry(zipEntry);
276        } else {
277            // Create a new entry so that the compressed len is recomputed.
278            newEntry = new ZipEntry(zipEntry.getName());
279        }
280        outZipStream.putNextEntry(newEntry);
281
282        final InputStream data = inZipFile.getInputStream(zipEntry);
283        try {
284            while ((num = data.read(buffer)) > 0) {
285                outZipStream.write(buffer, 0, num);
286            }
287            outZipStream.flush();
288        } finally {
289            IoUtils.closeQuietly(data);
290        }
291    }
292
293    public static boolean fixSdPermissions(String cid, int gid, String filename) {
294        try {
295            int rc = getMountService().fixPermissionsSecureContainer(cid, gid, filename);
296            if (rc != StorageResultCode.OperationSucceeded) {
297                Log.i(TAG, "Failed to fixperms container " + cid);
298                return false;
299            }
300            return true;
301        } catch (RemoteException e) {
302            Log.e(TAG, "Failed to fixperms container " + cid + " with exception " + e);
303        }
304        return false;
305    }
306
307    /**
308     * Given a requested {@link PackageInfo#installLocation} and calculated
309     * install size, pick the actual location to install the app.
310     */
311    public static int resolveInstallLocation(Context context, int installLocation, long sizeBytes,
312            int installFlags) {
313        final int prefer;
314        final boolean checkBoth;
315        if ((installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
316            prefer = RECOMMEND_INSTALL_INTERNAL;
317            checkBoth = false;
318        } else if ((installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
319            prefer = RECOMMEND_INSTALL_EXTERNAL;
320            checkBoth = false;
321        } else if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
322            prefer = RECOMMEND_INSTALL_INTERNAL;
323            checkBoth = false;
324        } else if (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
325            prefer = RECOMMEND_INSTALL_EXTERNAL;
326            checkBoth = true;
327        } else if (installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
328            prefer = RECOMMEND_INSTALL_INTERNAL;
329            checkBoth = true;
330        } else {
331            prefer = RECOMMEND_INSTALL_INTERNAL;
332            checkBoth = false;
333        }
334
335        final boolean emulated = Environment.isExternalStorageEmulated();
336        final StorageManager storage = StorageManager.from(context);
337
338        boolean fitsOnInternal = false;
339        if (checkBoth || prefer == RECOMMEND_INSTALL_INTERNAL) {
340            fitsOnInternal = (sizeBytes
341                    <= storage.getStorageBytesUntilLow(Environment.getDataDirectory()));
342        }
343
344        boolean fitsOnExternal = false;
345        if (!emulated && (checkBoth || prefer == RECOMMEND_INSTALL_EXTERNAL)) {
346            fitsOnExternal = (sizeBytes
347                    <= storage.getStorageBytesUntilLow(Environment.getExternalStorageDirectory()));
348        }
349
350        if (prefer == RECOMMEND_INSTALL_INTERNAL) {
351            if (fitsOnInternal) {
352                return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
353            }
354        } else if (!emulated && prefer == RECOMMEND_INSTALL_EXTERNAL) {
355            if (fitsOnExternal) {
356                return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
357            }
358        }
359
360        if (checkBoth) {
361            if (fitsOnInternal) {
362                return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
363            } else if (!emulated && fitsOnExternal) {
364                return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
365            }
366        }
367
368        /*
369         * If they requested to be on the external media by default, return that
370         * the media was unavailable. Otherwise, indicate there was insufficient
371         * storage space available.
372         */
373        if (!emulated && (checkBoth || prefer == RECOMMEND_INSTALL_EXTERNAL)
374                && !Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
375            return PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE;
376        } else {
377            return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
378        }
379    }
380}
381