1 /* <lambda>null2 * 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.data.repository 18 19 import android.app.usage.UsageEvents 20 import android.app.usage.UsageEventsQuery 21 import android.app.usage.UsageStatsManager 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.model.UsageStatsQuery 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.backgroundCoroutineContext 30 import com.android.systemui.kosmos.testScope 31 import com.android.systemui.testKosmos 32 import com.google.common.truth.Truth.assertThat 33 import kotlinx.coroutines.test.runTest 34 import org.junit.Test 35 import org.junit.runner.RunWith 36 import org.mockito.kotlin.any 37 import org.mockito.kotlin.doAnswer 38 import org.mockito.kotlin.mock 39 40 @SmallTest 41 @RunWith(AndroidJUnit4::class) 42 class UsageStatsRepositoryTest : SysuiTestCase() { 43 44 private val kosmos = testKosmos() 45 private val testScope = kosmos.testScope 46 47 private val fakeUsageStatsManager = FakeUsageStatsManager() 48 49 private val usageStatsManager = 50 mock<UsageStatsManager> { 51 on { queryEvents(any()) } doAnswer 52 { inv -> 53 val query = inv.getArgument(0) as UsageEventsQuery 54 fakeUsageStatsManager.queryEvents(query) 55 } 56 } 57 58 private val underTest by lazy { 59 UsageStatsRepositoryImpl( 60 bgContext = kosmos.backgroundCoroutineContext, 61 usageStatsManager = usageStatsManager, 62 ) 63 } 64 65 @Test 66 fun testQueryWithBeginAndEndTime() = 67 testScope.runTest { 68 with(fakeUsageStatsManager) { 69 // This event is outside the queried time, and therefore should 70 // not be returned. 71 addEvent( 72 type = UsageEvents.Event.ACTIVITY_RESUMED, 73 timestamp = 5, 74 instanceId = 1, 75 ) 76 addEvent( 77 type = UsageEvents.Event.ACTIVITY_PAUSED, 78 timestamp = 10, 79 instanceId = 1, 80 ) 81 addEvent( 82 type = UsageEvents.Event.ACTIVITY_STOPPED, 83 timestamp = 20, 84 instanceId = 2, 85 ) 86 // This event is outside the queried time, and therefore should 87 // not be returned. 88 addEvent( 89 type = UsageEvents.Event.ACTIVITY_DESTROYED, 90 timestamp = 50, 91 instanceId = 2, 92 ) 93 } 94 95 assertThat( 96 underTest.queryActivityEvents( 97 UsageStatsQuery(MAIN_USER, startTime = 10, endTime = 50), 98 ), 99 ) 100 .containsExactly( 101 ActivityEventModel( 102 instanceId = 1, 103 packageName = DEFAULT_PACKAGE, 104 lifecycle = Lifecycle.PAUSED, 105 timestamp = 10, 106 ), 107 ActivityEventModel( 108 instanceId = 2, 109 packageName = DEFAULT_PACKAGE, 110 lifecycle = Lifecycle.STOPPED, 111 timestamp = 20, 112 ), 113 ) 114 } 115 116 @Test 117 fun testQueryForDifferentUsers() = 118 testScope.runTest { 119 with(fakeUsageStatsManager) { 120 addEvent( 121 user = MAIN_USER, 122 type = UsageEvents.Event.ACTIVITY_PAUSED, 123 timestamp = 10, 124 instanceId = 1, 125 ) 126 addEvent( 127 user = SECONDARY_USER, 128 type = UsageEvents.Event.ACTIVITY_RESUMED, 129 timestamp = 11, 130 instanceId = 2, 131 ) 132 } 133 134 assertThat( 135 underTest.queryActivityEvents( 136 UsageStatsQuery(MAIN_USER, startTime = 10, endTime = 15), 137 ), 138 ) 139 .containsExactly( 140 ActivityEventModel( 141 instanceId = 1, 142 packageName = DEFAULT_PACKAGE, 143 lifecycle = Lifecycle.PAUSED, 144 timestamp = 10, 145 ), 146 ) 147 } 148 149 @Test 150 fun testQueryForSpecificPackages() = 151 testScope.runTest { 152 with(fakeUsageStatsManager) { 153 addEvent( 154 packageName = DEFAULT_PACKAGE, 155 type = UsageEvents.Event.ACTIVITY_PAUSED, 156 timestamp = 10, 157 instanceId = 1, 158 ) 159 addEvent( 160 packageName = OTHER_PACKAGE, 161 type = UsageEvents.Event.ACTIVITY_RESUMED, 162 timestamp = 11, 163 instanceId = 2, 164 ) 165 } 166 167 assertThat( 168 underTest.queryActivityEvents( 169 UsageStatsQuery( 170 MAIN_USER, 171 startTime = 10, 172 endTime = 10000, 173 packageNames = listOf(OTHER_PACKAGE), 174 ), 175 ), 176 ) 177 .containsExactly( 178 ActivityEventModel( 179 instanceId = 2, 180 packageName = OTHER_PACKAGE, 181 lifecycle = Lifecycle.RESUMED, 182 timestamp = 11, 183 ), 184 ) 185 } 186 187 @Test 188 fun testNonActivityEvent() = 189 testScope.runTest { 190 with(fakeUsageStatsManager) { 191 addEvent( 192 type = UsageEvents.Event.CHOOSER_ACTION, 193 timestamp = 10, 194 instanceId = 1, 195 ) 196 } 197 198 assertThat( 199 underTest.queryActivityEvents( 200 UsageStatsQuery( 201 MAIN_USER, 202 startTime = 1, 203 endTime = 20, 204 ), 205 ), 206 ) 207 .isEmpty() 208 } 209 210 private class FakeUsageStatsManager() { 211 private val events = mutableMapOf<Int, MutableList<UsageEvents.Event>>() 212 213 fun queryEvents(query: UsageEventsQuery): UsageEvents { 214 val results = 215 events 216 .getOrDefault(query.userId, emptyList()) 217 .filter { event -> 218 query.packageNames.isEmpty() || 219 query.packageNames.contains(event.packageName) 220 } 221 .filter { event -> 222 event.timeStamp in query.beginTimeMillis until query.endTimeMillis 223 } 224 .filter { event -> 225 query.eventTypes.isEmpty() || query.eventTypes.contains(event.eventType) 226 } 227 return UsageEvents(results, emptyArray()) 228 } 229 230 fun addEvent( 231 type: Int, 232 instanceId: Int = 0, 233 user: UserHandle = MAIN_USER, 234 packageName: String = DEFAULT_PACKAGE, 235 timestamp: Long, 236 ) { 237 events 238 .getOrPut(user.identifier) { mutableListOf() } 239 .add( 240 UsageEvents.Event(type, timestamp).apply { 241 mPackage = packageName 242 mInstanceId = instanceId 243 } 244 ) 245 } 246 } 247 248 private companion object { 249 const val DEFAULT_PACKAGE = "pkg.default" 250 const val OTHER_PACKAGE = "pkg.other" 251 val MAIN_USER: UserHandle = UserHandle.of(0) 252 val SECONDARY_USER: UserHandle = UserHandle.of(1) 253 } 254 } 255