blob: 3e1bedce49ea8d48b0c3bdfdda98cda6cf76f971 [file] [log] [blame]
Daichi Hironof4e7fa82016-03-28 16:07:45 +09001/*
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
17package com.android.mtp;
18
19import android.content.Context;
20import android.mtp.MtpObjectInfo;
21import android.os.ParcelFileDescriptor;
22import android.system.ErrnoException;
23import android.system.Os;
24import android.system.OsConstants;
25
26import com.android.internal.util.Preconditions;
27
28import java.io.File;
29import java.io.IOException;
30
31class 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}