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