blob: 0acf24bac52aca33a213b789ad40157079cee5cd [file] [log] [blame]
Jeff Sharkey78cc3402014-05-22 10:52:49 -07001/*
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;
Jeff Sharkey78cc3402014-05-22 10:52:49 -070034import 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;
Jeff Sharkeyec55ef02014-07-08 11:28:00 -070056 /** CMD_CLOSE */
57 private static final int CMD_CLOSE = 3;
Jeff Sharkey78cc3402014-05-22 10:52:49 -070058
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
Jeff Sharkey77d218e2014-09-06 12:20:37 -070078 public void forceClose() {
79 IoUtils.closeQuietly(mTarget);
80 IoUtils.closeQuietly(mServer);
81 IoUtils.closeQuietly(mClient);
82 mClosed = true;
83 }
84
Jeff Sharkey78cc3402014-05-22 10:52:49 -070085 public void setTargetFile(FileDescriptor target) {
86 mTarget = target;
87 }
88
89 public FileDescriptor getClientSocket() {
90 return mClient;
91 }
92
93 @Override
94 public void run() {
95 final byte[] temp = new byte[8192];
96 try {
97 while (IoBridge.read(mServer, temp, 0, MSG_LENGTH) == MSG_LENGTH) {
98 final int cmd = Memory.peekInt(temp, 0, ByteOrder.BIG_ENDIAN);
Jeff Sharkey78cc3402014-05-22 10:52:49 -070099 if (cmd == CMD_WRITE) {
100 // Shuttle data into local file
101 int len = Memory.peekInt(temp, 4, ByteOrder.BIG_ENDIAN);
102 while (len > 0) {
103 int n = IoBridge.read(mServer, temp, 0, Math.min(temp.length, len));
Jeff Sharkey5f1ed722014-08-21 17:54:29 -0700104 if (n == -1) {
105 throw new IOException(
106 "Unexpected EOF; still expected " + len + " bytes");
107 }
Jeff Sharkey78cc3402014-05-22 10:52:49 -0700108 IoBridge.write(mTarget, temp, 0, n);
109 len -= n;
110 }
111
112 } else if (cmd == CMD_FSYNC) {
113 // Sync and echo back to confirm
114 Os.fsync(mTarget);
115 IoBridge.write(mServer, temp, 0, MSG_LENGTH);
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700116
117 } else if (cmd == CMD_CLOSE) {
118 // Close and echo back to confirm
119 Os.fsync(mTarget);
120 Os.close(mTarget);
121 mClosed = true;
122 IoBridge.write(mServer, temp, 0, MSG_LENGTH);
123 break;
Jeff Sharkey78cc3402014-05-22 10:52:49 -0700124 }
125 }
126
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700127 } catch (ErrnoException | IOException e) {
Jeff Sharkeyd3ca9912014-08-26 14:23:28 -0700128 Log.wtf(TAG, "Failed during bridge", e);
Jeff Sharkey78cc3402014-05-22 10:52:49 -0700129 } finally {
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700130 forceClose();
Jeff Sharkey78cc3402014-05-22 10:52:49 -0700131 }
132 }
133
134 public static class FileBridgeOutputStream extends OutputStream {
Jeff Sharkey9a1507a2014-08-28 20:38:10 -0700135 private final ParcelFileDescriptor mClientPfd;
Jeff Sharkey78cc3402014-05-22 10:52:49 -0700136 private final FileDescriptor mClient;
137 private final byte[] mTemp = new byte[MSG_LENGTH];
138
Jeff Sharkey9a1507a2014-08-28 20:38:10 -0700139 public FileBridgeOutputStream(ParcelFileDescriptor clientPfd) {
140 mClientPfd = clientPfd;
141 mClient = clientPfd.getFileDescriptor();
Jeff Sharkey78cc3402014-05-22 10:52:49 -0700142 }
143
Jeff Sharkey73a82172014-08-29 00:10:14 -0700144 public FileBridgeOutputStream(FileDescriptor client) {
145 mClientPfd = null;
146 mClient = client;
147 }
148
Jeff Sharkey78cc3402014-05-22 10:52:49 -0700149 @Override
150 public void close() throws IOException {
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700151 try {
152 writeCommandAndBlock(CMD_CLOSE, "close()");
153 } finally {
154 IoBridge.closeAndSignalBlockedThreads(mClient);
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700155 IoUtils.closeQuietly(mClientPfd);
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700156 }
Jeff Sharkey78cc3402014-05-22 10:52:49 -0700157 }
158
Jeff Sharkeya1031142014-07-12 18:09:46 -0700159 public void fsync() throws IOException {
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700160 writeCommandAndBlock(CMD_FSYNC, "fsync()");
161 }
162
163 private void writeCommandAndBlock(int cmd, String cmdString) throws IOException {
164 Memory.pokeInt(mTemp, 0, cmd, ByteOrder.BIG_ENDIAN);
Jeff Sharkey78cc3402014-05-22 10:52:49 -0700165 IoBridge.write(mClient, mTemp, 0, MSG_LENGTH);
166
167 // Wait for server to ack
168 if (IoBridge.read(mClient, mTemp, 0, MSG_LENGTH) == MSG_LENGTH) {
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700169 if (Memory.peekInt(mTemp, 0, ByteOrder.BIG_ENDIAN) == cmd) {
Jeff Sharkey78cc3402014-05-22 10:52:49 -0700170 return;
171 }
172 }
173
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700174 throw new IOException("Failed to execute " + cmdString + " across bridge");
Jeff Sharkey78cc3402014-05-22 10:52:49 -0700175 }
176
177 @Override
178 public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException {
179 Arrays.checkOffsetAndCount(buffer.length, byteOffset, byteCount);
180 Memory.pokeInt(mTemp, 0, CMD_WRITE, ByteOrder.BIG_ENDIAN);
181 Memory.pokeInt(mTemp, 4, byteCount, ByteOrder.BIG_ENDIAN);
182 IoBridge.write(mClient, mTemp, 0, MSG_LENGTH);
183 IoBridge.write(mClient, buffer, byteOffset, byteCount);
184 }
185
186 @Override
187 public void write(int oneByte) throws IOException {
188 Streams.writeSingleByte(this, oneByte);
189 }
190 }
191}