Home | History | Annotate | Download | only in mtp
      1 /*
      2  * Copyright (C) 2016 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.content.Context;
     20 import android.mtp.MtpObjectInfo;
     21 import android.os.ParcelFileDescriptor;
     22 import android.system.ErrnoException;
     23 import android.system.Os;
     24 import android.system.OsConstants;
     25 
     26 import com.android.internal.util.Preconditions;
     27 
     28 import java.io.File;
     29 import java.io.IOException;
     30 
     31 class MtpFileWriter implements AutoCloseable {
     32     final ParcelFileDescriptor mCacheFd;
     33     final String mDocumentId;
     34     boolean mDirty;
     35 
     36     MtpFileWriter(Context context, String documentId) throws IOException {
     37         mDocumentId = documentId;
     38         mDirty = false;
     39         final File tempFile = File.createTempFile("mtp", "tmp", context.getCacheDir());
     40         mCacheFd = ParcelFileDescriptor.open(
     41                 tempFile,
     42                 ParcelFileDescriptor.MODE_READ_WRITE |
     43                 ParcelFileDescriptor.MODE_TRUNCATE |
     44                 ParcelFileDescriptor.MODE_CREATE);
     45         tempFile.delete();
     46     }
     47 
     48     String getDocumentId() {
     49         return mDocumentId;
     50     }
     51 
     52     int write(long offset, int size, byte[] bytes) throws IOException, ErrnoException {
     53         Preconditions.checkArgumentNonnegative(offset, "offset");
     54         Preconditions.checkArgumentNonnegative(size, "size");
     55         Preconditions.checkArgument(size <= bytes.length);
     56         if (size == 0) {
     57             return 0;
     58         }
     59         mDirty = true;
     60         Os.lseek(mCacheFd.getFileDescriptor(), offset, OsConstants.SEEK_SET);
     61         return Os.write(mCacheFd.getFileDescriptor(), bytes, 0, size);
     62     }
     63 
     64     void flush(MtpManager manager, MtpDatabase database, int[] operationsSupported)
     65             throws IOException, ErrnoException {
     66         // Skip unnecessary flush.
     67         if (!mDirty) {
     68             return;
     69         }
     70 
     71         // Get the placeholder object info.
     72         final Identifier identifier = database.createIdentifier(mDocumentId);
     73         final MtpObjectInfo placeholderObjectInfo =
     74                 manager.getObjectInfo(identifier.mDeviceId, identifier.mObjectHandle);
     75 
     76         // Delete the target object info if it already exists (as a placeholder).
     77         manager.deleteDocument(identifier.mDeviceId, identifier.mObjectHandle);
     78 
     79         // Create the target object info with a correct file size and upload the file.
     80         final long size = Os.lseek(mCacheFd.getFileDescriptor(), 0, OsConstants.SEEK_END);
     81         final MtpObjectInfo targetObjectInfo = new MtpObjectInfo.Builder(placeholderObjectInfo)
     82                 .setCompressedSize(size)
     83                 .build();
     84 
     85         Os.lseek(mCacheFd.getFileDescriptor(), 0, OsConstants.SEEK_SET);
     86         final int newObjectHandle = manager.createDocument(
     87                 identifier.mDeviceId, targetObjectInfo, mCacheFd);
     88 
     89         final MtpObjectInfo newObjectInfo = manager.getObjectInfo(
     90                 identifier.mDeviceId, newObjectHandle);
     91         final Identifier parentIdentifier =
     92                 database.getParentIdentifier(identifier.mDocumentId);
     93         database.updateObject(
     94                 identifier.mDocumentId,
     95                 identifier.mDeviceId,
     96                 parentIdentifier.mDocumentId,
     97                 operationsSupported,
     98                 newObjectInfo,
     99                 size);
    100 
    101         mDirty = false;
    102     }
    103 
    104     @Override
    105     public void close() throws IOException {
    106         mCacheFd.close();
    107     }
    108 }
    109