blob: 14c8806ce97d642452811892a1eeb4278f8a74ba [file] [log] [blame]
Kenny Root15a4d2f2010-03-11 18:20:12 -08001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Suchi Amalapurapuc028be42010-01-25 12:19:12 -080017package com.android.defcontainer;
18
19import com.android.internal.app.IMediaContainerService;
Suchi Amalapurapu5b993ce2010-02-12 09:43:29 -080020import com.android.internal.content.PackageHelper;
Suchi Amalapurapuc028be42010-01-25 12:19:12 -080021import android.content.Intent;
Dianne Hackborne83cefce2010-02-04 17:38:14 -080022import android.content.pm.IPackageManager;
Suchi Amalapurapu5b993ce2010-02-12 09:43:29 -080023import android.content.pm.PackageInfo;
Suchi Amalapurapua2b6c372010-03-05 17:40:11 -080024import android.content.pm.PackageInfoLite;
Dianne Hackborne83cefce2010-02-04 17:38:14 -080025import android.content.pm.PackageManager;
Suchi Amalapurapu5b993ce2010-02-12 09:43:29 -080026import android.content.pm.PackageParser;
Suchi Amalapurapuc028be42010-01-25 12:19:12 -080027import android.net.Uri;
Dianne Hackborne83cefce2010-02-04 17:38:14 -080028import android.os.Environment;
Suchi Amalapurapuc028be42010-01-25 12:19:12 -080029import android.os.IBinder;
Suchi Amalapurapuc028be42010-01-25 12:19:12 -080030import android.os.ParcelFileDescriptor;
31import android.os.Process;
32import android.os.RemoteException;
33import android.os.ServiceManager;
Suchi Amalapurapu5b993ce2010-02-12 09:43:29 -080034import android.os.StatFs;
Dianne Hackborne83cefce2010-02-04 17:38:14 -080035import android.app.IntentService;
Suchi Amalapurapu5b993ce2010-02-12 09:43:29 -080036import android.util.DisplayMetrics;
Suchi Amalapurapuc028be42010-01-25 12:19:12 -080037import android.util.Log;
38
39import java.io.File;
40import java.io.FileInputStream;
41import java.io.FileNotFoundException;
42import java.io.FileOutputStream;
43import java.io.IOException;
44import java.io.InputStream;
Suchi Amalapurapuc028be42010-01-25 12:19:12 -080045
46import android.os.FileUtils;
Suchi Amalapurapu5b993ce2010-02-12 09:43:29 -080047import android.provider.Settings;
Suchi Amalapurapuc028be42010-01-25 12:19:12 -080048
Suchi Amalapurapuc028be42010-01-25 12:19:12 -080049/*
50 * This service copies a downloaded apk to a file passed in as
51 * a ParcelFileDescriptor or to a newly created container specified
52 * by parameters. The DownloadManager gives access to this process
53 * based on its uid. This process also needs the ACCESS_DOWNLOAD_MANAGER
54 * permission to access apks downloaded via the download manager.
55 */
Dianne Hackborne83cefce2010-02-04 17:38:14 -080056public class DefaultContainerService extends IntentService {
Suchi Amalapurapuc028be42010-01-25 12:19:12 -080057 private static final String TAG = "DefContainer";
Suchi Amalapurapucf6eaea2010-02-23 19:37:45 -080058 private static final boolean localLOGV = true;
Suchi Amalapurapuc028be42010-01-25 12:19:12 -080059
60 private IMediaContainerService.Stub mBinder = new IMediaContainerService.Stub() {
61 /*
62 * Creates a new container and copies resource there.
63 * @param paackageURI the uri of resource to be copied. Can be either
64 * a content uri or a file uri
Suchi Amalapurapu679bba32010-02-16 11:52:44 -080065 * @param cid the id of the secure container that should
Suchi Amalapurapuc028be42010-01-25 12:19:12 -080066 * be used for creating a secure container into which the resource
67 * will be copied.
68 * @param key Refers to key used for encrypting the secure container
69 * @param resFileName Name of the target resource file(relative to newly
70 * created secure container)
71 * @return Returns the new cache path where the resource has been copied into
72 *
73 */
74 public String copyResourceToContainer(final Uri packageURI,
Suchi Amalapurapu679bba32010-02-16 11:52:44 -080075 final String cid,
Suchi Amalapurapuc028be42010-01-25 12:19:12 -080076 final String key, final String resFileName) {
Suchi Amalapurapu679bba32010-02-16 11:52:44 -080077 if (packageURI == null || cid == null) {
Suchi Amalapurapuc028be42010-01-25 12:19:12 -080078 return null;
79 }
Suchi Amalapurapu679bba32010-02-16 11:52:44 -080080 return copyResourceInner(packageURI, cid, key, resFileName);
Suchi Amalapurapuc028be42010-01-25 12:19:12 -080081 }
82
83 /*
84 * Copy specified resource to output stream
85 * @param packageURI the uri of resource to be copied. Should be a
86 * file uri
87 * @param outStream Remote file descriptor to be used for copying
88 * @return Returns true if copy succeded or false otherwise.
89 */
90 public boolean copyResource(final Uri packageURI,
91 ParcelFileDescriptor outStream) {
92 if (packageURI == null || outStream == null) {
93 return false;
94 }
95 ParcelFileDescriptor.AutoCloseOutputStream
96 autoOut = new ParcelFileDescriptor.AutoCloseOutputStream(outStream);
97 return copyFile(packageURI, autoOut);
98 }
Suchi Amalapurapu5b993ce2010-02-12 09:43:29 -080099
100 /*
101 * Determine the recommended install location for package
102 * specified by file uri location.
103 * @param fileUri the uri of resource to be copied. Should be a
104 * file uri
Suchi Amalapurapua2b6c372010-03-05 17:40:11 -0800105 * @return Returns PackageInfoLite object containing
106 * the package info and recommended app location.
Suchi Amalapurapu5b993ce2010-02-12 09:43:29 -0800107 */
Suchi Amalapurapu14b6abd2010-03-17 08:37:04 -0700108 public PackageInfoLite getMinimalPackageInfo(final Uri fileUri, int flags) {
Suchi Amalapurapua2b6c372010-03-05 17:40:11 -0800109 PackageInfoLite ret = new PackageInfoLite();
Suchi Amalapurapu3602f762010-03-03 17:29:33 -0800110 if (fileUri == null) {
111 Log.i(TAG, "Invalid package uri " + fileUri);
Suchi Amalapurapua2b6c372010-03-05 17:40:11 -0800112 ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
113 return ret;
Suchi Amalapurapu3602f762010-03-03 17:29:33 -0800114 }
115 String scheme = fileUri.getScheme();
116 if (scheme != null && !scheme.equals("file")) {
Suchi Amalapurapu5b993ce2010-02-12 09:43:29 -0800117 Log.w(TAG, "Falling back to installing on internal storage only");
Suchi Amalapurapua2b6c372010-03-05 17:40:11 -0800118 ret.recommendedInstallLocation = PackageHelper.RECOMMEND_INSTALL_INTERNAL;
119 return ret;
Suchi Amalapurapu5b993ce2010-02-12 09:43:29 -0800120 }
Suchi Amalapurapu3602f762010-03-03 17:29:33 -0800121 String archiveFilePath = fileUri.getPath();
Suchi Amalapurapu5b993ce2010-02-12 09:43:29 -0800122 PackageParser packageParser = new PackageParser(archiveFilePath);
123 File sourceFile = new File(archiveFilePath);
124 DisplayMetrics metrics = new DisplayMetrics();
125 metrics.setToDefaults();
Suchi Amalapurapua2b6c372010-03-05 17:40:11 -0800126 PackageParser.PackageLite pkg = packageParser.parsePackageLite(
127 archiveFilePath, 0);
128 ret.packageName = pkg.packageName;
129 ret.installLocation = pkg.installLocation;
Suchi Amalapurapu8946dd32010-02-19 09:19:34 -0800130 // Nuke the parser reference right away and force a gc
Suchi Amalapurapu8946dd32010-02-19 09:19:34 -0800131 packageParser = null;
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800132 Runtime.getRuntime().gc();
Suchi Amalapurapu5b993ce2010-02-12 09:43:29 -0800133 if (pkg == null) {
134 Log.w(TAG, "Failed to parse package");
Suchi Amalapurapua2b6c372010-03-05 17:40:11 -0800135 ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
136 return ret;
Suchi Amalapurapu5b993ce2010-02-12 09:43:29 -0800137 }
Suchi Amalapurapua2b6c372010-03-05 17:40:11 -0800138 ret.packageName = pkg.packageName;
Suchi Amalapurapu14b6abd2010-03-17 08:37:04 -0700139 ret.recommendedInstallLocation = recommendAppInstallLocation(pkg.installLocation, archiveFilePath, flags);
Suchi Amalapurapua2b6c372010-03-05 17:40:11 -0800140 return ret;
Suchi Amalapurapu5b993ce2010-02-12 09:43:29 -0800141 }
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800142
143 public boolean checkFreeStorage(boolean external, Uri fileUri) {
144 return checkFreeStorageInner(external, fileUri);
145 }
Suchi Amalapurapuc028be42010-01-25 12:19:12 -0800146 };
147
Dianne Hackborne83cefce2010-02-04 17:38:14 -0800148 public DefaultContainerService() {
149 super("DefaultContainerService");
150 setIntentRedelivery(true);
151 }
152
153 @Override
154 protected void onHandleIntent(Intent intent) {
155 if (PackageManager.ACTION_CLEAN_EXTERNAL_STORAGE.equals(intent.getAction())) {
156 IPackageManager pm = IPackageManager.Stub.asInterface(
157 ServiceManager.getService("package"));
158 String pkg = null;
159 try {
160 while ((pkg=pm.nextPackageToClean(pkg)) != null) {
161 eraseFiles(Environment.getExternalStorageAppDataDirectory(pkg));
162 eraseFiles(Environment.getExternalStorageAppMediaDirectory(pkg));
163 }
164 } catch (RemoteException e) {
165 }
166 }
167 }
168
169 void eraseFiles(File path) {
170 if (path.isDirectory()) {
171 String[] files = path.list();
172 if (files != null) {
173 for (String file : files) {
174 eraseFiles(new File(path, file));
175 }
176 }
177 }
Dianne Hackborne83cefce2010-02-04 17:38:14 -0800178 path.delete();
179 }
180
Suchi Amalapurapuc028be42010-01-25 12:19:12 -0800181 public IBinder onBind(Intent intent) {
182 return mBinder;
183 }
184
Suchi Amalapurapu679bba32010-02-16 11:52:44 -0800185 private String copyResourceInner(Uri packageURI, String newCid, String key, String resFileName) {
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800186 // Make sure the sdcard is mounted.
187 String status = Environment.getExternalStorageState();
188 if (!status.equals(Environment.MEDIA_MOUNTED)) {
189 Log.w(TAG, "Make sure sdcard is mounted.");
190 return null;
191 }
Suchi Amalapurapuc028be42010-01-25 12:19:12 -0800192 // Create new container at newCachePath
193 String codePath = packageURI.getPath();
Suchi Amalapurapu679bba32010-02-16 11:52:44 -0800194 File codeFile = new File(codePath);
Suchi Amalapurapuc028be42010-01-25 12:19:12 -0800195 String newCachePath = null;
Suchi Amalapurapuc028be42010-01-25 12:19:12 -0800196 // Create new container
Suchi Amalapurapu679bba32010-02-16 11:52:44 -0800197 if ((newCachePath = PackageHelper.createSdDir(codeFile,
Suchi Amalapurapua2b6c372010-03-05 17:40:11 -0800198 newCid, key, Process.myUid())) == null) {
199 Log.e(TAG, "Failed to create container " + newCid);
Suchi Amalapurapu9b10ef52010-03-03 09:45:24 -0800200 return null;
201 }
Suchi Amalapurapua2b6c372010-03-05 17:40:11 -0800202 if (localLOGV) Log.i(TAG, "Created container for " + newCid
203 + " at path : " + newCachePath);
204 File resFile = new File(newCachePath, resFileName);
205 if (!FileUtils.copyFile(new File(codePath), resFile)) {
206 Log.e(TAG, "Failed to copy " + codePath + " to " + resFile);
207 // Clean up container
208 PackageHelper.destroySdDir(newCid);
209 return null;
210 }
211 if (localLOGV) Log.i(TAG, "Copied " + codePath + " to " + resFile);
212 if (!PackageHelper.finalizeSdDir(newCid)) {
213 Log.e(TAG, "Failed to finalize " + newCid + " at path " + newCachePath);
214 // Clean up container
215 PackageHelper.destroySdDir(newCid);
216 }
217 if (localLOGV) Log.i(TAG, "Finalized container " + newCid);
218 if (PackageHelper.isContainerMounted(newCid)) {
219 if (localLOGV) Log.i(TAG, "Unmounting " + newCid +
220 " at path " + newCachePath);
221 // Force a gc to avoid being killed.
222 Runtime.getRuntime().gc();
223 PackageHelper.unMountSdDir(newCid);
224 } else {
225 if (localLOGV) Log.i(TAG, "Container " + newCid + " not mounted");
226 }
Suchi Amalapurapuc028be42010-01-25 12:19:12 -0800227 return newCachePath;
228 }
229
Suchi Amalapurapuc028be42010-01-25 12:19:12 -0800230 public static boolean copyToFile(InputStream inputStream, FileOutputStream out) {
231 try {
232 byte[] buffer = new byte[4096];
233 int bytesRead;
234 while ((bytesRead = inputStream.read(buffer)) >= 0) {
235 out.write(buffer, 0, bytesRead);
236 }
237 return true;
238 } catch (IOException e) {
239 Log.i(TAG, "Exception : " + e + " when copying file");
240 return false;
241 }
242 }
243
244 public static boolean copyToFile(File srcFile, FileOutputStream out) {
245 InputStream inputStream = null;
246 try {
247 inputStream = new FileInputStream(srcFile);
248 return copyToFile(inputStream, out);
249 } catch (IOException e) {
250 return false;
251 } finally {
252 try { if (inputStream != null) inputStream.close(); } catch (IOException e) {}
253 }
254 }
255
256 private boolean copyFile(Uri pPackageURI, FileOutputStream outStream) {
Suchi Amalapurapu3602f762010-03-03 17:29:33 -0800257 String scheme = pPackageURI.getScheme();
258 if (scheme == null || scheme.equals("file")) {
Suchi Amalapurapuc028be42010-01-25 12:19:12 -0800259 final File srcPackageFile = new File(pPackageURI.getPath());
260 // We copy the source package file to a temp file and then rename it to the
261 // destination file in order to eliminate a window where the package directory
262 // scanner notices the new package file but it's not completely copied yet.
263 if (!copyToFile(srcPackageFile, outStream)) {
264 Log.e(TAG, "Couldn't copy file: " + srcPackageFile);
265 return false;
266 }
Suchi Amalapurapu3602f762010-03-03 17:29:33 -0800267 } else if (scheme.equals("content")) {
Suchi Amalapurapuc028be42010-01-25 12:19:12 -0800268 ParcelFileDescriptor fd = null;
269 try {
270 fd = getContentResolver().openFileDescriptor(pPackageURI, "r");
271 } catch (FileNotFoundException e) {
272 Log.e(TAG, "Couldn't open file descriptor from download service. Failed with exception " + e);
273 return false;
274 }
275 if (fd == null) {
276 Log.e(TAG, "Couldn't open file descriptor from download service (null).");
277 return false;
278 } else {
279 if (localLOGV) {
280 Log.v(TAG, "Opened file descriptor from download service.");
281 }
282 ParcelFileDescriptor.AutoCloseInputStream
283 dlStream = new ParcelFileDescriptor.AutoCloseInputStream(fd);
284 // We copy the source package file to a temp file and then rename it to the
285 // destination file in order to eliminate a window where the package directory
286 // scanner notices the new package file but it's not completely copied yet.
287 if (!copyToFile(dlStream, outStream)) {
288 Log.e(TAG, "Couldn't copy " + pPackageURI + " to temp file.");
289 return false;
290 }
291 }
292 } else {
293 Log.e(TAG, "Package URI is not 'file:' or 'content:' - " + pPackageURI);
294 return false;
295 }
296 return true;
297 }
Suchi Amalapurapu5b993ce2010-02-12 09:43:29 -0800298
299 // Constants related to app heuristics
300 // No-installation limit for internal flash: 10% or less space available
301 private static final double LOW_NAND_FLASH_TRESHOLD = 0.1;
302
303 // SD-to-internal app size threshold: currently set to 1 MB
304 private static final long INSTALL_ON_SD_THRESHOLD = (1024 * 1024);
305 private static final int ERR_LOC = -1;
306
Suchi Amalapurapua2b6c372010-03-05 17:40:11 -0800307 private int recommendAppInstallLocation(int installLocation,
Suchi Amalapurapu14b6abd2010-03-17 08:37:04 -0700308 String archiveFilePath, int flags) {
309 boolean checkInt = false;
310 boolean checkExt = false;
311 boolean checkBoth = false;
312 check_inner : {
313 // Check flags.
314 if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) {
315 // Check for forward locked app
316 checkInt = true;
317 break check_inner;
318 } else if ((flags & PackageManager.INSTALL_INTERNAL) != 0) {
319 // Explicit flag to install internally.
320 // Check internal storage and return
321 checkInt = true;
322 break check_inner;
323 } else if ((flags & PackageManager.INSTALL_EXTERNAL) != 0) {
324 // Explicit flag to install externally.
325 // Check external storage and return
326 checkExt = true;
327 break check_inner;
328 }
329 // Check for manifest option
330 if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
331 checkInt = true;
332 break check_inner;
333 } else if (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
334 checkExt = true;
335 checkBoth = true;
336 break check_inner;
337 } else if (installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
338 checkInt = true;
339 checkBoth = true;
340 break check_inner;
341 }
342 // Check if user option is enabled
343 boolean setInstallLoc = Settings.System.getInt(getApplicationContext()
344 .getContentResolver(),
345 Settings.System.SET_INSTALL_LOCATION, 0) != 0;
346 if (setInstallLoc) {
347 // Pick user preference
348 int installPreference = Settings.System.getInt(getApplicationContext()
349 .getContentResolver(),
350 Settings.System.DEFAULT_INSTALL_LOCATION,
351 PackageHelper.APP_INSTALL_AUTO);
352 if (installPreference == PackageHelper.APP_INSTALL_INTERNAL) {
353 checkInt = true;
354 checkBoth = true;
355 break check_inner;
356 } else if (installPreference == PackageHelper.APP_INSTALL_EXTERNAL) {
357 checkExt = true;
358 checkBoth = true;
359 break check_inner;
360 }
361 }
362 // Fall back to default policy if nothing else is specified.
363 checkInt = true;
364 checkBoth = true;
365 }
366
Suchi Amalapurapu5b993ce2010-02-12 09:43:29 -0800367 // Package size = code size + cache size + data size
368 // If code size > 1 MB, install on SD card.
369 // Else install on internal NAND flash, unless space on NAND is less than 10%
Suchi Amalapurapua2b6c372010-03-05 17:40:11 -0800370 String status = Environment.getExternalStorageState();
371 long availSDSize = -1;
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800372 boolean mediaAvailable = false;
Suchi Amalapurapua2b6c372010-03-05 17:40:11 -0800373 if (status.equals(Environment.MEDIA_MOUNTED)) {
374 StatFs sdStats = new StatFs(
375 Environment.getExternalStorageDirectory().getPath());
376 availSDSize = (long)sdStats.getAvailableBlocks() *
377 (long)sdStats.getBlockSize();
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800378 mediaAvailable = true;
Suchi Amalapurapu5b993ce2010-02-12 09:43:29 -0800379 }
Suchi Amalapurapua2b6c372010-03-05 17:40:11 -0800380 StatFs internalStats = new StatFs(Environment.getDataDirectory().getPath());
381 long totalInternalSize = (long)internalStats.getBlockCount() *
382 (long)internalStats.getBlockSize();
383 long availInternalSize = (long)internalStats.getAvailableBlocks() *
384 (long)internalStats.getBlockSize();
Suchi Amalapurapu5b993ce2010-02-12 09:43:29 -0800385
Suchi Amalapurapua2b6c372010-03-05 17:40:11 -0800386 double pctNandFree = (double)availInternalSize / (double)totalInternalSize;
Suchi Amalapurapu5b993ce2010-02-12 09:43:29 -0800387
Suchi Amalapurapu5b993ce2010-02-12 09:43:29 -0800388 File apkFile = new File(archiveFilePath);
389 long pkgLen = apkFile.length();
Suchi Amalapurapu14b6abd2010-03-17 08:37:04 -0700390
Suchi Amalapurapu5b993ce2010-02-12 09:43:29 -0800391 // To make final copy
392 long reqInstallSize = pkgLen;
Suchi Amalapurapucf6eaea2010-02-23 19:37:45 -0800393 // For dex files. Just ignore and fail when extracting. Max limit of 2Gig for now.
394 long reqInternalSize = 0;
Suchi Amalapurapu5b993ce2010-02-12 09:43:29 -0800395 boolean intThresholdOk = (pctNandFree >= LOW_NAND_FLASH_TRESHOLD);
Suchi Amalapurapua2b6c372010-03-05 17:40:11 -0800396 boolean intAvailOk = ((reqInstallSize + reqInternalSize) < availInternalSize);
Suchi Amalapurapu14b6abd2010-03-17 08:37:04 -0700397 boolean fitsOnSd = false;
398 if (mediaAvailable && (reqInstallSize < availSDSize)) {
399 // If we do not have an internal size requirement
400 // don't do a threshold check.
401 if (reqInternalSize == 0) {
402 fitsOnSd = true;
403 } else if ((reqInternalSize < availInternalSize) && intThresholdOk) {
404 fitsOnSd = true;
405 }
406 }
Suchi Amalapurapu5b993ce2010-02-12 09:43:29 -0800407 boolean fitsOnInt = intThresholdOk && intAvailOk;
Suchi Amalapurapu14b6abd2010-03-17 08:37:04 -0700408 if (checkInt) {
409 // Check for internal memory availability
410 if (fitsOnInt) {
411 return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
412 }
413 } else if (checkExt) {
Suchi Amalapurapu5b993ce2010-02-12 09:43:29 -0800414 if (fitsOnSd) {
Suchi Amalapurapu14b6abd2010-03-17 08:37:04 -0700415 return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
Suchi Amalapurapu5b993ce2010-02-12 09:43:29 -0800416 }
417 }
Suchi Amalapurapu14b6abd2010-03-17 08:37:04 -0700418 if (checkBoth) {
419 // Check for internal first
420 if (fitsOnInt) {
421 return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
422 }
423 // Check for external next
424 if (fitsOnSd) {
425 return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
Suchi Amalapurapu5b993ce2010-02-12 09:43:29 -0800426 }
427 }
Suchi Amalapurapu14b6abd2010-03-17 08:37:04 -0700428 if (checkExt || checkBoth && !mediaAvailable) {
429 return PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE;
Suchi Amalapurapu5b993ce2010-02-12 09:43:29 -0800430 }
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800431 return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
432 }
433
434 private boolean checkFreeStorageInner(boolean external, Uri packageURI) {
435 File apkFile = new File(packageURI.getPath());
436 long size = apkFile.length();
437 if (external) {
438 String status = Environment.getExternalStorageState();
439 long availSDSize = -1;
440 if (status.equals(Environment.MEDIA_MOUNTED)) {
441 StatFs sdStats = new StatFs(
442 Environment.getExternalStorageDirectory().getPath());
443 availSDSize = (long)sdStats.getAvailableBlocks() *
444 (long)sdStats.getBlockSize();
445 }
446 return availSDSize > size;
447 }
448 StatFs internalStats = new StatFs(Environment.getDataDirectory().getPath());
449 long totalInternalSize = (long)internalStats.getBlockCount() *
450 (long)internalStats.getBlockSize();
451 long availInternalSize = (long)internalStats.getAvailableBlocks() *
452 (long)internalStats.getBlockSize();
453
454 double pctNandFree = (double)availInternalSize / (double)totalInternalSize;
455 // To make final copy
456 long reqInstallSize = size;
457 // For dex files. Just ignore and fail when extracting. Max limit of 2Gig for now.
458 long reqInternalSize = 0;
459 boolean intThresholdOk = (pctNandFree >= LOW_NAND_FLASH_TRESHOLD);
460 boolean intAvailOk = ((reqInstallSize + reqInternalSize) < availInternalSize);
461 return intThresholdOk && intAvailOk;
Suchi Amalapurapu5b993ce2010-02-12 09:43:29 -0800462 }
Suchi Amalapurapuc028be42010-01-25 12:19:12 -0800463}