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