Files
frameworks_base/packages/MtpDocumentsProvider/src/com/android/mtp/PipeManager.java
Daichi Hirono f4e7fa8038 Use AppFuse to write document.
Previously MtpDocumentsProvider used pipes to transfer bytes from an
application to the provider when writing a document.  The problem was
application could not ensure that the last chunk of bytes was
successfully written to MTP device, since pipes had been already closed
when the provider transferred bytes to MTP device. Though the provider
encountered an error, the provider could not report the error to an
application.

The CL switches the method to transfer bytes from pipes to AppFuse. Now
application can flush() bytes on the file descriptor, and flush will not
complete until the provider completes writing bytes to MTP device.

Fixed: 23093747
Change-Id: I4e28f8cbf19d6c97e591943349a7535241d768f7
2016-03-29 16:27:59 +09:00

122 lines
4.2 KiB
Java

/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.mtp;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
class PipeManager {
/**
* Milliseconds we wait for background thread when pausing.
*/
private final static long AWAIT_TERMINATION_TIMEOUT = 2000;
final ExecutorService mExecutor;
final MtpDatabase mDatabase;
PipeManager(MtpDatabase database) {
this(database, Executors.newSingleThreadExecutor());
}
PipeManager(MtpDatabase database, ExecutorService executor) {
this.mDatabase = database;
this.mExecutor = executor;
}
ParcelFileDescriptor readDocument(MtpManager model, Identifier identifier) throws IOException {
final Task task = new ImportFileTask(model, identifier);
mExecutor.execute(task);
return task.getReadingFileDescriptor();
}
ParcelFileDescriptor readThumbnail(MtpManager model, Identifier identifier) throws IOException {
final Task task = new GetThumbnailTask(model, identifier);
mExecutor.execute(task);
return task.getReadingFileDescriptor();
}
private static abstract class Task implements Runnable {
protected final MtpManager mManager;
protected final Identifier mIdentifier;
protected final ParcelFileDescriptor[] mDescriptors;
Task(MtpManager manager, Identifier identifier) throws IOException {
mManager = manager;
mIdentifier = identifier;
mDescriptors = ParcelFileDescriptor.createReliablePipe();
}
ParcelFileDescriptor getReadingFileDescriptor() {
return mDescriptors[0];
}
}
private static class ImportFileTask extends Task {
ImportFileTask(MtpManager model, Identifier identifier) throws IOException {
super(model, identifier);
}
@Override
public void run() {
try {
mManager.importFile(
mIdentifier.mDeviceId, mIdentifier.mObjectHandle, mDescriptors[1]);
mDescriptors[1].close();
} catch (IOException error) {
try {
mDescriptors[1].closeWithError("Failed to stream a file.");
} catch (IOException closeError) {
Log.w(MtpDocumentsProvider.TAG, closeError.getMessage());
}
}
}
}
private static class GetThumbnailTask extends Task {
GetThumbnailTask(MtpManager model, Identifier identifier) throws IOException {
super(model, identifier);
}
@Override
public void run() {
try {
try (final ParcelFileDescriptor.AutoCloseOutputStream stream =
new ParcelFileDescriptor.AutoCloseOutputStream(mDescriptors[1])) {
try {
stream.write(mManager.getThumbnail(
mIdentifier.mDeviceId, mIdentifier.mObjectHandle));
} catch (IOException error) {
mDescriptors[1].closeWithError("Failed to stream a thumbnail.");
}
}
} catch (IOException closeError) {
Log.w(MtpDocumentsProvider.TAG, closeError.getMessage());
}
}
}
boolean close() throws InterruptedException {
mExecutor.shutdownNow();
return mExecutor.awaitTermination(AWAIT_TERMINATION_TIMEOUT, TimeUnit.MILLISECONDS);
}
}