DefaultContainerService.java revision 1ebd74acf9977daa42133507e970dab88e08f0ef
115a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root/* 215a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * Copyright (C) 2010 The Android Open Source Project 315a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * 415a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * Licensed under the Apache License, Version 2.0 (the "License"); 515a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * you may not use this file except in compliance with the License. 615a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * You may obtain a copy of the License at 715a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * 815a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * http://www.apache.org/licenses/LICENSE-2.0 915a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * 1015a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * Unless required by applicable law or agreed to in writing, software 1115a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * distributed under the License is distributed on an "AS IS" BASIS, 1215a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1315a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * See the License for the specific language governing permissions and 1415a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * limitations under the License. 1515a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root */ 1615a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root 17c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapupackage com.android.defcontainer; 18c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu 19c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport com.android.internal.app.IMediaContainerService; 2085387d7ba36e56b291cbde87acb5a5b2200fe01cKenny Rootimport com.android.internal.content.NativeLibraryHelper; 215b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapuimport com.android.internal.content.PackageHelper; 2285387d7ba36e56b291cbde87acb5a5b2200fe01cKenny Root 23c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport android.content.Intent; 24e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackbornimport android.content.pm.IPackageManager; 255b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapuimport android.content.pm.PackageInfo; 26a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapuimport android.content.pm.PackageInfoLite; 27e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackbornimport android.content.pm.PackageManager; 285b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapuimport android.content.pm.PackageParser; 29a02b8b05dd1e8b8cf169e1f89542ef835b11fc13Kenny Rootimport android.content.res.ObbInfo; 30a02b8b05dd1e8b8cf169e1f89542ef835b11fc13Kenny Rootimport android.content.res.ObbScanner; 31c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport android.net.Uri; 32e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackbornimport android.os.Environment; 33c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport android.os.IBinder; 34c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport android.os.ParcelFileDescriptor; 35c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport android.os.Process; 36c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport android.os.RemoteException; 37c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport android.os.ServiceManager; 385b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapuimport android.os.StatFs; 39e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackbornimport android.app.IntentService; 405b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapuimport android.util.DisplayMetrics; 411ebd74acf9977daa42133507e970dab88e08f0efKenny Rootimport android.util.Slog; 42c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu 43c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport java.io.File; 44c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport java.io.FileInputStream; 45c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport java.io.FileNotFoundException; 46c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport java.io.FileOutputStream; 47c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport java.io.IOException; 48c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport java.io.InputStream; 49c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu 50c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport android.os.FileUtils; 515b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapuimport android.provider.Settings; 52c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu 53c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu/* 54c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu * This service copies a downloaded apk to a file passed in as 55c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu * a ParcelFileDescriptor or to a newly created container specified 56c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu * by parameters. The DownloadManager gives access to this process 57c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu * based on its uid. This process also needs the ACCESS_DOWNLOAD_MANAGER 58c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu * permission to access apks downloaded via the download manager. 59c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu */ 60e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackbornpublic class DefaultContainerService extends IntentService { 61c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu private static final String TAG = "DefContainer"; 62cf6eaeaae9e6745dd6e07540812c79821d7043c2Suchi Amalapurapu private static final boolean localLOGV = true; 63c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu 6485387d7ba36e56b291cbde87acb5a5b2200fe01cKenny Root private static final String LIB_DIR_NAME = "lib"; 6585387d7ba36e56b291cbde87acb5a5b2200fe01cKenny Root 66c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu private IMediaContainerService.Stub mBinder = new IMediaContainerService.Stub() { 67c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu /* 68c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu * Creates a new container and copies resource there. 69c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu * @param paackageURI the uri of resource to be copied. Can be either 70c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu * a content uri or a file uri 71679bba339ef6948091180c776d6a284cddd812f5Suchi Amalapurapu * @param cid the id of the secure container that should 72c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu * be used for creating a secure container into which the resource 73c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu * will be copied. 74c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu * @param key Refers to key used for encrypting the secure container 75c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu * @param resFileName Name of the target resource file(relative to newly 76c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu * created secure container) 77c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu * @return Returns the new cache path where the resource has been copied into 78c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu * 79c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu */ 80c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu public String copyResourceToContainer(final Uri packageURI, 81679bba339ef6948091180c776d6a284cddd812f5Suchi Amalapurapu final String cid, 82c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu final String key, final String resFileName) { 83679bba339ef6948091180c776d6a284cddd812f5Suchi Amalapurapu if (packageURI == null || cid == null) { 84c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu return null; 85c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 86679bba339ef6948091180c776d6a284cddd812f5Suchi Amalapurapu return copyResourceInner(packageURI, cid, key, resFileName); 87c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 88c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu 89c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu /* 90c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu * Copy specified resource to output stream 91c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu * @param packageURI the uri of resource to be copied. Should be a 92c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu * file uri 93c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu * @param outStream Remote file descriptor to be used for copying 94c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu * @return Returns true if copy succeded or false otherwise. 95c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu */ 96c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu public boolean copyResource(final Uri packageURI, 97c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu ParcelFileDescriptor outStream) { 98c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu if (packageURI == null || outStream == null) { 99c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu return false; 100c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 101c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu ParcelFileDescriptor.AutoCloseOutputStream 102c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu autoOut = new ParcelFileDescriptor.AutoCloseOutputStream(outStream); 103c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu return copyFile(packageURI, autoOut); 104c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 1055b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu 1065b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu /* 1075b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu * Determine the recommended install location for package 1085b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu * specified by file uri location. 1095b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu * @param fileUri the uri of resource to be copied. Should be a 1105b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu * file uri 111a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu * @return Returns PackageInfoLite object containing 112a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu * the package info and recommended app location. 1135b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu */ 11462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root public PackageInfoLite getMinimalPackageInfo(final Uri fileUri, int flags, long threshold) { 115a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu PackageInfoLite ret = new PackageInfoLite(); 1163602f76d417b0940a26f28b7ad892abcfe03ef7cSuchi Amalapurapu if (fileUri == null) { 1171ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.i(TAG, "Invalid package uri " + fileUri); 118a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK; 119a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu return ret; 1203602f76d417b0940a26f28b7ad892abcfe03ef7cSuchi Amalapurapu } 1213602f76d417b0940a26f28b7ad892abcfe03ef7cSuchi Amalapurapu String scheme = fileUri.getScheme(); 1223602f76d417b0940a26f28b7ad892abcfe03ef7cSuchi Amalapurapu if (scheme != null && !scheme.equals("file")) { 1231ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.w(TAG, "Falling back to installing on internal storage only"); 124a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu ret.recommendedInstallLocation = PackageHelper.RECOMMEND_INSTALL_INTERNAL; 125a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu return ret; 1265b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu } 1273602f76d417b0940a26f28b7ad892abcfe03ef7cSuchi Amalapurapu String archiveFilePath = fileUri.getPath(); 1285b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu DisplayMetrics metrics = new DisplayMetrics(); 1295b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu metrics.setToDefaults(); 1301ebd74acf9977daa42133507e970dab88e08f0efKenny Root 13162e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root PackageParser.PackageLite pkg = PackageParser.parsePackageLite(archiveFilePath, 0); 1325b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu if (pkg == null) { 1331ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.w(TAG, "Failed to parse package"); 1341ebd74acf9977daa42133507e970dab88e08f0efKenny Root 1351ebd74acf9977daa42133507e970dab88e08f0efKenny Root final File apkFile = new File(archiveFilePath); 1361ebd74acf9977daa42133507e970dab88e08f0efKenny Root if (!apkFile.exists()) { 1371ebd74acf9977daa42133507e970dab88e08f0efKenny Root ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_URI; 1381ebd74acf9977daa42133507e970dab88e08f0efKenny Root } else { 1391ebd74acf9977daa42133507e970dab88e08f0efKenny Root ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK; 1401ebd74acf9977daa42133507e970dab88e08f0efKenny Root } 1411ebd74acf9977daa42133507e970dab88e08f0efKenny Root 142a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu return ret; 1435b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu } 144a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu ret.packageName = pkg.packageName; 145930d3af75f9e9663222f4c4a1d75b326cf811e35Kenny Root ret.installLocation = pkg.installLocation; 1461ebd74acf9977daa42133507e970dab88e08f0efKenny Root 14762e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root ret.recommendedInstallLocation = recommendAppInstallLocation(pkg.installLocation, 14862e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root archiveFilePath, flags, threshold); 1491ebd74acf9977daa42133507e970dab88e08f0efKenny Root 150a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu return ret; 1515b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu } 1528a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu 15362e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root @Override 15462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root public boolean checkInternalFreeStorage(Uri packageUri, long threshold) 15562e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root throws RemoteException { 15662e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root final File apkFile = new File(packageUri.getPath()); 1571ebd74acf9977daa42133507e970dab88e08f0efKenny Root try { 1581ebd74acf9977daa42133507e970dab88e08f0efKenny Root return isUnderInternalThreshold(apkFile, threshold); 1591ebd74acf9977daa42133507e970dab88e08f0efKenny Root } catch (FileNotFoundException e) { 1601ebd74acf9977daa42133507e970dab88e08f0efKenny Root return true; 1611ebd74acf9977daa42133507e970dab88e08f0efKenny Root } 16262e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root } 16362e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 16462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root @Override 16562e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root public boolean checkExternalFreeStorage(Uri packageUri) throws RemoteException { 16662e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root final File apkFile = new File(packageUri.getPath()); 1671ebd74acf9977daa42133507e970dab88e08f0efKenny Root try { 1681ebd74acf9977daa42133507e970dab88e08f0efKenny Root return isUnderExternalThreshold(apkFile); 1691ebd74acf9977daa42133507e970dab88e08f0efKenny Root } catch (FileNotFoundException e) { 1701ebd74acf9977daa42133507e970dab88e08f0efKenny Root return true; 1711ebd74acf9977daa42133507e970dab88e08f0efKenny Root } 1728a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu } 173a02b8b05dd1e8b8cf169e1f89542ef835b11fc13Kenny Root 174a02b8b05dd1e8b8cf169e1f89542ef835b11fc13Kenny Root public ObbInfo getObbInfo(String filename) { 17505105f7abe02b2dff91d6260b3628c8b97816babKenny Root try { 17605105f7abe02b2dff91d6260b3628c8b97816babKenny Root return ObbScanner.getObbInfo(filename); 17705105f7abe02b2dff91d6260b3628c8b97816babKenny Root } catch (IOException e) { 1781ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.d(TAG, "Couldn't get OBB info for " + filename); 17905105f7abe02b2dff91d6260b3628c8b97816babKenny Root return null; 18005105f7abe02b2dff91d6260b3628c8b97816babKenny Root } 181a02b8b05dd1e8b8cf169e1f89542ef835b11fc13Kenny Root } 182aa183e2c9a279cb6aef7dc77855facfae795b6f8Kenny Root 183aa183e2c9a279cb6aef7dc77855facfae795b6f8Kenny Root @Override 184366949c2d934435ff9ef8082408ca36ff14a2241Kenny Root public long calculateDirectorySize(String path) throws RemoteException { 185366949c2d934435ff9ef8082408ca36ff14a2241Kenny Root final File directory = new File(path); 186366949c2d934435ff9ef8082408ca36ff14a2241Kenny Root if (directory.exists() && directory.isDirectory()) { 187366949c2d934435ff9ef8082408ca36ff14a2241Kenny Root return MeasurementUtils.measureDirectory(path); 188366949c2d934435ff9ef8082408ca36ff14a2241Kenny Root } else { 189366949c2d934435ff9ef8082408ca36ff14a2241Kenny Root return 0L; 190366949c2d934435ff9ef8082408ca36ff14a2241Kenny Root } 191aa183e2c9a279cb6aef7dc77855facfae795b6f8Kenny Root } 192c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu }; 193c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu 194e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn public DefaultContainerService() { 195e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn super("DefaultContainerService"); 196e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn setIntentRedelivery(true); 197e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn } 198e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn 199e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn @Override 200e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn protected void onHandleIntent(Intent intent) { 201e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn if (PackageManager.ACTION_CLEAN_EXTERNAL_STORAGE.equals(intent.getAction())) { 202e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn IPackageManager pm = IPackageManager.Stub.asInterface( 203e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn ServiceManager.getService("package")); 204e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn String pkg = null; 205e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn try { 206e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn while ((pkg=pm.nextPackageToClean(pkg)) != null) { 207e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn eraseFiles(Environment.getExternalStorageAppDataDirectory(pkg)); 208e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn eraseFiles(Environment.getExternalStorageAppMediaDirectory(pkg)); 209300c13a48132f03d48462b9cd3ec41331a71a411Kenny Root eraseFiles(Environment.getExternalStorageAppObbDirectory(pkg)); 210e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn } 211e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn } catch (RemoteException e) { 212e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn } 213e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn } 214e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn } 215e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn 216e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn void eraseFiles(File path) { 217e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn if (path.isDirectory()) { 218e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn String[] files = path.list(); 219e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn if (files != null) { 220e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn for (String file : files) { 221e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn eraseFiles(new File(path, file)); 222e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn } 223e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn } 224e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn } 225e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn path.delete(); 226e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn } 227e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn 228c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu public IBinder onBind(Intent intent) { 229c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu return mBinder; 230c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 231c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu 232679bba339ef6948091180c776d6a284cddd812f5Suchi Amalapurapu private String copyResourceInner(Uri packageURI, String newCid, String key, String resFileName) { 2338a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu // Make sure the sdcard is mounted. 2348a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu String status = Environment.getExternalStorageState(); 2358a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu if (!status.equals(Environment.MEDIA_MOUNTED)) { 2361ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.w(TAG, "Make sure sdcard is mounted."); 2378a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu return null; 2388a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu } 23985387d7ba36e56b291cbde87acb5a5b2200fe01cKenny Root 24085387d7ba36e56b291cbde87acb5a5b2200fe01cKenny Root // The .apk file 241c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu String codePath = packageURI.getPath(); 242679bba339ef6948091180c776d6a284cddd812f5Suchi Amalapurapu File codeFile = new File(codePath); 24385387d7ba36e56b291cbde87acb5a5b2200fe01cKenny Root 24462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root // Calculate size of container needed to hold base APK. 2451ebd74acf9977daa42133507e970dab88e08f0efKenny Root int sizeMb; 2461ebd74acf9977daa42133507e970dab88e08f0efKenny Root try { 2471ebd74acf9977daa42133507e970dab88e08f0efKenny Root sizeMb = calculateContainerSize(codeFile); 2481ebd74acf9977daa42133507e970dab88e08f0efKenny Root } catch (FileNotFoundException e) { 2491ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.w(TAG, "File does not exist when trying to copy " + codeFile.getPath()); 2501ebd74acf9977daa42133507e970dab88e08f0efKenny Root return null; 2511ebd74acf9977daa42133507e970dab88e08f0efKenny Root } 25285387d7ba36e56b291cbde87acb5a5b2200fe01cKenny Root 253c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu // Create new container 2541ebd74acf9977daa42133507e970dab88e08f0efKenny Root final String newCachePath; 25562e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root if ((newCachePath = PackageHelper.createSdDir(sizeMb, newCid, key, Process.myUid())) == null) { 2561ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.e(TAG, "Failed to create container " + newCid); 257a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu return null; 258c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 2591ebd74acf9977daa42133507e970dab88e08f0efKenny Root 2601ebd74acf9977daa42133507e970dab88e08f0efKenny Root if (localLOGV) { 2611ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.i(TAG, "Created container for " + newCid + " at path : " + newCachePath); 2621ebd74acf9977daa42133507e970dab88e08f0efKenny Root } 2631ebd74acf9977daa42133507e970dab88e08f0efKenny Root 2641ebd74acf9977daa42133507e970dab88e08f0efKenny Root final File resFile = new File(newCachePath, resFileName); 2651ebd74acf9977daa42133507e970dab88e08f0efKenny Root if (FileUtils.copyFile(new File(codePath), resFile)) { 2661ebd74acf9977daa42133507e970dab88e08f0efKenny Root if (localLOGV) { 2671ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.i(TAG, "Copied " + codePath + " to " + resFile); 2681ebd74acf9977daa42133507e970dab88e08f0efKenny Root } 2691ebd74acf9977daa42133507e970dab88e08f0efKenny Root } else { 2701ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.e(TAG, "Failed to copy " + codePath + " to " + resFile); 271a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu // Clean up container 272a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu PackageHelper.destroySdDir(newCid); 273c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu return null; 274c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 27585387d7ba36e56b291cbde87acb5a5b2200fe01cKenny Root 2761ebd74acf9977daa42133507e970dab88e08f0efKenny Root final File sharedLibraryDir = new File(newCachePath, LIB_DIR_NAME); 2771ebd74acf9977daa42133507e970dab88e08f0efKenny Root if (sharedLibraryDir.mkdir()) { 2781ebd74acf9977daa42133507e970dab88e08f0efKenny Root int ret = NativeLibraryHelper.copyNativeBinariesIfNeededLI(codeFile, sharedLibraryDir); 2791ebd74acf9977daa42133507e970dab88e08f0efKenny Root if (ret != PackageManager.INSTALL_SUCCEEDED) { 2801ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.e(TAG, "Could not copy native libraries to " + sharedLibraryDir.getPath()); 2811ebd74acf9977daa42133507e970dab88e08f0efKenny Root PackageHelper.destroySdDir(newCid); 2821ebd74acf9977daa42133507e970dab88e08f0efKenny Root return null; 28385387d7ba36e56b291cbde87acb5a5b2200fe01cKenny Root } 2841ebd74acf9977daa42133507e970dab88e08f0efKenny Root } else { 2851ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.e(TAG, "Could not create native lib directory: " + sharedLibraryDir.getPath()); 28685387d7ba36e56b291cbde87acb5a5b2200fe01cKenny Root PackageHelper.destroySdDir(newCid); 28785387d7ba36e56b291cbde87acb5a5b2200fe01cKenny Root return null; 28885387d7ba36e56b291cbde87acb5a5b2200fe01cKenny Root } 28985387d7ba36e56b291cbde87acb5a5b2200fe01cKenny Root 290a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu if (!PackageHelper.finalizeSdDir(newCid)) { 2911ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.e(TAG, "Failed to finalize " + newCid + " at path " + newCachePath); 292a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu // Clean up container 293a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu PackageHelper.destroySdDir(newCid); 2941ebd74acf9977daa42133507e970dab88e08f0efKenny Root return null; 2951ebd74acf9977daa42133507e970dab88e08f0efKenny Root } 2961ebd74acf9977daa42133507e970dab88e08f0efKenny Root 2971ebd74acf9977daa42133507e970dab88e08f0efKenny Root if (localLOGV) { 2981ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.i(TAG, "Finalized container " + newCid); 299a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu } 3001ebd74acf9977daa42133507e970dab88e08f0efKenny Root 301a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu if (PackageHelper.isContainerMounted(newCid)) { 3021ebd74acf9977daa42133507e970dab88e08f0efKenny Root if (localLOGV) { 3031ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.i(TAG, "Unmounting " + newCid + " at path " + newCachePath); 3041ebd74acf9977daa42133507e970dab88e08f0efKenny Root } 3051ebd74acf9977daa42133507e970dab88e08f0efKenny Root 306a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu // Force a gc to avoid being killed. 307a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu Runtime.getRuntime().gc(); 308a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu PackageHelper.unMountSdDir(newCid); 309a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu } else { 3101ebd74acf9977daa42133507e970dab88e08f0efKenny Root if (localLOGV) { 3111ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.i(TAG, "Container " + newCid + " not mounted"); 3121ebd74acf9977daa42133507e970dab88e08f0efKenny Root } 313a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu } 3141ebd74acf9977daa42133507e970dab88e08f0efKenny Root 315c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu return newCachePath; 316c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 317c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu 3181ebd74acf9977daa42133507e970dab88e08f0efKenny Root private static boolean copyToFile(InputStream inputStream, FileOutputStream out) { 319c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu try { 320c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu byte[] buffer = new byte[4096]; 321c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu int bytesRead; 322c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu while ((bytesRead = inputStream.read(buffer)) >= 0) { 323c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu out.write(buffer, 0, bytesRead); 324c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 325c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu return true; 326c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } catch (IOException e) { 3271ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.i(TAG, "Exception : " + e + " when copying file"); 328c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu return false; 329c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 330c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 331c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu 3321ebd74acf9977daa42133507e970dab88e08f0efKenny Root private static boolean copyToFile(File srcFile, FileOutputStream out) { 333c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu InputStream inputStream = null; 334c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu try { 335c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu inputStream = new FileInputStream(srcFile); 336c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu return copyToFile(inputStream, out); 337c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } catch (IOException e) { 338c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu return false; 339c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } finally { 340c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu try { if (inputStream != null) inputStream.close(); } catch (IOException e) {} 341c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 342c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 343c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu 3441ebd74acf9977daa42133507e970dab88e08f0efKenny Root private boolean copyFile(Uri pPackageURI, FileOutputStream outStream) { 3453602f76d417b0940a26f28b7ad892abcfe03ef7cSuchi Amalapurapu String scheme = pPackageURI.getScheme(); 3463602f76d417b0940a26f28b7ad892abcfe03ef7cSuchi Amalapurapu if (scheme == null || scheme.equals("file")) { 347c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu final File srcPackageFile = new File(pPackageURI.getPath()); 348c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu // We copy the source package file to a temp file and then rename it to the 349c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu // destination file in order to eliminate a window where the package directory 350c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu // scanner notices the new package file but it's not completely copied yet. 351c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu if (!copyToFile(srcPackageFile, outStream)) { 3521ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.e(TAG, "Couldn't copy file: " + srcPackageFile); 353c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu return false; 354c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 3553602f76d417b0940a26f28b7ad892abcfe03ef7cSuchi Amalapurapu } else if (scheme.equals("content")) { 356c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu ParcelFileDescriptor fd = null; 357c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu try { 358c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu fd = getContentResolver().openFileDescriptor(pPackageURI, "r"); 359c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } catch (FileNotFoundException e) { 3601ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.e(TAG, 3611ebd74acf9977daa42133507e970dab88e08f0efKenny Root "Couldn't open file descriptor from download service. Failed with exception " 3621ebd74acf9977daa42133507e970dab88e08f0efKenny Root + e); 363c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu return false; 364c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 365c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu if (fd == null) { 3661ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.e(TAG, "Couldn't open file descriptor from download service (null)."); 367c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu return false; 368c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } else { 369c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu if (localLOGV) { 3701ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.i(TAG, "Opened file descriptor from download service."); 371c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 372c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu ParcelFileDescriptor.AutoCloseInputStream 373c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu dlStream = new ParcelFileDescriptor.AutoCloseInputStream(fd); 374c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu // We copy the source package file to a temp file and then rename it to the 375c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu // destination file in order to eliminate a window where the package directory 3761ebd74acf9977daa42133507e970dab88e08f0efKenny Root // scanner notices the new package file but it's not completely 3771ebd74acf9977daa42133507e970dab88e08f0efKenny Root // cop 378c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu if (!copyToFile(dlStream, outStream)) { 3791ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.e(TAG, "Couldn't copy " + pPackageURI + " to temp file."); 380c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu return false; 381c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 382c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 383c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } else { 3841ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.e(TAG, "Package URI is not 'file:' or 'content:' - " + pPackageURI); 385c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu return false; 386c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 387c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu return true; 388c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 3895b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu 39062e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root private static final int PREFER_INTERNAL = 1; 39162e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root private static final int PREFER_EXTERNAL = 2; 3925b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu 39362e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root private int recommendAppInstallLocation(int installLocation, String archiveFilePath, int flags, 39462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root long threshold) { 39562e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root int prefer; 39614b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu boolean checkBoth = false; 39762e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 39814b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu check_inner : { 39962e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root /* 40062e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * Explicit install flags should override the manifest settings. 40162e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root */ 40214b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) { 40362e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root /* 40462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * Forward-locked applications cannot be installed on SD card, 40562e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * so only allow checking internal storage. 40662e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root */ 40762e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root prefer = PREFER_INTERNAL; 40814b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu break check_inner; 40914b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu } else if ((flags & PackageManager.INSTALL_INTERNAL) != 0) { 41062e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root prefer = PREFER_INTERNAL; 41114b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu break check_inner; 41214b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu } else if ((flags & PackageManager.INSTALL_EXTERNAL) != 0) { 41362e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root prefer = PREFER_EXTERNAL; 41414b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu break check_inner; 41514b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu } 41662e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 41762e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root /* No install flags. Check for manifest option. */ 41814b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) { 41962e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root prefer = PREFER_INTERNAL; 42014b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu break check_inner; 42114b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu } else if (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) { 42262e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root prefer = PREFER_EXTERNAL; 42314b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu checkBoth = true; 42414b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu break check_inner; 42514b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu } else if (installLocation == PackageInfo.INSTALL_LOCATION_AUTO) { 42662e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root // We default to preferring internal storage. 42762e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root prefer = PREFER_INTERNAL; 42814b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu checkBoth = true; 42914b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu break check_inner; 43014b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu } 43162e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 43240e472521a544f26cb6956995788f7c36fff1404Suchi Amalapurapu // Pick user preference 43340e472521a544f26cb6956995788f7c36fff1404Suchi Amalapurapu int installPreference = Settings.System.getInt(getApplicationContext() 43414b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu .getContentResolver(), 43540e472521a544f26cb6956995788f7c36fff1404Suchi Amalapurapu Settings.Secure.DEFAULT_INSTALL_LOCATION, 43640e472521a544f26cb6956995788f7c36fff1404Suchi Amalapurapu PackageHelper.APP_INSTALL_AUTO); 43740e472521a544f26cb6956995788f7c36fff1404Suchi Amalapurapu if (installPreference == PackageHelper.APP_INSTALL_INTERNAL) { 43862e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root prefer = PREFER_INTERNAL; 43940e472521a544f26cb6956995788f7c36fff1404Suchi Amalapurapu break check_inner; 44040e472521a544f26cb6956995788f7c36fff1404Suchi Amalapurapu } else if (installPreference == PackageHelper.APP_INSTALL_EXTERNAL) { 44162e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root prefer = PREFER_EXTERNAL; 44240e472521a544f26cb6956995788f7c36fff1404Suchi Amalapurapu break check_inner; 44314b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu } 44462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 44562e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root /* 44662e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * Fall back to default policy of internal-only if nothing else is 44762e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * specified. 44862e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root */ 44962e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root prefer = PREFER_INTERNAL; 45014b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu } 45114b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu 45262e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root final boolean emulated = Environment.isExternalStorageEmulated(); 45362e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 45462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root final File apkFile = new File(archiveFilePath); 45562e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 45662e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root boolean fitsOnInternal = false; 45762e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root if (checkBoth || prefer == PREFER_INTERNAL) { 4581ebd74acf9977daa42133507e970dab88e08f0efKenny Root try { 4591ebd74acf9977daa42133507e970dab88e08f0efKenny Root fitsOnInternal = isUnderInternalThreshold(apkFile, threshold); 4601ebd74acf9977daa42133507e970dab88e08f0efKenny Root } catch (FileNotFoundException e) { 4611ebd74acf9977daa42133507e970dab88e08f0efKenny Root return PackageHelper.RECOMMEND_FAILED_INVALID_URI; 4621ebd74acf9977daa42133507e970dab88e08f0efKenny Root } 4635b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu } 46462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 46514b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu boolean fitsOnSd = false; 46662e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root if (!emulated && (checkBoth || prefer == PREFER_EXTERNAL)) { 4671ebd74acf9977daa42133507e970dab88e08f0efKenny Root try { 4681ebd74acf9977daa42133507e970dab88e08f0efKenny Root fitsOnSd = isUnderExternalThreshold(apkFile); 4691ebd74acf9977daa42133507e970dab88e08f0efKenny Root } catch (FileNotFoundException e) { 4701ebd74acf9977daa42133507e970dab88e08f0efKenny Root return PackageHelper.RECOMMEND_FAILED_INVALID_URI; 4711ebd74acf9977daa42133507e970dab88e08f0efKenny Root } 47214b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu } 47362e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 47462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root if (prefer == PREFER_INTERNAL) { 47562e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root if (fitsOnInternal) { 47614b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu return PackageHelper.RECOMMEND_INSTALL_INTERNAL; 4775b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu } 47862e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root } else if (!emulated && prefer == PREFER_EXTERNAL) { 47914b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu if (fitsOnSd) { 48014b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu return PackageHelper.RECOMMEND_INSTALL_EXTERNAL; 4815b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu } 4825b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu } 48362e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 48414b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu if (checkBoth) { 48562e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root if (fitsOnInternal) { 48614b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu return PackageHelper.RECOMMEND_INSTALL_INTERNAL; 48762e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root } else if (!emulated && fitsOnSd) { 48814b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu return PackageHelper.RECOMMEND_INSTALL_EXTERNAL; 4895b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu } 4905b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu } 49162e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 49262e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root /* 49362e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * If they requested to be on the external media by default, return that 49462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * the media was unavailable. Otherwise, indicate there was insufficient 49562e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * storage space available. 49662e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root */ 49762e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root if (!emulated && (checkBoth || prefer == PREFER_EXTERNAL) 49862e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root && !Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { 49914b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu return PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE; 50062e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root } else { 50162e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE; 5025b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu } 5038a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu } 5048a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu 5051ebd74acf9977daa42133507e970dab88e08f0efKenny Root /** 5061ebd74acf9977daa42133507e970dab88e08f0efKenny Root * Measure a file to see if it fits within the free space threshold. 5071ebd74acf9977daa42133507e970dab88e08f0efKenny Root * 5081ebd74acf9977daa42133507e970dab88e08f0efKenny Root * @param apkFile file to check 5091ebd74acf9977daa42133507e970dab88e08f0efKenny Root * @param threshold byte threshold to compare against 5101ebd74acf9977daa42133507e970dab88e08f0efKenny Root * @return true if file fits under threshold 5111ebd74acf9977daa42133507e970dab88e08f0efKenny Root * @throws FileNotFoundException when APK does not exist 5121ebd74acf9977daa42133507e970dab88e08f0efKenny Root */ 5131ebd74acf9977daa42133507e970dab88e08f0efKenny Root private boolean isUnderInternalThreshold(File apkFile, long threshold) 5141ebd74acf9977daa42133507e970dab88e08f0efKenny Root throws FileNotFoundException { 51562e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root final long size = apkFile.length(); 5161ebd74acf9977daa42133507e970dab88e08f0efKenny Root if (size == 0 && !apkFile.exists()) { 5171ebd74acf9977daa42133507e970dab88e08f0efKenny Root throw new FileNotFoundException(); 5181ebd74acf9977daa42133507e970dab88e08f0efKenny Root } 51962e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 52062e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root final StatFs internalStats = new StatFs(Environment.getDataDirectory().getPath()); 52162e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root final long availInternalSize = (long) internalStats.getAvailableBlocks() 52262e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * (long) internalStats.getBlockSize(); 52362e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 52462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root return (availInternalSize - size) > threshold; 52562e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root } 52662e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 52762e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 5281ebd74acf9977daa42133507e970dab88e08f0efKenny Root /** 5291ebd74acf9977daa42133507e970dab88e08f0efKenny Root * Measure a file to see if it fits in the external free space. 5301ebd74acf9977daa42133507e970dab88e08f0efKenny Root * 5311ebd74acf9977daa42133507e970dab88e08f0efKenny Root * @param apkFile file to check 5321ebd74acf9977daa42133507e970dab88e08f0efKenny Root * @return true if file fits 5331ebd74acf9977daa42133507e970dab88e08f0efKenny Root * @throws IOException when file does not exist 5341ebd74acf9977daa42133507e970dab88e08f0efKenny Root */ 5351ebd74acf9977daa42133507e970dab88e08f0efKenny Root private boolean isUnderExternalThreshold(File apkFile) throws FileNotFoundException { 53662e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root if (Environment.isExternalStorageEmulated()) { 53762e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root return false; 53862e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root } 53962e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 5401ebd74acf9977daa42133507e970dab88e08f0efKenny Root final int sizeMb = calculateContainerSize(apkFile); 54162e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 54262e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root final int availSdMb; 54362e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { 54462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root StatFs sdStats = new StatFs(Environment.getExternalStorageDirectory().getPath()); 54562e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root long availSdSize = (long) (sdStats.getAvailableBlocks() * sdStats.getBlockSize()); 54662e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root availSdMb = (int) (availSdSize >> 20); 54762e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root } else { 54862e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root availSdMb = -1; 54962e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root } 55062e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 55162e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root return availSdMb > sizeMb; 55262e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root } 55362e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 55462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root /** 55562e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * Calculate the container size for an APK. Takes into account the 55662e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * 55762e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * @param apkFile file from which to calculate size 55862e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * @return size in megabytes (2^20 bytes) 5591ebd74acf9977daa42133507e970dab88e08f0efKenny Root * @throws FileNotFoundException when file does not exist 56062e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root */ 5611ebd74acf9977daa42133507e970dab88e08f0efKenny Root private int calculateContainerSize(File apkFile) throws FileNotFoundException { 56262e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root // Calculate size of container needed to hold base APK. 56362e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root long sizeBytes = apkFile.length(); 5641ebd74acf9977daa42133507e970dab88e08f0efKenny Root if (sizeBytes == 0 && !apkFile.exists()) { 5651ebd74acf9977daa42133507e970dab88e08f0efKenny Root throw new FileNotFoundException(); 5661ebd74acf9977daa42133507e970dab88e08f0efKenny Root } 56762e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 56862e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root // Check all the native files that need to be copied and add that to the 56962e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root // container size. 57066269ea6f68f2f25888ce1080c94ac782742fafcKenny Root sizeBytes += NativeLibraryHelper.sumNativeBinariesLI(apkFile); 57162e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 57262e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root int sizeMb = (int) (sizeBytes >> 20); 57362e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root if ((sizeBytes - (sizeMb * 1024 * 1024)) > 0) { 57462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root sizeMb++; 57562e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root } 57662e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 57762e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root /* 57862e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * Add buffer size because we don't have a good way to determine the 57962e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * real FAT size. Your FAT size varies with how many directory entries 58062e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * you need, how big the whole filesystem is, and other such headaches. 58162e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root */ 58262e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root sizeMb++; 58362e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 58462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root return sizeMb; 5855b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu } 586c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu} 587