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