1525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov/*
2525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * Copyright (C) 2014 The Android Open Source Project
3525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov *
4525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * Licensed under the Apache License, Version 2.0 (the "License");
5525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * you may not use this file except in compliance with the License.
6525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * You may obtain a copy of the License at
7525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov *
8525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov *      http://www.apache.org/licenses/LICENSE-2.0
9525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov *
10525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * Unless required by applicable law or agreed to in writing, software
11525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * distributed under the License is distributed on an "AS IS" BASIS,
12525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * See the License for the specific language governing permissions and
14525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * limitations under the License.
15525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov */
16525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
17525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovpackage com.android.printspooler.model;
18525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
19525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport android.util.Log;
20525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
21525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport java.io.File;
22525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport java.io.IOException;
23525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
24525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov/**
25525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * This class provides a shared file to several threads. Only one thread
26525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * at a time can use the file. To acquire the file a thread has to
27525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * request it in a blocking call to {@link #acquireFile(OnReleaseRequestCallback)}.
28525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * The provided callback is optional and is used to notify the owning thread
29525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * when another one wants to acquire the file. In case a release is requested
30525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * the thread owning the file must release it as soon as possible. If no
31525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * callback is provided a thread that acquires the file must release it
32525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * as soon as possible, i.e. even if callback was provided the thread cannot
33525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * have the file for less time.
34525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov */
35525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovpublic final class MutexFileProvider {
36525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private static final String LOG_TAG = "MutexFileProvider";
37525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
38525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private static final boolean DEBUG = true;
39525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
40525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private final Object mLock = new Object();
41525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
42525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private final File mFile;
43525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
44525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private Thread mOwnerThread;
45525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
46525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private OnReleaseRequestCallback mOnReleaseRequestCallback;
47525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
48525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public interface OnReleaseRequestCallback {
49525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        public void onReleaseRequested(File file);
50525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
51525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
52525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public MutexFileProvider(File file) throws IOException {
53525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mFile = file;
54525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (file.exists()) {
55525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            file.delete();
56525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
57525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        file.createNewFile();
58525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
59525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
60525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public File acquireFile(OnReleaseRequestCallback callback) {
61525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        synchronized (mLock) {
62525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            // If this thread has the file, nothing to do.
63525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            if (mOwnerThread == Thread.currentThread()) {
64525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                return mFile;
65525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
66525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
67525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            // Another thread wants file ask for a release.
68525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            if (mOwnerThread != null && mOnReleaseRequestCallback != null) {
69525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                mOnReleaseRequestCallback.onReleaseRequested(mFile);
70525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
71525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
72525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            // Wait until the file is released.
73525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            while (mOwnerThread != null) {
74525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                try {
75525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    mLock.wait();
76525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                } catch (InterruptedException ie) {
77525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    /* ignore */
78525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                }
79525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
80525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
81525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            // Update the owner and the callback.
82525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            mOwnerThread = Thread.currentThread();
83525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            mOnReleaseRequestCallback = callback;
84525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
85525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            if (DEBUG) {
86525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                Log.i(LOG_TAG, "Acquired file: " + mFile + " by thread: " + mOwnerThread);
87525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
88525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
89525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            return mFile;
90525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
91525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
92525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
93525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public void releaseFile() {
94525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        synchronized (mLock) {
95525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            if (mOwnerThread != Thread.currentThread()) {
96cf3a86b55981313e1fdfafbef89ab5ba7276a862Svetoslav                return;
97525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
98525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
99525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            if (DEBUG) {
100525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                Log.i(LOG_TAG, "Released file: " + mFile + " from thread: " + mOwnerThread);
101525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
102525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
103525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            // Update the owner and the callback.
104525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            mOwnerThread = null;
105525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            mOnReleaseRequestCallback = null;
106525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
107525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            mLock.notifyAll();
108525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
109525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
110525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov}
111