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.systemui.common.usagestats.domain.interactor
18 
19 import android.annotation.CurrentTimeMillisLong
20 import android.app.usage.UsageEvents
21 import android.content.pm.UserInfo
22 import android.os.UserHandle
23 import androidx.test.ext.junit.runners.AndroidJUnit4
24 import androidx.test.filters.SmallTest
25 import com.android.systemui.SysuiTestCase
26 import com.android.systemui.common.usagestats.data.repository.fakeUsageStatsRepository
27 import com.android.systemui.common.usagestats.shared.model.ActivityEventModel
28 import com.android.systemui.common.usagestats.shared.model.ActivityEventModel.Lifecycle
29 import com.android.systemui.kosmos.testScope
30 import com.android.systemui.settings.fakeUserTracker
31 import com.android.systemui.testKosmos
32 import com.android.systemui.util.time.fakeSystemClock
33 import com.google.common.truth.Truth.assertThat
34 import kotlinx.coroutines.test.runTest
35 import org.junit.Before
36 import org.junit.Test
37 import org.junit.runner.RunWith
38 
39 @SmallTest
40 @RunWith(AndroidJUnit4::class)
41 class UsageStatsInteractorTest : SysuiTestCase() {
42 
43     private val kosmos = testKosmos()
44     private val testScope = kosmos.testScope
45 
46     private val userTracker = kosmos.fakeUserTracker
47     private val systemClock = kosmos.fakeSystemClock
48     private val repository = kosmos.fakeUsageStatsRepository
49 
50     private val underTest = kosmos.usageStatsInteractor
51 
52     @Before
setUpnull53     fun setUp() {
54         userTracker.set(listOf(MAIN_USER, SECONDARY_USER), 0)
55     }
56 
57     @Test
testQueryWithBeginAndEndTimenull58     fun testQueryWithBeginAndEndTime() =
59         testScope.runTest {
60             // This event is outside the queried time, and therefore should
61             // not be returned.
62             addEvent(
63                 instanceId = 1,
64                 type = UsageEvents.Event.ACTIVITY_RESUMED,
65                 timestamp = 5,
66             )
67             addEvent(
68                 type = UsageEvents.Event.ACTIVITY_PAUSED,
69                 timestamp = 10,
70                 instanceId = 1,
71             )
72             addEvent(
73                 type = UsageEvents.Event.ACTIVITY_STOPPED,
74                 timestamp = 20,
75                 instanceId = 2,
76             )
77             // This event is outside the queried time, and therefore should
78             // not be returned.
79             addEvent(
80                 type = UsageEvents.Event.ACTIVITY_DESTROYED,
81                 timestamp = 50,
82                 instanceId = 2,
83             )
84 
85             assertThat(underTest.queryActivityEvents(startTime = 10, endTime = 50))
86                 .containsExactly(
87                     ActivityEventModel(
88                         instanceId = 1,
89                         packageName = DEFAULT_PACKAGE,
90                         lifecycle = Lifecycle.PAUSED,
91                         timestamp = 10,
92                     ),
93                     ActivityEventModel(
94                         instanceId = 2,
95                         packageName = DEFAULT_PACKAGE,
96                         lifecycle = Lifecycle.STOPPED,
97                         timestamp = 20,
98                     ),
99                 )
100         }
101 
102     @Test
testQueryForDifferentUsersnull103     fun testQueryForDifferentUsers() =
104         testScope.runTest {
105             addEvent(
106                 user = MAIN_USER.userHandle,
107                 type = UsageEvents.Event.ACTIVITY_PAUSED,
108                 timestamp = 10,
109                 instanceId = 1,
110             )
111             addEvent(
112                 user = SECONDARY_USER.userHandle,
113                 type = UsageEvents.Event.ACTIVITY_RESUMED,
114                 timestamp = 11,
115                 instanceId = 2,
116             )
117 
118             assertThat(underTest.queryActivityEvents(startTime = 10, endTime = 15))
119                 .containsExactly(
120                     ActivityEventModel(
121                         instanceId = 1,
122                         packageName = DEFAULT_PACKAGE,
123                         lifecycle = Lifecycle.PAUSED,
124                         timestamp = 10,
125                     ),
126                 )
127         }
128 
129     @Test
testQueryWithUserSpecifiednull130     fun testQueryWithUserSpecified() =
131         testScope.runTest {
132             addEvent(
133                 user = MAIN_USER.userHandle,
134                 type = UsageEvents.Event.ACTIVITY_PAUSED,
135                 timestamp = 10,
136                 instanceId = 1,
137             )
138             addEvent(
139                 user = SECONDARY_USER.userHandle,
140                 type = UsageEvents.Event.ACTIVITY_RESUMED,
141                 timestamp = 11,
142                 instanceId = 2,
143             )
144 
145             assertThat(
146                     underTest.queryActivityEvents(
147                         startTime = 10,
148                         endTime = 15,
149                         userHandle = SECONDARY_USER.userHandle,
150                     ),
151                 )
152                 .containsExactly(
153                     ActivityEventModel(
154                         instanceId = 2,
155                         packageName = DEFAULT_PACKAGE,
156                         lifecycle = Lifecycle.RESUMED,
157                         timestamp = 11,
158                     ),
159                 )
160         }
161 
162     @Test
testQueryForSpecificPackagesnull163     fun testQueryForSpecificPackages() =
164         testScope.runTest {
165             addEvent(
166                 packageName = DEFAULT_PACKAGE,
167                 type = UsageEvents.Event.ACTIVITY_PAUSED,
168                 timestamp = 10,
169                 instanceId = 1,
170             )
171             addEvent(
172                 packageName = OTHER_PACKAGE,
173                 type = UsageEvents.Event.ACTIVITY_RESUMED,
174                 timestamp = 11,
175                 instanceId = 2,
176             )
177 
178             assertThat(
179                     underTest.queryActivityEvents(
180                         startTime = 10,
181                         endTime = 10000,
182                         packageNames = listOf(OTHER_PACKAGE),
183                     ),
184                 )
185                 .containsExactly(
186                     ActivityEventModel(
187                         instanceId = 2,
188                         packageName = OTHER_PACKAGE,
189                         lifecycle = Lifecycle.RESUMED,
190                         timestamp = 11,
191                     ),
192                 )
193         }
194 
195     @Test
testNonActivityEventnull196     fun testNonActivityEvent() =
197         testScope.runTest {
198             addEvent(
199                 type = UsageEvents.Event.CHOOSER_ACTION,
200                 timestamp = 10,
201                 instanceId = 1,
202             )
203 
204             assertThat(underTest.queryActivityEvents(startTime = 1, endTime = 20)).isEmpty()
205         }
206 
207     @Test
testNoEndTimeSpecifiednull208     fun testNoEndTimeSpecified() =
209         testScope.runTest {
210             systemClock.setCurrentTimeMillis(30)
211 
212             addEvent(
213                 type = UsageEvents.Event.ACTIVITY_PAUSED,
214                 timestamp = 10,
215                 instanceId = 1,
216             )
217             addEvent(
218                 type = UsageEvents.Event.ACTIVITY_STOPPED,
219                 timestamp = 20,
220                 instanceId = 2,
221             )
222             // This event is outside the queried time, and therefore should
223             // not be returned.
224             addEvent(
225                 type = UsageEvents.Event.ACTIVITY_DESTROYED,
226                 timestamp = 50,
227                 instanceId = 2,
228             )
229 
230             assertThat(underTest.queryActivityEvents(startTime = 1))
231                 .containsExactly(
232                     ActivityEventModel(
233                         instanceId = 1,
234                         packageName = DEFAULT_PACKAGE,
235                         lifecycle = Lifecycle.PAUSED,
236                         timestamp = 10,
237                     ),
238                     ActivityEventModel(
239                         instanceId = 2,
240                         packageName = DEFAULT_PACKAGE,
241                         lifecycle = Lifecycle.STOPPED,
242                         timestamp = 20,
243                     ),
244                 )
245         }
246 
addEventnull247     private fun addEvent(
248         instanceId: Int,
249         user: UserHandle = MAIN_USER.userHandle,
250         packageName: String = DEFAULT_PACKAGE,
251         @UsageEvents.Event.EventType type: Int,
252         @CurrentTimeMillisLong timestamp: Long,
253     ) {
254         repository.addEvent(
255             instanceId = instanceId,
256             user = user,
257             packageName = packageName,
258             type = type,
259             timestamp = timestamp,
260         )
261     }
262 
263     private companion object {
264         const val DEFAULT_PACKAGE = "pkg.default"
265         const val OTHER_PACKAGE = "pkg.other"
266         val MAIN_USER: UserInfo = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
267         val SECONDARY_USER: UserInfo = UserInfo(10, "secondary", 0)
268     }
269 }
270