blob: 6a98405d50261b46ad20886bc0a39adf976c3497 [file] [log] [blame]
Daichi Hironobee50c02015-12-14 11:00:54 +09001/*
2 * Copyright (C) 2015 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
Daichi Hirono2f310f62016-01-27 12:34:29 +090019import android.annotation.WorkerThread;
Daichi Hironobee50c02015-12-14 11:00:54 +090020import android.os.ParcelFileDescriptor;
Daichi Hironocc9a7d72015-10-28 09:43:48 +090021import android.os.Process;
Daichi Hironobee50c02015-12-14 11:00:54 +090022import android.os.storage.StorageManager;
Daichi Hirono2f310f62016-01-27 12:34:29 +090023import android.system.OsConstants;
Daichi Hirono91e3b502015-12-16 09:24:16 +090024import android.util.Log;
Daichi Hironobee50c02015-12-14 11:00:54 +090025import com.android.internal.annotations.VisibleForTesting;
Daichi Hironoe6054c02016-01-20 15:36:04 +090026import com.android.internal.util.Preconditions;
Daichi Hironof52ef002016-01-11 18:07:01 +090027import com.android.mtp.annotations.UsedByNative;
Daichi Hironobee50c02015-12-14 11:00:54 +090028import java.io.File;
Daichi Hironocc9a7d72015-10-28 09:43:48 +090029import java.io.FileNotFoundException;
Daichi Hirono91e3b502015-12-16 09:24:16 +090030import java.io.IOException;
31
Daichi Hironobee50c02015-12-14 11:00:54 +090032public class AppFuse {
33 static {
34 System.loadLibrary("appfuse_jni");
35 }
36
Daichi Hironof52ef002016-01-11 18:07:01 +090037 /**
38 * Max read amount specified at the FUSE kernel implementation.
39 * The value is copied from sdcard.c.
40 */
41 static final int MAX_READ = 128 * 1024;
42
Daichi Hironobee50c02015-12-14 11:00:54 +090043 private final String mName;
Daichi Hironocc9a7d72015-10-28 09:43:48 +090044 private final Callback mCallback;
Daichi Hirono2f310f62016-01-27 12:34:29 +090045
46 /**
47 * Buffer for read bytes request.
48 * Don't use the buffer from the out of AppFuseMessageThread.
49 */
50 private byte[] mBuffer = new byte[MAX_READ];
51
Daichi Hironoe6054c02016-01-20 15:36:04 +090052 private Thread mMessageThread;
Daichi Hironobee50c02015-12-14 11:00:54 +090053 private ParcelFileDescriptor mDeviceFd;
54
Daichi Hironocc9a7d72015-10-28 09:43:48 +090055 AppFuse(String name, Callback callback) {
Daichi Hironobee50c02015-12-14 11:00:54 +090056 mName = name;
Daichi Hironocc9a7d72015-10-28 09:43:48 +090057 mCallback = callback;
Daichi Hironobee50c02015-12-14 11:00:54 +090058 }
59
Daichi Hironoe6054c02016-01-20 15:36:04 +090060 void mount(StorageManager storageManager) throws IOException {
61 Preconditions.checkState(mDeviceFd == null);
Daichi Hironobee50c02015-12-14 11:00:54 +090062 mDeviceFd = storageManager.mountAppFuse(mName);
Daichi Hironoe6054c02016-01-20 15:36:04 +090063 mMessageThread = new AppFuseMessageThread(mDeviceFd.dup().detachFd());
Daichi Hironobee50c02015-12-14 11:00:54 +090064 mMessageThread.start();
65 }
66
67 @VisibleForTesting
Daichi Hirono91e3b502015-12-16 09:24:16 +090068 void close() {
69 try {
70 // Remote side of ParcelFileDescriptor is tracking the close of mDeviceFd, and unmount
71 // the corresponding fuse file system. The mMessageThread will receive FUSE_FORGET, and
72 // then terminate itself.
73 mDeviceFd.close();
74 mMessageThread.join();
75 } catch (IOException exp) {
76 Log.e(MtpDocumentsProvider.TAG, "Failed to close device FD.", exp);
77 } catch (InterruptedException exp) {
78 Log.e(MtpDocumentsProvider.TAG, "Failed to terminate message thread.", exp);
79 }
80 }
81
Daichi Hironocc9a7d72015-10-28 09:43:48 +090082 public ParcelFileDescriptor openFile(int i) throws FileNotFoundException {
83 return ParcelFileDescriptor.open(new File(
84 getMountPoint(),
85 Integer.toString(i)),
86 ParcelFileDescriptor.MODE_READ_ONLY);
87 }
88
Daichi Hironobee50c02015-12-14 11:00:54 +090089 File getMountPoint() {
90 return new File("/mnt/appfuse/" + Process.myUid() + "_" + mName);
91 }
92
Daichi Hironocc9a7d72015-10-28 09:43:48 +090093 static interface Callback {
Daichi Hirono2f310f62016-01-27 12:34:29 +090094 /**
95 * Returns file size for the given inode.
96 * @param inode
97 * @return File size. Must not be negative.
98 * @throws FileNotFoundException
99 */
Daichi Hironocc9a7d72015-10-28 09:43:48 +0900100 long getFileSize(int inode) throws FileNotFoundException;
Daichi Hirono2f310f62016-01-27 12:34:29 +0900101
102 /**
103 * Returns flie bytes for the give inode.
104 * @param inode
105 * @param offset Offset for file bytes.
106 * @param size Size for file bytes.
107 * @param bytes Buffer to store file bytes.
108 * @return Number of read bytes. Must not be negative.
109 * @throws IOException
110 */
111 long readObjectBytes(int inode, long offset, long size, byte[] bytes) throws IOException;
Daichi Hironocc9a7d72015-10-28 09:43:48 +0900112 }
113
Daichi Hironof52ef002016-01-11 18:07:01 +0900114 @UsedByNative("com_android_mtp_AppFuse.cpp")
Daichi Hirono2f310f62016-01-27 12:34:29 +0900115 @WorkerThread
Daichi Hironocc9a7d72015-10-28 09:43:48 +0900116 private long getFileSize(int inode) {
117 try {
118 return mCallback.getFileSize(inode);
Daichi Hirono2f310f62016-01-27 12:34:29 +0900119 } catch (FileNotFoundException e) {
120 return -OsConstants.ENOENT;
Daichi Hironocc9a7d72015-10-28 09:43:48 +0900121 }
122 }
123
Daichi Hironof52ef002016-01-11 18:07:01 +0900124 @UsedByNative("com_android_mtp_AppFuse.cpp")
Daichi Hirono2f310f62016-01-27 12:34:29 +0900125 @WorkerThread
126 private long readObjectBytes(int inode, long offset, long size) {
Daichi Hironof52ef002016-01-11 18:07:01 +0900127 if (offset < 0 || size < 0 || size > MAX_READ) {
Daichi Hirono2f310f62016-01-27 12:34:29 +0900128 return -OsConstants.EINVAL;
Daichi Hironof52ef002016-01-11 18:07:01 +0900129 }
Daichi Hironocc9a7d72015-10-28 09:43:48 +0900130 try {
Daichi Hirono2f310f62016-01-27 12:34:29 +0900131 // It's OK to share the same mBuffer among requests because the requests are processed
132 // by AppFuseMessageThread sequentially.
133 return mCallback.readObjectBytes(inode, offset, size, mBuffer);
Daichi Hironocc9a7d72015-10-28 09:43:48 +0900134 } catch (IOException e) {
Daichi Hirono2f310f62016-01-27 12:34:29 +0900135 return -OsConstants.EIO;
Daichi Hironocc9a7d72015-10-28 09:43:48 +0900136 }
137 }
138
Daichi Hironobee50c02015-12-14 11:00:54 +0900139 private native boolean native_start_app_fuse_loop(int fd);
Daichi Hironoe6054c02016-01-20 15:36:04 +0900140
141 private class AppFuseMessageThread extends Thread {
142 /**
143 * File descriptor used by native loop.
144 * It's owned by native loop and does not need to close here.
145 */
146 private final int mRawFd;
147
148 AppFuseMessageThread(int fd) {
149 super("AppFuseMessageThread");
150 mRawFd = fd;
151 }
152
153 @Override
154 public void run() {
155 native_start_app_fuse_loop(mRawFd);
156 }
157 }
Daichi Hironobee50c02015-12-14 11:00:54 +0900158}