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