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