blob: 645bfcd3eeb45e41e55af97982310d753c27a5bc [file] [log] [blame]
Daichi Hirono8ba41912015-07-30 21:22:57 +09001/*
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
Tomasz Mikolajewskib80a3cf2015-08-24 16:10:51 +090019import android.content.Context;
20import android.mtp.MtpObjectInfo;
Daichi Hirono8ba41912015-07-30 21:22:57 +090021import android.os.ParcelFileDescriptor;
22import android.util.Log;
23
Tomasz Mikolajewskib80a3cf2015-08-24 16:10:51 +090024import java.io.File;
Tomasz Mikolajewskib80a3cf2015-08-24 16:10:51 +090025import java.io.FileOutputStream;
Daichi Hirono8ba41912015-07-30 21:22:57 +090026import java.io.IOException;
27import java.util.concurrent.ExecutorService;
28import java.util.concurrent.Executors;
29
30class PipeManager {
31 final ExecutorService mExecutor;
Daichi Hironof578fa22016-02-19 18:05:42 +090032 final MtpDatabase mDatabase;
Daichi Hirono8ba41912015-07-30 21:22:57 +090033
Daichi Hironof578fa22016-02-19 18:05:42 +090034 PipeManager(MtpDatabase database) {
35 this(database, Executors.newSingleThreadExecutor());
Daichi Hirono8ba41912015-07-30 21:22:57 +090036 }
37
Daichi Hironof578fa22016-02-19 18:05:42 +090038 PipeManager(MtpDatabase database, ExecutorService executor) {
39 this.mDatabase = database;
Daichi Hirono8ba41912015-07-30 21:22:57 +090040 this.mExecutor = executor;
41 }
42
Daichi Hirono3faa43a2015-08-05 17:15:35 +090043 ParcelFileDescriptor readDocument(MtpManager model, Identifier identifier) throws IOException {
Tomasz Mikolajewski52652ac2015-08-05 17:33:33 +090044 final Task task = new ImportFileTask(model, identifier);
Daichi Hirono8ba41912015-07-30 21:22:57 +090045 mExecutor.execute(task);
46 return task.getReadingFileDescriptor();
47 }
48
Tomasz Mikolajewskib80a3cf2015-08-24 16:10:51 +090049 ParcelFileDescriptor writeDocument(Context context, MtpManager model, Identifier identifier)
50 throws IOException {
Daichi Hironof578fa22016-02-19 18:05:42 +090051 final Task task = new WriteDocumentTask(context, model, identifier, mDatabase);
Tomasz Mikolajewskib80a3cf2015-08-24 16:10:51 +090052 mExecutor.execute(task);
53 return task.getWritingFileDescriptor();
54 }
55
Daichi Hirono3faa43a2015-08-05 17:15:35 +090056 ParcelFileDescriptor readThumbnail(MtpManager model, Identifier identifier) throws IOException {
57 final Task task = new GetThumbnailTask(model, identifier);
58 mExecutor.execute(task);
59 return task.getReadingFileDescriptor();
60 }
61
Daichi Hirono8ba41912015-07-30 21:22:57 +090062 private static abstract class Task implements Runnable {
Daichi Hirono2ff024f2015-08-11 20:02:58 +090063 protected final MtpManager mManager;
Tomasz Mikolajewski52652ac2015-08-05 17:33:33 +090064 protected final Identifier mIdentifier;
65 protected final ParcelFileDescriptor[] mDescriptors;
Daichi Hirono8ba41912015-07-30 21:22:57 +090066
Daichi Hirono2ff024f2015-08-11 20:02:58 +090067 Task(MtpManager manager, Identifier identifier) throws IOException {
68 mManager = manager;
Tomasz Mikolajewski52652ac2015-08-05 17:33:33 +090069 mIdentifier = identifier;
Daichi Hirono8ba41912015-07-30 21:22:57 +090070 mDescriptors = ParcelFileDescriptor.createReliablePipe();
71 }
72
Daichi Hirono8ba41912015-07-30 21:22:57 +090073 ParcelFileDescriptor getReadingFileDescriptor() {
74 return mDescriptors[0];
75 }
Tomasz Mikolajewskib80a3cf2015-08-24 16:10:51 +090076
77 ParcelFileDescriptor getWritingFileDescriptor() {
78 return mDescriptors[1];
79 }
Daichi Hirono8ba41912015-07-30 21:22:57 +090080 }
81
Tomasz Mikolajewski52652ac2015-08-05 17:33:33 +090082 private static class ImportFileTask extends Task {
83 ImportFileTask(MtpManager model, Identifier identifier) throws IOException {
84 super(model, identifier);
85 }
86
87 @Override
88 public void run() {
89 try {
Daichi Hirono2ff024f2015-08-11 20:02:58 +090090 mManager.importFile(
Daichi Hirono3faa43a2015-08-05 17:15:35 +090091 mIdentifier.mDeviceId, mIdentifier.mObjectHandle, mDescriptors[1]);
Tomasz Mikolajewski52652ac2015-08-05 17:33:33 +090092 mDescriptors[1].close();
93 } catch (IOException error) {
94 try {
95 mDescriptors[1].closeWithError("Failed to stream a file.");
96 } catch (IOException closeError) {
97 Log.w(MtpDocumentsProvider.TAG, closeError.getMessage());
98 }
99 }
100 }
101 }
102
Tomasz Mikolajewskib80a3cf2015-08-24 16:10:51 +0900103 private static class WriteDocumentTask extends Task {
104 private final Context mContext;
Daichi Hironof578fa22016-02-19 18:05:42 +0900105 private final MtpDatabase mDatabase;
Tomasz Mikolajewskib80a3cf2015-08-24 16:10:51 +0900106
Daichi Hironof578fa22016-02-19 18:05:42 +0900107 WriteDocumentTask(
108 Context context, MtpManager model, Identifier identifier, MtpDatabase database)
Tomasz Mikolajewskib80a3cf2015-08-24 16:10:51 +0900109 throws IOException {
110 super(model, identifier);
111 mContext = context;
Daichi Hironof578fa22016-02-19 18:05:42 +0900112 mDatabase = database;
Tomasz Mikolajewskib80a3cf2015-08-24 16:10:51 +0900113 }
114
115 @Override
116 public void run() {
117 File tempFile = null;
118 try {
119 // Obtain a temporary file and copy the data to it.
Daichi Hironof578fa22016-02-19 18:05:42 +0900120 tempFile = File.createTempFile("mtp", "tmp", mContext.getCacheDir());
Tomasz Mikolajewskib80a3cf2015-08-24 16:10:51 +0900121 try (
122 final FileOutputStream tempOutputStream =
123 new ParcelFileDescriptor.AutoCloseOutputStream(
124 ParcelFileDescriptor.open(
125 tempFile, ParcelFileDescriptor.MODE_WRITE_ONLY));
126 final ParcelFileDescriptor.AutoCloseInputStream inputStream =
127 new ParcelFileDescriptor.AutoCloseInputStream(mDescriptors[0])
128 ) {
129 final byte[] buffer = new byte[32 * 1024];
130 int bytes;
131 while ((bytes = inputStream.read(buffer)) != -1) {
132 mDescriptors[0].checkError();
133 tempOutputStream.write(buffer, 0, bytes);
134 }
135 tempOutputStream.flush();
136 }
137
138 // Get the placeholder object info.
139 final MtpObjectInfo placeholderObjectInfo =
140 mManager.getObjectInfo(mIdentifier.mDeviceId, mIdentifier.mObjectHandle);
141
142 // Delete the target object info if it already exists (as a placeholder).
143 mManager.deleteDocument(mIdentifier.mDeviceId, mIdentifier.mObjectHandle);
144
Tomasz Mikolajewskidf544172015-08-31 10:59:43 +0900145 // Create the target object info with a correct file size and upload the file.
146 final MtpObjectInfo targetObjectInfo =
147 new MtpObjectInfo.Builder(placeholderObjectInfo)
Daichi Hironof578fa22016-02-19 18:05:42 +0900148 .setCompressedSize(tempFile.length())
Tomasz Mikolajewskidf544172015-08-31 10:59:43 +0900149 .build();
Tomasz Mikolajewskib80a3cf2015-08-24 16:10:51 +0900150 final ParcelFileDescriptor tempInputDescriptor = ParcelFileDescriptor.open(
151 tempFile, ParcelFileDescriptor.MODE_READ_ONLY);
Daichi Hironof578fa22016-02-19 18:05:42 +0900152 final int newObjectHandle = mManager.createDocument(
153 mIdentifier.mDeviceId, targetObjectInfo, tempInputDescriptor);
154
155 final MtpObjectInfo newObjectInfo = mManager.getObjectInfo(
156 mIdentifier.mDeviceId, newObjectHandle);
157 final Identifier parentIdentifier =
158 mDatabase.getParentIdentifier(mIdentifier.mDocumentId);
159 mDatabase.updateObject(
160 mIdentifier.mDocumentId,
161 mIdentifier.mDeviceId,
162 parentIdentifier.mDocumentId,
163 newObjectInfo);
Tomasz Mikolajewskib80a3cf2015-08-24 16:10:51 +0900164 } catch (IOException error) {
165 Log.w(MtpDocumentsProvider.TAG,
166 "Failed to send a file because of: " + error.getMessage());
167 } finally {
168 if (tempFile != null) {
169 tempFile.delete();
170 }
171 }
172 }
173 }
174
Daichi Hirono3faa43a2015-08-05 17:15:35 +0900175 private static class GetThumbnailTask extends Task {
176 GetThumbnailTask(MtpManager model, Identifier identifier) throws IOException {
177 super(model, identifier);
178 }
179
180 @Override
181 public void run() {
182 try {
183 try (final ParcelFileDescriptor.AutoCloseOutputStream stream =
184 new ParcelFileDescriptor.AutoCloseOutputStream(mDescriptors[1])) {
185 try {
Daichi Hirono2ff024f2015-08-11 20:02:58 +0900186 stream.write(mManager.getThumbnail(
Daichi Hirono3faa43a2015-08-05 17:15:35 +0900187 mIdentifier.mDeviceId, mIdentifier.mObjectHandle));
188 } catch (IOException error) {
189 mDescriptors[1].closeWithError("Failed to stream a thumbnail.");
190 }
191 }
192 } catch (IOException closeError) {
193 Log.w(MtpDocumentsProvider.TAG, closeError.getMessage());
194 }
195 }
196 }
197
Daichi Hirono8ba41912015-07-30 21:22:57 +0900198 void close() {
199 mExecutor.shutdown();
200 }
201}