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