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.launcher3.pm 18 19 import android.content.pm.LauncherApps 20 import android.content.pm.PackageInstaller 21 import android.os.Build 22 import android.os.UserHandle 23 import android.platform.test.annotations.EnableFlags 24 import android.platform.test.flag.junit.SetFlagsRule 25 import androidx.test.ext.junit.runners.AndroidJUnit4 26 import androidx.test.filters.SdkSuppress 27 import androidx.test.filters.SmallTest 28 import com.android.launcher3.Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING 29 import com.android.launcher3.util.Executors.MODEL_EXECUTOR 30 import com.android.launcher3.util.LauncherModelHelper 31 import com.android.launcher3.util.PackageUserKey 32 import org.junit.After 33 import org.junit.Before 34 import org.junit.Rule 35 import org.junit.Test 36 import org.junit.runner.RunWith 37 import org.mockito.kotlin.any 38 import org.mockito.kotlin.doNothing 39 import org.mockito.kotlin.mock 40 import org.mockito.kotlin.spy 41 import org.mockito.kotlin.verify 42 import org.mockito.kotlin.whenever 43 44 @SmallTest 45 @RunWith(AndroidJUnit4::class) 46 class InstallSessionTrackerTest { 47 @get:Rule val setFlagsRule = SetFlagsRule() 48 49 private val mockInstallSessionHelper: InstallSessionHelper = mock() 50 private val mockCallback: InstallSessionTracker.Callback = mock() 51 private val mockPackageInstaller: PackageInstaller = mock() 52 53 private val launcherModelHelper = LauncherModelHelper() 54 private val sandboxContext = launcherModelHelper.sandboxContext 55 56 lateinit var launcherApps: LauncherApps 57 lateinit var installSessionTracker: InstallSessionTracker 58 59 @Before setupnull60 fun setup() { 61 launcherApps = sandboxContext.spyService(LauncherApps::class.java) 62 installSessionTracker = 63 InstallSessionTracker( 64 mockInstallSessionHelper, 65 mockCallback, 66 mockPackageInstaller, 67 launcherApps, 68 ) 69 } 70 71 @After teardownnull72 fun teardown() { 73 launcherModelHelper.destroy() 74 } 75 76 @Test onCreated triggers callbacks for setting up new install sessionnull77 fun `onCreated triggers callbacks for setting up new install session`() { 78 // Given 79 val expectedSessionId = 1 80 val expectedSession = 81 PackageInstaller.SessionInfo().apply { 82 sessionId = expectedSessionId 83 appPackageName = "appPackageName" 84 userId = 0 85 } 86 val expectedPackageKey = PackageUserKey("appPackageName", UserHandle(0)) 87 whenever(mockInstallSessionHelper.getVerifiedSessionInfo(expectedSessionId)) 88 .thenReturn(expectedSession) 89 // When 90 installSessionTracker.onCreated(expectedSessionId) 91 // Then 92 verify(mockCallback).onInstallSessionCreated(any()) 93 verify(mockCallback).onUpdateSessionDisplay(expectedPackageKey, expectedSession) 94 verify(mockInstallSessionHelper).tryQueuePromiseAppIcon(expectedSession) 95 } 96 97 @Test 98 @EnableFlags(FLAG_ENABLE_SUPPORT_FOR_ARCHIVING) onCreated for unarchival triggers onPackageStateChangednull99 fun `onCreated for unarchival triggers onPackageStateChanged`() { 100 // Given 101 val expectedSessionId = 1 102 val expectedSession = 103 spy(PackageInstaller.SessionInfo()).apply { 104 sessionId = expectedSessionId 105 appPackageName = "appPackageName" 106 userId = 0 107 whenever(isUnarchival).thenReturn(true) 108 } 109 whenever(mockInstallSessionHelper.getVerifiedSessionInfo(expectedSessionId)) 110 .thenReturn(expectedSession) 111 // When 112 installSessionTracker.onCreated(expectedSessionId) 113 // Then 114 verify(mockCallback).onPackageStateChanged(any()) 115 } 116 117 @Test onFinished triggers onPackageStateChanged if session found in cachenull118 fun `onFinished triggers onPackageStateChanged if session found in cache`() { 119 // Given 120 val expectedSessionId = 1 121 val expectedSession = 122 PackageInstaller.SessionInfo().apply { 123 sessionId = expectedSessionId 124 appPackageName = "appPackageName" 125 userId = 0 126 } 127 val expectedPackageKey = PackageUserKey("appPackageName", UserHandle(0)) 128 whenever(mockInstallSessionHelper.getVerifiedSessionInfo(expectedSessionId)) 129 .thenReturn(expectedSession) 130 whenever(mockInstallSessionHelper.activeSessions) 131 .thenReturn(hashMapOf(expectedPackageKey to expectedSession)) 132 // When 133 installSessionTracker.onFinished(expectedSessionId, /* success */ true) 134 // Then 135 verify(mockCallback).onPackageStateChanged(any()) 136 } 137 138 @Test onFinished failure calls onSessionFailure and promise icon removal for existing iconnull139 fun `onFinished failure calls onSessionFailure and promise icon removal for existing icon`() { 140 // Given 141 val expectedSessionId = 1 142 val expectedPackage = "appPackageName" 143 val expectedSession = 144 PackageInstaller.SessionInfo().apply { 145 sessionId = expectedSessionId 146 appPackageName = expectedPackage 147 userId = 0 148 } 149 val expectedPackageKey = PackageUserKey(expectedPackage, UserHandle(0)) 150 whenever(mockInstallSessionHelper.getVerifiedSessionInfo(expectedSessionId)) 151 .thenReturn(expectedSession) 152 whenever(mockInstallSessionHelper.activeSessions) 153 .thenReturn(hashMapOf(expectedPackageKey to expectedSession)) 154 whenever(mockInstallSessionHelper.promiseIconAddedForId(expectedSessionId)).thenReturn(true) 155 // When 156 installSessionTracker.onFinished(expectedSessionId, /* success */ false) 157 // Then 158 verify(mockCallback).onSessionFailure(expectedPackage, expectedPackageKey.mUser) 159 verify(mockInstallSessionHelper).removePromiseIconId(expectedSessionId) 160 } 161 162 @Test onProgressChanged triggers onPackageStateChanged if verified session foundnull163 fun `onProgressChanged triggers onPackageStateChanged if verified session found`() { 164 // Given 165 val expectedSessionId = 1 166 val expectedSession = 167 PackageInstaller.SessionInfo().apply { 168 sessionId = expectedSessionId 169 appPackageName = "appPackageName" 170 userId = 0 171 } 172 whenever(mockInstallSessionHelper.getVerifiedSessionInfo(expectedSessionId)) 173 .thenReturn(expectedSession) 174 // When 175 installSessionTracker.onProgressChanged(expectedSessionId, /* progress */ 50f) 176 // Then 177 verify(mockCallback).onPackageStateChanged(any()) 178 } 179 180 @Test onBadgingChanged triggers session display update and queues promise icon if verifiednull181 fun `onBadgingChanged triggers session display update and queues promise icon if verified`() { 182 // Given 183 val expectedSessionId = 1 184 val expectedSession = 185 PackageInstaller.SessionInfo().apply { 186 sessionId = expectedSessionId 187 appPackageName = "appPackageName" 188 userId = 0 189 } 190 val expectedPackageKey = PackageUserKey("appPackageName", UserHandle(0)) 191 whenever(mockInstallSessionHelper.getVerifiedSessionInfo(expectedSessionId)) 192 .thenReturn(expectedSession) 193 // When 194 installSessionTracker.onBadgingChanged(expectedSessionId) 195 // Then 196 verify(mockCallback).onUpdateSessionDisplay(expectedPackageKey, expectedSession) 197 verify(mockInstallSessionHelper).tryQueuePromiseAppIcon(expectedSession) 198 } 199 200 @Test 201 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q) register triggers registerPackageInstallerSessionCallback for versions from Qnull202 fun `register triggers registerPackageInstallerSessionCallback for versions from Q`() { 203 // Given 204 doNothing() 205 .whenever(launcherApps) 206 .registerPackageInstallerSessionCallback(MODEL_EXECUTOR, installSessionTracker) 207 // When 208 installSessionTracker.register() 209 // Then 210 verify(launcherApps) 211 .registerPackageInstallerSessionCallback(MODEL_EXECUTOR, installSessionTracker) 212 } 213 214 @Test 215 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q) unregister triggers unregisterPackageInstallerSessionCallback for versions from Qnull216 fun `unregister triggers unregisterPackageInstallerSessionCallback for versions from Q`() { 217 // Given 218 doNothing() 219 .whenever(launcherApps) 220 .unregisterPackageInstallerSessionCallback(installSessionTracker) 221 // When 222 installSessionTracker.close() 223 // Then 224 verify(launcherApps).unregisterPackageInstallerSessionCallback(installSessionTracker) 225 } 226 } 227