1 /* 2 * Copyright (C) 2020 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.wm.shell.bubbles.storage 18 19 import android.app.ActivityTaskManager.INVALID_TASK_ID 20 import android.content.pm.LauncherApps 21 import android.os.UserHandle 22 import android.testing.AndroidTestingRunner 23 import androidx.test.filters.SmallTest 24 import org.junit.Test 25 import com.android.wm.shell.ShellTestCase 26 import com.google.common.truth.Truth.assertThat 27 import junit.framework.Assert.assertEquals 28 import org.junit.Before 29 import org.junit.runner.RunWith 30 import org.mockito.ArgumentMatchers.any 31 import org.mockito.ArgumentMatchers.anyInt 32 import org.mockito.ArgumentMatchers.anyString 33 import org.mockito.ArgumentMatchers.eq 34 import org.mockito.Mockito 35 import org.mockito.Mockito.mock 36 import org.mockito.Mockito.never 37 import org.mockito.Mockito.reset 38 import org.mockito.Mockito.verify 39 40 @SmallTest 41 @RunWith(AndroidTestingRunner::class) 42 class BubbleVolatileRepositoryTest : ShellTestCase() { 43 44 private val user0 = UserHandle.of(0) 45 private val user10_managed = UserHandle.of(10) // In test, acts as workprofile of user0 46 private val user11 = UserHandle.of(11) 47 48 // user, package, shortcut, notification key, height, res-height, title, taskId, locusId 49 private val bubble1 = BubbleEntity(user0.identifier, 50 "com.example.messenger", "shortcut-1", "0key-1", 120, 0, null, 1, null) 51 private val bubble2 = BubbleEntity(user10_managed.identifier, 52 "com.example.chat", "alice and bob", "10key-2", 0, 16537428, "title", 2, null) 53 private val bubble3 = BubbleEntity(user0.identifier, 54 "com.example.messenger", "shortcut-2", "0key-3", 120, 0, null, INVALID_TASK_ID, null) 55 56 private val bubble11 = BubbleEntity(user11.identifier, 57 "com.example.messenger", "shortcut-1", "01key-1", 120, 0, null, 3) 58 private val bubble12 = BubbleEntity(user11.identifier, 59 "com.example.chat", "alice and bob", "11key-2", 0, 16537428, "title", INVALID_TASK_ID) 60 61 private val user0bubbles = listOf(bubble1, bubble2, bubble3) 62 private val user11bubbles = listOf(bubble11, bubble12) 63 64 private lateinit var repository: BubbleVolatileRepository 65 private lateinit var launcherApps: LauncherApps 66 67 @Before 68 fun setup() { 69 launcherApps = mock(LauncherApps::class.java) 70 repository = BubbleVolatileRepository(launcherApps) 71 } 72 73 @Test 74 fun testAddBubbles() { 75 repository.addBubbles(user0.identifier, user0bubbles) 76 repository.addBubbles(user11.identifier, user11bubbles) 77 78 assertEquals(user0bubbles, repository.getEntities(user0.identifier).toList()) 79 assertEquals(user11bubbles, repository.getEntities(user11.identifier).toList()) 80 81 verify(launcherApps).cacheShortcuts(eq(PKG_MESSENGER), 82 eq(listOf("shortcut-1", "shortcut-2")), eq(user0), 83 eq(LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS)) 84 verify(launcherApps).cacheShortcuts(eq(PKG_CHAT), 85 eq(listOf("alice and bob")), eq(user10_managed), 86 eq(LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS)) 87 88 verify(launcherApps).cacheShortcuts(eq(PKG_MESSENGER), 89 eq(listOf("shortcut-1")), eq(user11), 90 eq(LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS)) 91 verify(launcherApps).cacheShortcuts(eq(PKG_CHAT), 92 eq(listOf("alice and bob")), eq(user11), 93 eq(LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS)) 94 95 repository.addBubbles(user0.identifier, listOf(bubble1)) 96 assertEquals(listOf(bubble2, bubble3, bubble1), repository.getEntities(user0.identifier)) 97 98 repository.addBubbles(user11.identifier, listOf(bubble12)) 99 assertEquals(listOf(bubble11, bubble12), repository.getEntities(user11.identifier)) 100 101 Mockito.verifyNoMoreInteractions(launcherApps) 102 } 103 104 @Test 105 fun testRemoveBubbles() { 106 repository.addBubbles(user0.identifier, user0bubbles) 107 repository.addBubbles(user11.identifier, user11bubbles) 108 109 repository.removeBubbles(user0.identifier, listOf(bubble3)) 110 assertEquals(listOf(bubble1, bubble2), repository.getEntities(user0.identifier).toList()) 111 verify(launcherApps).uncacheShortcuts(eq(PKG_MESSENGER), 112 eq(listOf("shortcut-2")), eq(user0), 113 eq(LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS)) 114 115 reset(launcherApps) 116 117 repository.removeBubbles(user11.identifier, listOf(bubble12)) 118 assertEquals(listOf(bubble11), repository.getEntities(user11.identifier).toList()) 119 verify(launcherApps).uncacheShortcuts(eq(PKG_CHAT), 120 eq(listOf("alice and bob")), eq(user11), 121 eq(LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS)) 122 } 123 124 @Test 125 fun testAddAndRemoveBubblesWhenExceedingCapacity() { 126 repository.capacity = 2 127 // push bubbles beyond capacity 128 repository.addBubbles(user0.identifier, user0bubbles) 129 // verify it is trim down to capacity 130 assertEquals(listOf(bubble2, bubble3), repository.getEntities(user0.identifier).toList()) 131 verify(launcherApps).cacheShortcuts(eq(PKG_MESSENGER), 132 eq(listOf("shortcut-2")), eq(user0), 133 eq(LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS)) 134 verify(launcherApps).cacheShortcuts(eq(PKG_CHAT), 135 eq(listOf("alice and bob")), eq(user10_managed), 136 eq(LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS)) 137 138 repository.addBubbles(user0.identifier, listOf(bubble1)) 139 // verify the oldest bubble is popped 2, 3 140 assertEquals(listOf(bubble3, bubble1), repository.getEntities(user0.identifier).toList()) 141 verify(launcherApps).uncacheShortcuts(eq(PKG_CHAT), 142 eq(listOf("alice and bob")), eq(user10_managed), 143 eq(LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS)) 144 } 145 146 @Test 147 fun testAddBubbleMatchesByKey() { 148 val bubble = BubbleEntity(0, "com.example.pkg", "shortcut-id", "key", 120, 0, "title", 149 1, null) 150 repository.addBubbles(user0.identifier, listOf(bubble)) 151 assertEquals(bubble, repository.getEntities(user0.identifier).get(0)) 152 153 // Same key as first bubble but different entry 154 val bubbleModified = BubbleEntity(0, "com.example.pkg", "shortcut-id", "key", 120, 0, 155 "different title", 2) 156 repository.addBubbles(user0.identifier, listOf(bubbleModified)) 157 assertEquals(bubbleModified, repository.getEntities(user0.identifier).get(0)) 158 } 159 160 @Test 161 fun testRemoveBubblesForUser() { 162 repository.addBubbles(user0.identifier, user0bubbles) 163 assertThat(repository.getEntities(user0.identifier).toList()) 164 .isEqualTo(listOf(bubble1, bubble2, bubble3)) 165 166 val ret = repository.removeBubblesForUser(user0.identifier, -1) 167 assertThat(ret).isTrue() // bubbles were removed 168 169 assertThat(repository.getEntities(user0.identifier).toList()).isEmpty() 170 verify(launcherApps, never()).uncacheShortcuts(anyString(), 171 any(), 172 any(UserHandle::class.java), anyInt()) 173 } 174 175 @Test 176 fun testRemoveBubblesForUser_parentUserRemoved() { 177 repository.addBubbles(user0.identifier, user0bubbles) 178 // bubble2 is the work profile bubble 179 assertThat(repository.getEntities(user0.identifier).toList()) 180 .isEqualTo(listOf(bubble1, bubble2, bubble3)) 181 182 val ret = repository.removeBubblesForUser(user10_managed.identifier, user0.identifier) 183 assertThat(ret).isTrue() // bubbles were removed 184 185 assertThat(repository.getEntities(user0.identifier).toList()) 186 .isEqualTo(listOf(bubble1, bubble3)) 187 verify(launcherApps, never()).uncacheShortcuts(anyString(), 188 any(), 189 any(UserHandle::class.java), anyInt()) 190 } 191 192 @Test 193 fun testRemoveBubblesForUser_withoutBubbles() { 194 repository.addBubbles(user0.identifier, user0bubbles) 195 assertThat(repository.getEntities(user0.identifier).toList()) 196 .isEqualTo(listOf(bubble1, bubble2, bubble3)) 197 198 val ret = repository.removeBubblesForUser(user11.identifier, -1) 199 assertThat(ret).isFalse() // bubbles were NOT removed 200 201 assertThat(repository.getEntities(user0.identifier).toList()) 202 .isEqualTo(listOf(bubble1, bubble2, bubble3)) 203 verify(launcherApps, never()).uncacheShortcuts(anyString(), 204 any(), 205 any(UserHandle::class.java), anyInt()) 206 } 207 208 @Test 209 fun testSanitizeBubbles_noChanges() { 210 repository.addBubbles(user0.identifier, user0bubbles) 211 assertThat(repository.getEntities(user0.identifier).toList()) 212 .isEqualTo(listOf(bubble1, bubble2, bubble3)) 213 repository.addBubbles(user11.identifier, user11bubbles) 214 assertThat(repository.getEntities(user11.identifier).toList()) 215 .isEqualTo(listOf(bubble11, bubble12)) 216 217 val ret = repository.sanitizeBubbles(listOf(user0.identifier, 218 user10_managed.identifier, 219 user11.identifier)) 220 assertThat(ret).isFalse() // bubbles were NOT removed 221 222 verify(launcherApps, never()).uncacheShortcuts(anyString(), 223 any(), 224 any(UserHandle::class.java), anyInt()) 225 } 226 227 @Test 228 fun testSanitizeBubbles_userRemoved() { 229 repository.addBubbles(user0.identifier, user0bubbles) 230 assertThat(repository.getEntities(user0.identifier).toList()) 231 .isEqualTo(listOf(bubble1, bubble2, bubble3)) 232 repository.addBubbles(user11.identifier, user11bubbles) 233 assertThat(repository.getEntities(user11.identifier).toList()) 234 .isEqualTo(listOf(bubble11, bubble12)) 235 236 val ret = repository.sanitizeBubbles(listOf(user11.identifier)) 237 assertThat(ret).isTrue() // bubbles were removed 238 239 assertThat(repository.getEntities(user0.identifier).toList()).isEmpty() 240 verify(launcherApps, never()).uncacheShortcuts(anyString(), 241 any(), 242 any(UserHandle::class.java), anyInt()) 243 244 // User 11 bubbles should still be here 245 assertThat(repository.getEntities(user11.identifier).toList()) 246 .isEqualTo(listOf(bubble11, bubble12)) 247 } 248 249 @Test 250 fun testSanitizeBubbles_userParentRemoved() { 251 repository.addBubbles(user0.identifier, user0bubbles) 252 assertThat(repository.getEntities(user0.identifier).toList()) 253 .isEqualTo(listOf(bubble1, bubble2, bubble3)) 254 255 repository.addBubbles(user11.identifier, user11bubbles) 256 assertThat(repository.getEntities(user11.identifier).toList()) 257 .isEqualTo(listOf(bubble11, bubble12)) 258 259 val ret = repository.sanitizeBubbles(listOf(user0.identifier, user11.identifier)) 260 assertThat(ret).isTrue() // bubbles were removed 261 // bubble2 is the work profile bubble and should be removed 262 assertThat(repository.getEntities(user0.identifier).toList()) 263 .isEqualTo(listOf(bubble1, bubble3)) 264 verify(launcherApps, never()).uncacheShortcuts(anyString(), 265 any(), 266 any(UserHandle::class.java), anyInt()) 267 268 // User 11 bubbles should still be here 269 assertThat(repository.getEntities(user11.identifier).toList()) 270 .isEqualTo(listOf(bubble11, bubble12)) 271 } 272 273 @Test 274 fun testRemoveBubbleForUser_invalidInputDoesntCrash() { 275 repository.removeBubblesForUser(-1, 0) 276 repository.removeBubblesForUser(-1, -1) 277 } 278 } 279 280 private const val PKG_MESSENGER = "com.example.messenger" 281 private const val PKG_CHAT = "com.example.chat" 282