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