• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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