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