DefaultContainerService.java revision 63d15ee6ae4ff5079c7551ca4131c6d7f23ad91c
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 File resFile = new File(newCachePath, resFileName); 101 errCode = COPY_FAILED; 102 if (localLOGV) Log.i(TAG, "Trying to copy " + codePath + " to " + resFile); 103 // Copy file from codePath 104 if (FileUtils.copyFile(new File(codePath), resFile)) { 105 errCode = FINALIZE_FAILED; 106 if (finalizeSdDir(newCacheId)) { 107 errCode = PASS; 108 } 109 } 110 } 111 // Print error based on errCode 112 String errMsg = ""; 113 switch (errCode) { 114 case CREATE_FAILED: 115 errMsg = "CREATE_FAILED"; 116 break; 117 case COPY_FAILED: 118 errMsg = "COPY_FAILED"; 119 destroySdDir(newCacheId); 120 break; 121 case FINALIZE_FAILED: 122 errMsg = "FINALIZE_FAILED"; 123 destroySdDir(newCacheId); 124 break; 125 default: 126 errMsg = "PASS"; 127 unMountSdDir(newCacheId); 128 break; 129 } 130 Log.i(TAG, "Status: " + errMsg); 131 if (errCode != PASS) { 132 return null; 133 } 134 return newCachePath; 135 } 136 137 private String createSdDir(final Uri packageURI, 138 String containerId, String sdEncKey) { 139 File tmpPackageFile = new File(packageURI.getPath()); 140 // Create mount point via MountService 141 IMountService mountService = getMountService(); 142 long len = tmpPackageFile.length(); 143 int mbLen = (int) (len/(1024*1024)); 144 if ((len - (mbLen * 1024 * 1024)) > 0) { 145 mbLen++; 146 } 147 if (localLOGV) Log.i(TAG, "mbLen="+mbLen); 148 String cachePath = null; 149 int ownerUid = Process.myUid(); 150 try { 151 int rc = mountService.createSecureContainer( 152 containerId, mbLen, "vfat", sdEncKey, ownerUid); 153 154 if (rc != MountServiceResultCode.OperationSucceeded) { 155 Log.e(TAG, String.format("Container creation failed (%d)", rc)); 156 157 // XXX: This destroy should not be necessary 158 rc = mountService.destroySecureContainer(containerId); 159 if (rc != MountServiceResultCode.OperationSucceeded) { 160 Log.e(TAG, String.format("Container creation-cleanup failed (%d)", rc)); 161 return null; 162 } 163 164 // XXX: Does this ever actually succeed? 165 rc = mountService.createSecureContainer( 166 containerId, mbLen, "vfat", sdEncKey, ownerUid); 167 if (rc != MountServiceResultCode.OperationSucceeded) { 168 Log.e(TAG, String.format("Container creation retry failed (%d)", rc)); 169 } 170 } 171 172 cachePath = mountService.getSecureContainerPath(containerId); 173 if (localLOGV) Log.i(TAG, "Trying to create secure container for " 174 + containerId + ", cachePath =" + cachePath); 175 return cachePath; 176 } catch(RemoteException e) { 177 Log.e(TAG, "MountService not running?"); 178 return null; 179 } 180 } 181 182 private boolean destroySdDir(String containerId) { 183 try { 184 // We need to destroy right away 185 getMountService().destroySecureContainer(containerId); 186 return true; 187 } catch (IllegalStateException e) { 188 Log.i(TAG, "Failed to destroy container : " + containerId); 189 } catch(RemoteException e) { 190 Log.e(TAG, "MountService not running?"); 191 } 192 return false; 193 } 194 195 private boolean finalizeSdDir(String containerId){ 196 try { 197 getMountService().finalizeSecureContainer(containerId); 198 return true; 199 } catch (IllegalStateException e) { 200 Log.i(TAG, "Failed to finalize container for pkg : " + containerId); 201 } catch(RemoteException e) { 202 Log.e(TAG, "MountService not running?"); 203 } 204 return false; 205 } 206 207 private boolean unMountSdDir(String containerId) { 208 try { 209 getMountService().unmountSecureContainer(containerId); 210 return true; 211 } catch (IllegalStateException e) { 212 Log.e(TAG, "Failed to unmount id: " + containerId + " with exception " + e); 213 } catch(RemoteException e) { 214 Log.e(TAG, "MountService not running?"); 215 } 216 return false; 217 } 218 219 private String mountSdDir(String containerId, String key) { 220 try { 221 int rc = getMountService().mountSecureContainer(containerId, key, Process.myUid()); 222 if (rc == MountServiceResultCode.OperationSucceeded) { 223 return getMountService().getSecureContainerPath(containerId); 224 } else { 225 Log.e(TAG, String.format("Failed to mount id %s with rc %d ", containerId, rc)); 226 } 227 } catch(RemoteException e) { 228 Log.e(TAG, "MountService not running?"); 229 } 230 return null; 231 } 232 233 public static boolean copyToFile(InputStream inputStream, FileOutputStream out) { 234 try { 235 byte[] buffer = new byte[4096]; 236 int bytesRead; 237 while ((bytesRead = inputStream.read(buffer)) >= 0) { 238 out.write(buffer, 0, bytesRead); 239 } 240 return true; 241 } catch (IOException e) { 242 Log.i(TAG, "Exception : " + e + " when copying file"); 243 return false; 244 } 245 } 246 247 public static boolean copyToFile(File srcFile, FileOutputStream out) { 248 InputStream inputStream = null; 249 try { 250 inputStream = new FileInputStream(srcFile); 251 return copyToFile(inputStream, out); 252 } catch (IOException e) { 253 return false; 254 } finally { 255 try { if (inputStream != null) inputStream.close(); } catch (IOException e) {} 256 } 257 } 258 259 private boolean copyFile(Uri pPackageURI, FileOutputStream outStream) { 260 if (pPackageURI.getScheme().equals("file")) { 261 final File srcPackageFile = new File(pPackageURI.getPath()); 262 // We copy the source package file to a temp file and then rename it to the 263 // destination file in order to eliminate a window where the package directory 264 // scanner notices the new package file but it's not completely copied yet. 265 if (!copyToFile(srcPackageFile, outStream)) { 266 Log.e(TAG, "Couldn't copy file: " + srcPackageFile); 267 return false; 268 } 269 } else if (pPackageURI.getScheme().equals("content")) { 270 ParcelFileDescriptor fd = null; 271 try { 272 fd = getContentResolver().openFileDescriptor(pPackageURI, "r"); 273 } catch (FileNotFoundException e) { 274 Log.e(TAG, "Couldn't open file descriptor from download service. Failed with exception " + e); 275 return false; 276 } 277 if (fd == null) { 278 Log.e(TAG, "Couldn't open file descriptor from download service (null)."); 279 return false; 280 } else { 281 if (localLOGV) { 282 Log.v(TAG, "Opened file descriptor from download service."); 283 } 284 ParcelFileDescriptor.AutoCloseInputStream 285 dlStream = new ParcelFileDescriptor.AutoCloseInputStream(fd); 286 // We copy the source package file to a temp file and then rename it to the 287 // destination file in order to eliminate a window where the package directory 288 // scanner notices the new package file but it's not completely copied yet. 289 if (!copyToFile(dlStream, outStream)) { 290 Log.e(TAG, "Couldn't copy " + pPackageURI + " to temp file."); 291 return false; 292 } 293 } 294 } else { 295 Log.e(TAG, "Package URI is not 'file:' or 'content:' - " + pPackageURI); 296 return false; 297 } 298 return true; 299 } 300} 301