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 17 package android.os; 18 19 import static android.system.OsConstants.SOCK_STREAM; 20 21 import android.system.ErrnoException; 22 import android.system.Os; 23 import android.util.Log; 24 25 import libcore.io.IoBridge; 26 import libcore.io.IoUtils; 27 import libcore.io.Memory; 28 import libcore.io.Streams; 29 import libcore.util.ArrayUtils; 30 31 import java.io.FileDescriptor; 32 import java.io.IOException; 33 import java.io.OutputStream; 34 import java.nio.ByteOrder; 35 36 /** 37 * Simple bridge that allows file access across process boundaries without 38 * returning the underlying {@link FileDescriptor}. This is useful when the 39 * server side needs to strongly assert that a client side is completely 40 * hands-off. 41 * 42 * @hide 43 * @deprecated replaced by {@link RevocableFileDescriptor} 44 */ 45 @Deprecated 46 public 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 ParcelFileDescriptor mTarget; 61 62 private ParcelFileDescriptor mServer; 63 private ParcelFileDescriptor mClient; 64 65 private volatile boolean mClosed; 66 FileBridge()67 public FileBridge() { 68 try { 69 ParcelFileDescriptor[] fds = ParcelFileDescriptor.createSocketPair(SOCK_STREAM); 70 mServer = fds[0]; 71 mClient = fds[1]; 72 } catch (IOException e) { 73 throw new RuntimeException("Failed to create bridge"); 74 } 75 } 76 isClosed()77 public boolean isClosed() { 78 return mClosed; 79 } 80 forceClose()81 public void forceClose() { 82 IoUtils.closeQuietly(mTarget); 83 IoUtils.closeQuietly(mServer); 84 mClosed = true; 85 } 86 setTargetFile(ParcelFileDescriptor target)87 public void setTargetFile(ParcelFileDescriptor target) { 88 mTarget = target; 89 } 90 getClientSocket()91 public ParcelFileDescriptor getClientSocket() { 92 return mClient; 93 } 94 95 @Override run()96 public void run() { 97 final byte[] temp = new byte[8192]; 98 try { 99 while (IoBridge.read(mServer.getFileDescriptor(), 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.getFileDescriptor(), temp, 0, 106 Math.min(temp.length, len)); 107 if (n == -1) { 108 throw new IOException( 109 "Unexpected EOF; still expected " + len + " bytes"); 110 } 111 IoBridge.write(mTarget.getFileDescriptor(), temp, 0, n); 112 len -= n; 113 } 114 115 } else if (cmd == CMD_FSYNC) { 116 // Sync and echo back to confirm 117 Os.fsync(mTarget.getFileDescriptor()); 118 IoBridge.write(mServer.getFileDescriptor(), temp, 0, MSG_LENGTH); 119 120 } else if (cmd == CMD_CLOSE) { 121 // Close and echo back to confirm 122 Os.fsync(mTarget.getFileDescriptor()); 123 mTarget.close(); 124 mClosed = true; 125 IoBridge.write(mServer.getFileDescriptor(), temp, 0, MSG_LENGTH); 126 break; 127 } 128 } 129 130 } catch (ErrnoException | IOException e) { 131 Log.wtf(TAG, "Failed during bridge", e); 132 } finally { 133 forceClose(); 134 } 135 } 136 137 public static class FileBridgeOutputStream extends OutputStream { 138 private final ParcelFileDescriptor mClientPfd; 139 private final FileDescriptor mClient; 140 private final byte[] mTemp = new byte[MSG_LENGTH]; 141 FileBridgeOutputStream(ParcelFileDescriptor clientPfd)142 public FileBridgeOutputStream(ParcelFileDescriptor clientPfd) { 143 mClientPfd = clientPfd; 144 mClient = clientPfd.getFileDescriptor(); 145 } 146 147 @Override close()148 public void close() throws IOException { 149 try { 150 writeCommandAndBlock(CMD_CLOSE, "close()"); 151 } finally { 152 IoUtils.closeQuietly(mClientPfd); 153 } 154 } 155 fsync()156 public void fsync() throws IOException { 157 writeCommandAndBlock(CMD_FSYNC, "fsync()"); 158 } 159 writeCommandAndBlock(int cmd, String cmdString)160 private void writeCommandAndBlock(int cmd, String cmdString) throws IOException { 161 Memory.pokeInt(mTemp, 0, cmd, ByteOrder.BIG_ENDIAN); 162 IoBridge.write(mClient, mTemp, 0, MSG_LENGTH); 163 164 // Wait for server to ack 165 if (IoBridge.read(mClient, mTemp, 0, MSG_LENGTH) == MSG_LENGTH) { 166 if (Memory.peekInt(mTemp, 0, ByteOrder.BIG_ENDIAN) == cmd) { 167 return; 168 } 169 } 170 171 throw new IOException("Failed to execute " + cmdString + " across bridge"); 172 } 173 174 @Override write(byte[] buffer, int byteOffset, int byteCount)175 public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException { 176 ArrayUtils.throwsIfOutOfBounds(buffer.length, byteOffset, byteCount); 177 Memory.pokeInt(mTemp, 0, CMD_WRITE, ByteOrder.BIG_ENDIAN); 178 Memory.pokeInt(mTemp, 4, byteCount, ByteOrder.BIG_ENDIAN); 179 IoBridge.write(mClient, mTemp, 0, MSG_LENGTH); 180 IoBridge.write(mClient, buffer, byteOffset, byteCount); 181 } 182 183 @Override write(int oneByte)184 public void write(int oneByte) throws IOException { 185 Streams.writeSingleByte(this, oneByte); 186 } 187 } 188 } 189