• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package android.os;
18 
19 import static android.system.OsConstants.SOCK_STREAM;
20 
21 import android.system.ErrnoException;
22 import android.system.Os;
23 import android.util.Log;
24 
25 import com.android.internal.util.ArrayUtils;
26 
27 import libcore.io.IoBridge;
28 import libcore.io.IoUtils;
29 import libcore.io.Memory;
30 import libcore.io.Streams;
31 
32 import java.io.FileDescriptor;
33 import java.io.IOException;
34 import java.io.OutputStream;
35 import java.nio.ByteBuffer;
36 import java.nio.ByteOrder;
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  * @deprecated replaced by {@link RevocableFileDescriptor}
46  */
47 @Deprecated
48 public class FileBridge extends Thread {
49     private static final String TAG = "FileBridge";
50 
51     // TODO: consider extending to support bidirectional IO
52 
53     private static final int MSG_LENGTH = 8;
54 
55     /** CMD_WRITE [len] [data] */
56     private static final int CMD_WRITE = 1;
57     /** CMD_FSYNC */
58     private static final int CMD_FSYNC = 2;
59     /** CMD_CLOSE */
60     private static final int CMD_CLOSE = 3;
61 
62     private ParcelFileDescriptor mTarget;
63 
64     private ParcelFileDescriptor mServer;
65     private ParcelFileDescriptor mClient;
66 
67     private volatile boolean mClosed;
68 
FileBridge()69     public FileBridge() {
70         try {
71             ParcelFileDescriptor[] fds = ParcelFileDescriptor.createSocketPair(SOCK_STREAM);
72             mServer = fds[0];
73             mClient = fds[1];
74         } catch (IOException e) {
75             throw new RuntimeException("Failed to create bridge");
76         }
77     }
78 
isClosed()79     public boolean isClosed() {
80         return mClosed;
81     }
82 
forceClose()83     public void forceClose() {
84         IoUtils.closeQuietly(mTarget);
85         IoUtils.closeQuietly(mServer);
86         mClosed = true;
87     }
88 
setTargetFile(ParcelFileDescriptor target)89     public void setTargetFile(ParcelFileDescriptor target) {
90         mTarget = target;
91     }
92 
getClientSocket()93     public ParcelFileDescriptor getClientSocket() {
94         return mClient;
95     }
96 
97     @Override
run()98     public void run() {
99         final ByteBuffer tempBuffer = ByteBuffer.allocateDirect(8192);
100         final byte[] temp = tempBuffer.hasArray() ? tempBuffer.array() : new byte[8192];
101         try {
102             while (IoBridge.read(mServer.getFileDescriptor(), temp,
103                                  0, MSG_LENGTH) == MSG_LENGTH) {
104                 final int cmd = Memory.peekInt(temp, 0, ByteOrder.BIG_ENDIAN);
105                 if (cmd == CMD_WRITE) {
106                     // Shuttle data into local file
107                     int len = Memory.peekInt(temp, 4, ByteOrder.BIG_ENDIAN);
108                     while (len > 0) {
109                         int n = IoBridge.read(mServer.getFileDescriptor(), temp, 0,
110                                               Math.min(temp.length, len));
111                         if (n == -1) {
112                             throw new IOException(
113                                     "Unexpected EOF; still expected " + len + " bytes");
114                         }
115                         IoBridge.write(mTarget.getFileDescriptor(), temp, 0, n);
116                         len -= n;
117                     }
118 
119                 } else if (cmd == CMD_FSYNC) {
120                     // Sync and echo back to confirm
121                     Os.fsync(mTarget.getFileDescriptor());
122                     IoBridge.write(mServer.getFileDescriptor(), temp, 0, MSG_LENGTH);
123 
124                 } else if (cmd == CMD_CLOSE) {
125                     // Close and echo back to confirm
126                     Os.fsync(mTarget.getFileDescriptor());
127                     mTarget.close();
128                     mClosed = true;
129                     IoBridge.write(mServer.getFileDescriptor(), temp, 0, MSG_LENGTH);
130                     break;
131                 }
132             }
133 
134         } catch (ErrnoException | IOException e) {
135             Log.wtf(TAG, "Failed during bridge", e);
136         } finally {
137             forceClose();
138         }
139     }
140 
141     public static class FileBridgeOutputStream extends OutputStream {
142         private final ParcelFileDescriptor mClientPfd;
143         private final FileDescriptor mClient;
144         private final ByteBuffer mTempBuffer = ByteBuffer.allocateDirect(MSG_LENGTH);
145         private final byte[] mTemp = mTempBuffer.hasArray()
146                                      ? mTempBuffer.array()
147                                      : new byte[MSG_LENGTH];
148 
FileBridgeOutputStream(ParcelFileDescriptor clientPfd)149         public FileBridgeOutputStream(ParcelFileDescriptor clientPfd) {
150             mClientPfd = clientPfd;
151             mClient = clientPfd.getFileDescriptor();
152         }
153 
154         @Override
close()155         public void close() throws IOException {
156             try {
157                 writeCommandAndBlock(CMD_CLOSE, "close()");
158             } finally {
159                 IoUtils.closeQuietly(mClientPfd);
160             }
161         }
162 
fsync()163         public void fsync() throws IOException {
164             writeCommandAndBlock(CMD_FSYNC, "fsync()");
165         }
166 
writeCommandAndBlock(int cmd, String cmdString)167         private void writeCommandAndBlock(int cmd, String cmdString) throws IOException {
168             Memory.pokeInt(mTemp, 0, cmd, ByteOrder.BIG_ENDIAN);
169             IoBridge.write(mClient, mTemp, 0, MSG_LENGTH);
170 
171             // Wait for server to ack
172             if (IoBridge.read(mClient, mTemp, 0, MSG_LENGTH) == MSG_LENGTH) {
173                 if (Memory.peekInt(mTemp, 0, ByteOrder.BIG_ENDIAN) == cmd) {
174                     return;
175                 }
176             }
177 
178             throw new IOException("Failed to execute " + cmdString + " across bridge");
179         }
180 
181         @Override
write(byte[] buffer, int byteOffset, int byteCount)182         public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException {
183             ArrayUtils.throwsIfOutOfBounds(buffer.length, byteOffset, byteCount);
184             Memory.pokeInt(mTemp, 0, CMD_WRITE, ByteOrder.BIG_ENDIAN);
185             Memory.pokeInt(mTemp, 4, byteCount, ByteOrder.BIG_ENDIAN);
186             IoBridge.write(mClient, mTemp, 0, MSG_LENGTH);
187             IoBridge.write(mClient, buffer, byteOffset, byteCount);
188         }
189 
190         @Override
write(int oneByte)191         public void write(int oneByte) throws IOException {
192             Streams.writeSingleByte(this, oneByte);
193         }
194     }
195 }
196