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 @get:Rule val testName = TestName() 60 @get:Rule val expect = Expect.create() 61 62 private val looper = Looper.getMainLooper() 63 private val state = BluetoothAdapterState() 64 private val context = ApplicationProvider.getApplicationContext<Context>() 65 private val resolver = context.contentResolver 66 private val now = LocalDateTime.now() 67 private val timerTarget = LocalDateTime.of(now.toLocalDate(), LocalTime.of(5, 0)).plusDays(1) 68 69 private var callback_count = 0 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 apiSetUserEnableToTrue_whenAlreadyEnabled_doNothing() { 320 setUserEnabled(true) 321 322 assertThat(shadowOf(context as Application).getBroadcastIntents().size).isEqualTo(0) 323 } 324 325 @Test 326 fun pause_whenIdle_noTimeSave() { 327 pause() 328 329 expect.that(timer).isNull() 330 expect.that(callback_count).isEqualTo(0) 331 expectNoStorageTime() 332 } 333 334 @Test 335 fun pause_whenTimer_timeIsSaved() { 336 setupTimer() 337 338 pause() 339 340 expect.that(timer).isNull() 341 expect.that(callback_count).isEqualTo(0) 342 expectStorageTime() 343 } 344 345 @Test 346 fun setupTimer_whenIdle_timeIsSave() { 347 setupTimer() 348 349 expect.that(timer).isNotNull() 350 expect.that(callback_count).isEqualTo(0) 351 expectStorageTime() 352 } 353 354 @Test 355 fun setupTimer_whenPaused_isResumed() { 356 val now = LocalDateTime.now() 357 val alarmTime = LocalDateTime.of(now.toLocalDate(), LocalTime.of(5, 0)).plusDays(1) 358 Settings.Secure.putString(resolver, Timer.STORAGE_KEY, alarmTime.toString()) 359 shadowOf(looper).idle() 360 361 setupTimer() 362 363 expect.that(timer).isNotNull() 364 expect.that(callback_count).isEqualTo(0) 365 expectStorageTime() 366 } 367 368 @Test 369 fun setupTimer_whenSaveTimerIsExpired_triggerCallback() { 370 val pastTime = timerTarget.minusDays(3) 371 Settings.Secure.putString(resolver, Timer.STORAGE_KEY, pastTime.toString()) 372 shadowOf(looper).idle() 373 374 setupTimer() 375 376 expect.that(timer).isNull() 377 expect.that(callback_count).isEqualTo(1) 378 expectNoStorageTime() 379 } 380 381 @Test 382 fun setupTimer_whenSatelliteIsOn_isNotScheduled() { 383 val satelliteCallback: (m: Boolean) -> Unit = { _: Boolean -> } 384 385 SatelliteListener.setupSatelliteModeToOn(resolver, looper, satelliteCallback) 386 assertThat(isSatelliteModeOn).isTrue() 387 388 setupTimer() 389 390 SatelliteListener.setupSatelliteModeToOff(resolver, looper) 391 expect.that(timer).isNull() 392 expect.that(callback_count).isEqualTo(0) 393 expectNoStorageTime() 394 } 395 396 @Test 397 fun updateTimezone_whenTimerSchedule_isReScheduled() { 398 setupTimer() 399 400 // Fake storaged time so when receiving the intent, the test think we jump in the futur 401 val pastTime = timerTarget.minusDays(3) 402 Settings.Secure.putString(resolver, Timer.STORAGE_KEY, pastTime.toString()) 403 404 context.sendBroadcast(Intent(Intent.ACTION_TIMEZONE_CHANGED)) 405 shadowOf(looper).idle() 406 407 expect.that(timer).isNull() 408 expect.that(callback_count).isEqualTo(1) 409 expectNoStorageTime() 410 } 411 412 @Test 413 fun updateTime_whenTimerSchedule_isReScheduled() { 414 setupTimer() 415 416 // Fake stored time so when receiving the intent, the test think we jumped in the future 417 val pastTime = timerTarget.minusDays(3) 418 Settings.Secure.putString(resolver, Timer.STORAGE_KEY, pastTime.toString()) 419 420 context.sendBroadcast(Intent(Intent.ACTION_TIME_CHANGED)) 421 shadowOf(looper).idle() 422 423 expect.that(timer).isNull() 424 expect.that(callback_count).isEqualTo(1) 425 expectNoStorageTime() 426 } 427 428 @Test 429 fun updateDate_whenTimerSchedule_isReScheduled() { 430 setupTimer() 431 432 // Fake stored time so when receiving the intent, the test think we jumped in the future 433 val pastTime = timerTarget.minusDays(3) 434 Settings.Secure.putString(resolver, Timer.STORAGE_KEY, pastTime.toString()) 435 436 context.sendBroadcast(Intent(Intent.ACTION_DATE_CHANGED)) 437 shadowOf(looper).idle() 438 439 expect.that(timer).isNull() 440 expect.that(callback_count).isEqualTo(1) 441 expectNoStorageTime() 442 } 443 444 @Test 445 @kotlin.time.ExperimentalTime 446 fun setupTimer_whenLegacyAirplaneIsOn_isNotSchedule() { 447 val userCallback: () -> Context = { -> context } 448 AirplaneListener.setupAirplaneModeToOn(resolver, looper, userCallback, false) 449 assertThat(isAirplaneModeOn).isTrue() 450 451 setupTimer() 452 453 AirplaneListener.setupAirplaneModeToOff(resolver, looper) 454 expect.that(timer).isNull() 455 expect.that(callback_count).isEqualTo(0) 456 expectNoStorageTime() 457 } 458 459 @Test 460 @kotlin.time.ExperimentalTime 461 fun setupTimer_whenApmAirplaneIsOn_isSchedule() { 462 val userCallback: () -> Context = { -> context } 463 AirplaneListener.setupAirplaneModeToOn(resolver, looper, userCallback, true) 464 assertThat(isAirplaneModeOn).isTrue() 465 466 setupTimer() 467 468 AirplaneListener.setupAirplaneModeToOff(resolver, looper) 469 expect.that(timer).isNotNull() 470 expect.that(callback_count).isEqualTo(0) 471 expectStorageTime() 472 } 473 } 474