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