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