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.net.LocalSocket; 20 21 import com.android.internal.annotations.VisibleForTesting; 22 import com.android.libraries.testing.deviceshadower.internal.DeviceShadowEnvironmentImpl; 23 import com.android.libraries.testing.deviceshadower.internal.bluetooth.BluetoothConstants; 24 import com.android.libraries.testing.deviceshadower.internal.bluetooth.connection.RfcommDelegate; 25 26 import org.robolectric.annotation.Implementation; 27 import org.robolectric.annotation.Implements; 28 29 import java.io.FileDescriptor; 30 import java.io.IOException; 31 import java.io.InputStream; 32 import java.io.OutputStream; 33 34 /** 35 * Shadow implementation of a LocalSocket to make bluetooth connections function. 36 */ 37 @Implements(LocalSocket.class) 38 public class ShadowLocalSocket { 39 40 private String mRemoteAddress; 41 private FileDescriptor mFd; 42 private FileDescriptor mAncillaryFd; 43 ShadowLocalSocket()44 public ShadowLocalSocket() { 45 } 46 __constructor__(FileDescriptor fd)47 public void __constructor__(FileDescriptor fd) { 48 this.mFd = fd; 49 } 50 51 @Implementation getAncillaryFileDescriptors()52 public FileDescriptor[] getAncillaryFileDescriptors() throws IOException { 53 return new FileDescriptor[]{mAncillaryFd}; 54 } 55 56 @Implementation 57 @SuppressWarnings("InputStreamSlowMultibyteRead") getInputStream()58 public InputStream getInputStream() throws IOException { 59 final RfcommDelegate local = getLocalRfcommDelegate(); 60 return new InputStream() { 61 @Override 62 public int read() throws IOException { 63 int res = local.read(mRemoteAddress, mFd); 64 if (res == BluetoothConstants.SOCKET_CLOSE) { 65 throw new IOException("closed"); 66 } 67 return res & 0xFF; 68 } 69 }; 70 } 71 72 @Implementation 73 public OutputStream getOutputStream() throws IOException { 74 final RfcommDelegate local = getLocalRfcommDelegate(); 75 return new OutputStream() { 76 @Override 77 public void write(int b) throws IOException { 78 local.write(mRemoteAddress, mFd, b); 79 } 80 }; 81 } 82 83 @Implementation 84 public void setSoTimeout(int n) throws IOException { 85 // Nothing 86 } 87 88 @Implementation 89 public void shutdownInput() throws IOException { 90 getLocalRfcommDelegate().shutdownInput(mRemoteAddress, mFd); 91 } 92 93 @Implementation 94 public void shutdownOutput() throws IOException { 95 if (mRemoteAddress == null) { 96 return; 97 } 98 getLocalRfcommDelegate().shutdownOutput(mRemoteAddress, mFd); 99 } 100 101 void setAncillaryFd(FileDescriptor fd) { 102 mAncillaryFd = fd; 103 } 104 105 void setRemoteAddress(String address) { 106 mRemoteAddress = address; 107 } 108 109 @VisibleForTesting 110 void setFileDescriptorForTest(FileDescriptor fd) { 111 this.mFd = fd; 112 } 113 114 private RfcommDelegate getLocalRfcommDelegate() { 115 return DeviceShadowEnvironmentImpl.getLocalBlueletImpl().getRfcommDelegate(); 116 } 117 } 118