1 /* 2 * Copyright 2021 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 com.android.libraries.testing.deviceshadower.shadows.bluetooth; 18 19 import android.bluetooth.BluetoothServerSocket; 20 import android.bluetooth.BluetoothSocket; 21 import android.net.LocalSocket; 22 import android.os.ParcelFileDescriptor; 23 24 import com.android.internal.annotations.VisibleForTesting; 25 import com.android.libraries.testing.deviceshadower.internal.DeviceShadowEnvironmentImpl; 26 import com.android.libraries.testing.deviceshadower.internal.bluetooth.connection.RfcommDelegate; 27 28 import org.robolectric.annotation.Implementation; 29 import org.robolectric.annotation.Implements; 30 import org.robolectric.annotation.RealObject; 31 import org.robolectric.shadow.api.Shadow; 32 import org.robolectric.util.ReflectionHelpers; 33 import org.robolectric.util.ReflectionHelpers.ClassParameter; 34 35 import java.io.FileDescriptor; 36 import java.io.IOException; 37 38 /** 39 * Placeholder for BluetoothServerSocket updates 40 */ 41 @Implements(BluetoothServerSocket.class) 42 public class ShadowBluetoothServerSocket { 43 44 @RealObject 45 BluetoothServerSocket mRealServerSocket; 46 ShadowBluetoothServerSocket()47 public ShadowBluetoothServerSocket() { 48 } 49 50 @Implementation accept(int timeout)51 public BluetoothSocket accept(int timeout) throws IOException { 52 FileDescriptor serverSocketFd = getServerSocketFileDescriptor(); 53 if (serverSocketFd == null) { 54 throw new IOException("socket is closed."); 55 } 56 RfcommDelegate local = getLocalRfcommDelegate(); 57 local.checkInterrupt(); 58 FileDescriptor clientFd = local.processNextConnectionRequest(serverSocketFd); 59 // configure the LocalSocket of the BluetoothServerSocket 60 BluetoothSocket internalSocket = ReflectionHelpers.getField(mRealServerSocket, "mSocket"); 61 ShadowLocalSocket internalLocalSocket = getLocalSocketShadow(internalSocket); 62 internalLocalSocket.setAncillaryFd(local.getServerFd(clientFd)); 63 64 // call original method 65 BluetoothSocket socket = Shadow.directlyOn(mRealServerSocket, BluetoothServerSocket.class, 66 "accept", ClassParameter.from(int.class, timeout)); 67 68 // setup local socket of the returned BluetoothSocket 69 String remoteAddress = socket.getRemoteDevice().getAddress(); 70 ShadowLocalSocket shadowLocalSocket = getLocalSocketShadow(socket); 71 shadowLocalSocket.setRemoteAddress(remoteAddress); 72 // init connection to client 73 local.initiateConnectToClient(clientFd, getPort()); 74 local.waitForConnectionEstablished(clientFd); 75 return socket; 76 } 77 78 @Implementation close()79 public void close() throws IOException { 80 getLocalRfcommDelegate().closeServerSocket(getServerSocketFileDescriptor()); 81 Shadow.directlyOn(mRealServerSocket, BluetoothServerSocket.class, "close"); 82 } 83 84 @VisibleForTesting getServerSocketFileDescriptor()85 FileDescriptor getServerSocketFileDescriptor() { 86 BluetoothSocket socket = ReflectionHelpers.getField(mRealServerSocket, "mSocket"); 87 ParcelFileDescriptor pfd = ReflectionHelpers.getField(socket, "mPfd"); 88 if (pfd == null) { 89 return null; 90 } 91 return pfd.getFileDescriptor(); 92 } 93 94 @VisibleForTesting getPort()95 int getPort() { 96 BluetoothSocket socket = ReflectionHelpers.getField(mRealServerSocket, "mSocket"); 97 return ReflectionHelpers.getField(socket, "mPort"); 98 } 99 getLocalSocketShadow(BluetoothSocket socket)100 private ShadowLocalSocket getLocalSocketShadow(BluetoothSocket socket) { 101 LocalSocket localSocket = ReflectionHelpers.getField(socket, "mSocket"); 102 return (ShadowLocalSocket) Shadow.extract(localSocket); 103 } 104 getLocalRfcommDelegate()105 private RfcommDelegate getLocalRfcommDelegate() { 106 return DeviceShadowEnvironmentImpl.getLocalBlueletImpl().getRfcommDelegate(); 107 } 108 } 109