178cc340c2de873d6995c283b777476f7237d690fJeff Sharkey/*
278cc340c2de873d6995c283b777476f7237d690fJeff Sharkey * Copyright (C) 2014 The Android Open Source Project
378cc340c2de873d6995c283b777476f7237d690fJeff Sharkey *
478cc340c2de873d6995c283b777476f7237d690fJeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License");
578cc340c2de873d6995c283b777476f7237d690fJeff Sharkey * you may not use this file except in compliance with the License.
678cc340c2de873d6995c283b777476f7237d690fJeff Sharkey * You may obtain a copy of the License at
778cc340c2de873d6995c283b777476f7237d690fJeff Sharkey *
878cc340c2de873d6995c283b777476f7237d690fJeff Sharkey *      http://www.apache.org/licenses/LICENSE-2.0
978cc340c2de873d6995c283b777476f7237d690fJeff Sharkey *
1078cc340c2de873d6995c283b777476f7237d690fJeff Sharkey * Unless required by applicable law or agreed to in writing, software
1178cc340c2de873d6995c283b777476f7237d690fJeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS,
1278cc340c2de873d6995c283b777476f7237d690fJeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1378cc340c2de873d6995c283b777476f7237d690fJeff Sharkey * See the License for the specific language governing permissions and
1478cc340c2de873d6995c283b777476f7237d690fJeff Sharkey * limitations under the License.
1578cc340c2de873d6995c283b777476f7237d690fJeff Sharkey */
1678cc340c2de873d6995c283b777476f7237d690fJeff Sharkey
1778cc340c2de873d6995c283b777476f7237d690fJeff Sharkeypackage android.os;
1878cc340c2de873d6995c283b777476f7237d690fJeff Sharkey
1978cc340c2de873d6995c283b777476f7237d690fJeff Sharkeyimport static android.system.OsConstants.AF_UNIX;
2078cc340c2de873d6995c283b777476f7237d690fJeff Sharkeyimport static android.system.OsConstants.SOCK_STREAM;
2178cc340c2de873d6995c283b777476f7237d690fJeff Sharkey
2278cc340c2de873d6995c283b777476f7237d690fJeff Sharkeyimport android.system.ErrnoException;
2378cc340c2de873d6995c283b777476f7237d690fJeff Sharkeyimport android.system.Os;
2478cc340c2de873d6995c283b777476f7237d690fJeff Sharkeyimport android.util.Log;
2578cc340c2de873d6995c283b777476f7237d690fJeff Sharkey
2678cc340c2de873d6995c283b777476f7237d690fJeff Sharkeyimport libcore.io.IoBridge;
2778cc340c2de873d6995c283b777476f7237d690fJeff Sharkeyimport libcore.io.IoUtils;
2878cc340c2de873d6995c283b777476f7237d690fJeff Sharkeyimport libcore.io.Memory;
2978cc340c2de873d6995c283b777476f7237d690fJeff Sharkeyimport libcore.io.Streams;
3078cc340c2de873d6995c283b777476f7237d690fJeff Sharkey
3178cc340c2de873d6995c283b777476f7237d690fJeff Sharkeyimport java.io.FileDescriptor;
3278cc340c2de873d6995c283b777476f7237d690fJeff Sharkeyimport java.io.IOException;
3378cc340c2de873d6995c283b777476f7237d690fJeff Sharkeyimport java.io.OutputStream;
3478cc340c2de873d6995c283b777476f7237d690fJeff Sharkeyimport java.nio.ByteOrder;
3578cc340c2de873d6995c283b777476f7237d690fJeff Sharkeyimport java.util.Arrays;
3678cc340c2de873d6995c283b777476f7237d690fJeff Sharkey
3778cc340c2de873d6995c283b777476f7237d690fJeff Sharkey/**
3878cc340c2de873d6995c283b777476f7237d690fJeff Sharkey * Simple bridge that allows file access across process boundaries without
3978cc340c2de873d6995c283b777476f7237d690fJeff Sharkey * returning the underlying {@link FileDescriptor}. This is useful when the
4078cc340c2de873d6995c283b777476f7237d690fJeff Sharkey * server side needs to strongly assert that a client side is completely
4178cc340c2de873d6995c283b777476f7237d690fJeff Sharkey * hands-off.
4278cc340c2de873d6995c283b777476f7237d690fJeff Sharkey *
4378cc340c2de873d6995c283b777476f7237d690fJeff Sharkey * @hide
4478cc340c2de873d6995c283b777476f7237d690fJeff Sharkey */
4578cc340c2de873d6995c283b777476f7237d690fJeff Sharkeypublic class FileBridge extends Thread {
4678cc340c2de873d6995c283b777476f7237d690fJeff Sharkey    private static final String TAG = "FileBridge";
4778cc340c2de873d6995c283b777476f7237d690fJeff Sharkey
4878cc340c2de873d6995c283b777476f7237d690fJeff Sharkey    // TODO: consider extending to support bidirectional IO
4978cc340c2de873d6995c283b777476f7237d690fJeff Sharkey
5078cc340c2de873d6995c283b777476f7237d690fJeff Sharkey    private static final int MSG_LENGTH = 8;
5178cc340c2de873d6995c283b777476f7237d690fJeff Sharkey
5278cc340c2de873d6995c283b777476f7237d690fJeff Sharkey    /** CMD_WRITE [len] [data] */
5378cc340c2de873d6995c283b777476f7237d690fJeff Sharkey    private static final int CMD_WRITE = 1;
5478cc340c2de873d6995c283b777476f7237d690fJeff Sharkey    /** CMD_FSYNC */
5578cc340c2de873d6995c283b777476f7237d690fJeff Sharkey    private static final int CMD_FSYNC = 2;
56ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey    /** CMD_CLOSE */
57ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey    private static final int CMD_CLOSE = 3;
5878cc340c2de873d6995c283b777476f7237d690fJeff Sharkey
5978cc340c2de873d6995c283b777476f7237d690fJeff Sharkey    private FileDescriptor mTarget;
6078cc340c2de873d6995c283b777476f7237d690fJeff Sharkey
6178cc340c2de873d6995c283b777476f7237d690fJeff Sharkey    private final FileDescriptor mServer = new FileDescriptor();
6278cc340c2de873d6995c283b777476f7237d690fJeff Sharkey    private final FileDescriptor mClient = new FileDescriptor();
6378cc340c2de873d6995c283b777476f7237d690fJeff Sharkey
6478cc340c2de873d6995c283b777476f7237d690fJeff Sharkey    private volatile boolean mClosed;
6578cc340c2de873d6995c283b777476f7237d690fJeff Sharkey
6678cc340c2de873d6995c283b777476f7237d690fJeff Sharkey    public FileBridge() {
6778cc340c2de873d6995c283b777476f7237d690fJeff Sharkey        try {
6878cc340c2de873d6995c283b777476f7237d690fJeff Sharkey            Os.socketpair(AF_UNIX, SOCK_STREAM, 0, mServer, mClient);
6978cc340c2de873d6995c283b777476f7237d690fJeff Sharkey        } catch (ErrnoException e) {
7078cc340c2de873d6995c283b777476f7237d690fJeff Sharkey            throw new RuntimeException("Failed to create bridge");
7178cc340c2de873d6995c283b777476f7237d690fJeff Sharkey        }
7278cc340c2de873d6995c283b777476f7237d690fJeff Sharkey    }
7378cc340c2de873d6995c283b777476f7237d690fJeff Sharkey
7478cc340c2de873d6995c283b777476f7237d690fJeff Sharkey    public boolean isClosed() {
7578cc340c2de873d6995c283b777476f7237d690fJeff Sharkey        return mClosed;
7678cc340c2de873d6995c283b777476f7237d690fJeff Sharkey    }
7778cc340c2de873d6995c283b777476f7237d690fJeff Sharkey
7877d218e1869e69c8d436b09cd11dcfe45e50b2cfJeff Sharkey    public void forceClose() {
7977d218e1869e69c8d436b09cd11dcfe45e50b2cfJeff Sharkey        IoUtils.closeQuietly(mTarget);
8077d218e1869e69c8d436b09cd11dcfe45e50b2cfJeff Sharkey        IoUtils.closeQuietly(mServer);
8177d218e1869e69c8d436b09cd11dcfe45e50b2cfJeff Sharkey        IoUtils.closeQuietly(mClient);
8277d218e1869e69c8d436b09cd11dcfe45e50b2cfJeff Sharkey        mClosed = true;
8377d218e1869e69c8d436b09cd11dcfe45e50b2cfJeff Sharkey    }
8477d218e1869e69c8d436b09cd11dcfe45e50b2cfJeff Sharkey
8578cc340c2de873d6995c283b777476f7237d690fJeff Sharkey    public void setTargetFile(FileDescriptor target) {
8678cc340c2de873d6995c283b777476f7237d690fJeff Sharkey        mTarget = target;
8778cc340c2de873d6995c283b777476f7237d690fJeff Sharkey    }
8878cc340c2de873d6995c283b777476f7237d690fJeff Sharkey
8978cc340c2de873d6995c283b777476f7237d690fJeff Sharkey    public FileDescriptor getClientSocket() {
9078cc340c2de873d6995c283b777476f7237d690fJeff Sharkey        return mClient;
9178cc340c2de873d6995c283b777476f7237d690fJeff Sharkey    }
9278cc340c2de873d6995c283b777476f7237d690fJeff Sharkey
9378cc340c2de873d6995c283b777476f7237d690fJeff Sharkey    @Override
9478cc340c2de873d6995c283b777476f7237d690fJeff Sharkey    public void run() {
9578cc340c2de873d6995c283b777476f7237d690fJeff Sharkey        final byte[] temp = new byte[8192];
9678cc340c2de873d6995c283b777476f7237d690fJeff Sharkey        try {
9778cc340c2de873d6995c283b777476f7237d690fJeff Sharkey            while (IoBridge.read(mServer, temp, 0, MSG_LENGTH) == MSG_LENGTH) {
9878cc340c2de873d6995c283b777476f7237d690fJeff Sharkey                final int cmd = Memory.peekInt(temp, 0, ByteOrder.BIG_ENDIAN);
9978cc340c2de873d6995c283b777476f7237d690fJeff Sharkey                if (cmd == CMD_WRITE) {
10078cc340c2de873d6995c283b777476f7237d690fJeff Sharkey                    // Shuttle data into local file
10178cc340c2de873d6995c283b777476f7237d690fJeff Sharkey                    int len = Memory.peekInt(temp, 4, ByteOrder.BIG_ENDIAN);
10278cc340c2de873d6995c283b777476f7237d690fJeff Sharkey                    while (len > 0) {
10378cc340c2de873d6995c283b777476f7237d690fJeff Sharkey                        int n = IoBridge.read(mServer, temp, 0, Math.min(temp.length, len));
1045f1ed727e27cc00267539974372f062104052f56Jeff Sharkey                        if (n == -1) {
1055f1ed727e27cc00267539974372f062104052f56Jeff Sharkey                            throw new IOException(
1065f1ed727e27cc00267539974372f062104052f56Jeff Sharkey                                    "Unexpected EOF; still expected " + len + " bytes");
1075f1ed727e27cc00267539974372f062104052f56Jeff Sharkey                        }
10878cc340c2de873d6995c283b777476f7237d690fJeff Sharkey                        IoBridge.write(mTarget, temp, 0, n);
10978cc340c2de873d6995c283b777476f7237d690fJeff Sharkey                        len -= n;
11078cc340c2de873d6995c283b777476f7237d690fJeff Sharkey                    }
11178cc340c2de873d6995c283b777476f7237d690fJeff Sharkey
11278cc340c2de873d6995c283b777476f7237d690fJeff Sharkey                } else if (cmd == CMD_FSYNC) {
11378cc340c2de873d6995c283b777476f7237d690fJeff Sharkey                    // Sync and echo back to confirm
11478cc340c2de873d6995c283b777476f7237d690fJeff Sharkey                    Os.fsync(mTarget);
11578cc340c2de873d6995c283b777476f7237d690fJeff Sharkey                    IoBridge.write(mServer, temp, 0, MSG_LENGTH);
116ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey
117ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey                } else if (cmd == CMD_CLOSE) {
118ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey                    // Close and echo back to confirm
119ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey                    Os.fsync(mTarget);
120ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey                    Os.close(mTarget);
121ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey                    mClosed = true;
122ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey                    IoBridge.write(mServer, temp, 0, MSG_LENGTH);
123ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey                    break;
12478cc340c2de873d6995c283b777476f7237d690fJeff Sharkey                }
12578cc340c2de873d6995c283b777476f7237d690fJeff Sharkey            }
12678cc340c2de873d6995c283b777476f7237d690fJeff Sharkey
12777d218e1869e69c8d436b09cd11dcfe45e50b2cfJeff Sharkey        } catch (ErrnoException | IOException e) {
128d3ca9917003a5e0650b559d58cf1eacf3b52bf34Jeff Sharkey            Log.wtf(TAG, "Failed during bridge", e);
12978cc340c2de873d6995c283b777476f7237d690fJeff Sharkey        } finally {
13077d218e1869e69c8d436b09cd11dcfe45e50b2cfJeff Sharkey            forceClose();
13178cc340c2de873d6995c283b777476f7237d690fJeff Sharkey        }
13278cc340c2de873d6995c283b777476f7237d690fJeff Sharkey    }
13378cc340c2de873d6995c283b777476f7237d690fJeff Sharkey
13478cc340c2de873d6995c283b777476f7237d690fJeff Sharkey    public static class FileBridgeOutputStream extends OutputStream {
1359a1507aa10577badabcbe00396613a967302e456Jeff Sharkey        private final ParcelFileDescriptor mClientPfd;
13678cc340c2de873d6995c283b777476f7237d690fJeff Sharkey        private final FileDescriptor mClient;
13778cc340c2de873d6995c283b777476f7237d690fJeff Sharkey        private final byte[] mTemp = new byte[MSG_LENGTH];
13878cc340c2de873d6995c283b777476f7237d690fJeff Sharkey
1399a1507aa10577badabcbe00396613a967302e456Jeff Sharkey        public FileBridgeOutputStream(ParcelFileDescriptor clientPfd) {
1409a1507aa10577badabcbe00396613a967302e456Jeff Sharkey            mClientPfd = clientPfd;
1419a1507aa10577badabcbe00396613a967302e456Jeff Sharkey            mClient = clientPfd.getFileDescriptor();
14278cc340c2de873d6995c283b777476f7237d690fJeff Sharkey        }
14378cc340c2de873d6995c283b777476f7237d690fJeff Sharkey
14473a821780334bc7972bca28d848cbce70cc3f825Jeff Sharkey        public FileBridgeOutputStream(FileDescriptor client) {
14573a821780334bc7972bca28d848cbce70cc3f825Jeff Sharkey            mClientPfd = null;
14673a821780334bc7972bca28d848cbce70cc3f825Jeff Sharkey            mClient = client;
14773a821780334bc7972bca28d848cbce70cc3f825Jeff Sharkey        }
14873a821780334bc7972bca28d848cbce70cc3f825Jeff Sharkey
14978cc340c2de873d6995c283b777476f7237d690fJeff Sharkey        @Override
15078cc340c2de873d6995c283b777476f7237d690fJeff Sharkey        public void close() throws IOException {
151ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey            try {
152ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey                writeCommandAndBlock(CMD_CLOSE, "close()");
153ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey            } finally {
154ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey                IoBridge.closeAndSignalBlockedThreads(mClient);
15577d218e1869e69c8d436b09cd11dcfe45e50b2cfJeff Sharkey                IoUtils.closeQuietly(mClientPfd);
156ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey            }
15778cc340c2de873d6995c283b777476f7237d690fJeff Sharkey        }
15878cc340c2de873d6995c283b777476f7237d690fJeff Sharkey
159a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey        public void fsync() throws IOException {
160ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey            writeCommandAndBlock(CMD_FSYNC, "fsync()");
161ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey        }
162ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey
163ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey        private void writeCommandAndBlock(int cmd, String cmdString) throws IOException {
164ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey            Memory.pokeInt(mTemp, 0, cmd, ByteOrder.BIG_ENDIAN);
16578cc340c2de873d6995c283b777476f7237d690fJeff Sharkey            IoBridge.write(mClient, mTemp, 0, MSG_LENGTH);
16678cc340c2de873d6995c283b777476f7237d690fJeff Sharkey
16778cc340c2de873d6995c283b777476f7237d690fJeff Sharkey            // Wait for server to ack
16878cc340c2de873d6995c283b777476f7237d690fJeff Sharkey            if (IoBridge.read(mClient, mTemp, 0, MSG_LENGTH) == MSG_LENGTH) {
169ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey                if (Memory.peekInt(mTemp, 0, ByteOrder.BIG_ENDIAN) == cmd) {
17078cc340c2de873d6995c283b777476f7237d690fJeff Sharkey                    return;
17178cc340c2de873d6995c283b777476f7237d690fJeff Sharkey                }
17278cc340c2de873d6995c283b777476f7237d690fJeff Sharkey            }
17378cc340c2de873d6995c283b777476f7237d690fJeff Sharkey
174ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey            throw new IOException("Failed to execute " + cmdString + " across bridge");
17578cc340c2de873d6995c283b777476f7237d690fJeff Sharkey        }
17678cc340c2de873d6995c283b777476f7237d690fJeff Sharkey
17778cc340c2de873d6995c283b777476f7237d690fJeff Sharkey        @Override
17878cc340c2de873d6995c283b777476f7237d690fJeff Sharkey        public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException {
17978cc340c2de873d6995c283b777476f7237d690fJeff Sharkey            Arrays.checkOffsetAndCount(buffer.length, byteOffset, byteCount);
18078cc340c2de873d6995c283b777476f7237d690fJeff Sharkey            Memory.pokeInt(mTemp, 0, CMD_WRITE, ByteOrder.BIG_ENDIAN);
18178cc340c2de873d6995c283b777476f7237d690fJeff Sharkey            Memory.pokeInt(mTemp, 4, byteCount, ByteOrder.BIG_ENDIAN);
18278cc340c2de873d6995c283b777476f7237d690fJeff Sharkey            IoBridge.write(mClient, mTemp, 0, MSG_LENGTH);
18378cc340c2de873d6995c283b777476f7237d690fJeff Sharkey            IoBridge.write(mClient, buffer, byteOffset, byteCount);
18478cc340c2de873d6995c283b777476f7237d690fJeff Sharkey        }
18578cc340c2de873d6995c283b777476f7237d690fJeff Sharkey
18678cc340c2de873d6995c283b777476f7237d690fJeff Sharkey        @Override
18778cc340c2de873d6995c283b777476f7237d690fJeff Sharkey        public void write(int oneByte) throws IOException {
18878cc340c2de873d6995c283b777476f7237d690fJeff Sharkey            Streams.writeSingleByte(this, oneByte);
18978cc340c2de873d6995c283b777476f7237d690fJeff Sharkey        }
19078cc340c2de873d6995c283b777476f7237d690fJeff Sharkey    }
19178cc340c2de873d6995c283b777476f7237d690fJeff Sharkey}
192