1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.camera.session;
18 
19 import android.content.Context;
20 
21 import com.android.camera.debug.Log;
22 import com.android.camera.util.FileUtil;
23 
24 import java.io.File;
25 import java.io.FileFilter;
26 import java.io.IOException;
27 
28 /**
29  * Default implementation of {@link SessionStorageManager}.
30  */
31 public class SessionStorageManagerImpl implements SessionStorageManager {
32     private static final Log.Tag TAG = new Log.Tag("SesnStorageMgrImpl");
33 
34     /** Delete temporary session directory remnants after ONE day. */
35     private static final int MAX_SESSION_AGE_MILLIS = 24 * 60 * 60 * 1000;
36 
37     /** The base directory for all temporary data. */
38     private final File mBaseDirectory;
39 
40     /**
41      * This is where we used to store temp session data, but it was the wrong
42      * place. But we want to make sure it's cleaned up for users that upgraded.
43      */
44     private final File mDeprecatedBaseDirectory;
45 
46     /**
47      * Creates a new {@link SessionStorageManager} instance.
48      *
49      * @param context A valid Android context to be used for determining the
50      *            base directory.
51      * @return A session storage manager.
52      */
create(Context context)53     public static SessionStorageManager create(Context context) {
54         return new SessionStorageManagerImpl(context.getExternalCacheDir(),
55                 context.getExternalFilesDir(null));
56     }
57 
SessionStorageManagerImpl(File baseDirectory, File deprecatedBaseDirectory)58     SessionStorageManagerImpl(File baseDirectory, File deprecatedBaseDirectory) {
59         mBaseDirectory = baseDirectory;
60         mDeprecatedBaseDirectory = deprecatedBaseDirectory;
61     }
62 
63     @Override
getSessionDirectory(String subDirectory)64     public File getSessionDirectory(String subDirectory) throws IOException {
65         File sessionDirectory = new File(mBaseDirectory, subDirectory);
66         if (!sessionDirectory.exists() && !sessionDirectory.mkdirs()) {
67             throw new IOException("Could not create session directory: " + sessionDirectory);
68         }
69 
70         if (!sessionDirectory.isDirectory()) {
71             throw new IOException("Session directory is not a directory: " + sessionDirectory);
72         }
73 
74         // Make sure there are no expired sessions in this directory.
75         cleanUpExpiredSessions(sessionDirectory);
76 
77         // For upgraded users, make sure we also clean up the old location.
78         File deprecatedSessionDirectory = new File(mDeprecatedBaseDirectory, subDirectory);
79         cleanUpExpiredSessions(deprecatedSessionDirectory);
80 
81         return sessionDirectory;
82     }
83 
84     @Override
createTemporaryOutputPath(String subDirectory, String title)85     public File createTemporaryOutputPath(String subDirectory, String title) throws IOException {
86         File tempDirectory = null;
87         try {
88             tempDirectory = new File(
89                     getSessionDirectory(subDirectory), title);
90         } catch (IOException e) {
91             Log.e(TAG, "Could not get temp session directory", e);
92             throw new IOException("Could not get temp session directory", e);
93         }
94         if (!tempDirectory.mkdirs()) {
95             throw new IOException("Could not create output data directory.");
96         }
97         File tempFile = new File(tempDirectory, title + ".jpg");
98         try {
99             if (!tempFile.exists()) {
100                 if (!tempFile.createNewFile()) {
101                     throw new IOException("Could not create output data file.");
102                 }
103             }
104         } catch (IOException e) {
105             Log.e(TAG, "Could not create temp session file", e);
106             throw new IOException("Could not create temp session file", e);
107         }
108 
109         if (!tempFile.canWrite()) {
110             throw new IOException("Temporary output file is not writeable.");
111         }
112         return tempFile;
113     }
114 
115     /**
116      * Goes through all temporary sessions and deletes the ones that are older
117      * than a certain age.
118      */
cleanUpExpiredSessions(File baseDirectory)119     private void cleanUpExpiredSessions(File baseDirectory) {
120         File[] sessionDirs = baseDirectory.listFiles(new FileFilter() {
121             @Override
122             public boolean accept(File file) {
123                 return file.isDirectory();
124             }
125         });
126         if (sessionDirs == null) {
127             return;
128         }
129 
130         final long nowInMillis = System.currentTimeMillis();
131         for (File sessionDir : sessionDirs) {
132             Log.v(TAG, "Check for potential clean-up: " + sessionDir.getAbsolutePath());
133             if (sessionDir.lastModified() < (nowInMillis - MAX_SESSION_AGE_MILLIS)) {
134                 if (!FileUtil.deleteDirectoryRecursively(sessionDir)) {
135                     Log.w(TAG, "Could not clean up " + sessionDir.getAbsolutePath());
136                 }
137             }
138         }
139     }
140 }
141