1 /* <lambda>null2 * Copyright 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 package com.android.server.bluetooth.test 17 18 import android.app.AlarmManager 19 import android.app.Application 20 import android.bluetooth.BluetoothAdapter 21 import android.content.Context 22 import android.content.Intent 23 import android.os.Looper 24 import android.provider.Settings 25 import androidx.test.core.app.ApplicationProvider 26 import androidx.test.ext.truth.content.IntentSubject.assertThat 27 import com.android.server.bluetooth.BluetoothAdapterState 28 import com.android.server.bluetooth.Log 29 import com.android.server.bluetooth.Timer 30 import com.android.server.bluetooth.USER_SETTINGS_KEY 31 import com.android.server.bluetooth.airplane.isOnOverrode as isAirplaneModeOn 32 import com.android.server.bluetooth.airplane.test.ModeListenerTest as AirplaneListener 33 import com.android.server.bluetooth.isUserEnabled 34 import com.android.server.bluetooth.isUserSupported 35 import com.android.server.bluetooth.notifyBluetoothOn 36 import com.android.server.bluetooth.pause 37 import com.android.server.bluetooth.resetAutoOnTimerForUser 38 import com.android.server.bluetooth.satellite.isOn as isSatelliteModeOn 39 import com.android.server.bluetooth.satellite.test.ModeListenerTest as SatelliteListener 40 import com.android.server.bluetooth.setUserEnabled 41 import com.android.server.bluetooth.timer 42 import com.google.common.truth.Expect 43 import com.google.common.truth.Truth.assertThat 44 import java.time.LocalDateTime 45 import java.time.LocalTime 46 import kotlin.test.assertFailsWith 47 import org.junit.After 48 import org.junit.Before 49 import org.junit.Rule 50 import org.junit.Test 51 import org.junit.rules.TestName 52 import org.junit.runner.RunWith 53 import org.robolectric.RobolectricTestRunner 54 import org.robolectric.Shadows.shadowOf 55 56 @RunWith(RobolectricTestRunner::class) 57 @kotlinx.coroutines.ExperimentalCoroutinesApi 58 class AutoOnFeatureTest { 59 private val looper = Looper.getMainLooper() 60 private val state = BluetoothAdapterState() 61 private val context = ApplicationProvider.getApplicationContext<Context>() 62 private val resolver = context.contentResolver 63 private val now = LocalDateTime.now() 64 private val timerTarget = LocalDateTime.of(now.toLocalDate(), LocalTime.of(5, 0)).plusDays(1) 65 66 private var callback_count = 0 67 68 @JvmField @Rule val testName = TestName() 69 @JvmField @Rule val expect = Expect.create() 70 71 @Before 72 fun setUp() { 73 Log.i("AutoOnFeatureTest", "\t--> setUp(${testName.getMethodName()})") 74 75 enableUserSettings() 76 } 77 78 @After 79 fun tearDown() { 80 callback_count = 0 81 timer?.cancel() 82 timer = null 83 restoreSavedTimer() 84 } 85 86 private fun setupTimer() { 87 resetAutoOnTimerForUser(looper, context, state, this::callback_on) 88 } 89 90 private fun setUserEnabled(status: Boolean) { 91 setUserEnabled(looper, context, state, status, this::callback_on) 92 } 93 94 private fun enableUserSettings() { 95 Settings.Secure.putInt(resolver, USER_SETTINGS_KEY, 1) 96 shadowOf(looper).idle() 97 } 98 99 private fun disableUserSettings() { 100 Settings.Secure.putInt(resolver, USER_SETTINGS_KEY, 0) 101 shadowOf(looper).idle() 102 } 103 104 private fun restoreSettings() { 105 Settings.Secure.putString(resolver, USER_SETTINGS_KEY, null) 106 shadowOf(looper).idle() 107 } 108 109 private fun restoreSavedTimer() { 110 Settings.Secure.putString(resolver, Timer.STORAGE_KEY, null) 111 shadowOf(looper).idle() 112 } 113 114 private fun expectStorageTime() { 115 shadowOf(looper).idle() 116 expect 117 .that(Settings.Secure.getString(resolver, Timer.STORAGE_KEY)) 118 .isEqualTo(timerTarget.toString()) 119 } 120 121 private fun expectNoStorageTime() { 122 shadowOf(looper).idle() 123 expect.that(Settings.Secure.getString(resolver, Timer.STORAGE_KEY)).isNull() 124 } 125 126 private fun callback_on() { 127 callback_count++ 128 } 129 130 @Test 131 fun setupTimer_whenItWasNeverUsed_isNotScheduled() { 132 restoreSettings() 133 134 setupTimer() 135 136 expect.that(timer).isNull() 137 expect.that(callback_count).isEqualTo(0) 138 } 139 140 @Test 141 fun setupTimer_whenBtOn_isNotScheduled() { 142 state.set(BluetoothAdapter.STATE_ON) 143 144 setupTimer() 145 146 state.set(BluetoothAdapter.STATE_OFF) 147 expect.that(timer).isNull() 148 expect.that(callback_count).isEqualTo(0) 149 } 150 151 @Test 152 fun setupTimer_whenBtOffAndUserEnabled_isScheduled() { 153 setupTimer() 154 155 expect.that(timer).isNotNull() 156 } 157 158 @Test 159 fun setupTimer_whenBtOffAndUserEnabled_triggerCallback() { 160 setupTimer() 161 162 val shadowAlarmManager = shadowOf(context.getSystemService(AlarmManager::class.java)) 163 shadowAlarmManager.fireAlarm(shadowAlarmManager.peekNextScheduledAlarm()) 164 165 shadowOf(looper).runOneTask() 166 167 expect.that(callback_count).isEqualTo(1) 168 expect.that(timer).isNull() 169 } 170 171 @Test 172 fun setupTimer_whenAlreadySetup_triggerCallbackOnce() { 173 setupTimer() 174 setupTimer() 175 setupTimer() 176 177 val shadowAlarmManager = shadowOf(context.getSystemService(AlarmManager::class.java)) 178 shadowAlarmManager.fireAlarm(shadowAlarmManager.peekNextScheduledAlarm()) 179 180 shadowOf(looper).runOneTask() 181 182 expect.that(callback_count).isEqualTo(1) 183 expect.that(timer).isNull() 184 } 185 186 @Test 187 fun notifyBluetoothOn_whenNoTimer_noCrash() { 188 notifyBluetoothOn(context) 189 190 assertThat(timer).isNull() 191 } 192 193 @Test 194 fun notifyBluetoothOn_whenTimer_isNotScheduled() { 195 setupTimer() 196 notifyBluetoothOn(context) 197 198 shadowOf(looper).runToEndOfTasks() 199 expect.that(callback_count).isEqualTo(0) 200 expect.that(timer).isNull() 201 } 202 203 @Test 204 fun notifyBluetoothOn_whenItWasNeverUsed_enableSettings() { 205 restoreSettings() 206 207 notifyBluetoothOn(context) 208 209 assertThat(isUserSupported(resolver)).isTrue() 210 } 211 212 @Test 213 fun notifyBluetoothOn_whenStorage_resetStorage() { 214 Settings.Secure.putString(resolver, Timer.STORAGE_KEY, timerTarget.toString()) 215 shadowOf(looper).idle() 216 217 notifyBluetoothOn(context) 218 219 expectNoStorageTime() 220 } 221 222 @Test 223 fun apiIsUserEnable_whenItWasNeverUsed_throwException() { 224 restoreSettings() 225 226 assertFailsWith<IllegalStateException> { isUserEnabled(context) } 227 } 228 229 @Test 230 fun apiSetUserEnabled_whenItWasNeverUsed_throwException() { 231 restoreSettings() 232 233 assertFailsWith<IllegalStateException> { setUserEnabled(true) } 234 } 235 236 @Test 237 fun apiIsUserEnable_whenEnabled_isTrue() { 238 assertThat(isUserEnabled(context)).isTrue() 239 } 240 241 @Test 242 fun apiIsUserEnable_whenDisabled_isFalse() { 243 disableUserSettings() 244 assertThat(isUserEnabled(context)).isFalse() 245 } 246 247 @Test 248 fun apiSetUserEnableToFalse_whenScheduled_isNotScheduled() { 249 setupTimer() 250 251 setUserEnabled(false) 252 253 assertThat(isUserEnabled(context)).isFalse() 254 assertThat(callback_count).isEqualTo(0) 255 assertThat(timer).isNull() 256 } 257 258 @Test 259 fun apiSetUserEnableToFalse_whenIdle_isNotScheduled() { 260 setUserEnabled(false) 261 262 assertThat(isUserEnabled(context)).isFalse() 263 assertThat(callback_count).isEqualTo(0) 264 assertThat(timer).isNull() 265 } 266 267 @Test 268 fun apiSetUserEnableToTrue_whenIdle_canSchedule() { 269 disableUserSettings() 270 271 setUserEnabled(true) 272 273 assertThat(timer).isNotNull() 274 } 275 276 @Test 277 fun apiSetUserEnableToggle_whenScheduled_isRescheduled() { 278 val pastTime = timerTarget.minusDays(3) 279 Settings.Secure.putString(resolver, Timer.STORAGE_KEY, pastTime.toString()) 280 shadowOf(looper).idle() 281 282 setUserEnabled(false) 283 expectNoStorageTime() 284 285 setUserEnabled(true) 286 expectStorageTime() 287 288 assertThat(timer).isNotNull() 289 } 290 291 @Test 292 fun apiSetUserEnableToFalse_whenEnabled_broadcastIntent() { 293 setUserEnabled(false) 294 295 assertThat(shadowOf(context as Application).getBroadcastIntents().get(0)).run { 296 hasAction(BluetoothAdapter.ACTION_AUTO_ON_STATE_CHANGED) 297 hasFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY) 298 extras() 299 .integer(BluetoothAdapter.EXTRA_AUTO_ON_STATE) 300 .isEqualTo(BluetoothAdapter.AUTO_ON_STATE_DISABLED) 301 } 302 } 303 304 @Test 305 fun apiSetUserEnableToTrue_whenDisabled_broadcastIntent() { 306 disableUserSettings() 307 setUserEnabled(true) 308 309 assertThat(shadowOf(context as Application).getBroadcastIntents().get(0)).run { 310 hasAction(BluetoothAdapter.ACTION_AUTO_ON_STATE_CHANGED) 311 hasFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY) 312 extras() 313 .integer(BluetoothAdapter.EXTRA_AUTO_ON_STATE) 314 .isEqualTo(BluetoothAdapter.AUTO_ON_STATE_ENABLED) 315 } 316 } 317 318 @Test 319 fun pause_whenIdle_noTimeSave() { 320 pause() 321 322 expect.that(timer).isNull() 323 expect.that(callback_count).isEqualTo(0) 324 expectNoStorageTime() 325 } 326 327 @Test 328 fun pause_whenTimer_timeIsSaved() { 329 setupTimer() 330 331 pause() 332 333 expect.that(timer).isNull() 334 expect.that(callback_count).isEqualTo(0) 335 expectStorageTime() 336 } 337 338 @Test 339 fun setupTimer_whenIdle_timeIsSave() { 340 setupTimer() 341 342 expect.that(timer).isNotNull() 343 expect.that(callback_count).isEqualTo(0) 344 expectStorageTime() 345 } 346 347 @Test 348 fun setupTimer_whenPaused_isResumed() { 349 val now = LocalDateTime.now() 350 val alarmTime = LocalDateTime.of(now.toLocalDate(), LocalTime.of(5, 0)).plusDays(1) 351 Settings.Secure.putString(resolver, Timer.STORAGE_KEY, alarmTime.toString()) 352 shadowOf(looper).idle() 353 354 setupTimer() 355 356 expect.that(timer).isNotNull() 357 expect.that(callback_count).isEqualTo(0) 358 expectStorageTime() 359 } 360 361 @Test 362 fun setupTimer_whenSaveTimerIsExpired_triggerCallback() { 363 val pastTime = timerTarget.minusDays(3) 364 Settings.Secure.putString(resolver, Timer.STORAGE_KEY, pastTime.toString()) 365 shadowOf(looper).idle() 366 367 setupTimer() 368 369 expect.that(timer).isNull() 370 expect.that(callback_count).isEqualTo(1) 371 expectNoStorageTime() 372 } 373 374 @Test 375 fun setupTimer_whenSatelliteIsOn_isNotScheduled() { 376 val satelliteCallback: (m: Boolean) -> Unit = { _: Boolean -> } 377 378 SatelliteListener.setupSatelliteModeToOn(resolver, looper, satelliteCallback) 379 assertThat(isSatelliteModeOn).isTrue() 380 381 setupTimer() 382 383 SatelliteListener.setupSatelliteModeToOff(resolver, looper) 384 expect.that(timer).isNull() 385 expect.that(callback_count).isEqualTo(0) 386 expectNoStorageTime() 387 } 388 389 @Test 390 fun updateTimezone_whenTimerSchedule_isReScheduled() { 391 setupTimer() 392 393 // Fake storaged time so when receiving the intent, the test think we jump in the futur 394 val pastTime = timerTarget.minusDays(3) 395 Settings.Secure.putString(resolver, Timer.STORAGE_KEY, pastTime.toString()) 396 397 context.sendBroadcast(Intent(Intent.ACTION_TIMEZONE_CHANGED)) 398 shadowOf(looper).idle() 399 400 expect.that(timer).isNull() 401 expect.that(callback_count).isEqualTo(1) 402 expectNoStorageTime() 403 } 404 405 @Test 406 fun updateTime_whenTimerSchedule_isReScheduled() { 407 setupTimer() 408 409 // Fake stored time so when receiving the intent, the test think we jumped in the future 410 val pastTime = timerTarget.minusDays(3) 411 Settings.Secure.putString(resolver, Timer.STORAGE_KEY, pastTime.toString()) 412 413 context.sendBroadcast(Intent(Intent.ACTION_TIME_CHANGED)) 414 shadowOf(looper).idle() 415 416 expect.that(timer).isNull() 417 expect.that(callback_count).isEqualTo(1) 418 expectNoStorageTime() 419 } 420 421 @Test 422 fun updateDate_whenTimerSchedule_isReScheduled() { 423 setupTimer() 424 425 // Fake stored time so when receiving the intent, the test think we jumped in the future 426 val pastTime = timerTarget.minusDays(3) 427 Settings.Secure.putString(resolver, Timer.STORAGE_KEY, pastTime.toString()) 428 429 context.sendBroadcast(Intent(Intent.ACTION_DATE_CHANGED)) 430 shadowOf(looper).idle() 431 432 expect.that(timer).isNull() 433 expect.that(callback_count).isEqualTo(1) 434 expectNoStorageTime() 435 } 436 437 @Test 438 @kotlin.time.ExperimentalTime 439 fun setupTimer_whenLegacyAirplaneIsOn_isNotSchedule() { 440 val userCallback: () -> Context = { -> context } 441 AirplaneListener.setupAirplaneModeToOn(resolver, looper, userCallback, false) 442 assertThat(isAirplaneModeOn).isTrue() 443 444 setupTimer() 445 446 AirplaneListener.setupAirplaneModeToOff(resolver, looper) 447 expect.that(timer).isNull() 448 expect.that(callback_count).isEqualTo(0) 449 expectNoStorageTime() 450 } 451 452 @Test 453 @kotlin.time.ExperimentalTime 454 fun setupTimer_whenApmAirplaneIsOn_isSchedule() { 455 val userCallback: () -> Context = { -> context } 456 AirplaneListener.setupAirplaneModeToOn(resolver, looper, userCallback, true) 457 assertThat(isAirplaneModeOn).isTrue() 458 459 setupTimer() 460 461 AirplaneListener.setupAirplaneModeToOff(resolver, looper) 462 expect.that(timer).isNotNull() 463 expect.that(callback_count).isEqualTo(0) 464 expectStorageTime() 465 } 466 } 467