DefaultContainerService.java revision c028be4f3b8c7476b46859f66c3f33d528adf181
1package com.android.defcontainer; 2 3import com.android.internal.app.IMediaContainerService; 4 5import android.content.Intent; 6import android.net.Uri; 7import android.os.Debug; 8import android.os.IBinder; 9import android.os.IMountService; 10import android.os.ParcelFileDescriptor; 11import android.os.Process; 12import android.os.RemoteException; 13import android.os.ServiceManager; 14import android.app.Service; 15import android.util.Log; 16 17import java.io.File; 18import java.io.FileInputStream; 19import java.io.FileNotFoundException; 20import java.io.FileOutputStream; 21import java.io.IOException; 22import java.io.InputStream; 23import java.io.OutputStream; 24 25import android.os.FileUtils; 26 27 28/* 29 * This service copies a downloaded apk to a file passed in as 30 * a ParcelFileDescriptor or to a newly created container specified 31 * by parameters. The DownloadManager gives access to this process 32 * based on its uid. This process also needs the ACCESS_DOWNLOAD_MANAGER 33 * permission to access apks downloaded via the download manager. 34 */ 35public class DefaultContainerService extends Service { 36 private static final String TAG = "DefContainer"; 37 private static final boolean localLOGV = false; 38 39 private IMediaContainerService.Stub mBinder = new IMediaContainerService.Stub() { 40 /* 41 * Creates a new container and copies resource there. 42 * @param paackageURI the uri of resource to be copied. Can be either 43 * a content uri or a file uri 44 * @param containerId the id of the secure container that should 45 * be used for creating a secure container into which the resource 46 * will be copied. 47 * @param key Refers to key used for encrypting the secure container 48 * @param resFileName Name of the target resource file(relative to newly 49 * created secure container) 50 * @return Returns the new cache path where the resource has been copied into 51 * 52 */ 53 public String copyResourceToContainer(final Uri packageURI, 54 final String containerId, 55 final String key, final String resFileName) { 56 if (packageURI == null || containerId == null) { 57 return null; 58 } 59 return copyResourceInner(packageURI, containerId, key, resFileName); 60 } 61 62 /* 63 * Copy specified resource to output stream 64 * @param packageURI the uri of resource to be copied. Should be a 65 * file uri 66 * @param outStream Remote file descriptor to be used for copying 67 * @return Returns true if copy succeded or false otherwise. 68 */ 69 public boolean copyResource(final Uri packageURI, 70 ParcelFileDescriptor outStream) { 71 if (packageURI == null || outStream == null) { 72 return false; 73 } 74 ParcelFileDescriptor.AutoCloseOutputStream 75 autoOut = new ParcelFileDescriptor.AutoCloseOutputStream(outStream); 76 return copyFile(packageURI, autoOut); 77 } 78 }; 79 80 public IBinder onBind(Intent intent) { 81 return mBinder; 82 } 83 84 private IMountService getMountService() { 85 return IMountService.Stub.asInterface(ServiceManager.getService("mount")); 86 } 87 88 private String copyResourceInner(Uri packageURI, String newCacheId, String key, String resFileName) { 89 // Create new container at newCachePath 90 String codePath = packageURI.getPath(); 91 String newCachePath = null; 92 final int CREATE_FAILED = 1; 93 final int COPY_FAILED = 2; 94 final int FINALIZE_FAILED = 3; 95 final int PASS = 4; 96 int errCode = CREATE_FAILED; 97 // Create new container 98 if ((newCachePath = createSdDir(packageURI, newCacheId, key)) != null) { 99 File resFile = new File(newCachePath, resFileName); 100 errCode = COPY_FAILED; 101 if (localLOGV) Log.i(TAG, "Trying to copy " + codePath + " to " + resFile); 102 // Copy file from codePath 103 if (FileUtils.copyFile(new File(codePath), resFile)) { 104 errCode = FINALIZE_FAILED; 105 if (finalizeSdDir(newCacheId)) { 106 errCode = PASS; 107 } 108 } 109 } 110 // Print error based on errCode 111 String errMsg = ""; 112 switch (errCode) { 113 case CREATE_FAILED: 114 errMsg = "CREATE_FAILED"; 115 break; 116 case COPY_FAILED: 117 errMsg = "COPY_FAILED"; 118 destroySdDir(newCacheId); 119 break; 120 case FINALIZE_FAILED: 121 errMsg = "FINALIZE_FAILED"; 122 destroySdDir(newCacheId); 123 break; 124 default: 125 errMsg = "PASS"; 126 unMountSdDir(newCacheId); 127 break; 128 } 129 Log.i(TAG, "Status: " + errMsg); 130 if (errCode != PASS) { 131 return null; 132 } 133 return newCachePath; 134 } 135 136 private String createSdDir(final Uri packageURI, 137 String containerId, String sdEncKey) { 138 File tmpPackageFile = new File(packageURI.getPath()); 139 // Create mount point via MountService 140 IMountService mountService = getMountService(); 141 long len = tmpPackageFile.length(); 142 int mbLen = (int) (len/(1024*1024)); 143 if ((len - (mbLen * 1024 * 1024)) > 0) { 144 mbLen++; 145 } 146 if (localLOGV) Log.i(TAG, "mbLen="+mbLen); 147 String cachePath = null; 148 int ownerUid = Process.myUid(); 149 try { 150 cachePath = mountService.createSecureContainer(containerId, 151 mbLen, 152 "vfat", sdEncKey, ownerUid); 153 if (localLOGV) Log.i(TAG, "Trying to create secure container for " 154 + containerId + ", cachePath =" + cachePath); 155 return cachePath; 156 } catch(IllegalStateException e) { 157 Log.e(TAG, "Failed to create storage on sdcard with exception: " + e); 158 } catch(RemoteException e) { 159 Log.e(TAG, "MounteService not running?"); 160 return null; 161 } 162 // TODO just fail here and let the user delete later on. 163 try { 164 mountService.destroySecureContainer(containerId); 165 if (localLOGV) Log.i(TAG, "Destroying cache for " + containerId 166 + ", cachePath =" + cachePath); 167 } catch(IllegalStateException e) { 168 Log.e(TAG, "Failed to destroy existing cache: " + e); 169 return null; 170 } catch(RemoteException e) { 171 Log.e(TAG, "MounteService not running?"); 172 return null; 173 } 174 try { 175 cachePath = mountService.createSecureContainer(containerId, 176 mbLen, 177 "vfat", sdEncKey, ownerUid); 178 if (localLOGV) Log.i(TAG, "Trying to install again " + containerId 179 + ", cachePath =" + cachePath); 180 return cachePath; 181 } catch(IllegalStateException e) { 182 Log.e(TAG, "Failed to create storage on sdcard with exception: " + e); 183 } catch(RemoteException e) { 184 Log.e(TAG, "MounteService not running?"); 185 } 186 return null; 187 } 188 189 private boolean destroySdDir(String containerId) { 190 try { 191 // We need to destroy right away 192 getMountService().destroySecureContainer(containerId); 193 return true; 194 } catch (IllegalStateException e) { 195 Log.i(TAG, "Failed to destroy container : " + containerId); 196 } catch(RemoteException e) { 197 Log.e(TAG, "MounteService not running?"); 198 } 199 return false; 200 } 201 202 private boolean finalizeSdDir(String containerId){ 203 try { 204 getMountService().finalizeSecureContainer(containerId); 205 return true; 206 } catch (IllegalStateException e) { 207 Log.i(TAG, "Failed to finalize container for pkg : " + containerId); 208 } catch(RemoteException e) { 209 Log.e(TAG, "MounteService not running?"); 210 } 211 return false; 212 } 213 214 private boolean unMountSdDir(String containerId) { 215 try { 216 getMountService().unmountSecureContainer(containerId); 217 return true; 218 } catch (IllegalStateException e) { 219 Log.e(TAG, "Failed to unmount id: " + containerId + " with exception " + e); 220 } catch(RemoteException e) { 221 Log.e(TAG, "MounteService not running?"); 222 } 223 return false; 224 } 225 226 private String mountSdDir(String containerId, String key) { 227 try { 228 return getMountService().mountSecureContainer(containerId, key, Process.myUid()); 229 } catch (IllegalStateException e) { 230 Log.e(TAG, "Failed to mount id: " + 231 containerId + " with exception " + e); 232 } catch(RemoteException e) { 233 Log.e(TAG, "MounteService not running?"); 234 } 235 return null; 236 } 237 238 public static boolean copyToFile(InputStream inputStream, FileOutputStream out) { 239 try { 240 byte[] buffer = new byte[4096]; 241 int bytesRead; 242 while ((bytesRead = inputStream.read(buffer)) >= 0) { 243 out.write(buffer, 0, bytesRead); 244 } 245 return true; 246 } catch (IOException e) { 247 Log.i(TAG, "Exception : " + e + " when copying file"); 248 return false; 249 } 250 } 251 252 public static boolean copyToFile(File srcFile, FileOutputStream out) { 253 InputStream inputStream = null; 254 try { 255 inputStream = new FileInputStream(srcFile); 256 return copyToFile(inputStream, out); 257 } catch (IOException e) { 258 return false; 259 } finally { 260 try { if (inputStream != null) inputStream.close(); } catch (IOException e) {} 261 } 262 } 263 264 private boolean copyFile(Uri pPackageURI, FileOutputStream outStream) { 265 if (pPackageURI.getScheme().equals("file")) { 266 final File srcPackageFile = new File(pPackageURI.getPath()); 267 // We copy the source package file to a temp file and then rename it to the 268 // destination file in order to eliminate a window where the package directory 269 // scanner notices the new package file but it's not completely copied yet. 270 if (!copyToFile(srcPackageFile, outStream)) { 271 Log.e(TAG, "Couldn't copy file: " + srcPackageFile); 272 return false; 273 } 274 } else if (pPackageURI.getScheme().equals("content")) { 275 ParcelFileDescriptor fd = null; 276 try { 277 fd = getContentResolver().openFileDescriptor(pPackageURI, "r"); 278 } catch (FileNotFoundException e) { 279 Log.e(TAG, "Couldn't open file descriptor from download service. Failed with exception " + e); 280 return false; 281 } 282 if (fd == null) { 283 Log.e(TAG, "Couldn't open file descriptor from download service (null)."); 284 return false; 285 } else { 286 if (localLOGV) { 287 Log.v(TAG, "Opened file descriptor from download service."); 288 } 289 ParcelFileDescriptor.AutoCloseInputStream 290 dlStream = new ParcelFileDescriptor.AutoCloseInputStream(fd); 291 // We copy the source package file to a temp file and then rename it to the 292 // destination file in order to eliminate a window where the package directory 293 // scanner notices the new package file but it's not completely copied yet. 294 if (!copyToFile(dlStream, outStream)) { 295 Log.e(TAG, "Couldn't copy " + pPackageURI + " to temp file."); 296 return false; 297 } 298 } 299 } else { 300 Log.e(TAG, "Package URI is not 'file:' or 'content:' - " + pPackageURI); 301 return false; 302 } 303 return true; 304 } 305} 306