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