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
17package com.android.camera.session;
18
19import android.content.Context;
20
21import com.android.camera.debug.Log;
22import com.android.camera.util.FileUtil;
23
24import java.io.File;
25import java.io.FileFilter;
26import java.io.IOException;
27
28/**
29 * Default implementation of {@link SessionStorageManager}.
30 */
31public 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     */
53    public static SessionStorageManager create(Context context) {
54        return new SessionStorageManagerImpl(context.getExternalCacheDir(),
55                context.getExternalFilesDir(null));
56    }
57
58    SessionStorageManagerImpl(File baseDirectory, File deprecatedBaseDirectory) {
59        mBaseDirectory = baseDirectory;
60        mDeprecatedBaseDirectory = deprecatedBaseDirectory;
61    }
62
63    @Override
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
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     */
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