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