FileBridge.java revision 9a1507aa10577badabcbe00396613a967302e456
1/*
2 * Copyright (C) 2014 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 android.os;
18
19import static android.system.OsConstants.AF_UNIX;
20import static android.system.OsConstants.SOCK_STREAM;
21
22import android.system.ErrnoException;
23import android.system.Os;
24import android.util.Log;
25
26import libcore.io.IoBridge;
27import libcore.io.IoUtils;
28import libcore.io.Memory;
29import libcore.io.Streams;
30
31import java.io.FileDescriptor;
32import java.io.IOException;
33import java.io.OutputStream;
34import java.nio.ByteOrder;
35import java.util.Arrays;
36
37/**
38 * Simple bridge that allows file access across process boundaries without
39 * returning the underlying {@link FileDescriptor}. This is useful when the
40 * server side needs to strongly assert that a client side is completely
41 * hands-off.
42 *
43 * @hide
44 */
45public class FileBridge extends Thread {
46    private static final String TAG = "FileBridge";
47
48    // TODO: consider extending to support bidirectional IO
49
50    private static final int MSG_LENGTH = 8;
51
52    /** CMD_WRITE [len] [data] */
53    private static final int CMD_WRITE = 1;
54    /** CMD_FSYNC */
55    private static final int CMD_FSYNC = 2;
56    /** CMD_CLOSE */
57    private static final int CMD_CLOSE = 3;
58
59    private FileDescriptor mTarget;
60
61    private final FileDescriptor mServer = new FileDescriptor();
62    private final FileDescriptor mClient = new FileDescriptor();
63
64    private volatile boolean mClosed;
65
66    public FileBridge() {
67        try {
68            Os.socketpair(AF_UNIX, SOCK_STREAM, 0, mServer, mClient);
69        } catch (ErrnoException e) {
70            throw new RuntimeException("Failed to create bridge");
71        }
72    }
73
74    public boolean isClosed() {
75        return mClosed;
76    }
77
78    public void setTargetFile(FileDescriptor target) {
79        mTarget = target;
80    }
81
82    public FileDescriptor getClientSocket() {
83        return mClient;
84    }
85
86    @Override
87    public void run() {
88        final byte[] temp = new byte[8192];
89        try {
90            while (IoBridge.read(mServer, temp, 0, MSG_LENGTH) == MSG_LENGTH) {
91                final int cmd = Memory.peekInt(temp, 0, ByteOrder.BIG_ENDIAN);
92
93                if (cmd == CMD_WRITE) {
94                    // Shuttle data into local file
95                    int len = Memory.peekInt(temp, 4, ByteOrder.BIG_ENDIAN);
96                    while (len > 0) {
97                        int n = IoBridge.read(mServer, temp, 0, Math.min(temp.length, len));
98                        if (n == -1) {
99                            throw new IOException(
100                                    "Unexpected EOF; still expected " + len + " bytes");
101                        }
102                        IoBridge.write(mTarget, temp, 0, n);
103                        len -= n;
104                    }
105
106                } else if (cmd == CMD_FSYNC) {
107                    // Sync and echo back to confirm
108                    Os.fsync(mTarget);
109                    IoBridge.write(mServer, temp, 0, MSG_LENGTH);
110
111                } else if (cmd == CMD_CLOSE) {
112                    // Close and echo back to confirm
113                    Os.fsync(mTarget);
114                    Os.close(mTarget);
115                    mClosed = true;
116                    IoBridge.write(mServer, temp, 0, MSG_LENGTH);
117                    break;
118                }
119            }
120
121        } catch (ErrnoException e) {
122            Log.wtf(TAG, "Failed during bridge", e);
123        } catch (IOException e) {
124            Log.wtf(TAG, "Failed during bridge", e);
125        } finally {
126            IoUtils.closeQuietly(mTarget);
127            IoUtils.closeQuietly(mServer);
128            IoUtils.closeQuietly(mClient);
129            mClosed = true;
130        }
131    }
132
133    public static class FileBridgeOutputStream extends OutputStream {
134        private final ParcelFileDescriptor mClientPfd;
135        private final FileDescriptor mClient;
136        private final byte[] mTemp = new byte[MSG_LENGTH];
137
138        public FileBridgeOutputStream(ParcelFileDescriptor clientPfd) {
139            mClientPfd = clientPfd;
140            mClient = clientPfd.getFileDescriptor();
141        }
142
143        @Override
144        public void close() throws IOException {
145            try {
146                writeCommandAndBlock(CMD_CLOSE, "close()");
147            } finally {
148                IoBridge.closeAndSignalBlockedThreads(mClient);
149            }
150        }
151
152        public void fsync() throws IOException {
153            writeCommandAndBlock(CMD_FSYNC, "fsync()");
154        }
155
156        private void writeCommandAndBlock(int cmd, String cmdString) throws IOException {
157            Memory.pokeInt(mTemp, 0, cmd, ByteOrder.BIG_ENDIAN);
158            IoBridge.write(mClient, mTemp, 0, MSG_LENGTH);
159
160            // Wait for server to ack
161            if (IoBridge.read(mClient, mTemp, 0, MSG_LENGTH) == MSG_LENGTH) {
162                if (Memory.peekInt(mTemp, 0, ByteOrder.BIG_ENDIAN) == cmd) {
163                    return;
164                }
165            }
166
167            throw new IOException("Failed to execute " + cmdString + " across bridge");
168        }
169
170        @Override
171        public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException {
172            Arrays.checkOffsetAndCount(buffer.length, byteOffset, byteCount);
173            Memory.pokeInt(mTemp, 0, CMD_WRITE, ByteOrder.BIG_ENDIAN);
174            Memory.pokeInt(mTemp, 4, byteCount, ByteOrder.BIG_ENDIAN);
175            IoBridge.write(mClient, mTemp, 0, MSG_LENGTH);
176            IoBridge.write(mClient, buffer, byteOffset, byteCount);
177        }
178
179        @Override
180        public void write(int oneByte) throws IOException {
181            Streams.writeSingleByte(this, oneByte);
182        }
183    }
184}
185