1 /* 2 * Copyright (C) 2024 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.settings.bluetooth; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.mockito.ArgumentMatchers.any; 22 import static org.mockito.ArgumentMatchers.eq; 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.verifyNoInteractions; 27 import static org.mockito.Mockito.when; 28 29 import android.app.Notification; 30 import android.app.NotificationManager; 31 import android.bluetooth.BluetoothAdapter; 32 import android.bluetooth.BluetoothDevice; 33 import android.content.BroadcastReceiver; 34 import android.content.Context; 35 import android.content.Intent; 36 import android.content.pm.ApplicationInfo; 37 import android.content.pm.PackageManager; 38 import android.os.UserHandle; 39 import android.platform.test.annotations.DisableFlags; 40 import android.platform.test.annotations.EnableFlags; 41 import android.platform.test.flag.junit.SetFlagsRule; 42 43 import com.android.settings.flags.Flags; 44 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; 45 import com.android.settings.testutils.shadow.ShadowBluetoothUtils; 46 import com.android.settingslib.bluetooth.LocalBluetoothManager; 47 48 import org.junit.After; 49 import org.junit.Before; 50 import org.junit.Ignore; 51 import org.junit.Rule; 52 import org.junit.Test; 53 import org.junit.runner.RunWith; 54 import org.mockito.ArgumentCaptor; 55 import org.mockito.Mock; 56 import org.mockito.junit.MockitoJUnit; 57 import org.mockito.junit.MockitoRule; 58 import org.robolectric.RobolectricTestRunner; 59 import org.robolectric.RuntimeEnvironment; 60 import org.robolectric.annotation.Config; 61 import org.robolectric.shadow.api.Shadow; 62 import org.robolectric.shadows.ShadowApplication; 63 64 import java.util.List; 65 import java.util.stream.Collectors; 66 67 @RunWith(RobolectricTestRunner.class) 68 @Config(shadows = {ShadowBluetoothAdapter.class, ShadowBluetoothUtils.class}) 69 public class BluetoothKeyMissingReceiverTest { 70 @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); 71 @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); 72 73 private static final String TEST_EXCLUSIVE_MANAGER = "com.test.manager"; 74 75 private Context mContext; 76 private ShadowApplication mShadowApplication; 77 private ShadowBluetoothAdapter mShadowBluetoothAdapter; 78 @Mock private PackageManager mPackageManager; 79 @Mock private LocalBluetoothManager mLocalBtManager; 80 @Mock private NotificationManager mNm; 81 @Mock private BluetoothDevice mBluetoothDevice; 82 83 @Before setUp()84 public void setUp() { 85 mContext = spy(RuntimeEnvironment.getApplication()); 86 when(mContext.getPackageManager()).thenReturn(mPackageManager); 87 mShadowApplication = Shadow.extract(mContext); 88 mShadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm); 89 mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); 90 mShadowBluetoothAdapter.setEnabled(true); 91 ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager; 92 } 93 94 @After tearDown()95 public void tearDown() { 96 ShadowBluetoothUtils.reset(); 97 } 98 99 @Test broadcastReceiver_isRegistered()100 public void broadcastReceiver_isRegistered() { 101 List<ShadowApplication.Wrapper> registeredReceivers = 102 mShadowApplication.getRegisteredReceivers(); 103 104 int matchedCount = 105 registeredReceivers.stream() 106 .filter( 107 receiver -> 108 BluetoothKeyMissingReceiver.class 109 .getSimpleName() 110 .equals( 111 receiver.broadcastReceiver 112 .getClass() 113 .getSimpleName())) 114 .collect(Collectors.toList()) 115 .size(); 116 assertThat(matchedCount).isEqualTo(1); 117 } 118 119 @Test 120 @DisableFlags(Flags.FLAG_ENABLE_BLUETOOTH_KEY_MISSING_DIALOG) broadcastReceiver_receiveKeyMissingIntentFlagOff_doNothing()121 public void broadcastReceiver_receiveKeyMissingIntentFlagOff_doNothing() { 122 Intent intent = spy(new Intent(BluetoothDevice.ACTION_KEY_MISSING)); 123 when(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)).thenReturn(mBluetoothDevice); 124 BluetoothKeyMissingReceiver bluetoothKeyMissingReceiver = getReceiver(intent); 125 bluetoothKeyMissingReceiver.onReceive(mContext, intent); 126 127 verifyNoInteractions(mNm); 128 } 129 130 @Test 131 @EnableFlags(Flags.FLAG_ENABLE_BLUETOOTH_KEY_MISSING_DIALOG) broadcastReceiver_exclusiveManaged_skip()132 public void broadcastReceiver_exclusiveManaged_skip() throws Exception { 133 Intent intent = spy(new Intent(BluetoothDevice.ACTION_KEY_MISSING)); 134 when(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)).thenReturn(mBluetoothDevice); 135 when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); 136 when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn( 137 TEST_EXCLUSIVE_MANAGER.getBytes()); 138 when(mPackageManager.getApplicationInfo( 139 TEST_EXCLUSIVE_MANAGER, 0)).thenReturn(new ApplicationInfo()); 140 BluetoothKeyMissingReceiver bluetoothKeyMissingReceiver = getReceiver(intent); 141 142 bluetoothKeyMissingReceiver.onReceive(mContext, intent); 143 144 verifyNoInteractions(mNm); 145 verify(mContext, never()).startActivityAsUser(any(), any()); 146 } 147 148 @Test 149 @Ignore("Cannot test reflection") 150 @EnableFlags(Flags.FLAG_ENABLE_BLUETOOTH_KEY_MISSING_DIALOG) broadcastReceiver_background_showNotification()151 public void broadcastReceiver_background_showNotification() { 152 Intent intent = spy(new Intent(BluetoothDevice.ACTION_KEY_MISSING)); 153 when(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)).thenReturn(mBluetoothDevice); 154 when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); 155 BluetoothKeyMissingReceiver bluetoothKeyMissingReceiver = getReceiver(intent); 156 bluetoothKeyMissingReceiver.onReceive(mContext, intent); 157 158 verify(mNm).notify(eq(android.R.drawable.stat_sys_data_bluetooth), any(Notification.class)); 159 verify(mContext, never()).startActivityAsUser(any(), any()); 160 } 161 162 @Test 163 @Ignore("Cannot test reflection") 164 @EnableFlags(Flags.FLAG_ENABLE_BLUETOOTH_KEY_MISSING_DIALOG) broadcastReceiver_foreground_receiveKeyMissingIntent_showDialog()165 public void broadcastReceiver_foreground_receiveKeyMissingIntent_showDialog() { 166 when(mLocalBtManager.isForegroundActivity()).thenReturn(true); 167 Intent intent = spy(new Intent(BluetoothDevice.ACTION_KEY_MISSING)); 168 when(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)).thenReturn(mBluetoothDevice); 169 when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); 170 BluetoothKeyMissingReceiver bluetoothKeyMissingReceiver = getReceiver(intent); 171 bluetoothKeyMissingReceiver.onReceive(mContext, intent); 172 173 verifyNoInteractions(mNm); 174 ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); 175 verify(mContext).startActivityAsUser(captor.capture(), eq(UserHandle.CURRENT)); 176 assertThat(captor.getValue().getComponent().getClassName()) 177 .isEqualTo(BluetoothKeyMissingDialog.class.getName()); 178 } 179 getReceiver(Intent intent)180 private BluetoothKeyMissingReceiver getReceiver(Intent intent) { 181 assertThat(mShadowApplication.hasReceiverForIntent(intent)).isTrue(); 182 List<BroadcastReceiver> receiversForIntent = 183 mShadowApplication.getReceiversForIntent(intent); 184 assertThat(receiversForIntent).hasSize(1); 185 BroadcastReceiver broadcastReceiver = receiversForIntent.get(0); 186 assertThat(broadcastReceiver).isInstanceOf(BluetoothKeyMissingReceiver.class); 187 return (BluetoothKeyMissingReceiver) broadcastReceiver; 188 } 189 } 190