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