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 23f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Rootimport android.app.IntentService; 24c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport android.content.Intent; 25e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackbornimport android.content.pm.IPackageManager; 265b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapuimport android.content.pm.PackageInfo; 27a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapuimport android.content.pm.PackageInfoLite; 28e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackbornimport android.content.pm.PackageManager; 295b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapuimport android.content.pm.PackageParser; 30a02b8b05dd1e8b8cf169e1f89542ef835b11fc13Kenny Rootimport android.content.res.ObbInfo; 31a02b8b05dd1e8b8cf169e1f89542ef835b11fc13Kenny Rootimport android.content.res.ObbScanner; 32c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport android.net.Uri; 33e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackbornimport android.os.Environment; 34f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Rootimport android.os.FileUtils; 35c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport android.os.IBinder; 36c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport android.os.ParcelFileDescriptor; 37c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport android.os.Process; 38c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport android.os.RemoteException; 39c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport android.os.ServiceManager; 405b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapuimport android.os.StatFs; 41f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Rootimport android.provider.Settings; 425b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapuimport android.util.DisplayMetrics; 431ebd74acf9977daa42133507e970dab88e08f0efKenny Rootimport android.util.Slog; 44c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu 45f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Rootimport java.io.BufferedInputStream; 46c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport java.io.File; 47c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport java.io.FileInputStream; 48c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport java.io.FileNotFoundException; 49c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport java.io.IOException; 50c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport java.io.InputStream; 51f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Rootimport java.io.OutputStream; 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 91f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root * @param packageURI the uri of resource to be copied. Should be a file 92f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root * uri 93c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu * @param outStream Remote file descriptor to be used for copying 94f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root * @return returns status code according to those in {@link 95f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root * PackageManager} 96c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu */ 97f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root public int copyResource(final Uri packageURI, ParcelFileDescriptor outStream) { 98f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root if (packageURI == null || outStream == null) { 99f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root return PackageManager.INSTALL_FAILED_INVALID_URI; 100f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root } 101f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root 102f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root ParcelFileDescriptor.AutoCloseOutputStream autoOut 103f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root = new ParcelFileDescriptor.AutoCloseOutputStream(outStream); 104f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root 105f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root try { 106f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root copyFile(packageURI, autoOut); 107f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root return PackageManager.INSTALL_SUCCEEDED; 108f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root } catch (FileNotFoundException e) { 109f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root Slog.e(TAG, "Could not copy URI " + packageURI.toString() + " FNF: " 110f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root + e.getMessage()); 111f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root return PackageManager.INSTALL_FAILED_INVALID_URI; 112f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root } catch (IOException e) { 113f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root Slog.e(TAG, "Could not copy URI " + packageURI.toString() + " IO: " 114f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root + e.getMessage()); 115f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; 116c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 117c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 1185b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu 1195b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu /* 1205b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu * Determine the recommended install location for package 1215b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu * specified by file uri location. 1225b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu * @param fileUri the uri of resource to be copied. Should be a 1235b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu * file uri 124a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu * @return Returns PackageInfoLite object containing 125a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu * the package info and recommended app location. 1265b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu */ 12762e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root public PackageInfoLite getMinimalPackageInfo(final Uri fileUri, int flags, long threshold) { 128a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu PackageInfoLite ret = new PackageInfoLite(); 1293602f76d417b0940a26f28b7ad892abcfe03ef7cSuchi Amalapurapu if (fileUri == null) { 1301ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.i(TAG, "Invalid package uri " + fileUri); 131a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK; 132a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu return ret; 1333602f76d417b0940a26f28b7ad892abcfe03ef7cSuchi Amalapurapu } 1343602f76d417b0940a26f28b7ad892abcfe03ef7cSuchi Amalapurapu String scheme = fileUri.getScheme(); 1353602f76d417b0940a26f28b7ad892abcfe03ef7cSuchi Amalapurapu if (scheme != null && !scheme.equals("file")) { 1361ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.w(TAG, "Falling back to installing on internal storage only"); 137a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu ret.recommendedInstallLocation = PackageHelper.RECOMMEND_INSTALL_INTERNAL; 138a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu return ret; 1395b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu } 1403602f76d417b0940a26f28b7ad892abcfe03ef7cSuchi Amalapurapu String archiveFilePath = fileUri.getPath(); 1415b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu DisplayMetrics metrics = new DisplayMetrics(); 1425b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu metrics.setToDefaults(); 1431ebd74acf9977daa42133507e970dab88e08f0efKenny Root 14462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root PackageParser.PackageLite pkg = PackageParser.parsePackageLite(archiveFilePath, 0); 1455b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu if (pkg == null) { 1461ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.w(TAG, "Failed to parse package"); 1471ebd74acf9977daa42133507e970dab88e08f0efKenny Root 1481ebd74acf9977daa42133507e970dab88e08f0efKenny Root final File apkFile = new File(archiveFilePath); 1491ebd74acf9977daa42133507e970dab88e08f0efKenny Root if (!apkFile.exists()) { 1501ebd74acf9977daa42133507e970dab88e08f0efKenny Root ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_URI; 1511ebd74acf9977daa42133507e970dab88e08f0efKenny Root } else { 1521ebd74acf9977daa42133507e970dab88e08f0efKenny Root ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK; 1531ebd74acf9977daa42133507e970dab88e08f0efKenny Root } 1541ebd74acf9977daa42133507e970dab88e08f0efKenny Root 155a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu return ret; 1565b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu } 157a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu ret.packageName = pkg.packageName; 158930d3af75f9e9663222f4c4a1d75b326cf811e35Kenny Root ret.installLocation = pkg.installLocation; 15905ca4c90644921df9193d92b2abdc81ef77e4a62Kenny Root ret.verifiers = pkg.verifiers; 1601ebd74acf9977daa42133507e970dab88e08f0efKenny Root 16162e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root ret.recommendedInstallLocation = recommendAppInstallLocation(pkg.installLocation, 16262e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root archiveFilePath, flags, threshold); 1631ebd74acf9977daa42133507e970dab88e08f0efKenny Root 164a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu return ret; 1655b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu } 1668a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu 16762e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root @Override 16862e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root public boolean checkInternalFreeStorage(Uri packageUri, long threshold) 16962e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root throws RemoteException { 17062e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root final File apkFile = new File(packageUri.getPath()); 1711ebd74acf9977daa42133507e970dab88e08f0efKenny Root try { 1721ebd74acf9977daa42133507e970dab88e08f0efKenny Root return isUnderInternalThreshold(apkFile, threshold); 1731ebd74acf9977daa42133507e970dab88e08f0efKenny Root } catch (FileNotFoundException e) { 1741ebd74acf9977daa42133507e970dab88e08f0efKenny Root return true; 1751ebd74acf9977daa42133507e970dab88e08f0efKenny Root } 17662e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root } 17762e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 17862e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root @Override 17962e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root public boolean checkExternalFreeStorage(Uri packageUri) throws RemoteException { 18062e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root final File apkFile = new File(packageUri.getPath()); 1811ebd74acf9977daa42133507e970dab88e08f0efKenny Root try { 1821ebd74acf9977daa42133507e970dab88e08f0efKenny Root return isUnderExternalThreshold(apkFile); 1831ebd74acf9977daa42133507e970dab88e08f0efKenny Root } catch (FileNotFoundException e) { 1841ebd74acf9977daa42133507e970dab88e08f0efKenny Root return true; 1851ebd74acf9977daa42133507e970dab88e08f0efKenny Root } 1868a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu } 187a02b8b05dd1e8b8cf169e1f89542ef835b11fc13Kenny Root 188a02b8b05dd1e8b8cf169e1f89542ef835b11fc13Kenny Root public ObbInfo getObbInfo(String filename) { 18905105f7abe02b2dff91d6260b3628c8b97816babKenny Root try { 19005105f7abe02b2dff91d6260b3628c8b97816babKenny Root return ObbScanner.getObbInfo(filename); 19105105f7abe02b2dff91d6260b3628c8b97816babKenny Root } catch (IOException e) { 1921ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.d(TAG, "Couldn't get OBB info for " + filename); 19305105f7abe02b2dff91d6260b3628c8b97816babKenny Root return null; 19405105f7abe02b2dff91d6260b3628c8b97816babKenny Root } 195a02b8b05dd1e8b8cf169e1f89542ef835b11fc13Kenny Root } 196aa183e2c9a279cb6aef7dc77855facfae795b6f8Kenny Root 197aa183e2c9a279cb6aef7dc77855facfae795b6f8Kenny Root @Override 198366949c2d934435ff9ef8082408ca36ff14a2241Kenny Root public long calculateDirectorySize(String path) throws RemoteException { 199366949c2d934435ff9ef8082408ca36ff14a2241Kenny Root final File directory = new File(path); 200366949c2d934435ff9ef8082408ca36ff14a2241Kenny Root if (directory.exists() && directory.isDirectory()) { 201366949c2d934435ff9ef8082408ca36ff14a2241Kenny Root return MeasurementUtils.measureDirectory(path); 202366949c2d934435ff9ef8082408ca36ff14a2241Kenny Root } else { 203366949c2d934435ff9ef8082408ca36ff14a2241Kenny Root return 0L; 204366949c2d934435ff9ef8082408ca36ff14a2241Kenny Root } 205aa183e2c9a279cb6aef7dc77855facfae795b6f8Kenny Root } 206c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu }; 207c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu 208e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn public DefaultContainerService() { 209e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn super("DefaultContainerService"); 210e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn setIntentRedelivery(true); 211e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn } 212e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn 213e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn @Override 214e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn protected void onHandleIntent(Intent intent) { 215e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn if (PackageManager.ACTION_CLEAN_EXTERNAL_STORAGE.equals(intent.getAction())) { 216e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn IPackageManager pm = IPackageManager.Stub.asInterface( 217e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn ServiceManager.getService("package")); 218e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn String pkg = null; 219e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn try { 220e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn while ((pkg=pm.nextPackageToClean(pkg)) != null) { 221e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn eraseFiles(Environment.getExternalStorageAppDataDirectory(pkg)); 222e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn eraseFiles(Environment.getExternalStorageAppMediaDirectory(pkg)); 223300c13a48132f03d48462b9cd3ec41331a71a411Kenny Root eraseFiles(Environment.getExternalStorageAppObbDirectory(pkg)); 224e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn } 225e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn } catch (RemoteException e) { 226e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn } 227e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn } 228e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn } 229e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn 230e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn void eraseFiles(File path) { 231e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn if (path.isDirectory()) { 232e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn String[] files = path.list(); 233e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn if (files != null) { 234e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn for (String file : files) { 235e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn eraseFiles(new File(path, file)); 236e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn } 237e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn } 238e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn } 239e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn path.delete(); 240e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn } 241e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn 242c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu public IBinder onBind(Intent intent) { 243c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu return mBinder; 244c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 245c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu 246679bba339ef6948091180c776d6a284cddd812f5Suchi Amalapurapu private String copyResourceInner(Uri packageURI, String newCid, String key, String resFileName) { 2478a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu // Make sure the sdcard is mounted. 2488a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu String status = Environment.getExternalStorageState(); 2498a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu if (!status.equals(Environment.MEDIA_MOUNTED)) { 2501ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.w(TAG, "Make sure sdcard is mounted."); 2518a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu return null; 2528a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu } 25385387d7ba36e56b291cbde87acb5a5b2200fe01cKenny Root 25485387d7ba36e56b291cbde87acb5a5b2200fe01cKenny Root // The .apk file 255c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu String codePath = packageURI.getPath(); 256679bba339ef6948091180c776d6a284cddd812f5Suchi Amalapurapu File codeFile = new File(codePath); 25785387d7ba36e56b291cbde87acb5a5b2200fe01cKenny Root 25862e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root // Calculate size of container needed to hold base APK. 2591ebd74acf9977daa42133507e970dab88e08f0efKenny Root int sizeMb; 2601ebd74acf9977daa42133507e970dab88e08f0efKenny Root try { 2611ebd74acf9977daa42133507e970dab88e08f0efKenny Root sizeMb = calculateContainerSize(codeFile); 2621ebd74acf9977daa42133507e970dab88e08f0efKenny Root } catch (FileNotFoundException e) { 2631ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.w(TAG, "File does not exist when trying to copy " + codeFile.getPath()); 2641ebd74acf9977daa42133507e970dab88e08f0efKenny Root return null; 2651ebd74acf9977daa42133507e970dab88e08f0efKenny Root } 26685387d7ba36e56b291cbde87acb5a5b2200fe01cKenny Root 267c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu // Create new container 2681ebd74acf9977daa42133507e970dab88e08f0efKenny Root final String newCachePath; 26962e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root if ((newCachePath = PackageHelper.createSdDir(sizeMb, newCid, key, Process.myUid())) == null) { 2701ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.e(TAG, "Failed to create container " + newCid); 271a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu return null; 272c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 2731ebd74acf9977daa42133507e970dab88e08f0efKenny Root 2741ebd74acf9977daa42133507e970dab88e08f0efKenny Root if (localLOGV) { 2751ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.i(TAG, "Created container for " + newCid + " at path : " + newCachePath); 2761ebd74acf9977daa42133507e970dab88e08f0efKenny Root } 2771ebd74acf9977daa42133507e970dab88e08f0efKenny Root 2781ebd74acf9977daa42133507e970dab88e08f0efKenny Root final File resFile = new File(newCachePath, resFileName); 2791ebd74acf9977daa42133507e970dab88e08f0efKenny Root if (FileUtils.copyFile(new File(codePath), resFile)) { 2801ebd74acf9977daa42133507e970dab88e08f0efKenny Root if (localLOGV) { 2811ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.i(TAG, "Copied " + codePath + " to " + resFile); 2821ebd74acf9977daa42133507e970dab88e08f0efKenny Root } 2831ebd74acf9977daa42133507e970dab88e08f0efKenny Root } else { 2841ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.e(TAG, "Failed to copy " + codePath + " to " + resFile); 285a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu // Clean up container 286a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu PackageHelper.destroySdDir(newCid); 287c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu return null; 288c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 28985387d7ba36e56b291cbde87acb5a5b2200fe01cKenny Root 2901ebd74acf9977daa42133507e970dab88e08f0efKenny Root final File sharedLibraryDir = new File(newCachePath, LIB_DIR_NAME); 2911ebd74acf9977daa42133507e970dab88e08f0efKenny Root if (sharedLibraryDir.mkdir()) { 2921ebd74acf9977daa42133507e970dab88e08f0efKenny Root int ret = NativeLibraryHelper.copyNativeBinariesIfNeededLI(codeFile, sharedLibraryDir); 2931ebd74acf9977daa42133507e970dab88e08f0efKenny Root if (ret != PackageManager.INSTALL_SUCCEEDED) { 2941ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.e(TAG, "Could not copy native libraries to " + sharedLibraryDir.getPath()); 2951ebd74acf9977daa42133507e970dab88e08f0efKenny Root PackageHelper.destroySdDir(newCid); 2961ebd74acf9977daa42133507e970dab88e08f0efKenny Root return null; 29785387d7ba36e56b291cbde87acb5a5b2200fe01cKenny Root } 2981ebd74acf9977daa42133507e970dab88e08f0efKenny Root } else { 2991ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.e(TAG, "Could not create native lib directory: " + sharedLibraryDir.getPath()); 30085387d7ba36e56b291cbde87acb5a5b2200fe01cKenny Root PackageHelper.destroySdDir(newCid); 30185387d7ba36e56b291cbde87acb5a5b2200fe01cKenny Root return null; 30285387d7ba36e56b291cbde87acb5a5b2200fe01cKenny Root } 30385387d7ba36e56b291cbde87acb5a5b2200fe01cKenny Root 304a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu if (!PackageHelper.finalizeSdDir(newCid)) { 3051ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.e(TAG, "Failed to finalize " + newCid + " at path " + newCachePath); 306a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu // Clean up container 307a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu PackageHelper.destroySdDir(newCid); 3081ebd74acf9977daa42133507e970dab88e08f0efKenny Root return null; 3091ebd74acf9977daa42133507e970dab88e08f0efKenny Root } 3101ebd74acf9977daa42133507e970dab88e08f0efKenny Root 3111ebd74acf9977daa42133507e970dab88e08f0efKenny Root if (localLOGV) { 3121ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.i(TAG, "Finalized container " + newCid); 313a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu } 3141ebd74acf9977daa42133507e970dab88e08f0efKenny Root 315a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu if (PackageHelper.isContainerMounted(newCid)) { 3161ebd74acf9977daa42133507e970dab88e08f0efKenny Root if (localLOGV) { 3171ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.i(TAG, "Unmounting " + newCid + " at path " + newCachePath); 3181ebd74acf9977daa42133507e970dab88e08f0efKenny Root } 3191ebd74acf9977daa42133507e970dab88e08f0efKenny Root 320a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu // Force a gc to avoid being killed. 321a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu Runtime.getRuntime().gc(); 322a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu PackageHelper.unMountSdDir(newCid); 323a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu } else { 3241ebd74acf9977daa42133507e970dab88e08f0efKenny Root if (localLOGV) { 3251ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.i(TAG, "Container " + newCid + " not mounted"); 3261ebd74acf9977daa42133507e970dab88e08f0efKenny Root } 327a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu } 3281ebd74acf9977daa42133507e970dab88e08f0efKenny Root 329c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu return newCachePath; 330c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 331c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu 332f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root private static void copyToFile(InputStream inputStream, OutputStream out) throws IOException { 333f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root byte[] buffer = new byte[16384]; 334f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root int bytesRead; 335f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root while ((bytesRead = inputStream.read(buffer)) >= 0) { 336f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root out.write(buffer, 0, bytesRead); 337c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 338c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 339c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu 340f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root private static void copyToFile(File srcFile, OutputStream out) 341f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root throws FileNotFoundException, IOException { 342f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root InputStream inputStream = new BufferedInputStream(new FileInputStream(srcFile)); 343c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu try { 344f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root copyToFile(inputStream, out); 345c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } finally { 346f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root try { inputStream.close(); } catch (IOException e) {} 347c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 348c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 349c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu 350f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root private void copyFile(Uri pPackageURI, OutputStream outStream) throws FileNotFoundException, 351f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root IOException { 3523602f76d417b0940a26f28b7ad892abcfe03ef7cSuchi Amalapurapu String scheme = pPackageURI.getScheme(); 3533602f76d417b0940a26f28b7ad892abcfe03ef7cSuchi Amalapurapu if (scheme == null || scheme.equals("file")) { 354c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu final File srcPackageFile = new File(pPackageURI.getPath()); 355c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu // We copy the source package file to a temp file and then rename it to the 356c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu // destination file in order to eliminate a window where the package directory 357c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu // scanner notices the new package file but it's not completely copied yet. 358f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root copyToFile(srcPackageFile, outStream); 3593602f76d417b0940a26f28b7ad892abcfe03ef7cSuchi Amalapurapu } else if (scheme.equals("content")) { 360c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu ParcelFileDescriptor fd = null; 361c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu try { 362c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu fd = getContentResolver().openFileDescriptor(pPackageURI, "r"); 363c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } catch (FileNotFoundException e) { 364f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root Slog.e(TAG, "Couldn't open file descriptor from download service. " 365f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root + "Failed with exception " + e); 366f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root throw e; 367c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 368f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root 369c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu if (fd == null) { 370f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root Slog.e(TAG, "Provider returned no file descriptor for " + pPackageURI.toString()); 371f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root throw new FileNotFoundException("provider returned no file descriptor"); 372c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } else { 373c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu if (localLOGV) { 3741ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.i(TAG, "Opened file descriptor from download service."); 375c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 376f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root ParcelFileDescriptor.AutoCloseInputStream dlStream 377f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root = new ParcelFileDescriptor.AutoCloseInputStream(fd); 378f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root 379c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu // We copy the source package file to a temp file and then rename it to the 380c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu // destination file in order to eliminate a window where the package directory 3811ebd74acf9977daa42133507e970dab88e08f0efKenny Root // scanner notices the new package file but it's not completely 382f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root // copied 383f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root copyToFile(dlStream, outStream); 384c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 385c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } else { 3861ebd74acf9977daa42133507e970dab88e08f0efKenny Root Slog.e(TAG, "Package URI is not 'file:' or 'content:' - " + pPackageURI); 387f5121a9b802c6ddd3661ed5cae602380dbe67090Kenny Root throw new FileNotFoundException("Package URI is not 'file:' or 'content:'"); 388c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 389c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu } 3905b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu 39162e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root private static final int PREFER_INTERNAL = 1; 39262e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root private static final int PREFER_EXTERNAL = 2; 3935b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu 39462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root private int recommendAppInstallLocation(int installLocation, String archiveFilePath, int flags, 39562e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root long threshold) { 39662e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root int prefer; 39714b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu boolean checkBoth = false; 39862e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 39914b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu check_inner : { 40062e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root /* 40162e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * Explicit install flags should override the manifest settings. 40262e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root */ 40314b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) { 40462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root /* 40562e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * Forward-locked applications cannot be installed on SD card, 40662e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * so only allow checking internal storage. 40762e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root */ 40862e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root prefer = PREFER_INTERNAL; 40914b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu break check_inner; 41014b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu } else if ((flags & PackageManager.INSTALL_INTERNAL) != 0) { 41162e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root prefer = PREFER_INTERNAL; 41214b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu break check_inner; 41314b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu } else if ((flags & PackageManager.INSTALL_EXTERNAL) != 0) { 41462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root prefer = PREFER_EXTERNAL; 41514b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu break check_inner; 41614b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu } 41762e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 41862e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root /* No install flags. Check for manifest option. */ 41914b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) { 42062e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root prefer = PREFER_INTERNAL; 42114b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu break check_inner; 42214b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu } else if (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) { 42362e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root prefer = PREFER_EXTERNAL; 42414b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu checkBoth = true; 42514b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu break check_inner; 42614b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu } else if (installLocation == PackageInfo.INSTALL_LOCATION_AUTO) { 42762e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root // We default to preferring internal storage. 42862e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root prefer = PREFER_INTERNAL; 42914b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu checkBoth = true; 43014b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu break check_inner; 43114b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu } 43262e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 43340e472521a544f26cb6956995788f7c36fff1404Suchi Amalapurapu // Pick user preference 43440e472521a544f26cb6956995788f7c36fff1404Suchi Amalapurapu int installPreference = Settings.System.getInt(getApplicationContext() 43514b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu .getContentResolver(), 43640e472521a544f26cb6956995788f7c36fff1404Suchi Amalapurapu Settings.Secure.DEFAULT_INSTALL_LOCATION, 43740e472521a544f26cb6956995788f7c36fff1404Suchi Amalapurapu PackageHelper.APP_INSTALL_AUTO); 43840e472521a544f26cb6956995788f7c36fff1404Suchi Amalapurapu if (installPreference == PackageHelper.APP_INSTALL_INTERNAL) { 43962e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root prefer = PREFER_INTERNAL; 44040e472521a544f26cb6956995788f7c36fff1404Suchi Amalapurapu break check_inner; 44140e472521a544f26cb6956995788f7c36fff1404Suchi Amalapurapu } else if (installPreference == PackageHelper.APP_INSTALL_EXTERNAL) { 44262e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root prefer = PREFER_EXTERNAL; 44340e472521a544f26cb6956995788f7c36fff1404Suchi Amalapurapu break check_inner; 44414b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu } 44562e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 44662e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root /* 44762e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * Fall back to default policy of internal-only if nothing else is 44862e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * specified. 44962e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root */ 45062e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root prefer = PREFER_INTERNAL; 45114b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu } 45214b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu 45362e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root final boolean emulated = Environment.isExternalStorageEmulated(); 45462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 45562e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root final File apkFile = new File(archiveFilePath); 45662e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 45762e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root boolean fitsOnInternal = false; 45862e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root if (checkBoth || prefer == PREFER_INTERNAL) { 4591ebd74acf9977daa42133507e970dab88e08f0efKenny Root try { 4601ebd74acf9977daa42133507e970dab88e08f0efKenny Root fitsOnInternal = isUnderInternalThreshold(apkFile, threshold); 4611ebd74acf9977daa42133507e970dab88e08f0efKenny Root } catch (FileNotFoundException e) { 4621ebd74acf9977daa42133507e970dab88e08f0efKenny Root return PackageHelper.RECOMMEND_FAILED_INVALID_URI; 4631ebd74acf9977daa42133507e970dab88e08f0efKenny Root } 4645b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu } 46562e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 46614b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu boolean fitsOnSd = false; 46762e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root if (!emulated && (checkBoth || prefer == PREFER_EXTERNAL)) { 4681ebd74acf9977daa42133507e970dab88e08f0efKenny Root try { 4691ebd74acf9977daa42133507e970dab88e08f0efKenny Root fitsOnSd = isUnderExternalThreshold(apkFile); 4701ebd74acf9977daa42133507e970dab88e08f0efKenny Root } catch (FileNotFoundException e) { 4711ebd74acf9977daa42133507e970dab88e08f0efKenny Root return PackageHelper.RECOMMEND_FAILED_INVALID_URI; 4721ebd74acf9977daa42133507e970dab88e08f0efKenny Root } 47314b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu } 47462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 47562e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root if (prefer == PREFER_INTERNAL) { 47662e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root if (fitsOnInternal) { 47714b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu return PackageHelper.RECOMMEND_INSTALL_INTERNAL; 4785b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu } 47962e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root } else if (!emulated && prefer == PREFER_EXTERNAL) { 48014b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu if (fitsOnSd) { 48114b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu return PackageHelper.RECOMMEND_INSTALL_EXTERNAL; 4825b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu } 4835b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu } 48462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 48514b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu if (checkBoth) { 48662e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root if (fitsOnInternal) { 48714b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu return PackageHelper.RECOMMEND_INSTALL_INTERNAL; 48862e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root } else if (!emulated && fitsOnSd) { 48914b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu return PackageHelper.RECOMMEND_INSTALL_EXTERNAL; 4905b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu } 4915b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu } 49262e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 49362e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root /* 49462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * If they requested to be on the external media by default, return that 49562e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * the media was unavailable. Otherwise, indicate there was insufficient 49662e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * storage space available. 49762e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root */ 49862e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root if (!emulated && (checkBoth || prefer == PREFER_EXTERNAL) 49962e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root && !Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { 50014b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu return PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE; 50162e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root } else { 50262e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE; 5035b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu } 5048a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu } 5058a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu 5061ebd74acf9977daa42133507e970dab88e08f0efKenny Root /** 5071ebd74acf9977daa42133507e970dab88e08f0efKenny Root * Measure a file to see if it fits within the free space threshold. 5081ebd74acf9977daa42133507e970dab88e08f0efKenny Root * 5091ebd74acf9977daa42133507e970dab88e08f0efKenny Root * @param apkFile file to check 5101ebd74acf9977daa42133507e970dab88e08f0efKenny Root * @param threshold byte threshold to compare against 5111ebd74acf9977daa42133507e970dab88e08f0efKenny Root * @return true if file fits under threshold 5121ebd74acf9977daa42133507e970dab88e08f0efKenny Root * @throws FileNotFoundException when APK does not exist 5131ebd74acf9977daa42133507e970dab88e08f0efKenny Root */ 5141ebd74acf9977daa42133507e970dab88e08f0efKenny Root private boolean isUnderInternalThreshold(File apkFile, long threshold) 5151ebd74acf9977daa42133507e970dab88e08f0efKenny Root throws FileNotFoundException { 51662e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root final long size = apkFile.length(); 5171ebd74acf9977daa42133507e970dab88e08f0efKenny Root if (size == 0 && !apkFile.exists()) { 5181ebd74acf9977daa42133507e970dab88e08f0efKenny Root throw new FileNotFoundException(); 5191ebd74acf9977daa42133507e970dab88e08f0efKenny Root } 52062e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 52162e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root final StatFs internalStats = new StatFs(Environment.getDataDirectory().getPath()); 52262e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root final long availInternalSize = (long) internalStats.getAvailableBlocks() 52362e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * (long) internalStats.getBlockSize(); 52462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 52562e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root return (availInternalSize - size) > threshold; 52662e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root } 52762e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 52862e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 5291ebd74acf9977daa42133507e970dab88e08f0efKenny Root /** 5301ebd74acf9977daa42133507e970dab88e08f0efKenny Root * Measure a file to see if it fits in the external free space. 5311ebd74acf9977daa42133507e970dab88e08f0efKenny Root * 5321ebd74acf9977daa42133507e970dab88e08f0efKenny Root * @param apkFile file to check 5331ebd74acf9977daa42133507e970dab88e08f0efKenny Root * @return true if file fits 5341ebd74acf9977daa42133507e970dab88e08f0efKenny Root * @throws IOException when file does not exist 5351ebd74acf9977daa42133507e970dab88e08f0efKenny Root */ 5361ebd74acf9977daa42133507e970dab88e08f0efKenny Root private boolean isUnderExternalThreshold(File apkFile) throws FileNotFoundException { 53762e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root if (Environment.isExternalStorageEmulated()) { 53862e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root return false; 53962e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root } 54062e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 5411ebd74acf9977daa42133507e970dab88e08f0efKenny Root final int sizeMb = calculateContainerSize(apkFile); 54262e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 54362e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root final int availSdMb; 54462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { 54561942c579dd8d30f77a9a501b31bd7a485b0d4bcKenny Root final StatFs sdStats = new StatFs(Environment.getExternalStorageDirectory().getPath()); 54661942c579dd8d30f77a9a501b31bd7a485b0d4bcKenny Root final int blocksToMb = (1 << 20) / sdStats.getBlockSize(); 54761942c579dd8d30f77a9a501b31bd7a485b0d4bcKenny Root availSdMb = sdStats.getAvailableBlocks() * blocksToMb; 54862e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root } else { 54962e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root availSdMb = -1; 55062e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root } 55162e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 55262e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root return availSdMb > sizeMb; 55362e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root } 55462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 55562e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root /** 55662e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * Calculate the container size for an APK. Takes into account the 55762e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * 55862e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * @param apkFile file from which to calculate size 55962e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * @return size in megabytes (2^20 bytes) 5601ebd74acf9977daa42133507e970dab88e08f0efKenny Root * @throws FileNotFoundException when file does not exist 56162e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root */ 5621ebd74acf9977daa42133507e970dab88e08f0efKenny Root private int calculateContainerSize(File apkFile) throws FileNotFoundException { 56362e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root // Calculate size of container needed to hold base APK. 56462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root long sizeBytes = apkFile.length(); 5651ebd74acf9977daa42133507e970dab88e08f0efKenny Root if (sizeBytes == 0 && !apkFile.exists()) { 5661ebd74acf9977daa42133507e970dab88e08f0efKenny Root throw new FileNotFoundException(); 5671ebd74acf9977daa42133507e970dab88e08f0efKenny Root } 56862e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 56962e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root // Check all the native files that need to be copied and add that to the 57062e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root // container size. 57166269ea6f68f2f25888ce1080c94ac782742fafcKenny Root sizeBytes += NativeLibraryHelper.sumNativeBinariesLI(apkFile); 57262e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 57362e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root int sizeMb = (int) (sizeBytes >> 20); 57462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root if ((sizeBytes - (sizeMb * 1024 * 1024)) > 0) { 57562e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root sizeMb++; 57662e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root } 57762e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 57862e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root /* 57962e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * Add buffer size because we don't have a good way to determine the 58062e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * real FAT size. Your FAT size varies with how many directory entries 58162e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root * you need, how big the whole filesystem is, and other such headaches. 58262e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root */ 58362e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root sizeMb++; 58462e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root 58562e1b4e9d41a01db423b5e4684ecf529ed46106dKenny Root return sizeMb; 5865b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu } 587c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu} 588