/* * Copyright (C) 2016 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.content.Context; import android.mtp.MtpObjectInfo; import android.os.ParcelFileDescriptor; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; import com.android.internal.util.Preconditions; import java.io.File; import java.io.IOException; class MtpFileWriter implements AutoCloseable { final ParcelFileDescriptor mCacheFd; final String mDocumentId; boolean mDirty; MtpFileWriter(Context context, String documentId) throws IOException { mDocumentId = documentId; mDirty = false; final File tempFile = File.createTempFile("mtp", "tmp", context.getCacheDir()); mCacheFd = ParcelFileDescriptor.open( tempFile, ParcelFileDescriptor.MODE_READ_WRITE | ParcelFileDescriptor.MODE_TRUNCATE | ParcelFileDescriptor.MODE_CREATE); tempFile.delete(); } String getDocumentId() { return mDocumentId; } int write(long offset, int size, byte[] bytes) throws IOException, ErrnoException { Preconditions.checkArgumentNonnegative(offset, "offset"); Preconditions.checkArgumentNonnegative(size, "size"); Preconditions.checkArgument(size <= bytes.length); if (size == 0) { return 0; } mDirty = true; Os.lseek(mCacheFd.getFileDescriptor(), offset, OsConstants.SEEK_SET); return Os.write(mCacheFd.getFileDescriptor(), bytes, 0, size); } void flush(MtpManager manager, MtpDatabase database, int[] operationsSupported) throws IOException, ErrnoException { // Skip unnecessary flush. if (!mDirty) { return; } // Get the placeholder object info. final Identifier identifier = database.createIdentifier(mDocumentId); final MtpObjectInfo placeholderObjectInfo = manager.getObjectInfo(identifier.mDeviceId, identifier.mObjectHandle); // Delete the target object info if it already exists (as a placeholder). manager.deleteDocument(identifier.mDeviceId, identifier.mObjectHandle); // Create the target object info with a correct file size and upload the file. final long size = Os.lseek(mCacheFd.getFileDescriptor(), 0, OsConstants.SEEK_END); final MtpObjectInfo targetObjectInfo = new MtpObjectInfo.Builder(placeholderObjectInfo) .setCompressedSize(size) .build(); Os.lseek(mCacheFd.getFileDescriptor(), 0, OsConstants.SEEK_SET); final int newObjectHandle = manager.createDocument( identifier.mDeviceId, targetObjectInfo, mCacheFd); final MtpObjectInfo newObjectInfo = manager.getObjectInfo( identifier.mDeviceId, newObjectHandle); final Identifier parentIdentifier = database.getParentIdentifier(identifier.mDocumentId); database.updateObject( identifier.mDocumentId, identifier.mDeviceId, parentIdentifier.mDocumentId, operationsSupported, newObjectInfo, size); mDirty = false; } @Override public void close() throws IOException { mCacheFd.close(); } }