1/*
2 * Copyright (C) 2015 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.mtp;
18
19import android.os.ParcelFileDescriptor;
20import android.util.Log;
21
22import java.io.IOException;
23import java.util.concurrent.ExecutorService;
24import java.util.concurrent.Executors;
25import java.util.concurrent.TimeUnit;
26
27class PipeManager {
28    /**
29     * Milliseconds we wait for background thread when pausing.
30     */
31    private final static long AWAIT_TERMINATION_TIMEOUT = 2000;
32
33    final ExecutorService mExecutor;
34    final MtpDatabase mDatabase;
35
36    PipeManager(MtpDatabase database) {
37        this(database, Executors.newSingleThreadExecutor());
38    }
39
40    PipeManager(MtpDatabase database, ExecutorService executor) {
41        this.mDatabase = database;
42        this.mExecutor = executor;
43    }
44
45    ParcelFileDescriptor readDocument(MtpManager model, Identifier identifier) throws IOException {
46        final Task task = new ImportFileTask(model, identifier);
47        mExecutor.execute(task);
48        return task.getReadingFileDescriptor();
49    }
50
51    ParcelFileDescriptor readThumbnail(MtpManager model, Identifier identifier) throws IOException {
52        final Task task = new GetThumbnailTask(model, identifier);
53        mExecutor.execute(task);
54        return task.getReadingFileDescriptor();
55    }
56
57    private static abstract class Task implements Runnable {
58        protected final MtpManager mManager;
59        protected final Identifier mIdentifier;
60        protected final ParcelFileDescriptor[] mDescriptors;
61
62        Task(MtpManager manager, Identifier identifier) throws IOException {
63            mManager = manager;
64            mIdentifier = identifier;
65            mDescriptors = ParcelFileDescriptor.createReliablePipe();
66        }
67
68        ParcelFileDescriptor getReadingFileDescriptor() {
69            return mDescriptors[0];
70        }
71    }
72
73    private static class ImportFileTask extends Task {
74        ImportFileTask(MtpManager model, Identifier identifier) throws IOException {
75            super(model, identifier);
76        }
77
78        @Override
79        public void run() {
80            try {
81                mManager.importFile(
82                        mIdentifier.mDeviceId, mIdentifier.mObjectHandle, mDescriptors[1]);
83                mDescriptors[1].close();
84            } catch (IOException error) {
85                try {
86                    mDescriptors[1].closeWithError("Failed to stream a file.");
87                } catch (IOException closeError) {
88                    Log.w(MtpDocumentsProvider.TAG, closeError.getMessage());
89                }
90            }
91        }
92    }
93
94    private static class GetThumbnailTask extends Task {
95        GetThumbnailTask(MtpManager model, Identifier identifier) throws IOException {
96            super(model, identifier);
97        }
98
99        @Override
100        public void run() {
101            try {
102                try (final ParcelFileDescriptor.AutoCloseOutputStream stream =
103                        new ParcelFileDescriptor.AutoCloseOutputStream(mDescriptors[1])) {
104                    try {
105                        stream.write(mManager.getThumbnail(
106                                mIdentifier.mDeviceId, mIdentifier.mObjectHandle));
107                    } catch (IOException error) {
108                        mDescriptors[1].closeWithError("Failed to stream a thumbnail.");
109                    }
110                }
111            } catch (IOException closeError) {
112                Log.w(MtpDocumentsProvider.TAG, closeError.getMessage());
113            }
114        }
115    }
116
117    boolean close() throws InterruptedException {
118        mExecutor.shutdownNow();
119        return mExecutor.awaitTermination(AWAIT_TERMINATION_TIMEOUT, TimeUnit.MILLISECONDS);
120    }
121}
122