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 
17 package com.android.mtp;
18 
19 import android.os.ParcelFileDescriptor;
20 import android.util.Log;
21 
22 import java.io.IOException;
23 import java.util.concurrent.ExecutorService;
24 import java.util.concurrent.Executors;
25 import java.util.concurrent.TimeUnit;
26 
27 class 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 
PipeManager(MtpDatabase database)36     PipeManager(MtpDatabase database) {
37         this(database, Executors.newSingleThreadExecutor());
38     }
39 
PipeManager(MtpDatabase database, ExecutorService executor)40     PipeManager(MtpDatabase database, ExecutorService executor) {
41         this.mDatabase = database;
42         this.mExecutor = executor;
43     }
44 
readDocument(MtpManager model, Identifier identifier)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 
readThumbnail(MtpManager model, Identifier identifier)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 
Task(MtpManager manager, Identifier identifier)62         Task(MtpManager manager, Identifier identifier) throws IOException {
63             mManager = manager;
64             mIdentifier = identifier;
65             mDescriptors = ParcelFileDescriptor.createReliablePipe();
66         }
67 
getReadingFileDescriptor()68         ParcelFileDescriptor getReadingFileDescriptor() {
69             return mDescriptors[0];
70         }
71     }
72 
73     private static class ImportFileTask extends Task {
ImportFileTask(MtpManager model, Identifier identifier)74         ImportFileTask(MtpManager model, Identifier identifier) throws IOException {
75             super(model, identifier);
76         }
77 
78         @Override
run()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 {
GetThumbnailTask(MtpManager model, Identifier identifier)95         GetThumbnailTask(MtpManager model, Identifier identifier) throws IOException {
96             super(model, identifier);
97         }
98 
99         @Override
run()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 
close()117     boolean close() throws InterruptedException {
118         mExecutor.shutdownNow();
119         return mExecutor.awaitTermination(AWAIT_TERMINATION_TIMEOUT, TimeUnit.MILLISECONDS);
120     }
121 }
122