CachedBluetoothDeviceTest.java revision f050932e2a7a35f9d64a77fcdaff475bdab8f749
16258aae54824a7c3cd3974822b0b6835ab8a45deJack He/* 26258aae54824a7c3cd3974822b0b6835ab8a45deJack He * Copyright (C) 2017 The Android Open Source Project 36258aae54824a7c3cd3974822b0b6835ab8a45deJack He * 46258aae54824a7c3cd3974822b0b6835ab8a45deJack He * Licensed under the Apache License, Version 2.0 (the "License"); 56258aae54824a7c3cd3974822b0b6835ab8a45deJack He * you may not use this file except in compliance with the License. 66258aae54824a7c3cd3974822b0b6835ab8a45deJack He * You may obtain a copy of the License at 76258aae54824a7c3cd3974822b0b6835ab8a45deJack He * 86258aae54824a7c3cd3974822b0b6835ab8a45deJack He * http://www.apache.org/licenses/LICENSE-2.0 96258aae54824a7c3cd3974822b0b6835ab8a45deJack He * 106258aae54824a7c3cd3974822b0b6835ab8a45deJack He * Unless required by applicable law or agreed to in writing, software 116258aae54824a7c3cd3974822b0b6835ab8a45deJack He * distributed under the License is distributed on an "AS IS" BASIS, 126258aae54824a7c3cd3974822b0b6835ab8a45deJack He * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136258aae54824a7c3cd3974822b0b6835ab8a45deJack He * See the License for the specific language governing permissions and 146258aae54824a7c3cd3974822b0b6835ab8a45deJack He * limitations under the License. 156258aae54824a7c3cd3974822b0b6835ab8a45deJack He */ 166258aae54824a7c3cd3974822b0b6835ab8a45deJack Hepackage com.android.settingslib.bluetooth; 176258aae54824a7c3cd3974822b0b6835ab8a45deJack He 186258aae54824a7c3cd3974822b0b6835ab8a45deJack Heimport static com.google.common.truth.Truth.assertThat; 19c219bc9b884f70b087b2d55412b128846a5d0ce1Jack Heimport static org.mockito.Matchers.any; 20c219bc9b884f70b087b2d55412b128846a5d0ce1Jack Heimport static org.mockito.Matchers.anyString; 216258aae54824a7c3cd3974822b0b6835ab8a45deJack Heimport static org.mockito.Mockito.doAnswer; 22c219bc9b884f70b087b2d55412b128846a5d0ce1Jack Heimport static org.mockito.Mockito.never; 236258aae54824a7c3cd3974822b0b6835ab8a45deJack Heimport static org.mockito.Mockito.spy; 24c219bc9b884f70b087b2d55412b128846a5d0ce1Jack Heimport static org.mockito.Mockito.verify; 256258aae54824a7c3cd3974822b0b6835ab8a45deJack Heimport static org.mockito.Mockito.when; 26f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypengimport static org.robolectric.Shadows.shadowOf; 276258aae54824a7c3cd3974822b0b6835ab8a45deJack He 286258aae54824a7c3cd3974822b0b6835ab8a45deJack Heimport android.bluetooth.BluetoothAdapter; 296258aae54824a7c3cd3974822b0b6835ab8a45deJack Heimport android.bluetooth.BluetoothDevice; 306258aae54824a7c3cd3974822b0b6835ab8a45deJack Heimport android.bluetooth.BluetoothProfile; 316258aae54824a7c3cd3974822b0b6835ab8a45deJack Heimport android.content.Context; 32f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypengimport android.media.AudioManager; 33f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng 34f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypengimport com.android.settingslib.SettingsLibRobolectricTestRunner; 356258aae54824a7c3cd3974822b0b6835ab8a45deJack He 366258aae54824a7c3cd3974822b0b6835ab8a45deJack Heimport org.junit.Before; 376258aae54824a7c3cd3974822b0b6835ab8a45deJack Heimport org.junit.Test; 386258aae54824a7c3cd3974822b0b6835ab8a45deJack Heimport org.junit.runner.RunWith; 396258aae54824a7c3cd3974822b0b6835ab8a45deJack Heimport org.mockito.Mock; 406258aae54824a7c3cd3974822b0b6835ab8a45deJack Heimport org.mockito.MockitoAnnotations; 416258aae54824a7c3cd3974822b0b6835ab8a45deJack Heimport org.robolectric.RuntimeEnvironment; 42f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypengimport org.robolectric.shadows.ShadowAudioManager; 436258aae54824a7c3cd3974822b0b6835ab8a45deJack He 44f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng@RunWith(SettingsLibRobolectricTestRunner.class) 456258aae54824a7c3cd3974822b0b6835ab8a45deJack Hepublic class CachedBluetoothDeviceTest { 46c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He private final static String DEVICE_NAME = "TestName"; 47c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He private final static String DEVICE_ALIAS = "TestAlias"; 48c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He private final static String DEVICE_ADDRESS = "AA:BB:CC:DD:EE:FF"; 49c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He private final static String DEVICE_ALIAS_NEW = "TestAliasNew"; 506258aae54824a7c3cd3974822b0b6835ab8a45deJack He @Mock 516258aae54824a7c3cd3974822b0b6835ab8a45deJack He private LocalBluetoothAdapter mAdapter; 526258aae54824a7c3cd3974822b0b6835ab8a45deJack He @Mock 536258aae54824a7c3cd3974822b0b6835ab8a45deJack He private LocalBluetoothProfileManager mProfileManager; 546258aae54824a7c3cd3974822b0b6835ab8a45deJack He @Mock 556258aae54824a7c3cd3974822b0b6835ab8a45deJack He private HeadsetProfile mHfpProfile; 566258aae54824a7c3cd3974822b0b6835ab8a45deJack He @Mock 576258aae54824a7c3cd3974822b0b6835ab8a45deJack He private A2dpProfile mA2dpProfile; 586258aae54824a7c3cd3974822b0b6835ab8a45deJack He @Mock 591c1bc25dfc1c68fb7c8955ac5767136272dd2aafHansong Zhang private PanProfile mPanProfile; 606258aae54824a7c3cd3974822b0b6835ab8a45deJack He @Mock 61d7b359165968199e034bd46ca97526dbf719602dHansong Zhang private HearingAidProfile mHearingAidProfile; 62d7b359165968199e034bd46ca97526dbf719602dHansong Zhang @Mock 636258aae54824a7c3cd3974822b0b6835ab8a45deJack He private BluetoothDevice mDevice; 646258aae54824a7c3cd3974822b0b6835ab8a45deJack He private CachedBluetoothDevice mCachedDevice; 65f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng private ShadowAudioManager mShadowAudioManager; 666258aae54824a7c3cd3974822b0b6835ab8a45deJack He private Context mContext; 676258aae54824a7c3cd3974822b0b6835ab8a45deJack He private int mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 686258aae54824a7c3cd3974822b0b6835ab8a45deJack He 696258aae54824a7c3cd3974822b0b6835ab8a45deJack He @Before 706258aae54824a7c3cd3974822b0b6835ab8a45deJack He public void setUp() { 716258aae54824a7c3cd3974822b0b6835ab8a45deJack He MockitoAnnotations.initMocks(this); 726258aae54824a7c3cd3974822b0b6835ab8a45deJack He mContext = RuntimeEnvironment.application; 73f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng mShadowAudioManager = shadowOf(mContext.getSystemService(AudioManager.class)); 74c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He when(mDevice.getAddress()).thenReturn(DEVICE_ADDRESS); 756258aae54824a7c3cd3974822b0b6835ab8a45deJack He when(mAdapter.getBluetoothState()).thenReturn(BluetoothAdapter.STATE_ON); 766258aae54824a7c3cd3974822b0b6835ab8a45deJack He when(mHfpProfile.isProfileReady()).thenReturn(true); 776258aae54824a7c3cd3974822b0b6835ab8a45deJack He when(mA2dpProfile.isProfileReady()).thenReturn(true); 781c1bc25dfc1c68fb7c8955ac5767136272dd2aafHansong Zhang when(mPanProfile.isProfileReady()).thenReturn(true); 79d7b359165968199e034bd46ca97526dbf719602dHansong Zhang when(mHearingAidProfile.isProfileReady()).thenReturn(true); 806258aae54824a7c3cd3974822b0b6835ab8a45deJack He mCachedDevice = spy( 816258aae54824a7c3cd3974822b0b6835ab8a45deJack He new CachedBluetoothDevice(mContext, mAdapter, mProfileManager, mDevice)); 826258aae54824a7c3cd3974822b0b6835ab8a45deJack He doAnswer((invocation) -> mBatteryLevel).when(mCachedDevice).getBatteryLevel(); 836258aae54824a7c3cd3974822b0b6835ab8a45deJack He } 846258aae54824a7c3cd3974822b0b6835ab8a45deJack He 856258aae54824a7c3cd3974822b0b6835ab8a45deJack He @Test 866258aae54824a7c3cd3974822b0b6835ab8a45deJack He public void testGetConnectionSummary_testSingleProfileConnectDisconnect() { 876258aae54824a7c3cd3974822b0b6835ab8a45deJack He // Test without battery level 881c1bc25dfc1c68fb7c8955ac5767136272dd2aafHansong Zhang // Set PAN profile to be connected and test connection state summary 891c1bc25dfc1c68fb7c8955ac5767136272dd2aafHansong Zhang mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED); 90f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng assertThat(mCachedDevice.getConnectionSummary()).isNull(); 916258aae54824a7c3cd3974822b0b6835ab8a45deJack He 921c1bc25dfc1c68fb7c8955ac5767136272dd2aafHansong Zhang // Set PAN profile to be disconnected and test connection state summary 931c1bc25dfc1c68fb7c8955ac5767136272dd2aafHansong Zhang mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED); 946258aae54824a7c3cd3974822b0b6835ab8a45deJack He assertThat(mCachedDevice.getConnectionSummary()).isNull(); 956258aae54824a7c3cd3974822b0b6835ab8a45deJack He 966258aae54824a7c3cd3974822b0b6835ab8a45deJack He // Test with battery level 976258aae54824a7c3cd3974822b0b6835ab8a45deJack He mBatteryLevel = 10; 981c1bc25dfc1c68fb7c8955ac5767136272dd2aafHansong Zhang // Set PAN profile to be connected and test connection state summary 991c1bc25dfc1c68fb7c8955ac5767136272dd2aafHansong Zhang mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED); 100f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("10% battery"); 1016258aae54824a7c3cd3974822b0b6835ab8a45deJack He 1021c1bc25dfc1c68fb7c8955ac5767136272dd2aafHansong Zhang // Set PAN profile to be disconnected and test connection state summary 1031c1bc25dfc1c68fb7c8955ac5767136272dd2aafHansong Zhang mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED); 1046258aae54824a7c3cd3974822b0b6835ab8a45deJack He assertThat(mCachedDevice.getConnectionSummary()).isNull(); 1056258aae54824a7c3cd3974822b0b6835ab8a45deJack He 1066258aae54824a7c3cd3974822b0b6835ab8a45deJack He // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level 1076258aae54824a7c3cd3974822b0b6835ab8a45deJack He mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 1086258aae54824a7c3cd3974822b0b6835ab8a45deJack He 1091c1bc25dfc1c68fb7c8955ac5767136272dd2aafHansong Zhang // Set PAN profile to be connected and test connection state summary 1101c1bc25dfc1c68fb7c8955ac5767136272dd2aafHansong Zhang mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED); 111f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng assertThat(mCachedDevice.getConnectionSummary()).isNull(); 1126258aae54824a7c3cd3974822b0b6835ab8a45deJack He 1131c1bc25dfc1c68fb7c8955ac5767136272dd2aafHansong Zhang // Set PAN profile to be disconnected and test connection state summary 1141c1bc25dfc1c68fb7c8955ac5767136272dd2aafHansong Zhang mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED); 1156258aae54824a7c3cd3974822b0b6835ab8a45deJack He assertThat(mCachedDevice.getConnectionSummary()).isNull(); 1166258aae54824a7c3cd3974822b0b6835ab8a45deJack He } 1176258aae54824a7c3cd3974822b0b6835ab8a45deJack He 1186258aae54824a7c3cd3974822b0b6835ab8a45deJack He @Test 1196258aae54824a7c3cd3974822b0b6835ab8a45deJack He public void testGetConnectionSummary_testMultipleProfileConnectDisconnect() { 1206258aae54824a7c3cd3974822b0b6835ab8a45deJack He mBatteryLevel = 10; 1216258aae54824a7c3cd3974822b0b6835ab8a45deJack He 1221c1bc25dfc1c68fb7c8955ac5767136272dd2aafHansong Zhang // Set HFP, A2DP and PAN profile to be connected and test connection state summary 1236258aae54824a7c3cd3974822b0b6835ab8a45deJack He mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED); 1246258aae54824a7c3cd3974822b0b6835ab8a45deJack He mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); 1251c1bc25dfc1c68fb7c8955ac5767136272dd2aafHansong Zhang mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED); 126f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("10% battery"); 1276258aae54824a7c3cd3974822b0b6835ab8a45deJack He 1286258aae54824a7c3cd3974822b0b6835ab8a45deJack He // Disconnect HFP only and test connection state summary 1296258aae54824a7c3cd3974822b0b6835ab8a45deJack He mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); 130e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov assertThat(mCachedDevice.getConnectionSummary()).isEqualTo( 131f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng "10% battery"); 1326258aae54824a7c3cd3974822b0b6835ab8a45deJack He 1336258aae54824a7c3cd3974822b0b6835ab8a45deJack He // Disconnect A2DP only and test connection state summary 1346258aae54824a7c3cd3974822b0b6835ab8a45deJack He mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED); 1356258aae54824a7c3cd3974822b0b6835ab8a45deJack He mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED); 136e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov assertThat(mCachedDevice.getConnectionSummary()).isEqualTo( 137f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng "10% battery"); 1386258aae54824a7c3cd3974822b0b6835ab8a45deJack He 1396258aae54824a7c3cd3974822b0b6835ab8a45deJack He // Disconnect both HFP and A2DP and test connection state summary 1406258aae54824a7c3cd3974822b0b6835ab8a45deJack He mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); 141e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov assertThat(mCachedDevice.getConnectionSummary()).isEqualTo( 142f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng "10% battery"); 1436258aae54824a7c3cd3974822b0b6835ab8a45deJack He 1446258aae54824a7c3cd3974822b0b6835ab8a45deJack He // Disconnect all profiles and test connection state summary 1451c1bc25dfc1c68fb7c8955ac5767136272dd2aafHansong Zhang mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED); 1466258aae54824a7c3cd3974822b0b6835ab8a45deJack He assertThat(mCachedDevice.getConnectionSummary()).isNull(); 1476258aae54824a7c3cd3974822b0b6835ab8a45deJack He } 148c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He 149c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He @Test 150e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov public void testGetConnectionSummary_testSingleProfileActiveDeviceA2dp() { 151e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov // Test without battery level 152e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov // Set A2DP profile to be connected and test connection state summary 153e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); 154f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng assertThat(mCachedDevice.getConnectionSummary()).isNull(); 155e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov 156e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov // Set device as Active for A2DP and test connection state summary 157c285d559fa8f0d597e8d5f1c8176dbb187e2d6acPavlin Radoslavov mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP); 158f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active"); 159e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov 160e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov // Test with battery level 161e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov mBatteryLevel = 10; 162f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng assertThat(mCachedDevice.getConnectionSummary()).isEqualTo( 163f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng "Active, 10% battery"); 164e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov 165e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov // Set A2DP profile to be disconnected and test connection state summary 166e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED); 167e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov assertThat(mCachedDevice.getConnectionSummary()).isNull(); 168e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov 169e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level 170e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 171e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov // Set A2DP profile to be connected, Active and test connection state summary 172e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); 173c285d559fa8f0d597e8d5f1c8176dbb187e2d6acPavlin Radoslavov mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP); 174f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active"); 175e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov 176e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov // Set A2DP profile to be disconnected and test connection state summary 177e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED); 178e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov assertThat(mCachedDevice.getConnectionSummary()).isNull(); 179e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov } 180e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov 181e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov @Test 182e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov public void testGetConnectionSummary_testSingleProfileActiveDeviceHfp() { 183e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov // Test without battery level 184e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov // Set HFP profile to be connected and test connection state summary 185e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED); 186f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng assertThat(mCachedDevice.getConnectionSummary()).isNull(); 187e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov 188e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov // Set device as Active for HFP and test connection state summary 189f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng mCachedDevice.onAudioModeChanged(); 190f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng mShadowAudioManager.setMode(AudioManager.MODE_IN_CALL); 191c285d559fa8f0d597e8d5f1c8176dbb187e2d6acPavlin Radoslavov mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET); 192f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active"); 193e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov 194e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov // Test with battery level 195e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov mBatteryLevel = 10; 196f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng assertThat(mCachedDevice.getConnectionSummary()).isEqualTo( 197f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng "Active, 10% battery"); 198e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov 199e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov // Set HFP profile to be disconnected and test connection state summary 200e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); 201e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov assertThat(mCachedDevice.getConnectionSummary()).isNull(); 202e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov 203e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level 204e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 205e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov // Set HFP profile to be connected, Active and test connection state summary 206e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED); 207c285d559fa8f0d597e8d5f1c8176dbb187e2d6acPavlin Radoslavov mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET); 208f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active"); 209e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov 210e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov // Set HFP profile to be disconnected and test connection state summary 211e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); 212d7b359165968199e034bd46ca97526dbf719602dHansong Zhang assertThat(mCachedDevice.getConnectionSummary()).isNull(); 213d7b359165968199e034bd46ca97526dbf719602dHansong Zhang } 214d7b359165968199e034bd46ca97526dbf719602dHansong Zhang 215d7b359165968199e034bd46ca97526dbf719602dHansong Zhang @Test 216d7b359165968199e034bd46ca97526dbf719602dHansong Zhang public void testGetConnectionSummary_testSingleProfileActiveDeviceHearingAid() { 217d7b359165968199e034bd46ca97526dbf719602dHansong Zhang // Test without battery level 218d7b359165968199e034bd46ca97526dbf719602dHansong Zhang // Set Hearing Aid profile to be connected and test connection state summary 219d7b359165968199e034bd46ca97526dbf719602dHansong Zhang mCachedDevice.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED); 220f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng assertThat(mCachedDevice.getConnectionSummary()).isNull(); 221d7b359165968199e034bd46ca97526dbf719602dHansong Zhang 222d7b359165968199e034bd46ca97526dbf719602dHansong Zhang // Set device as Active for Hearing Aid and test connection state summary 223d7b359165968199e034bd46ca97526dbf719602dHansong Zhang mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID); 224f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active"); 225d7b359165968199e034bd46ca97526dbf719602dHansong Zhang 226d7b359165968199e034bd46ca97526dbf719602dHansong Zhang // Set Hearing Aid profile to be disconnected and test connection state summary 227d7b359165968199e034bd46ca97526dbf719602dHansong Zhang mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEARING_AID); 228f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng mCachedDevice. 229f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_DISCONNECTED); 230e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov assertThat(mCachedDevice.getConnectionSummary()).isNull(); 231e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov } 232e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov 233e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov @Test 234e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov public void testGetConnectionSummary_testMultipleProfilesActiveDevice() { 235e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov // Test without battery level 236e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov // Set A2DP and HFP profiles to be connected and test connection state summary 237e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); 238e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED); 239f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng assertThat(mCachedDevice.getConnectionSummary()).isNull(); 240e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov 241e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov // Set device as Active for A2DP and HFP and test connection state summary 242c285d559fa8f0d597e8d5f1c8176dbb187e2d6acPavlin Radoslavov mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP); 243c285d559fa8f0d597e8d5f1c8176dbb187e2d6acPavlin Radoslavov mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET); 244f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active"); 245e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov 246e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov // Test with battery level 247e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov mBatteryLevel = 10; 248e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov assertThat(mCachedDevice.getConnectionSummary()).isEqualTo( 249f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng "Active, 10% battery"); 250e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov 251e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov // Disconnect A2DP only and test connection state summary 252c285d559fa8f0d597e8d5f1c8176dbb187e2d6acPavlin Radoslavov mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.A2DP); 253e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED); 254e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov assertThat(mCachedDevice.getConnectionSummary()).isEqualTo( 255f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng "10% battery"); 256e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov 257e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov // Disconnect HFP only and test connection state summary 258c285d559fa8f0d597e8d5f1c8176dbb187e2d6acPavlin Radoslavov mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEADSET); 259e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); 260e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); 261c285d559fa8f0d597e8d5f1c8176dbb187e2d6acPavlin Radoslavov mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP); 262e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov assertThat(mCachedDevice.getConnectionSummary()).isEqualTo( 263f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng "Active, 10% battery"); 264e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov 265e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level 266e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 267e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov // Set A2DP and HFP profiles to be connected, Active and test connection state summary 268e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); 269e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED); 270c285d559fa8f0d597e8d5f1c8176dbb187e2d6acPavlin Radoslavov mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP); 271c285d559fa8f0d597e8d5f1c8176dbb187e2d6acPavlin Radoslavov mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET); 272f050932e2a7a35f9d64a77fcdaff475bdab8f749timhypeng assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active"); 273e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov 274e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov // Set A2DP and HFP profiles to be disconnected and test connection state summary 275e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED); 276e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); 277e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov assertThat(mCachedDevice.getConnectionSummary()).isNull(); 278e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov } 279e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov 280e6e080ff5cb9c7c56003695c1b787981e0114599Pavlin Radoslavov @Test 281c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He public void testDeviceName_testAliasNameAvailable() { 282c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He when(mDevice.getAliasName()).thenReturn(DEVICE_ALIAS); 283c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He when(mDevice.getName()).thenReturn(DEVICE_NAME); 284c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He CachedBluetoothDevice cachedBluetoothDevice = 285c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He new CachedBluetoothDevice(mContext, mAdapter, mProfileManager, mDevice); 286c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He // Verify alias is returned on getName 287c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He assertThat(cachedBluetoothDevice.getName()).isEqualTo(DEVICE_ALIAS); 288c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He // Verify device is visible 289c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He assertThat(cachedBluetoothDevice.hasHumanReadableName()).isTrue(); 290c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He } 291c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He 292c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He @Test 293c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He public void testDeviceName_testNameNotAvailable() { 294c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He CachedBluetoothDevice cachedBluetoothDevice = 295c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He new CachedBluetoothDevice(mContext, mAdapter, mProfileManager, mDevice); 296c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He // Verify device address is returned on getName 297c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He assertThat(cachedBluetoothDevice.getName()).isEqualTo(DEVICE_ADDRESS); 298c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He // Verify device is not visible 299c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He assertThat(cachedBluetoothDevice.hasHumanReadableName()).isFalse(); 300c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He } 301c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He 302c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He @Test 303c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He public void testDeviceName_testRenameDevice() { 304c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He final String[] alias = {DEVICE_ALIAS}; 305c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He doAnswer(invocation -> alias[0]).when(mDevice).getAliasName(); 306c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He doAnswer(invocation -> { 307c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He alias[0] = (String) invocation.getArguments()[0]; 308c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He return true; 309c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He }).when(mDevice).setAlias(anyString()); 310c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He when(mDevice.getName()).thenReturn(DEVICE_NAME); 311c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He CachedBluetoothDevice cachedBluetoothDevice = 312c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He new CachedBluetoothDevice(mContext, mAdapter, mProfileManager, mDevice); 313c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He // Verify alias is returned on getName 314c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He assertThat(cachedBluetoothDevice.getName()).isEqualTo(DEVICE_ALIAS); 315c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He // Verify null name does not get set 316c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He cachedBluetoothDevice.setName(null); 317c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He verify(mDevice, never()).setAlias(any()); 318c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He // Verify new name is set properly 319c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He cachedBluetoothDevice.setName(DEVICE_ALIAS_NEW); 320c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He verify(mDevice).setAlias(DEVICE_ALIAS_NEW); 321c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He // Verify new alias is returned on getName 322c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He assertThat(cachedBluetoothDevice.getName()).isEqualTo(DEVICE_ALIAS_NEW); 323c219bc9b884f70b087b2d55412b128846a5d0ce1Jack He } 3246a416327e5b52d6bb78202bd05a86ab84cf2304eHansong Zhang 3256a416327e5b52d6bb78202bd05a86ab84cf2304eHansong Zhang @Test 3266a416327e5b52d6bb78202bd05a86ab84cf2304eHansong Zhang public void testSetActive() { 3276a416327e5b52d6bb78202bd05a86ab84cf2304eHansong Zhang when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile); 3286a416327e5b52d6bb78202bd05a86ab84cf2304eHansong Zhang when(mProfileManager.getHeadsetProfile()).thenReturn(mHfpProfile); 3296a416327e5b52d6bb78202bd05a86ab84cf2304eHansong Zhang when(mA2dpProfile.setActiveDevice(any(BluetoothDevice.class))).thenReturn(true); 3306a416327e5b52d6bb78202bd05a86ab84cf2304eHansong Zhang when(mHfpProfile.setActiveDevice(any(BluetoothDevice.class))).thenReturn(true); 3316a416327e5b52d6bb78202bd05a86ab84cf2304eHansong Zhang 3326a416327e5b52d6bb78202bd05a86ab84cf2304eHansong Zhang assertThat(mCachedDevice.setActive()).isFalse(); 3336a416327e5b52d6bb78202bd05a86ab84cf2304eHansong Zhang 3346a416327e5b52d6bb78202bd05a86ab84cf2304eHansong Zhang mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); 3356a416327e5b52d6bb78202bd05a86ab84cf2304eHansong Zhang assertThat(mCachedDevice.setActive()).isTrue(); 3366a416327e5b52d6bb78202bd05a86ab84cf2304eHansong Zhang 3376a416327e5b52d6bb78202bd05a86ab84cf2304eHansong Zhang mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED); 3386a416327e5b52d6bb78202bd05a86ab84cf2304eHansong Zhang mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED); 3396a416327e5b52d6bb78202bd05a86ab84cf2304eHansong Zhang assertThat(mCachedDevice.setActive()).isTrue(); 3406a416327e5b52d6bb78202bd05a86ab84cf2304eHansong Zhang 3416a416327e5b52d6bb78202bd05a86ab84cf2304eHansong Zhang mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); 3426a416327e5b52d6bb78202bd05a86ab84cf2304eHansong Zhang assertThat(mCachedDevice.setActive()).isFalse(); 3436a416327e5b52d6bb78202bd05a86ab84cf2304eHansong Zhang } 34423b947e2458b3968f3ae611fe5aa73198921fe6ahughchen 34523b947e2458b3968f3ae611fe5aa73198921fe6ahughchen @Test 34623b947e2458b3968f3ae611fe5aa73198921fe6ahughchen public void testIsA2dpDevice_isA2dpDevice() { 34723b947e2458b3968f3ae611fe5aa73198921fe6ahughchen when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile); 34823b947e2458b3968f3ae611fe5aa73198921fe6ahughchen when(mA2dpProfile.getConnectionStatus(mDevice)). 34923b947e2458b3968f3ae611fe5aa73198921fe6ahughchen thenReturn(BluetoothProfile.STATE_CONNECTED); 35023b947e2458b3968f3ae611fe5aa73198921fe6ahughchen 35123b947e2458b3968f3ae611fe5aa73198921fe6ahughchen assertThat(mCachedDevice.isA2dpDevice()).isTrue(); 35223b947e2458b3968f3ae611fe5aa73198921fe6ahughchen } 35323b947e2458b3968f3ae611fe5aa73198921fe6ahughchen 35423b947e2458b3968f3ae611fe5aa73198921fe6ahughchen @Test 35523b947e2458b3968f3ae611fe5aa73198921fe6ahughchen public void testIsA2dpDevice_isNotA2dpDevice() { 35623b947e2458b3968f3ae611fe5aa73198921fe6ahughchen when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile); 35723b947e2458b3968f3ae611fe5aa73198921fe6ahughchen when(mA2dpProfile.getConnectionStatus(mDevice)). 35823b947e2458b3968f3ae611fe5aa73198921fe6ahughchen thenReturn(BluetoothProfile.STATE_DISCONNECTING); 35923b947e2458b3968f3ae611fe5aa73198921fe6ahughchen 36023b947e2458b3968f3ae611fe5aa73198921fe6ahughchen assertThat(mCachedDevice.isA2dpDevice()).isFalse(); 36123b947e2458b3968f3ae611fe5aa73198921fe6ahughchen } 36223b947e2458b3968f3ae611fe5aa73198921fe6ahughchen 36323b947e2458b3968f3ae611fe5aa73198921fe6ahughchen @Test 36423b947e2458b3968f3ae611fe5aa73198921fe6ahughchen public void testIsHfpDevice_isHfpDevice() { 36523b947e2458b3968f3ae611fe5aa73198921fe6ahughchen when(mProfileManager.getHeadsetProfile()).thenReturn(mHfpProfile); 36623b947e2458b3968f3ae611fe5aa73198921fe6ahughchen when(mHfpProfile.getConnectionStatus(mDevice)). 36723b947e2458b3968f3ae611fe5aa73198921fe6ahughchen thenReturn(BluetoothProfile.STATE_CONNECTED); 36823b947e2458b3968f3ae611fe5aa73198921fe6ahughchen 36923b947e2458b3968f3ae611fe5aa73198921fe6ahughchen assertThat(mCachedDevice.isHfpDevice()).isTrue(); 37023b947e2458b3968f3ae611fe5aa73198921fe6ahughchen } 37123b947e2458b3968f3ae611fe5aa73198921fe6ahughchen 37223b947e2458b3968f3ae611fe5aa73198921fe6ahughchen @Test 37323b947e2458b3968f3ae611fe5aa73198921fe6ahughchen public void testIsHfpDevice_isNotHfpDevice() { 37423b947e2458b3968f3ae611fe5aa73198921fe6ahughchen when(mProfileManager.getHeadsetProfile()).thenReturn(mHfpProfile); 37523b947e2458b3968f3ae611fe5aa73198921fe6ahughchen when(mHfpProfile.getConnectionStatus(mDevice)). 37623b947e2458b3968f3ae611fe5aa73198921fe6ahughchen thenReturn(BluetoothProfile.STATE_DISCONNECTING); 37723b947e2458b3968f3ae611fe5aa73198921fe6ahughchen 37823b947e2458b3968f3ae611fe5aa73198921fe6ahughchen assertThat(mCachedDevice.isHfpDevice()).isFalse(); 37923b947e2458b3968f3ae611fe5aa73198921fe6ahughchen } 3806258aae54824a7c3cd3974822b0b6835ab8a45deJack He} 381