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