1 /*
2  * Copyright 2018 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 package com.android.bluetooth.hfp;
17 
18 import static org.mockito.ArgumentMatchers.any;
19 import static org.mockito.Mockito.*;
20 
21 import android.app.PropertyInvalidatedCache;
22 import android.bluetooth.BluetoothAdapter;
23 import android.bluetooth.BluetoothDevice;
24 import android.content.Context;
25 import android.os.HandlerThread;
26 import android.os.IBinder;
27 import android.os.Looper;
28 import android.os.ServiceManager;
29 import android.telephony.PhoneStateListener;
30 import android.telephony.SubscriptionManager;
31 import android.telephony.TelephonyManager;
32 
33 import androidx.test.filters.MediumTest;
34 import androidx.test.runner.AndroidJUnit4;
35 
36 import com.android.bluetooth.TestUtils;
37 import com.android.internal.telephony.ISub;
38 
39 import org.junit.After;
40 import org.junit.Before;
41 import org.junit.Test;
42 import org.junit.runner.RunWith;
43 import org.mockito.Mock;
44 import org.mockito.MockitoAnnotations;
45 
46 import java.util.HashMap;
47 
48 /**
49  * Unit test to verify various methods in {@link HeadsetPhoneState}
50  */
51 @MediumTest
52 @RunWith(AndroidJUnit4.class)
53 public class HeadsetPhoneStateTest {
54     @Mock private ISub mISub;
55     @Mock private IBinder mISubBinder;
56     @Mock private HeadsetService mHeadsetService;
57     @Mock private TelephonyManager mTelephonyManager;
58     @Mock private SubscriptionManager mSubscriptionManager;
59     private HeadsetPhoneState mHeadsetPhoneState;
60     private BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
61     private HandlerThread mHandlerThread;
62     private HashMap<String, IBinder> mServiceManagerMockedServices = new HashMap<>();
63     private Object mServiceManagerOriginalServices;
64 
65     @Before
setUp()66     public void setUp() throws Exception {
67         if (Looper.myLooper() == null) {
68             Looper.prepare();
69         }
70         PropertyInvalidatedCache.disableForTestMode();
71         MockitoAnnotations.initMocks(this);
72         SubscriptionManager.disableCaching();
73         TelephonyManager.disableServiceHandleCaching();
74         // Mock SubscriptionManager.getDefaultSubscriptionId() to return a valid value
75         when(mISub.getDefaultSubId()).thenReturn(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
76         when(mISubBinder.queryLocalInterface(anyString())).thenReturn(mISub);
77         mServiceManagerMockedServices.put("isub", mISubBinder);
78         mServiceManagerOriginalServices =
79                 TestUtils.replaceField(ServiceManager.class, "sCache", null,
80                         mServiceManagerMockedServices);
81         // Stub other methods
82         when(mHeadsetService.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(
83                 mTelephonyManager);
84         when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
85         when(mHeadsetService.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)).thenReturn(
86                 mSubscriptionManager);
87         mHandlerThread = new HandlerThread("HeadsetStateMachineTestHandlerThread");
88         mHandlerThread.start();
89         when(mHeadsetService.getStateMachinesThreadLooper()).thenReturn(mHandlerThread.getLooper());
90         mHeadsetPhoneState = new HeadsetPhoneState(mHeadsetService);
91     }
92 
93     @After
tearDown()94     public void tearDown() throws Exception {
95         mHeadsetPhoneState.cleanup();
96         mHandlerThread.quit();
97         if (mServiceManagerOriginalServices != null) {
98             TestUtils.replaceField(ServiceManager.class, "sCache", null,
99                     mServiceManagerOriginalServices);
100             mServiceManagerOriginalServices = null;
101         }
102         TelephonyManager.enableServiceHandleCaching();
103     }
104 
105     /**
106      * Verify that {@link PhoneStateListener#LISTEN_NONE} should result in no subscription
107      */
108     @Test
testListenForPhoneState_NoneResultsNoListen()109     public void testListenForPhoneState_NoneResultsNoListen() {
110         BluetoothDevice device1 = TestUtils.getTestDevice(mAdapter, 1);
111         mHeadsetPhoneState.listenForPhoneState(device1, PhoneStateListener.LISTEN_NONE);
112         verifyZeroInteractions(mTelephonyManager);
113     }
114 
115     /**
116      * Verify that {@link PhoneStateListener#LISTEN_SERVICE_STATE} should result in corresponding
117      * subscription
118      */
119     @Test
testListenForPhoneState_ServiceOnly()120     public void testListenForPhoneState_ServiceOnly() {
121         BluetoothDevice device1 = TestUtils.getTestDevice(mAdapter, 1);
122         mHeadsetPhoneState.listenForPhoneState(device1, PhoneStateListener.LISTEN_SERVICE_STATE);
123         verify(mTelephonyManager).listen(any(), eq(PhoneStateListener.LISTEN_SERVICE_STATE));
124         verifyNoMoreInteractions(mTelephonyManager);
125     }
126 
127     /**
128      * Verify that {@link PhoneStateListener#LISTEN_SERVICE_STATE} and
129      * {@link PhoneStateListener#LISTEN_SIGNAL_STRENGTHS} should result in corresponding
130      * subscription
131      */
132     @Test
testListenForPhoneState_ServiceAndSignalStrength()133     public void testListenForPhoneState_ServiceAndSignalStrength() {
134         BluetoothDevice device1 = TestUtils.getTestDevice(mAdapter, 1);
135         mHeadsetPhoneState.listenForPhoneState(device1, PhoneStateListener.LISTEN_SERVICE_STATE
136                 | PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH);
137         verify(mTelephonyManager).listen(any(), eq(PhoneStateListener.LISTEN_SERVICE_STATE
138                 | PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH));
139     }
140 
141     /**
142      * Verify that partially turnning off {@link PhoneStateListener#LISTEN_SIGNAL_STRENGTHS} update
143      * should only unsubscribe signal strength update
144      */
145     @Test
testListenForPhoneState_ServiceAndSignalStrengthUpdateTurnOffSignalStrengh()146     public void testListenForPhoneState_ServiceAndSignalStrengthUpdateTurnOffSignalStrengh() {
147         BluetoothDevice device1 = TestUtils.getTestDevice(mAdapter, 1);
148         mHeadsetPhoneState.listenForPhoneState(device1, PhoneStateListener.LISTEN_SERVICE_STATE
149                 | PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH);
150         verify(mTelephonyManager).listen(any(), eq(PhoneStateListener.LISTEN_SERVICE_STATE
151                 | PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH));
152         mHeadsetPhoneState.listenForPhoneState(device1, PhoneStateListener.LISTEN_SERVICE_STATE);
153         verify(mTelephonyManager).listen(any(), eq(PhoneStateListener.LISTEN_NONE));
154         verify(mTelephonyManager).listen(any(), eq(PhoneStateListener.LISTEN_SERVICE_STATE));
155     }
156 
157     /**
158      * Verify that completely disabling all updates should unsubscribe from everything
159      */
160     @Test
testListenForPhoneState_ServiceAndSignalStrengthUpdateTurnOffAll()161     public void testListenForPhoneState_ServiceAndSignalStrengthUpdateTurnOffAll() {
162         BluetoothDevice device1 = TestUtils.getTestDevice(mAdapter, 1);
163         mHeadsetPhoneState.listenForPhoneState(device1, PhoneStateListener.LISTEN_SERVICE_STATE
164                 | PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH);
165         verify(mTelephonyManager).listen(any(), eq(PhoneStateListener.LISTEN_SERVICE_STATE
166                 | PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH));
167         mHeadsetPhoneState.listenForPhoneState(device1, PhoneStateListener.LISTEN_NONE);
168         verify(mTelephonyManager).listen(any(), eq(PhoneStateListener.LISTEN_NONE));
169     }
170 
171     /**
172      * Verify that when multiple devices tries to subscribe to the same indicator, the same
173      * subscription is not triggered twice. Also, when one of the device is unsubsidised from an
174      * indicator, the other device still remain subscribed.
175      */
176     @Test
testListenForPhoneState_MultiDevice_AllUpAllDown()177     public void testListenForPhoneState_MultiDevice_AllUpAllDown() {
178         BluetoothDevice device1 = TestUtils.getTestDevice(mAdapter, 1);
179         BluetoothDevice device2 = TestUtils.getTestDevice(mAdapter, 2);
180         // Enabling updates from first device should trigger subscription
181         mHeadsetPhoneState.listenForPhoneState(device1, PhoneStateListener.LISTEN_SERVICE_STATE
182                 | PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH);
183         verify(mTelephonyManager).listen(any(), eq(PhoneStateListener.LISTEN_SERVICE_STATE
184                 | PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH));
185         // Enabling updates from second device should not trigger the same subscription
186         mHeadsetPhoneState.listenForPhoneState(device2, PhoneStateListener.LISTEN_SERVICE_STATE
187                 | PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH);
188         // Disabling updates from first device should not cancel subscription
189         mHeadsetPhoneState.listenForPhoneState(device1, PhoneStateListener.LISTEN_NONE);
190         // Disabling updates from second device should cancel subscription
191         mHeadsetPhoneState.listenForPhoneState(device2, PhoneStateListener.LISTEN_NONE);
192         verify(mTelephonyManager).listen(any(), eq(PhoneStateListener.LISTEN_NONE));
193     }
194 
195     /**
196      * Verity that two device each partially subscribe to an indicator result in subscription to
197      * both indicators. Also unsubscribing from one indicator from one device will not cause
198      * unrelated indicator from other device from being unsubscribed.
199      */
200     @Test
testListenForPhoneState_MultiDevice_PartialUpPartialDown()201     public void testListenForPhoneState_MultiDevice_PartialUpPartialDown() {
202         BluetoothDevice device1 = TestUtils.getTestDevice(mAdapter, 1);
203         BluetoothDevice device2 = TestUtils.getTestDevice(mAdapter, 2);
204         // Partially enabling updates from first device should trigger partial subscription
205         mHeadsetPhoneState.listenForPhoneState(device1, PhoneStateListener.LISTEN_SERVICE_STATE);
206         verify(mTelephonyManager).listen(any(), eq(PhoneStateListener.LISTEN_SERVICE_STATE));
207         verifyNoMoreInteractions(mTelephonyManager);
208         // Partially enabling updates from second device should trigger partial subscription
209         mHeadsetPhoneState.listenForPhoneState(device2,
210                 PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH);
211         verify(mTelephonyManager).listen(any(), eq(PhoneStateListener.LISTEN_NONE));
212         verify(mTelephonyManager).listen(any(), eq(PhoneStateListener.LISTEN_SERVICE_STATE
213                 | PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH));
214         // Partially disabling updates from first device should not cancel all subscription
215         mHeadsetPhoneState.listenForPhoneState(device1, PhoneStateListener.LISTEN_NONE);
216         verify(mTelephonyManager, times(2)).listen(any(), eq(PhoneStateListener.LISTEN_NONE));
217         verify(mTelephonyManager).listen(
218                 any(), eq(PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH));
219         // Partially disabling updates from second device should cancel subscription
220         mHeadsetPhoneState.listenForPhoneState(device2, PhoneStateListener.LISTEN_NONE);
221         verify(mTelephonyManager, times(3)).listen(any(), eq(PhoneStateListener.LISTEN_NONE));
222     }
223 }
224