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