DefaultContainerService.java revision 930d3af75f9e9663222f4c4a1d75b326cf811e35
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;
205b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapuimport com.android.internal.content.PackageHelper;
21c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport android.content.Intent;
22e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackbornimport android.content.pm.IPackageManager;
235b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapuimport android.content.pm.PackageInfo;
24a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapuimport android.content.pm.PackageInfoLite;
25e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackbornimport android.content.pm.PackageManager;
265b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapuimport android.content.pm.PackageParser;
27c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport android.net.Uri;
28e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackbornimport android.os.Environment;
29c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport android.os.IBinder;
30c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport android.os.ParcelFileDescriptor;
31c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport android.os.Process;
32c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport android.os.RemoteException;
33c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport android.os.ServiceManager;
345b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapuimport android.os.StatFs;
35e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackbornimport android.app.IntentService;
365b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapuimport android.util.DisplayMetrics;
37c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport android.util.Log;
38c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu
39c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport java.io.File;
40c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport java.io.FileInputStream;
41c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport java.io.FileNotFoundException;
42c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport java.io.FileOutputStream;
43c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport java.io.IOException;
44c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport java.io.InputStream;
45c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu
46c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapuimport android.os.FileUtils;
475b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapuimport android.provider.Settings;
48c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu
49c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu/*
50c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu * This service copies a downloaded apk to a file passed in as
51c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu * a ParcelFileDescriptor or to a newly created container specified
52c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu * by parameters. The DownloadManager gives access to this process
53c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu * based on its uid. This process also needs the ACCESS_DOWNLOAD_MANAGER
54c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu * permission to access apks downloaded via the download manager.
55c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu */
56e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackbornpublic class DefaultContainerService extends IntentService {
57c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu    private static final String TAG = "DefContainer";
58cf6eaeaae9e6745dd6e07540812c79821d7043c2Suchi Amalapurapu    private static final boolean localLOGV = true;
59c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu
60c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu    private IMediaContainerService.Stub mBinder = new IMediaContainerService.Stub() {
61c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu        /*
62c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu         * Creates a new container and copies resource there.
63c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu         * @param paackageURI the uri of resource to be copied. Can be either
64c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu         * a content uri or a file uri
65679bba339ef6948091180c776d6a284cddd812f5Suchi Amalapurapu         * @param cid the id of the secure container that should
66c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu         * be used for creating a secure container into which the resource
67c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu         * will be copied.
68c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu         * @param key Refers to key used for encrypting the secure container
69c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu         * @param resFileName Name of the target resource file(relative to newly
70c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu         * created secure container)
71c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu         * @return Returns the new cache path where the resource has been copied into
72c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu         *
73c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu         */
74c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu        public String copyResourceToContainer(final Uri packageURI,
75679bba339ef6948091180c776d6a284cddd812f5Suchi Amalapurapu                final String cid,
76c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu                final String key, final String resFileName) {
77679bba339ef6948091180c776d6a284cddd812f5Suchi Amalapurapu            if (packageURI == null || cid == null) {
78c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu                return null;
79c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            }
80679bba339ef6948091180c776d6a284cddd812f5Suchi Amalapurapu            return copyResourceInner(packageURI, cid, key, resFileName);
81c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu        }
82c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu
83c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu        /*
84c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu         * Copy specified resource to output stream
85c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu         * @param packageURI the uri of resource to be copied. Should be a
86c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu         * file uri
87c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu         * @param outStream Remote file descriptor to be used for copying
88c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu         * @return Returns true if copy succeded or false otherwise.
89c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu         */
90c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu        public boolean copyResource(final Uri packageURI,
91c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu                ParcelFileDescriptor outStream) {
92c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            if (packageURI == null ||  outStream == null) {
93c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu                return false;
94c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            }
95c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            ParcelFileDescriptor.AutoCloseOutputStream
96c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            autoOut = new ParcelFileDescriptor.AutoCloseOutputStream(outStream);
97c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            return copyFile(packageURI, autoOut);
98c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu        }
995b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu
1005b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu        /*
1015b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu         * Determine the recommended install location for package
1025b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu         * specified by file uri location.
1035b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu         * @param fileUri the uri of resource to be copied. Should be a
1045b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu         * file uri
105a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu         * @return Returns PackageInfoLite object containing
106a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu         * the package info and recommended app location.
1075b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu         */
10814b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu        public PackageInfoLite getMinimalPackageInfo(final Uri fileUri, int flags) {
109a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu            PackageInfoLite ret = new PackageInfoLite();
1103602f76d417b0940a26f28b7ad892abcfe03ef7cSuchi Amalapurapu            if (fileUri == null) {
1113602f76d417b0940a26f28b7ad892abcfe03ef7cSuchi Amalapurapu                Log.i(TAG, "Invalid package uri " + fileUri);
112a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu                ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
113a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu                return ret;
1143602f76d417b0940a26f28b7ad892abcfe03ef7cSuchi Amalapurapu            }
1153602f76d417b0940a26f28b7ad892abcfe03ef7cSuchi Amalapurapu            String scheme = fileUri.getScheme();
1163602f76d417b0940a26f28b7ad892abcfe03ef7cSuchi Amalapurapu            if (scheme != null && !scheme.equals("file")) {
1175b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu                Log.w(TAG, "Falling back to installing on internal storage only");
118a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu                ret.recommendedInstallLocation = PackageHelper.RECOMMEND_INSTALL_INTERNAL;
119a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu                return ret;
1205b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu            }
1213602f76d417b0940a26f28b7ad892abcfe03ef7cSuchi Amalapurapu            String archiveFilePath = fileUri.getPath();
1225b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu            PackageParser packageParser = new PackageParser(archiveFilePath);
1235b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu            File sourceFile = new File(archiveFilePath);
1245b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu            DisplayMetrics metrics = new DisplayMetrics();
1255b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu            metrics.setToDefaults();
126a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu            PackageParser.PackageLite pkg = packageParser.parsePackageLite(
127a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu                    archiveFilePath, 0);
1288946dd3355fc1dcbad872c0546e356474d4cc5deSuchi Amalapurapu            // Nuke the parser reference right away and force a gc
1298946dd3355fc1dcbad872c0546e356474d4cc5deSuchi Amalapurapu            packageParser = null;
1308a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu            Runtime.getRuntime().gc();
1315b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu            if (pkg == null) {
1325b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu                Log.w(TAG, "Failed to parse package");
133a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu                ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
134a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu                return ret;
1355b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu            }
136a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu            ret.packageName = pkg.packageName;
137930d3af75f9e9663222f4c4a1d75b326cf811e35Kenny Root            ret.installLocation = pkg.installLocation;
13814b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu            ret.recommendedInstallLocation = recommendAppInstallLocation(pkg.installLocation, archiveFilePath, flags);
139a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu            return ret;
1405b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu        }
1418a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu
1428a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu        public boolean checkFreeStorage(boolean external, Uri fileUri) {
1438a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu            return checkFreeStorageInner(external, fileUri);
1448a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu        }
145c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu    };
146c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu
147e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn    public DefaultContainerService() {
148e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn        super("DefaultContainerService");
149e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn        setIntentRedelivery(true);
150e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn    }
151e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn
152e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn    @Override
153e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn    protected void onHandleIntent(Intent intent) {
154e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn        if (PackageManager.ACTION_CLEAN_EXTERNAL_STORAGE.equals(intent.getAction())) {
155e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn            IPackageManager pm = IPackageManager.Stub.asInterface(
156e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn                    ServiceManager.getService("package"));
157e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn            String pkg = null;
158e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn            try {
159e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn                while ((pkg=pm.nextPackageToClean(pkg)) != null) {
160e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn                    eraseFiles(Environment.getExternalStorageAppDataDirectory(pkg));
161e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn                    eraseFiles(Environment.getExternalStorageAppMediaDirectory(pkg));
162e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn                }
163e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn            } catch (RemoteException e) {
164e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn            }
165e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn        }
166e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn    }
167e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn
168e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn    void eraseFiles(File path) {
169e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn        if (path.isDirectory()) {
170e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn            String[] files = path.list();
171e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn            if (files != null) {
172e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn                for (String file : files) {
173e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn                    eraseFiles(new File(path, file));
174e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn                }
175e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn            }
176e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn        }
177e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn        path.delete();
178e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn    }
179e83cefcef07f9ac025642c1ffec76b4c7ab39cf2Dianne Hackborn
180c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu    public IBinder onBind(Intent intent) {
181c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu        return mBinder;
182c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu    }
183c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu
184679bba339ef6948091180c776d6a284cddd812f5Suchi Amalapurapu    private String copyResourceInner(Uri packageURI, String newCid, String key, String resFileName) {
1858a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu        // Make sure the sdcard is mounted.
1868a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu        String status = Environment.getExternalStorageState();
1878a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu        if (!status.equals(Environment.MEDIA_MOUNTED)) {
1888a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu            Log.w(TAG, "Make sure sdcard is mounted.");
1898a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu            return null;
1908a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu        }
191c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu        // Create new container at newCachePath
192c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu        String codePath = packageURI.getPath();
193679bba339ef6948091180c776d6a284cddd812f5Suchi Amalapurapu        File codeFile = new File(codePath);
194c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu        String newCachePath = null;
195c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu        // Create new container
196679bba339ef6948091180c776d6a284cddd812f5Suchi Amalapurapu        if ((newCachePath = PackageHelper.createSdDir(codeFile,
197a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu                newCid, key, Process.myUid())) == null) {
198a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu            Log.e(TAG, "Failed to create container " + newCid);
199a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu            return null;
200c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu        }
201a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu        if (localLOGV) Log.i(TAG, "Created container for " + newCid
202a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu                + " at path : " + newCachePath);
203a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu        File resFile = new File(newCachePath, resFileName);
204a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu        if (!FileUtils.copyFile(new File(codePath), resFile)) {
205a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu            Log.e(TAG, "Failed to copy " + codePath + " to " + resFile);
206a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu            // Clean up container
207a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu            PackageHelper.destroySdDir(newCid);
208c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            return null;
209c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu        }
210a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu        if (localLOGV) Log.i(TAG, "Copied " + codePath + " to " + resFile);
211a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu        if (!PackageHelper.finalizeSdDir(newCid)) {
212a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu            Log.e(TAG, "Failed to finalize " + newCid + " at path " + newCachePath);
213a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu            // Clean up container
214a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu            PackageHelper.destroySdDir(newCid);
215a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu        }
216a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu        if (localLOGV) Log.i(TAG, "Finalized container " + newCid);
217a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu        if (PackageHelper.isContainerMounted(newCid)) {
218a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu            if (localLOGV) Log.i(TAG, "Unmounting " + newCid +
219a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu                    " at path " + newCachePath);
220a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu            // Force a gc to avoid being killed.
221a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu            Runtime.getRuntime().gc();
222a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu            PackageHelper.unMountSdDir(newCid);
223a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu        } else {
224a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu            if (localLOGV) Log.i(TAG, "Container " + newCid + " not mounted");
225a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu        }
226c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu        return newCachePath;
227c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu    }
228c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu
229c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu    public static boolean copyToFile(InputStream inputStream, FileOutputStream out) {
230c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu        try {
231c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            byte[] buffer = new byte[4096];
232c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            int bytesRead;
233c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            while ((bytesRead = inputStream.read(buffer)) >= 0) {
234c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu                out.write(buffer, 0, bytesRead);
235c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            }
236c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            return true;
237c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu        } catch (IOException e) {
238c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            Log.i(TAG, "Exception : " + e + " when copying file");
239c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            return false;
240c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu        }
241c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu    }
242c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu
243c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu    public static boolean copyToFile(File srcFile, FileOutputStream out) {
244c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu        InputStream inputStream = null;
245c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu        try {
246c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            inputStream = new FileInputStream(srcFile);
247c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            return copyToFile(inputStream, out);
248c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu        } catch (IOException e) {
249c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            return false;
250c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu        } finally {
251c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            try { if (inputStream != null) inputStream.close(); } catch (IOException e) {}
252c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu        }
253c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu    }
254c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu
255c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu    private  boolean copyFile(Uri pPackageURI, FileOutputStream outStream) {
2563602f76d417b0940a26f28b7ad892abcfe03ef7cSuchi Amalapurapu        String scheme = pPackageURI.getScheme();
2573602f76d417b0940a26f28b7ad892abcfe03ef7cSuchi Amalapurapu        if (scheme == null || scheme.equals("file")) {
258c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            final File srcPackageFile = new File(pPackageURI.getPath());
259c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            // We copy the source package file to a temp file and then rename it to the
260c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            // destination file in order to eliminate a window where the package directory
261c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            // scanner notices the new package file but it's not completely copied yet.
262c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            if (!copyToFile(srcPackageFile, outStream)) {
263c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu                Log.e(TAG, "Couldn't copy file: " + srcPackageFile);
264c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu                return false;
265c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            }
2663602f76d417b0940a26f28b7ad892abcfe03ef7cSuchi Amalapurapu        } else if (scheme.equals("content")) {
267c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            ParcelFileDescriptor fd = null;
268c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            try {
269c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu                fd = getContentResolver().openFileDescriptor(pPackageURI, "r");
270c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            } catch (FileNotFoundException e) {
271c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu                Log.e(TAG, "Couldn't open file descriptor from download service. Failed with exception " + e);
272c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu                return false;
273c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            }
274c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            if (fd == null) {
275c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu                Log.e(TAG, "Couldn't open file descriptor from download service (null).");
276c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu                return false;
277c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            } else {
278c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu                if (localLOGV) {
279c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu                    Log.v(TAG, "Opened file descriptor from download service.");
280c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu                }
281c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu                ParcelFileDescriptor.AutoCloseInputStream
282c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu                dlStream = new ParcelFileDescriptor.AutoCloseInputStream(fd);
283c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu                // We copy the source package file to a temp file and then rename it to the
284c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu                // destination file in order to eliminate a window where the package directory
285c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu                // scanner notices the new package file but it's not completely copied yet.
286c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu                if (!copyToFile(dlStream, outStream)) {
287c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu                    Log.e(TAG, "Couldn't copy " + pPackageURI + " to temp file.");
288c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu                    return false;
289c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu                }
290c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            }
291c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu        } else {
292c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            Log.e(TAG, "Package URI is not 'file:' or 'content:' - " + pPackageURI);
293c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu            return false;
294c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu        }
295c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu        return true;
296c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu    }
2975b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu
2985b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu    // Constants related to app heuristics
2995b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu    // No-installation limit for internal flash: 10% or less space available
3005b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu    private static final double LOW_NAND_FLASH_TRESHOLD = 0.1;
3015b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu
3025b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu    // SD-to-internal app size threshold: currently set to 1 MB
3035b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu    private static final long INSTALL_ON_SD_THRESHOLD = (1024 * 1024);
3045b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu    private static final int ERR_LOC = -1;
3055b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu
306a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu    private int recommendAppInstallLocation(int installLocation,
30714b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu            String archiveFilePath, int flags) {
30814b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu        boolean checkInt = false;
30914b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu        boolean checkExt = false;
31014b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu        boolean checkBoth = false;
31114b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu        check_inner : {
31214b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu            // Check flags.
31314b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu            if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) {
31414b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu                // Check for forward locked app
31514b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu                checkInt = true;
31614b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu                break check_inner;
31714b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu            } else if ((flags & PackageManager.INSTALL_INTERNAL) != 0) {
31814b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu                // Explicit flag to install internally.
31914b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu                // Check internal storage and return
32014b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu                checkInt = true;
32114b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu                break check_inner;
32214b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu            } else if ((flags & PackageManager.INSTALL_EXTERNAL) != 0) {
32314b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu                // Explicit flag to install externally.
32414b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu                // Check external storage and return
32514b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu                checkExt = true;
32614b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu                break check_inner;
32714b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu            }
32814b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu            // Check for manifest option
32914b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu            if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
33014b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu                checkInt = true;
33114b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu                break check_inner;
33214b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu            } else if (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
33314b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu                checkExt = true;
33414b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu                checkBoth = true;
33514b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu                break check_inner;
33614b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu            } else if (installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
33714b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu                checkInt = true;
33814b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu                checkBoth = true;
33914b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu                break check_inner;
34014b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu            }
34140e472521a544f26cb6956995788f7c36fff1404Suchi Amalapurapu            // Pick user preference
34240e472521a544f26cb6956995788f7c36fff1404Suchi Amalapurapu            int installPreference = Settings.System.getInt(getApplicationContext()
34314b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu                    .getContentResolver(),
34440e472521a544f26cb6956995788f7c36fff1404Suchi Amalapurapu                    Settings.Secure.DEFAULT_INSTALL_LOCATION,
34540e472521a544f26cb6956995788f7c36fff1404Suchi Amalapurapu                    PackageHelper.APP_INSTALL_AUTO);
34640e472521a544f26cb6956995788f7c36fff1404Suchi Amalapurapu            if (installPreference == PackageHelper.APP_INSTALL_INTERNAL) {
34740e472521a544f26cb6956995788f7c36fff1404Suchi Amalapurapu                checkInt = true;
34840e472521a544f26cb6956995788f7c36fff1404Suchi Amalapurapu                break check_inner;
34940e472521a544f26cb6956995788f7c36fff1404Suchi Amalapurapu            } else if (installPreference == PackageHelper.APP_INSTALL_EXTERNAL) {
35040e472521a544f26cb6956995788f7c36fff1404Suchi Amalapurapu                checkExt = true;
35140e472521a544f26cb6956995788f7c36fff1404Suchi Amalapurapu                break check_inner;
35214b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu            }
35314b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu            // Fall back to default policy if nothing else is specified.
35414b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu            checkInt = true;
35514b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu        }
35614b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu
3575b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu        // Package size = code size + cache size + data size
3585b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu        // If code size > 1 MB, install on SD card.
3595b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu        // Else install on internal NAND flash, unless space on NAND is less than 10%
360a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu        String status = Environment.getExternalStorageState();
361a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu        long availSDSize = -1;
3628a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu        boolean mediaAvailable = false;
363a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu        if (status.equals(Environment.MEDIA_MOUNTED)) {
364a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu            StatFs sdStats = new StatFs(
365a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu                    Environment.getExternalStorageDirectory().getPath());
366a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu            availSDSize = (long)sdStats.getAvailableBlocks() *
367a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu                    (long)sdStats.getBlockSize();
3688a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu            mediaAvailable = true;
3695b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu        }
370a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu        StatFs internalStats = new StatFs(Environment.getDataDirectory().getPath());
371a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu        long totalInternalSize = (long)internalStats.getBlockCount() *
372a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu                (long)internalStats.getBlockSize();
373a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu        long availInternalSize = (long)internalStats.getAvailableBlocks() *
374a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu                (long)internalStats.getBlockSize();
3755b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu
376a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu        double pctNandFree = (double)availInternalSize / (double)totalInternalSize;
3775b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu
3785b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu        File apkFile = new File(archiveFilePath);
3795b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu        long pkgLen = apkFile.length();
38014b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu
3815b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu        // To make final copy
3825b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu        long reqInstallSize = pkgLen;
383cf6eaeaae9e6745dd6e07540812c79821d7043c2Suchi Amalapurapu        // For dex files. Just ignore and fail when extracting. Max limit of 2Gig for now.
384cf6eaeaae9e6745dd6e07540812c79821d7043c2Suchi Amalapurapu        long reqInternalSize = 0;
3855b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu        boolean intThresholdOk = (pctNandFree >= LOW_NAND_FLASH_TRESHOLD);
386a2b6c3775ed6b8924232d6a01bae4a19740a15f8Suchi Amalapurapu        boolean intAvailOk = ((reqInstallSize + reqInternalSize) < availInternalSize);
38714b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu        boolean fitsOnSd = false;
38814b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu        if (mediaAvailable && (reqInstallSize < availSDSize)) {
38914b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu            // If we do not have an internal size requirement
39014b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu            // don't do a threshold check.
39114b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu            if (reqInternalSize == 0) {
39214b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu                fitsOnSd = true;
39314b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu            } else if ((reqInternalSize < availInternalSize) && intThresholdOk) {
39414b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu                fitsOnSd = true;
39514b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu            }
39614b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu        }
3975b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu        boolean fitsOnInt = intThresholdOk && intAvailOk;
39814b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu        if (checkInt) {
39914b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu            // Check for internal memory availability
40014b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu            if (fitsOnInt) {
40114b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu                return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
4025b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu            }
40314b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu        } else if (checkExt) {
40414b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu            if (fitsOnSd) {
40514b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu                return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
4065b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu            }
4075b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu        }
40814b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu        if (checkBoth) {
40914b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu            // Check for internal first
41014b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu            if (fitsOnInt) {
41114b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu                return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
41214b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu            }
41314b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu            // Check for external next
41414b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu            if (fitsOnSd) {
41514b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu                return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
4165b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu            }
4175b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu        }
418d345bb6e5b42ff644ce224fe57e4d7b4e546a48dSuchi Amalapurapu        if ((checkExt || checkBoth) && !mediaAvailable) {
41914b6abda1309631d49d4bebbb0317a7e1dfc0a50Suchi Amalapurapu            return PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE;
4205b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu        }
4218a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu        return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
4228a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu    }
4238a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu
4248a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu    private boolean checkFreeStorageInner(boolean external, Uri packageURI) {
4258a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu        File apkFile = new File(packageURI.getPath());
4268a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu        long size = apkFile.length();
4278a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu        if (external) {
4288a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu            String status = Environment.getExternalStorageState();
4298a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu            long availSDSize = -1;
4308a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu            if (status.equals(Environment.MEDIA_MOUNTED)) {
4318a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu                StatFs sdStats = new StatFs(
4328a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu                        Environment.getExternalStorageDirectory().getPath());
4338a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu                availSDSize = (long)sdStats.getAvailableBlocks() *
4348a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu                (long)sdStats.getBlockSize();
4358a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu            }
4368a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu            return availSDSize > size;
4378a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu        }
4388a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu        StatFs internalStats = new StatFs(Environment.getDataDirectory().getPath());
4398a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu        long totalInternalSize = (long)internalStats.getBlockCount() *
4408a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu        (long)internalStats.getBlockSize();
4418a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu        long availInternalSize = (long)internalStats.getAvailableBlocks() *
4428a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu        (long)internalStats.getBlockSize();
4438a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu
4448a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu        double pctNandFree = (double)availInternalSize / (double)totalInternalSize;
4458a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu        // To make final copy
4468a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu        long reqInstallSize = size;
4478a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu        // For dex files. Just ignore and fail when extracting. Max limit of 2Gig for now.
4488a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu        long reqInternalSize = 0;
4498a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu        boolean intThresholdOk = (pctNandFree >= LOW_NAND_FLASH_TRESHOLD);
4508a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu        boolean intAvailOk = ((reqInstallSize + reqInternalSize) < availInternalSize);
4518a9ab24a5c9b595ac0268fcade4b5bbfe7c45c2dSuchi Amalapurapu        return intThresholdOk && intAvailOk;
4525b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2Suchi Amalapurapu    }
453c028be4f3b8c7476b46859f66c3f33d528adf181Suchi Amalapurapu}
454