1 /*
2  * Copyright (C) 2017 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.settings.bluetooth;
17 
18 import static com.google.common.truth.Truth.assertThat;
19 
20 import static org.mockito.ArgumentMatchers.any;
21 import static org.mockito.ArgumentMatchers.anyBoolean;
22 import static org.mockito.Mockito.mock;
23 import static org.mockito.Mockito.never;
24 import static org.mockito.Mockito.spy;
25 import static org.mockito.Mockito.verify;
26 import static org.mockito.Mockito.when;
27 
28 import android.bluetooth.BluetoothAdapter;
29 import android.content.BroadcastReceiver;
30 import android.content.ComponentName;
31 import android.content.Context;
32 import android.content.Intent;
33 import android.content.IntentFilter;
34 import android.os.UserHandle;
35 import android.os.UserManager;
36 import android.view.View;
37 
38 import androidx.preference.PreferenceViewHolder;
39 
40 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
41 import com.android.settings.widget.SwitchBar;
42 import com.android.settings.widget.SwitchBarController;
43 import com.android.settings.widget.SwitchWidgetController;
44 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
45 import com.android.settingslib.RestrictedSwitchPreference;
46 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
47 
48 import org.junit.Before;
49 import org.junit.BeforeClass;
50 import org.junit.Test;
51 import org.junit.runner.RunWith;
52 import org.mockito.ArgumentCaptor;
53 import org.mockito.Mock;
54 import org.mockito.MockitoAnnotations;
55 import org.robolectric.RobolectricTestRunner;
56 import org.robolectric.RuntimeEnvironment;
57 import org.robolectric.annotation.Config;
58 import org.robolectric.shadow.api.Shadow;
59 
60 @RunWith(RobolectricTestRunner.class)
61 @Config(shadows = ShadowBluetoothAdapter.class)
62 public class BluetoothEnablerTest {
63 
64     private static EnforcedAdmin sFakeEnforcedAdmin;
65     private PreferenceViewHolder mHolder;
66     private RestrictedSwitchPreference mRestrictedSwitchPreference;
67 
68     @BeforeClass
beforeClass()69     public static void beforeClass() {
70         sFakeEnforcedAdmin = new EnforcedAdmin(new ComponentName("test.package", "test.Class"),
71                 UserHandle.of(10));
72     }
73 
74     @Mock
75     private MetricsFeatureProvider mMetricsFeatureProvider;
76     @Mock
77     private RestrictionUtils mRestrictionUtils;
78     @Mock
79     private SwitchWidgetController.OnSwitchChangeListener mCallback;
80 
81     private Context mContext;
82     private SwitchWidgetController mSwitchController;
83     private BluetoothEnabler mBluetoothEnabler;
84     private ShadowBluetoothAdapter mShadowBluetoothAdapter;
85 
86     @Before
setUp()87     public void setUp() {
88         MockitoAnnotations.initMocks(this);
89         mContext = spy(RuntimeEnvironment.application);
90 
91         mRestrictedSwitchPreference = new RestrictedSwitchPreference(mContext);
92         mSwitchController = spy(new SwitchBarController(new SwitchBar(mContext)));
93         mBluetoothEnabler = new BluetoothEnabler(
94                 mContext,
95                 mSwitchController,
96                 mMetricsFeatureProvider,
97                 123,
98                 mRestrictionUtils);
99         mHolder = PreferenceViewHolder.createInstanceForTests(mock(View.class));
100         mRestrictedSwitchPreference.onBindViewHolder(mHolder);
101         mBluetoothEnabler.setToggleCallback(mCallback);
102         mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
103     }
104 
105     @Test
onSwitchToggled_shouldLogActionWithSuppliedEvent()106     public void onSwitchToggled_shouldLogActionWithSuppliedEvent() {
107         // WHEN the switch is toggled...
108         mBluetoothEnabler.onSwitchToggled(false);
109 
110         // THEN the corresponding metrics action is logged.
111         verify(mMetricsFeatureProvider).action(mContext, 123, false);
112     }
113 
114     @Test
onSwitchToggled_shouldTriggerCallback()115     public void onSwitchToggled_shouldTriggerCallback() {
116         // WHEN the switch is toggled...
117         mBluetoothEnabler.onSwitchToggled(false);
118 
119         // THEN the callback is triggered
120         verify(mCallback).onSwitchToggled(false);
121     }
122 
123     @Test
maybeEnforceRestrictions_noRestrictions()124     public void maybeEnforceRestrictions_noRestrictions() {
125         // GIVEN there are no restrictions set...
126         when(mRestrictionUtils.checkIfRestrictionEnforced(any(Context.class), any(String.class)))
127                 .thenReturn(null);
128 
129         // WHEN the maybeEnforceRestrictions is called...
130         // THEN false is returned to indicate there was no restriction to enforce
131         assertThat(mBluetoothEnabler.maybeEnforceRestrictions()).isFalse();
132 
133         // THEN a null EnfoceAdmin is set.
134         verify(mSwitchController).setDisabledByAdmin(null);
135         // THEN the state of the switch isn't changed.
136         verify(mSwitchController, never()).setChecked(anyBoolean());
137     }
138 
139     @Test
maybeEnforceRestrictions_disallowBluetoothRestrictionSet()140     public void maybeEnforceRestrictions_disallowBluetoothRestrictionSet() {
141         // GIVEN Bluetooth has been disallowed...
142         when(mRestrictionUtils.checkIfRestrictionEnforced(
143                 mContext, UserManager.DISALLOW_BLUETOOTH)).thenReturn(sFakeEnforcedAdmin);
144         when(mRestrictionUtils.checkIfRestrictionEnforced(
145                 mContext, UserManager.DISALLOW_CONFIG_BLUETOOTH)).thenReturn(null);
146 
147         // WHEN the maybeEnforceRestrictions is called...
148         // THEN true is returned to indicate there was a restriction to enforce.
149         assertThat(mBluetoothEnabler.maybeEnforceRestrictions()).isTrue();
150 
151         // THEN the expected EnfoceAdmin is set.
152         verify(mSwitchController).setDisabledByAdmin(sFakeEnforcedAdmin);
153 
154         // THEN the switch is unchecked.
155         verify(mSwitchController).setChecked(false);
156     }
157 
158     @Test
maybeEnforceRestrictions_disallowConfigBluetoothRestrictionSet()159     public void maybeEnforceRestrictions_disallowConfigBluetoothRestrictionSet() {
160         // GIVEN configuring Bluetooth has been disallowed...
161         when(mRestrictionUtils.checkIfRestrictionEnforced(
162                 mContext, UserManager.DISALLOW_BLUETOOTH)).thenReturn(null);
163         when(mRestrictionUtils.checkIfRestrictionEnforced(
164                 mContext, UserManager.DISALLOW_CONFIG_BLUETOOTH)).thenReturn(sFakeEnforcedAdmin);
165 
166         // WHEN the maybeEnforceRestrictions is called...
167         // THEN true is returned to indicate there was a restriction to enforce.
168         assertThat(mBluetoothEnabler.maybeEnforceRestrictions()).isTrue();
169 
170         // THEN the expected EnfoceAdmin is set.
171         verify(mSwitchController).setDisabledByAdmin(sFakeEnforcedAdmin);
172 
173         // THEN the switch is unchecked.
174         verify(mSwitchController).setChecked(false);
175     }
176 
177     @Test
maybeEnforceRestrictions_disallowBluetoothNotOverriden()178     public void maybeEnforceRestrictions_disallowBluetoothNotOverriden() {
179         // GIVEN Bluetooth has been disallowed...
180         when(mRestrictionUtils.checkIfRestrictionEnforced(
181                 mContext, UserManager.DISALLOW_BLUETOOTH)).thenReturn(sFakeEnforcedAdmin);
182         when(mRestrictionUtils.checkIfRestrictionEnforced(
183                 mContext, UserManager.DISALLOW_CONFIG_BLUETOOTH)).thenReturn(null);
184 
185         mBluetoothEnabler.resume(mContext);
186 
187         verify(mSwitchController, never()).setEnabled(true);
188     }
189 
190     @Test
startWithBluetoothOff_switchIsOff()191     public void startWithBluetoothOff_switchIsOff() {
192         mShadowBluetoothAdapter.setState(BluetoothAdapter.STATE_OFF);
193         verify(mSwitchController, never()).setChecked(anyBoolean());
194         mBluetoothEnabler.resume(mContext);
195         verify(mSwitchController, never()).setChecked(true);
196     }
197 
198     @Test
startWithBluetoothOn_switchIsOn()199     public void startWithBluetoothOn_switchIsOn() {
200         mShadowBluetoothAdapter.setState(BluetoothAdapter.STATE_ON);
201         verify(mSwitchController, never()).setChecked(anyBoolean());
202         mBluetoothEnabler.resume(mContext);
203         verify(mSwitchController, never()).setChecked(false);
204         verify(mSwitchController).setChecked(true);
205     }
206 
207     @Test
bluetoothTurnsOff_switchTurnsOff()208     public void bluetoothTurnsOff_switchTurnsOff() {
209         // Start up with bluetooth turned on. The switch should get turned on.
210         ArgumentCaptor<BroadcastReceiver> captor = ArgumentCaptor.forClass(BroadcastReceiver.class);
211         when(mContext.registerReceiver(captor.capture(), any(IntentFilter.class))).thenReturn(null);
212         mShadowBluetoothAdapter.setState(BluetoothAdapter.STATE_ON);
213         verify(mSwitchController, never()).setChecked(anyBoolean());
214         mBluetoothEnabler.resume(mContext);
215         verify(mSwitchController, never()).setChecked(false);
216         verify(mSwitchController).setChecked(true);
217 
218         // Now simulate bluetooth being turned off via an event.
219         BroadcastReceiver receiver = captor.getValue();
220         Intent turningOff = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
221         turningOff.putExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_TURNING_OFF);
222         receiver.onReceive(mContext, turningOff);
223         Intent off = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
224         off.putExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF);
225         receiver.onReceive(mContext, off);
226 
227         // Make sure the switch was turned off.
228         verify(mSwitchController).setChecked(false);
229     }
230 
231     @Test
bluetoothTurnsOn_switchTurnsOn()232     public void bluetoothTurnsOn_switchTurnsOn() {
233         // Start up with bluetooth turned on. The switch should be left off.
234         ArgumentCaptor<BroadcastReceiver> captor = ArgumentCaptor.forClass(BroadcastReceiver.class);
235         when(mContext.registerReceiver(captor.capture(), any(IntentFilter.class))).thenReturn(null);
236         mShadowBluetoothAdapter.setState(BluetoothAdapter.STATE_OFF);
237         verify(mSwitchController, never()).setChecked(anyBoolean());
238         mBluetoothEnabler.resume(mContext);
239         verify(mSwitchController, never()).setChecked(anyBoolean());
240 
241         // Now simulate bluetooth being turned on via an event.
242         BroadcastReceiver receiver = captor.getValue();
243         Intent turningOn = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
244         turningOn.putExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_TURNING_ON);
245         receiver.onReceive(mContext, turningOn);
246         Intent on = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
247         on.putExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_ON);
248         receiver.onReceive(mContext, on);
249 
250         // Make sure the switch was turned on.
251         verify(mSwitchController).setChecked(true);
252     }
253 }
254