1f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono/* 2f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono * Copyright (C) 2016 The Android Open Source Project 3f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono * 4f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono * Licensed under the Apache License, Version 2.0 (the "License"); 5f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono * you may not use this file except in compliance with the License. 6f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono * You may obtain a copy of the License at 7f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono * 8f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono * http://www.apache.org/licenses/LICENSE-2.0 9f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono * 10f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono * Unless required by applicable law or agreed to in writing, software 11f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono * distributed under the License is distributed on an "AS IS" BASIS, 12f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono * See the License for the specific language governing permissions and 14f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono * limitations under the License. 15f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono */ 16f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono 17f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hironopackage com.android.mtp; 18f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono 19f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hironoimport android.content.Context; 20f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hironoimport android.mtp.MtpObjectInfo; 21f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hironoimport android.os.ParcelFileDescriptor; 22f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hironoimport android.system.ErrnoException; 23f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hironoimport android.system.Os; 24f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hironoimport android.system.OsConstants; 25f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono 26f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hironoimport com.android.internal.util.Preconditions; 27f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono 28f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hironoimport java.io.File; 29f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hironoimport java.io.IOException; 30f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono 31f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hironoclass MtpFileWriter implements AutoCloseable { 32f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono final ParcelFileDescriptor mCacheFd; 33f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono final String mDocumentId; 34f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono boolean mDirty; 35f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono 36f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono MtpFileWriter(Context context, String documentId) throws IOException { 37f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono mDocumentId = documentId; 38f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono mDirty = false; 39f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono final File tempFile = File.createTempFile("mtp", "tmp", context.getCacheDir()); 40f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono mCacheFd = ParcelFileDescriptor.open( 41f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono tempFile, 42f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono ParcelFileDescriptor.MODE_READ_WRITE | 43f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono ParcelFileDescriptor.MODE_TRUNCATE | 44f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono ParcelFileDescriptor.MODE_CREATE); 45f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono tempFile.delete(); 46f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono } 47f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono 48f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono String getDocumentId() { 49f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono return mDocumentId; 50f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono } 51f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono 52f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono int write(long offset, int size, byte[] bytes) throws IOException, ErrnoException { 53f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono Preconditions.checkArgumentNonnegative(offset, "offset"); 54f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono Preconditions.checkArgumentNonnegative(size, "size"); 55f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono Preconditions.checkArgument(size <= bytes.length); 56f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono if (size == 0) { 57f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono return 0; 58f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono } 59f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono mDirty = true; 60f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono Os.lseek(mCacheFd.getFileDescriptor(), offset, OsConstants.SEEK_SET); 61f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono return Os.write(mCacheFd.getFileDescriptor(), bytes, 0, size); 62f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono } 63f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono 64f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono void flush(MtpManager manager, MtpDatabase database, int[] operationsSupported) 65f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono throws IOException, ErrnoException { 66f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono // Skip unnecessary flush. 67f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono if (!mDirty) { 68f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono return; 69f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono } 70f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono 71f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono // Get the placeholder object info. 72f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono final Identifier identifier = database.createIdentifier(mDocumentId); 73f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono final MtpObjectInfo placeholderObjectInfo = 74f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono manager.getObjectInfo(identifier.mDeviceId, identifier.mObjectHandle); 75f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono 76f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono // Delete the target object info if it already exists (as a placeholder). 77f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono manager.deleteDocument(identifier.mDeviceId, identifier.mObjectHandle); 78f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono 79f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono // Create the target object info with a correct file size and upload the file. 80f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono final long size = Os.lseek(mCacheFd.getFileDescriptor(), 0, OsConstants.SEEK_END); 81f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono final MtpObjectInfo targetObjectInfo = new MtpObjectInfo.Builder(placeholderObjectInfo) 82f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono .setCompressedSize(size) 83f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono .build(); 84f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono 85f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono Os.lseek(mCacheFd.getFileDescriptor(), 0, OsConstants.SEEK_SET); 86f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono final int newObjectHandle = manager.createDocument( 87f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono identifier.mDeviceId, targetObjectInfo, mCacheFd); 88f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono 89f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono final MtpObjectInfo newObjectInfo = manager.getObjectInfo( 90f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono identifier.mDeviceId, newObjectHandle); 91f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono final Identifier parentIdentifier = 92f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono database.getParentIdentifier(identifier.mDocumentId); 93f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono database.updateObject( 94f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono identifier.mDocumentId, 95f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono identifier.mDeviceId, 96f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono parentIdentifier.mDocumentId, 97f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono operationsSupported, 98f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono newObjectInfo, 99f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono size); 100f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono 101f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono mDirty = false; 102f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono } 103f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono 104f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono @Override 105f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono public void close() throws IOException { 106f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono mCacheFd.close(); 107f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono } 108f4e7fa80384ac72d0228ca5de6e949a9162cefbfDaichi Hirono} 109