DefaultContainerService.java revision ff110bd61a69f7ed8602ae14b27f7befec76b2e7
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.defcontainer; 18 19import android.app.IntentService; 20import android.content.Intent; 21import android.content.pm.IPackageManager; 22import android.content.pm.PackageCleanItem; 23import android.content.pm.PackageInfo; 24import android.content.pm.PackageInfoLite; 25import android.content.pm.PackageManager; 26import android.content.pm.PackageParser; 27import android.content.pm.PackageParser.PackageLite; 28import android.content.pm.PackageParser.PackageParserException; 29import android.content.res.ObbInfo; 30import android.content.res.ObbScanner; 31import android.os.Build; 32import android.os.Environment; 33import android.os.Environment.UserEnvironment; 34import android.os.FileUtils; 35import android.os.IBinder; 36import android.os.ParcelFileDescriptor; 37import android.os.Process; 38import android.os.RemoteException; 39import android.os.ServiceManager; 40import android.os.StatFs; 41import android.provider.Settings; 42import android.system.ErrnoException; 43import android.system.Os; 44import android.system.StructStatVfs; 45import android.util.Slog; 46 47import com.android.internal.app.IMediaContainerService; 48import com.android.internal.content.NativeLibraryHelper; 49import com.android.internal.content.PackageHelper; 50import com.android.internal.os.IParcelFileDescriptorFactory; 51import com.android.internal.util.ArrayUtils; 52 53import dalvik.system.VMRuntime; 54import libcore.io.IoUtils; 55import libcore.io.Streams; 56 57import java.io.File; 58import java.io.FileInputStream; 59import java.io.FileNotFoundException; 60import java.io.IOException; 61import java.io.InputStream; 62import java.io.OutputStream; 63 64/** 65 * Service that offers to inspect and copy files that may reside on removable 66 * storage. This is designed to prevent the system process from holding onto 67 * open files that cause the kernel to kill it when the underlying device is 68 * removed. 69 */ 70public class DefaultContainerService extends IntentService { 71 private static final String TAG = "DefContainer"; 72 private static final boolean localLOGV = false; 73 74 private static final String LIB_DIR_NAME = "lib"; 75 76 // TODO: migrate native code unpacking to always be a derivative work 77 78 private IMediaContainerService.Stub mBinder = new IMediaContainerService.Stub() { 79 /** 80 * Creates a new container and copies package there. 81 * 82 * @param packagePath absolute path to the package to be copied. Can be 83 * a single monolithic APK file or a cluster directory 84 * containing one or more APKs. 85 * @param containerId the id of the secure container that should be used 86 * for creating a secure container into which the resource 87 * will be copied. 88 * @param key Refers to key used for encrypting the secure container 89 * @return Returns the new cache path where the resource has been copied 90 * into 91 */ 92 @Override 93 public String copyPackageToContainer(String packagePath, String containerId, String key, 94 boolean isExternal, boolean isForwardLocked, String abiOverride) { 95 if (packagePath == null || containerId == null) { 96 return null; 97 } 98 99 if (isExternal) { 100 // Make sure the sdcard is mounted. 101 String status = Environment.getExternalStorageState(); 102 if (!status.equals(Environment.MEDIA_MOUNTED)) { 103 Slog.w(TAG, "Make sure sdcard is mounted."); 104 return null; 105 } 106 } 107 108 PackageLite pkg = null; 109 NativeLibraryHelper.Handle handle = null; 110 try { 111 final File packageFile = new File(packagePath); 112 pkg = PackageParser.parsePackageLite(packageFile, 0); 113 handle = NativeLibraryHelper.Handle.create(pkg); 114 return copyPackageToContainerInner(pkg, handle, containerId, key, isExternal, 115 isForwardLocked, abiOverride); 116 } catch (PackageParserException | IOException e) { 117 Slog.w(TAG, "Failed to parse package at " + packagePath); 118 return null; 119 } finally { 120 IoUtils.closeQuietly(handle); 121 } 122 } 123 124 /** 125 * Copy package to the target location. 126 * 127 * @param packagePath absolute path to the package to be copied. Can be 128 * a single monolithic APK file or a cluster directory 129 * containing one or more APKs. 130 * @return returns status code according to those in 131 * {@link PackageManager} 132 */ 133 @Override 134 public int copyPackage(String packagePath, IParcelFileDescriptorFactory target) { 135 if (packagePath == null || target == null) { 136 return PackageManager.INSTALL_FAILED_INVALID_URI; 137 } 138 139 PackageLite pkg = null; 140 try { 141 final File packageFile = new File(packagePath); 142 pkg = PackageParser.parsePackageLite(packageFile, 0); 143 return copyPackageInner(pkg, target); 144 } catch (PackageParserException | IOException | RemoteException e) { 145 Slog.w(TAG, "Failed to copy package at " + packagePath + ": " + e); 146 return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; 147 } 148 } 149 150 /** 151 * Parse given package and return minimal details. 152 * 153 * @param packagePath absolute path to the package to be copied. Can be 154 * a single monolithic APK file or a cluster directory 155 * containing one or more APKs. 156 */ 157 @Override 158 public PackageInfoLite getMinimalPackageInfo(final String packagePath, int flags, 159 long threshold, String abiOverride) { 160 PackageInfoLite ret = new PackageInfoLite(); 161 162 if (packagePath == null) { 163 Slog.i(TAG, "Invalid package file " + packagePath); 164 ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK; 165 return ret; 166 } 167 168 final File packageFile = new File(packagePath); 169 final PackageParser.PackageLite pkg; 170 try { 171 pkg = PackageParser.parsePackageLite(packageFile, 0); 172 } catch (PackageParserException e) { 173 Slog.w(TAG, "Failed to parse package at " + packagePath); 174 175 if (!packageFile.exists()) { 176 ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_URI; 177 } else { 178 ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK; 179 } 180 181 return ret; 182 } 183 184 ret.packageName = pkg.packageName; 185 ret.versionCode = pkg.versionCode; 186 ret.installLocation = pkg.installLocation; 187 ret.verifiers = pkg.verifiers; 188 ret.recommendedInstallLocation = recommendAppInstallLocation(pkg, flags, threshold, 189 abiOverride); 190 ret.multiArch = pkg.multiArch; 191 192 return ret; 193 } 194 195 /** 196 * Determine if package will fit on internal storage. 197 * 198 * @param packagePath absolute path to the package to be copied. Can be 199 * a single monolithic APK file or a cluster directory 200 * containing one or more APKs. 201 */ 202 @Override 203 public boolean checkInternalFreeStorage(String packagePath, boolean isForwardLocked, 204 long threshold) throws RemoteException { 205 final File packageFile = new File(packagePath); 206 final PackageParser.PackageLite pkg; 207 try { 208 pkg = PackageParser.parsePackageLite(packageFile, 0); 209 return isUnderInternalThreshold(pkg, isForwardLocked, threshold); 210 } catch (PackageParserException | IOException e) { 211 Slog.w(TAG, "Failed to parse package at " + packagePath); 212 return false; 213 } 214 } 215 216 /** 217 * Determine if package will fit on external storage. 218 * 219 * @param packagePath absolute path to the package to be copied. Can be 220 * a single monolithic APK file or a cluster directory 221 * containing one or more APKs. 222 */ 223 @Override 224 public boolean checkExternalFreeStorage(String packagePath, boolean isForwardLocked, 225 String abiOverride) throws RemoteException { 226 final File packageFile = new File(packagePath); 227 final PackageParser.PackageLite pkg; 228 try { 229 pkg = PackageParser.parsePackageLite(packageFile, 0); 230 return isUnderExternalThreshold(pkg, isForwardLocked, abiOverride); 231 } catch (PackageParserException | IOException e) { 232 Slog.w(TAG, "Failed to parse package at " + packagePath); 233 return false; 234 } 235 } 236 237 @Override 238 public ObbInfo getObbInfo(String filename) { 239 try { 240 return ObbScanner.getObbInfo(filename); 241 } catch (IOException e) { 242 Slog.d(TAG, "Couldn't get OBB info for " + filename); 243 return null; 244 } 245 } 246 247 @Override 248 public long calculateDirectorySize(String path) throws RemoteException { 249 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 250 251 final File dir = Environment.maybeTranslateEmulatedPathToInternal(new File(path)); 252 if (dir.exists() && dir.isDirectory()) { 253 final String targetPath = dir.getAbsolutePath(); 254 return MeasurementUtils.measureDirectory(targetPath); 255 } else { 256 return 0L; 257 } 258 } 259 260 @Override 261 public long[] getFileSystemStats(String path) { 262 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 263 264 try { 265 final StructStatVfs stat = Os.statvfs(path); 266 final long totalSize = stat.f_blocks * stat.f_bsize; 267 final long availSize = stat.f_bavail * stat.f_bsize; 268 return new long[] { totalSize, availSize }; 269 } catch (ErrnoException e) { 270 throw new IllegalStateException(e); 271 } 272 } 273 274 @Override 275 public void clearDirectory(String path) throws RemoteException { 276 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 277 278 final File directory = new File(path); 279 if (directory.exists() && directory.isDirectory()) { 280 eraseFiles(directory); 281 } 282 } 283 284 /** 285 * Calculate estimated footprint of given package post-installation. 286 * 287 * @param packagePath absolute path to the package to be copied. Can be 288 * a single monolithic APK file or a cluster directory 289 * containing one or more APKs. 290 */ 291 @Override 292 public long calculateInstalledSize(String packagePath, boolean isForwardLocked, 293 String abiOverride) throws RemoteException { 294 final File packageFile = new File(packagePath); 295 final PackageParser.PackageLite pkg; 296 try { 297 pkg = PackageParser.parsePackageLite(packageFile, 0); 298 return calculateContainerSize(pkg, isForwardLocked, abiOverride) * 1024 * 1024; 299 } catch (PackageParserException | IOException e) { 300 /* 301 * Okay, something failed, so let's just estimate it to be 2x 302 * the file size. Note this will be 0 if the file doesn't exist. 303 */ 304 return packageFile.length() * 2; 305 } 306 } 307 }; 308 309 public DefaultContainerService() { 310 super("DefaultContainerService"); 311 setIntentRedelivery(true); 312 } 313 314 @Override 315 protected void onHandleIntent(Intent intent) { 316 if (PackageManager.ACTION_CLEAN_EXTERNAL_STORAGE.equals(intent.getAction())) { 317 final IPackageManager pm = IPackageManager.Stub.asInterface( 318 ServiceManager.getService("package")); 319 PackageCleanItem item = null; 320 try { 321 while ((item = pm.nextPackageToClean(item)) != null) { 322 final UserEnvironment userEnv = new UserEnvironment(item.userId); 323 eraseFiles(userEnv.buildExternalStorageAppDataDirs(item.packageName)); 324 eraseFiles(userEnv.buildExternalStorageAppMediaDirs(item.packageName)); 325 if (item.andCode) { 326 eraseFiles(userEnv.buildExternalStorageAppObbDirs(item.packageName)); 327 } 328 } 329 } catch (RemoteException e) { 330 } 331 } 332 } 333 334 void eraseFiles(File[] paths) { 335 for (File path : paths) { 336 eraseFiles(path); 337 } 338 } 339 340 void eraseFiles(File path) { 341 if (path.isDirectory()) { 342 String[] files = path.list(); 343 if (files != null) { 344 for (String file : files) { 345 eraseFiles(new File(path, file)); 346 } 347 } 348 } 349 path.delete(); 350 } 351 352 @Override 353 public IBinder onBind(Intent intent) { 354 return mBinder; 355 } 356 357 private String copyPackageToContainerInner(PackageLite pkg, NativeLibraryHelper.Handle handle, 358 String newCid, String key, boolean isExternal, boolean isForwardLocked, 359 String abiOverride) { 360 // TODO: extend to support copying all split APKs 361 if (!ArrayUtils.isEmpty(pkg.splitNames)) { 362 throw new UnsupportedOperationException("Copying split APKs not yet supported"); 363 } 364 365 final String resFileName = "pkg.apk"; 366 final String publicResFileName = "res.zip"; 367 368 if (pkg.multiArch) { 369 // TODO: Support multiArch installs on ASEC. 370 throw new IllegalArgumentException("multiArch not supported on ASEC installs."); 371 } 372 373 // The .apk file 374 final String codePath = pkg.baseCodePath; 375 final File codeFile = new File(codePath); 376 final String[] abis; 377 try { 378 abis = calculateAbiList(handle, abiOverride, pkg.multiArch); 379 } catch (IOException ioe) { 380 Slog.w(TAG, "Problem determining app ABIS: " + ioe); 381 return null; 382 } 383 384 // Calculate size of container needed to hold base APK. 385 final int sizeMb; 386 try { 387 sizeMb = calculateContainerSize(pkg, handle, isForwardLocked, abis); 388 } catch (IOException e) { 389 Slog.w(TAG, "Problem when trying to copy " + codeFile.getPath()); 390 return null; 391 } 392 393 // Create new container 394 final String newCachePath = PackageHelper.createSdDir(sizeMb, newCid, key, Process.myUid(), 395 isExternal); 396 if (newCachePath == null) { 397 Slog.e(TAG, "Failed to create container " + newCid); 398 return null; 399 } 400 401 if (localLOGV) { 402 Slog.i(TAG, "Created container for " + newCid + " at path : " + newCachePath); 403 } 404 405 final File resFile = new File(newCachePath, resFileName); 406 if (FileUtils.copyFile(new File(codePath), resFile)) { 407 if (localLOGV) { 408 Slog.i(TAG, "Copied " + codePath + " to " + resFile); 409 } 410 } else { 411 Slog.e(TAG, "Failed to copy " + codePath + " to " + resFile); 412 // Clean up container 413 PackageHelper.destroySdDir(newCid); 414 return null; 415 } 416 417 try { 418 Os.chmod(resFile.getAbsolutePath(), 0640); 419 } catch (ErrnoException e) { 420 Slog.e(TAG, "Could not chown APK: " + e.getMessage()); 421 PackageHelper.destroySdDir(newCid); 422 return null; 423 } 424 425 if (isForwardLocked) { 426 File publicZipFile = new File(newCachePath, publicResFileName); 427 try { 428 PackageHelper.extractPublicFiles(resFile.getAbsolutePath(), publicZipFile); 429 if (localLOGV) { 430 Slog.i(TAG, "Copied resources to " + publicZipFile); 431 } 432 } catch (IOException e) { 433 Slog.e(TAG, "Could not chown public APK " + publicZipFile.getAbsolutePath() + ": " 434 + e.getMessage()); 435 PackageHelper.destroySdDir(newCid); 436 return null; 437 } 438 439 try { 440 Os.chmod(publicZipFile.getAbsolutePath(), 0644); 441 } catch (ErrnoException e) { 442 Slog.e(TAG, "Could not chown public resource file: " + e.getMessage()); 443 PackageHelper.destroySdDir(newCid); 444 return null; 445 } 446 } 447 448 final File sharedLibraryDir = new File(newCachePath, LIB_DIR_NAME); 449 if (sharedLibraryDir.mkdir()) { 450 int ret = PackageManager.INSTALL_SUCCEEDED; 451 if (abis != null) { 452 // TODO(multiArch): Support multi-arch installs on asecs. Note that we are NOT 453 // using an ISA specific subdir here for now. 454 final String abi = abis[0]; 455 ret = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, 456 sharedLibraryDir, abi); 457 458 if (ret != PackageManager.INSTALL_SUCCEEDED) { 459 Slog.e(TAG, "Could not copy native libraries to " + sharedLibraryDir.getPath()); 460 PackageHelper.destroySdDir(newCid); 461 return null; 462 } 463 } 464 } else { 465 Slog.e(TAG, "Could not create native lib directory: " + sharedLibraryDir.getPath()); 466 PackageHelper.destroySdDir(newCid); 467 return null; 468 } 469 470 if (!PackageHelper.finalizeSdDir(newCid)) { 471 Slog.e(TAG, "Failed to finalize " + newCid + " at path " + newCachePath); 472 // Clean up container 473 PackageHelper.destroySdDir(newCid); 474 return null; 475 } 476 477 if (localLOGV) { 478 Slog.i(TAG, "Finalized container " + newCid); 479 } 480 481 if (PackageHelper.isContainerMounted(newCid)) { 482 if (localLOGV) { 483 Slog.i(TAG, "Unmounting " + newCid + " at path " + newCachePath); 484 } 485 486 // Force a gc to avoid being killed. 487 Runtime.getRuntime().gc(); 488 PackageHelper.unMountSdDir(newCid); 489 } else { 490 if (localLOGV) { 491 Slog.i(TAG, "Container " + newCid + " not mounted"); 492 } 493 } 494 495 return newCachePath; 496 } 497 498 private int copyPackageInner(PackageLite pkg, IParcelFileDescriptorFactory target) 499 throws IOException, RemoteException { 500 copyFile(pkg.baseCodePath, "base.apk", target); 501 if (!ArrayUtils.isEmpty(pkg.splitNames)) { 502 for (int i = 0; i < pkg.splitNames.length; i++) { 503 copyFile(pkg.splitCodePaths[i], "split_" + pkg.splitNames[i] + ".apk", target); 504 } 505 } 506 507 return PackageManager.INSTALL_SUCCEEDED; 508 } 509 510 private void copyFile(String sourcePath, String targetName, 511 IParcelFileDescriptorFactory target) throws IOException, RemoteException { 512 Slog.d(TAG, "Copying " + sourcePath + " to " + targetName); 513 InputStream in = null; 514 OutputStream out = null; 515 try { 516 in = new FileInputStream(sourcePath); 517 out = new ParcelFileDescriptor.AutoCloseOutputStream( 518 target.open(targetName, ParcelFileDescriptor.MODE_READ_WRITE)); 519 Streams.copy(in, out); 520 } finally { 521 IoUtils.closeQuietly(out); 522 IoUtils.closeQuietly(in); 523 } 524 } 525 526 private static final int PREFER_INTERNAL = 1; 527 private static final int PREFER_EXTERNAL = 2; 528 529 private int recommendAppInstallLocation(PackageLite pkg, int flags, long threshold, 530 String abiOverride) { 531 int prefer; 532 boolean checkBoth = false; 533 534 final boolean isForwardLocked = (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0; 535 536 check_inner : { 537 /* 538 * Explicit install flags should override the manifest settings. 539 */ 540 if ((flags & PackageManager.INSTALL_INTERNAL) != 0) { 541 prefer = PREFER_INTERNAL; 542 break check_inner; 543 } else if ((flags & PackageManager.INSTALL_EXTERNAL) != 0) { 544 prefer = PREFER_EXTERNAL; 545 break check_inner; 546 } 547 548 /* No install flags. Check for manifest option. */ 549 if (pkg.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) { 550 prefer = PREFER_INTERNAL; 551 break check_inner; 552 } else if (pkg.installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) { 553 prefer = PREFER_EXTERNAL; 554 checkBoth = true; 555 break check_inner; 556 } else if (pkg.installLocation == PackageInfo.INSTALL_LOCATION_AUTO) { 557 // We default to preferring internal storage. 558 prefer = PREFER_INTERNAL; 559 checkBoth = true; 560 break check_inner; 561 } 562 563 // Pick user preference 564 int installPreference = Settings.Global.getInt(getApplicationContext() 565 .getContentResolver(), 566 Settings.Global.DEFAULT_INSTALL_LOCATION, 567 PackageHelper.APP_INSTALL_AUTO); 568 if (installPreference == PackageHelper.APP_INSTALL_INTERNAL) { 569 prefer = PREFER_INTERNAL; 570 break check_inner; 571 } else if (installPreference == PackageHelper.APP_INSTALL_EXTERNAL) { 572 prefer = PREFER_EXTERNAL; 573 break check_inner; 574 } 575 576 /* 577 * Fall back to default policy of internal-only if nothing else is 578 * specified. 579 */ 580 prefer = PREFER_INTERNAL; 581 } 582 583 final boolean emulated = Environment.isExternalStorageEmulated(); 584 585 boolean fitsOnInternal = false; 586 if (checkBoth || prefer == PREFER_INTERNAL) { 587 try { 588 fitsOnInternal = isUnderInternalThreshold(pkg, isForwardLocked, threshold); 589 } catch (IOException e) { 590 return PackageHelper.RECOMMEND_FAILED_INVALID_URI; 591 } 592 } 593 594 boolean fitsOnSd = false; 595 if (!emulated && (checkBoth || prefer == PREFER_EXTERNAL)) { 596 try { 597 fitsOnSd = isUnderExternalThreshold(pkg, isForwardLocked, abiOverride); 598 } catch (IOException e) { 599 return PackageHelper.RECOMMEND_FAILED_INVALID_URI; 600 } 601 } 602 603 if (prefer == PREFER_INTERNAL) { 604 if (fitsOnInternal) { 605 return PackageHelper.RECOMMEND_INSTALL_INTERNAL; 606 } 607 } else if (!emulated && prefer == PREFER_EXTERNAL) { 608 if (fitsOnSd) { 609 return PackageHelper.RECOMMEND_INSTALL_EXTERNAL; 610 } 611 } 612 613 if (checkBoth) { 614 if (fitsOnInternal) { 615 return PackageHelper.RECOMMEND_INSTALL_INTERNAL; 616 } else if (!emulated && fitsOnSd) { 617 return PackageHelper.RECOMMEND_INSTALL_EXTERNAL; 618 } 619 } 620 621 /* 622 * If they requested to be on the external media by default, return that 623 * the media was unavailable. Otherwise, indicate there was insufficient 624 * storage space available. 625 */ 626 if (!emulated && (checkBoth || prefer == PREFER_EXTERNAL) 627 && !Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { 628 return PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE; 629 } else { 630 return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE; 631 } 632 } 633 634 /** 635 * Measure a file to see if it fits within the free space threshold. 636 * 637 * @param threshold byte threshold to compare against 638 * @return true if file fits under threshold 639 * @throws FileNotFoundException when APK does not exist 640 */ 641 private boolean isUnderInternalThreshold(PackageLite pkg, boolean isForwardLocked, 642 long threshold) throws IOException { 643 long sizeBytes = 0; 644 for (String codePath : pkg.getAllCodePaths()) { 645 sizeBytes += new File(codePath).length(); 646 647 if (isForwardLocked) { 648 sizeBytes += PackageHelper.extractPublicFiles(codePath, null); 649 } 650 } 651 652 final StatFs stat = new StatFs(Environment.getDataDirectory().getPath()); 653 final long availBytes = stat.getAvailableBytes(); 654 return (availBytes - sizeBytes) > threshold; 655 } 656 657 /** 658 * Measure a file to see if it fits in the external free space. 659 * 660 * @return true if file fits 661 * @throws IOException when file does not exist 662 */ 663 private boolean isUnderExternalThreshold(PackageLite pkg, boolean isForwardLocked, 664 String abiOverride) throws IOException { 665 if (Environment.isExternalStorageEmulated()) { 666 return false; 667 } 668 669 final int sizeMb = calculateContainerSize(pkg, isForwardLocked, abiOverride); 670 671 final int availSdMb; 672 if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { 673 final StatFs sdStats = new StatFs(Environment.getExternalStorageDirectory().getPath()); 674 final int blocksToMb = (1 << 20) / sdStats.getBlockSize(); 675 availSdMb = sdStats.getAvailableBlocks() * blocksToMb; 676 } else { 677 availSdMb = -1; 678 } 679 680 return availSdMb > sizeMb; 681 } 682 683 private int calculateContainerSize(PackageLite pkg, boolean isForwardLocked, String abiOverride) 684 throws IOException { 685 NativeLibraryHelper.Handle handle = null; 686 try { 687 handle = NativeLibraryHelper.Handle.create(pkg); 688 return calculateContainerSize(pkg, handle, isForwardLocked, 689 calculateAbiList(handle, abiOverride, pkg.multiArch)); 690 } finally { 691 IoUtils.closeQuietly(handle); 692 } 693 } 694 695 private String[] calculateAbiList(NativeLibraryHelper.Handle handle, String abiOverride, 696 boolean isMultiArch) throws IOException { 697 if (isMultiArch) { 698 final int abi32 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS); 699 final int abi64 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS); 700 701 if (abi32 >= 0 && abi64 >= 0) { 702 return new String[] { Build.SUPPORTED_64_BIT_ABIS[abi64], Build.SUPPORTED_32_BIT_ABIS[abi32] }; 703 } else if (abi64 >= 0) { 704 return new String[] { Build.SUPPORTED_64_BIT_ABIS[abi64] }; 705 } else if (abi32 >= 0) { 706 return new String[] { Build.SUPPORTED_32_BIT_ABIS[abi32] }; 707 } 708 709 if (abi64 != PackageManager.NO_NATIVE_LIBRARIES || abi32 != PackageManager.NO_NATIVE_LIBRARIES) { 710 throw new IOException("Error determining ABI list: errorCode=[" + abi32 + "," + abi64 + "]"); 711 } 712 713 } else { 714 String[] abiList = Build.SUPPORTED_ABIS; 715 if (abiOverride != null) { 716 abiList = new String[] { abiOverride }; 717 } else if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && 718 NativeLibraryHelper.hasRenderscriptBitcode(handle)) { 719 abiList = Build.SUPPORTED_32_BIT_ABIS; 720 } 721 722 final int abi = NativeLibraryHelper.findSupportedAbi(handle,abiList); 723 if (abi >= 0) { 724 return new String[]{Build.SUPPORTED_ABIS[abi]}; 725 } 726 727 if (abi != PackageManager.NO_NATIVE_LIBRARIES) { 728 throw new IOException("Error determining ABI list: errorCode=" + abi); 729 } 730 } 731 732 return null; 733 } 734 735 /** 736 * Calculate the container size for a package. 737 * 738 * @return size in megabytes (2^20 bytes) 739 * @throws IOException when there is a problem reading the file 740 */ 741 private int calculateContainerSize(PackageLite pkg, NativeLibraryHelper.Handle handle, 742 boolean isForwardLocked, String[] abis) throws IOException { 743 // Calculate size of container needed to hold APKs. 744 long sizeBytes = 0; 745 for (String codePath : pkg.getAllCodePaths()) { 746 sizeBytes += new File(codePath).length(); 747 748 if (isForwardLocked) { 749 sizeBytes += PackageHelper.extractPublicFiles(codePath, null); 750 } 751 } 752 753 // Check all the native files that need to be copied and add that to the 754 // container size. 755 if (abis != null) { 756 sizeBytes += NativeLibraryHelper.sumNativeBinariesLI(handle, abis); 757 } 758 759 int sizeMb = (int) (sizeBytes >> 20); 760 if ((sizeBytes - (sizeMb * 1024 * 1024)) > 0) { 761 sizeMb++; 762 } 763 764 /* 765 * Add buffer size because we don't have a good way to determine the 766 * real FAT size. Your FAT size varies with how many directory entries 767 * you need, how big the whole filesystem is, and other such headaches. 768 */ 769 sizeMb++; 770 771 return sizeMb; 772 } 773} 774