1 /* 2 * Copyright 2019 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.bluetooth.btservice; 18 19 import static android.Manifest.permission.BLUETOOTH_CONNECT; 20 import static org.mockito.Mockito.*; 21 22 import android.bluetooth.BluetoothA2dp; 23 import android.bluetooth.BluetoothAdapter; 24 import android.bluetooth.BluetoothDevice; 25 import android.bluetooth.BluetoothHeadset; 26 import android.bluetooth.BluetoothProfile; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.os.Bundle; 30 import android.os.HandlerThread; 31 import android.os.Looper; 32 import android.os.UserHandle; 33 34 import androidx.test.InstrumentationRegistry; 35 import androidx.test.filters.MediumTest; 36 import androidx.test.runner.AndroidJUnit4; 37 38 import com.android.bluetooth.TestUtils; 39 import com.android.bluetooth.a2dp.A2dpService; 40 import com.android.bluetooth.hfp.HeadsetService; 41 42 import org.junit.After; 43 import org.junit.Assert; 44 import org.junit.Before; 45 import org.junit.Test; 46 import org.junit.runner.RunWith; 47 import org.mockito.ArgumentCaptor; 48 import org.mockito.Mock; 49 import org.mockito.MockitoAnnotations; 50 51 @MediumTest 52 @RunWith(AndroidJUnit4.class) 53 public class SilenceDeviceManagerTest { 54 private BluetoothAdapter mAdapter; 55 private Context mContext; 56 private BluetoothDevice mTestDevice; 57 private SilenceDeviceManager mSilenceDeviceManager; 58 private HandlerThread mHandlerThread; 59 private Looper mLooper; 60 private static final String TEST_BT_ADDR = "11:22:33:44:55:66"; 61 private int mVerifyCount = 0; 62 63 @Mock private AdapterService mAdapterService; 64 @Mock private ServiceFactory mServiceFactory; 65 @Mock private A2dpService mA2dpService; 66 @Mock private HeadsetService mHeadsetService; 67 68 69 @Before setUp()70 public void setUp() throws Exception { 71 mContext = InstrumentationRegistry.getTargetContext(); 72 73 // Set up mocks and test assets 74 MockitoAnnotations.initMocks(this); 75 TestUtils.setAdapterService(mAdapterService); 76 when(mServiceFactory.getA2dpService()).thenReturn(mA2dpService); 77 when(mServiceFactory.getHeadsetService()).thenReturn(mHeadsetService); 78 79 // Get devices for testing 80 mTestDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(TEST_BT_ADDR); 81 82 mHandlerThread = new HandlerThread("SilenceManagerTestHandlerThread"); 83 mHandlerThread.start(); 84 mLooper = mHandlerThread.getLooper(); 85 mSilenceDeviceManager = new SilenceDeviceManager(mAdapterService, mServiceFactory, 86 mLooper); 87 mSilenceDeviceManager.start(); 88 } 89 90 @After tearDown()91 public void tearDown() throws Exception { 92 mSilenceDeviceManager.cleanup(); 93 mHandlerThread.quit(); 94 TestUtils.clearAdapterService(mAdapterService); 95 } 96 97 @Test testSetGetDeviceSilence()98 public void testSetGetDeviceSilence() { 99 testSetGetDeviceSilenceConnectedCase(false, true); 100 testSetGetDeviceSilenceConnectedCase(false, false); 101 testSetGetDeviceSilenceConnectedCase(true, true); 102 testSetGetDeviceSilenceConnectedCase(true, false); 103 104 testSetGetDeviceSilenceDisconnectedCase(false); 105 testSetGetDeviceSilenceDisconnectedCase(true); 106 } 107 testSetGetDeviceSilenceConnectedCase(boolean wasSilenced, boolean enableSilence)108 void testSetGetDeviceSilenceConnectedCase(boolean wasSilenced, boolean enableSilence) { 109 ArgumentCaptor<Intent> intentArgument = ArgumentCaptor.forClass(Intent.class); 110 doReturn(true).when(mA2dpService).setSilenceMode(mTestDevice, enableSilence); 111 doReturn(true).when(mHeadsetService).setSilenceMode(mTestDevice, enableSilence); 112 113 // Send A2DP/HFP connected intent 114 a2dpConnected(mTestDevice); 115 headsetConnected(mTestDevice); 116 117 // Set pre-state for mSilenceDeviceManager 118 if (wasSilenced) { 119 Assert.assertTrue(mSilenceDeviceManager.setSilenceMode(mTestDevice, true)); 120 TestUtils.waitForLooperToFinishScheduledTask(mLooper); 121 verify(mAdapterService, times(++mVerifyCount)).sendBroadcastAsUser( 122 intentArgument.capture(), eq(UserHandle.ALL), 123 eq(BLUETOOTH_CONNECT), any(Bundle.class)); 124 } 125 126 // Set silence state and check whether state changed successfully 127 Assert.assertTrue(mSilenceDeviceManager.setSilenceMode(mTestDevice, enableSilence)); 128 TestUtils.waitForLooperToFinishScheduledTask(mLooper); 129 Assert.assertEquals(enableSilence, mSilenceDeviceManager.getSilenceMode(mTestDevice)); 130 131 // Check for silence state changed intent 132 if (wasSilenced != enableSilence) { 133 verify(mAdapterService, times(++mVerifyCount)).sendBroadcastAsUser( 134 intentArgument.capture(), eq(UserHandle.ALL), 135 eq(BLUETOOTH_CONNECT), any(Bundle.class)); 136 verifySilenceStateIntent(intentArgument.getValue()); 137 } 138 139 // Remove test devices 140 a2dpDisconnected(mTestDevice); 141 headsetDisconnected(mTestDevice); 142 143 Assert.assertFalse(mSilenceDeviceManager.getSilenceMode(mTestDevice)); 144 if (enableSilence) { 145 // If the silence mode is enabled, it should be automatically disabled 146 // after device is disconnected. 147 verify(mAdapterService, times(++mVerifyCount)).sendBroadcastAsUser( 148 intentArgument.capture(), eq(UserHandle.ALL), 149 eq(BLUETOOTH_CONNECT), any(Bundle.class)); 150 } 151 } 152 testSetGetDeviceSilenceDisconnectedCase(boolean enableSilence)153 void testSetGetDeviceSilenceDisconnectedCase(boolean enableSilence) { 154 ArgumentCaptor<Intent> intentArgument = ArgumentCaptor.forClass(Intent.class); 155 // Set silence mode and it should stay disabled 156 Assert.assertTrue(mSilenceDeviceManager.setSilenceMode(mTestDevice, enableSilence)); 157 TestUtils.waitForLooperToFinishScheduledTask(mLooper); 158 Assert.assertFalse(mSilenceDeviceManager.getSilenceMode(mTestDevice)); 159 160 // Should be no intent been broadcasted 161 verify(mAdapterService, times(mVerifyCount)).sendBroadcastAsUser( 162 intentArgument.capture(), eq(UserHandle.ALL), 163 eq(BLUETOOTH_CONNECT), any(Bundle.class)); 164 } 165 verifySilenceStateIntent(Intent intent)166 void verifySilenceStateIntent(Intent intent) { 167 Assert.assertEquals(BluetoothDevice.ACTION_SILENCE_MODE_CHANGED, intent.getAction()); 168 Assert.assertEquals(mTestDevice, intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)); 169 } 170 171 /** 172 * Helper to indicate A2dp connected for a device. 173 */ a2dpConnected(BluetoothDevice device)174 private void a2dpConnected(BluetoothDevice device) { 175 Intent intent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); 176 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 177 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_DISCONNECTED); 178 intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED); 179 mSilenceDeviceManager.getBroadcastReceiver().onReceive(mContext, intent); 180 TestUtils.waitForLooperToFinishScheduledTask(mLooper); 181 } 182 183 /** 184 * Helper to indicate A2dp disconnected for a device. 185 */ a2dpDisconnected(BluetoothDevice device)186 private void a2dpDisconnected(BluetoothDevice device) { 187 Intent intent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); 188 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 189 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTED); 190 intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED); 191 mSilenceDeviceManager.getBroadcastReceiver().onReceive(mContext, intent); 192 TestUtils.waitForLooperToFinishScheduledTask(mLooper); 193 } 194 195 /** 196 * Helper to indicate Headset connected for a device. 197 */ headsetConnected(BluetoothDevice device)198 private void headsetConnected(BluetoothDevice device) { 199 Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 200 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 201 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_DISCONNECTED); 202 intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED); 203 mSilenceDeviceManager.getBroadcastReceiver().onReceive(mContext, intent); 204 TestUtils.waitForLooperToFinishScheduledTask(mLooper); 205 } 206 207 /** 208 * Helper to indicate Headset disconnected for a device. 209 */ headsetDisconnected(BluetoothDevice device)210 private void headsetDisconnected(BluetoothDevice device) { 211 Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 212 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 213 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTED); 214 intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED); 215 mSilenceDeviceManager.getBroadcastReceiver().onReceive(mContext, intent); 216 TestUtils.waitForLooperToFinishScheduledTask(mLooper); 217 } 218 } 219 220