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