diff options
author | Suchi Amalapurapu <asuchitra@google.com> | 2010-01-25 12:19:12 -0800 |
---|---|---|
committer | Suchi Amalapurapu <asuchitra@google.com> | 2010-01-27 10:26:43 -0800 |
commit | c028be4f3b8c7476b46859f66c3f33d528adf181 (patch) | |
tree | 8a740e6207e958572706a636fcb92425dbf13a6e /packages/DefaultContainerService/src | |
parent | aeb4126736c1b93abe5252e1723c568a13da7c81 (diff) | |
download | frameworks_base-c028be4f3b8c7476b46859f66c3f33d528adf181.zip frameworks_base-c028be4f3b8c7476b46859f66c3f33d528adf181.tar.gz frameworks_base-c028be4f3b8c7476b46859f66c3f33d528adf181.tar.bz2 |
AppsOnSd feature - Add default container
Add new remote interface to do temporary copies. The new
remote stub handling is done on mHandler thread and doesn't need locking
for now.
Add new InstallArgs class and subclasses to isolate cases for installation.
Move resource deletion for failed installs/upgrades to later on in installation
cycle.
Fix code path for forward locked apps when using scanPackageLI
TODO's
Fix installation paths to completely use InstallArgs based design later on.
Get rid of using flags in various install/uninstall code paths.
Ideally InstallArgs should be created using these flags and used in the
rest of the code.
Function renames.
Revisit mount api's.
Diffstat (limited to 'packages/DefaultContainerService/src')
-rw-r--r-- | packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java new file mode 100644 index 0000000..d36207b --- /dev/null +++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java @@ -0,0 +1,305 @@ +package com.android.defcontainer; + +import com.android.internal.app.IMediaContainerService; + +import android.content.Intent; +import android.net.Uri; +import android.os.Debug; +import android.os.IBinder; +import android.os.IMountService; +import android.os.ParcelFileDescriptor; +import android.os.Process; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.app.Service; +import android.util.Log; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import android.os.FileUtils; + + +/* + * This service copies a downloaded apk to a file passed in as + * a ParcelFileDescriptor or to a newly created container specified + * by parameters. The DownloadManager gives access to this process + * based on its uid. This process also needs the ACCESS_DOWNLOAD_MANAGER + * permission to access apks downloaded via the download manager. + */ +public class DefaultContainerService extends Service { + private static final String TAG = "DefContainer"; + private static final boolean localLOGV = false; + + private IMediaContainerService.Stub mBinder = new IMediaContainerService.Stub() { + /* + * Creates a new container and copies resource there. + * @param paackageURI the uri of resource to be copied. Can be either + * a content uri or a file uri + * @param containerId the id of the secure container that should + * be used for creating a secure container into which the resource + * will be copied. + * @param key Refers to key used for encrypting the secure container + * @param resFileName Name of the target resource file(relative to newly + * created secure container) + * @return Returns the new cache path where the resource has been copied into + * + */ + public String copyResourceToContainer(final Uri packageURI, + final String containerId, + final String key, final String resFileName) { + if (packageURI == null || containerId == null) { + return null; + } + return copyResourceInner(packageURI, containerId, key, resFileName); + } + + /* + * Copy specified resource to output stream + * @param packageURI the uri of resource to be copied. Should be a + * file uri + * @param outStream Remote file descriptor to be used for copying + * @return Returns true if copy succeded or false otherwise. + */ + public boolean copyResource(final Uri packageURI, + ParcelFileDescriptor outStream) { + if (packageURI == null || outStream == null) { + return false; + } + ParcelFileDescriptor.AutoCloseOutputStream + autoOut = new ParcelFileDescriptor.AutoCloseOutputStream(outStream); + return copyFile(packageURI, autoOut); + } + }; + + public IBinder onBind(Intent intent) { + return mBinder; + } + + private IMountService getMountService() { + return IMountService.Stub.asInterface(ServiceManager.getService("mount")); + } + + private String copyResourceInner(Uri packageURI, String newCacheId, String key, String resFileName) { + // Create new container at newCachePath + String codePath = packageURI.getPath(); + String newCachePath = null; + final int CREATE_FAILED = 1; + final int COPY_FAILED = 2; + final int FINALIZE_FAILED = 3; + final int PASS = 4; + int errCode = CREATE_FAILED; + // Create new container + if ((newCachePath = createSdDir(packageURI, newCacheId, key)) != null) { + File resFile = new File(newCachePath, resFileName); + errCode = COPY_FAILED; + if (localLOGV) Log.i(TAG, "Trying to copy " + codePath + " to " + resFile); + // Copy file from codePath + if (FileUtils.copyFile(new File(codePath), resFile)) { + errCode = FINALIZE_FAILED; + if (finalizeSdDir(newCacheId)) { + errCode = PASS; + } + } + } + // Print error based on errCode + String errMsg = ""; + switch (errCode) { + case CREATE_FAILED: + errMsg = "CREATE_FAILED"; + break; + case COPY_FAILED: + errMsg = "COPY_FAILED"; + destroySdDir(newCacheId); + break; + case FINALIZE_FAILED: + errMsg = "FINALIZE_FAILED"; + destroySdDir(newCacheId); + break; + default: + errMsg = "PASS"; + unMountSdDir(newCacheId); + break; + } + Log.i(TAG, "Status: " + errMsg); + if (errCode != PASS) { + return null; + } + return newCachePath; + } + + private String createSdDir(final Uri packageURI, + String containerId, String sdEncKey) { + File tmpPackageFile = new File(packageURI.getPath()); + // Create mount point via MountService + IMountService mountService = getMountService(); + long len = tmpPackageFile.length(); + int mbLen = (int) (len/(1024*1024)); + if ((len - (mbLen * 1024 * 1024)) > 0) { + mbLen++; + } + if (localLOGV) Log.i(TAG, "mbLen="+mbLen); + String cachePath = null; + int ownerUid = Process.myUid(); + try { + cachePath = mountService.createSecureContainer(containerId, + mbLen, + "vfat", sdEncKey, ownerUid); + if (localLOGV) Log.i(TAG, "Trying to create secure container for " + + containerId + ", cachePath =" + cachePath); + return cachePath; + } catch(IllegalStateException e) { + Log.e(TAG, "Failed to create storage on sdcard with exception: " + e); + } catch(RemoteException e) { + Log.e(TAG, "MounteService not running?"); + return null; + } + // TODO just fail here and let the user delete later on. + try { + mountService.destroySecureContainer(containerId); + if (localLOGV) Log.i(TAG, "Destroying cache for " + containerId + + ", cachePath =" + cachePath); + } catch(IllegalStateException e) { + Log.e(TAG, "Failed to destroy existing cache: " + e); + return null; + } catch(RemoteException e) { + Log.e(TAG, "MounteService not running?"); + return null; + } + try { + cachePath = mountService.createSecureContainer(containerId, + mbLen, + "vfat", sdEncKey, ownerUid); + if (localLOGV) Log.i(TAG, "Trying to install again " + containerId + + ", cachePath =" + cachePath); + return cachePath; + } catch(IllegalStateException e) { + Log.e(TAG, "Failed to create storage on sdcard with exception: " + e); + } catch(RemoteException e) { + Log.e(TAG, "MounteService not running?"); + } + return null; + } + + private boolean destroySdDir(String containerId) { + try { + // We need to destroy right away + getMountService().destroySecureContainer(containerId); + return true; + } catch (IllegalStateException e) { + Log.i(TAG, "Failed to destroy container : " + containerId); + } catch(RemoteException e) { + Log.e(TAG, "MounteService not running?"); + } + return false; + } + + private boolean finalizeSdDir(String containerId){ + try { + getMountService().finalizeSecureContainer(containerId); + return true; + } catch (IllegalStateException e) { + Log.i(TAG, "Failed to finalize container for pkg : " + containerId); + } catch(RemoteException e) { + Log.e(TAG, "MounteService not running?"); + } + return false; + } + + private boolean unMountSdDir(String containerId) { + try { + getMountService().unmountSecureContainer(containerId); + return true; + } catch (IllegalStateException e) { + Log.e(TAG, "Failed to unmount id: " + containerId + " with exception " + e); + } catch(RemoteException e) { + Log.e(TAG, "MounteService not running?"); + } + return false; + } + + private String mountSdDir(String containerId, String key) { + try { + return getMountService().mountSecureContainer(containerId, key, Process.myUid()); + } catch (IllegalStateException e) { + Log.e(TAG, "Failed to mount id: " + + containerId + " with exception " + e); + } catch(RemoteException e) { + Log.e(TAG, "MounteService not running?"); + } + return null; + } + + public static boolean copyToFile(InputStream inputStream, FileOutputStream out) { + try { + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) >= 0) { + out.write(buffer, 0, bytesRead); + } + return true; + } catch (IOException e) { + Log.i(TAG, "Exception : " + e + " when copying file"); + return false; + } + } + + public static boolean copyToFile(File srcFile, FileOutputStream out) { + InputStream inputStream = null; + try { + inputStream = new FileInputStream(srcFile); + return copyToFile(inputStream, out); + } catch (IOException e) { + return false; + } finally { + try { if (inputStream != null) inputStream.close(); } catch (IOException e) {} + } + } + + private boolean copyFile(Uri pPackageURI, FileOutputStream outStream) { + if (pPackageURI.getScheme().equals("file")) { + final File srcPackageFile = new File(pPackageURI.getPath()); + // We copy the source package file to a temp file and then rename it to the + // destination file in order to eliminate a window where the package directory + // scanner notices the new package file but it's not completely copied yet. + if (!copyToFile(srcPackageFile, outStream)) { + Log.e(TAG, "Couldn't copy file: " + srcPackageFile); + return false; + } + } else if (pPackageURI.getScheme().equals("content")) { + ParcelFileDescriptor fd = null; + try { + fd = getContentResolver().openFileDescriptor(pPackageURI, "r"); + } catch (FileNotFoundException e) { + Log.e(TAG, "Couldn't open file descriptor from download service. Failed with exception " + e); + return false; + } + if (fd == null) { + Log.e(TAG, "Couldn't open file descriptor from download service (null)."); + return false; + } else { + if (localLOGV) { + Log.v(TAG, "Opened file descriptor from download service."); + } + ParcelFileDescriptor.AutoCloseInputStream + dlStream = new ParcelFileDescriptor.AutoCloseInputStream(fd); + // We copy the source package file to a temp file and then rename it to the + // destination file in order to eliminate a window where the package directory + // scanner notices the new package file but it's not completely copied yet. + if (!copyToFile(dlStream, outStream)) { + Log.e(TAG, "Couldn't copy " + pPackageURI + " to temp file."); + return false; + } + } + } else { + Log.e(TAG, "Package URI is not 'file:' or 'content:' - " + pPackageURI); + return false; + } + return true; + } +} |