Merge "[SPA] Add biometric authentication for package modification" into udc-qpr-dev

This commit is contained in:
Treehugger Robot
2025-02-04 20:46:20 -08:00
committed by Android (Google) Code Review
6 changed files with 219 additions and 43 deletions

View File

@@ -56,6 +56,7 @@ import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowUtils;
import com.android.settingslib.applications.AppUtils;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.instantapps.InstantAppDataProvider;
@@ -81,6 +82,7 @@ import org.robolectric.util.ReflectionHelpers;
import java.util.Set;
@Config(shadows = {ShadowUtils.class})
@RunWith(RobolectricTestRunner.class)
public class AppButtonsPreferenceControllerTest {
@@ -164,6 +166,7 @@ public class AppButtonsPreferenceControllerTest {
@After
public void tearDown() {
ShadowAppUtils.reset();
ShadowUtils.reset();
}
@Test

View File

@@ -50,6 +50,7 @@ public class ShadowUtils {
private static ArraySet<String> sResultLinks = new ArraySet<>();
private static boolean sIsBatteryPresent;
private static boolean sIsMultipleBiometricsSupported;
private static boolean sIsProtectedPackage;
@Implementation
protected static int enforceSameOwner(Context context, int userId) {
@@ -82,6 +83,7 @@ public class ShadowUtils {
sResultLinks = new ArraySet<>();
sIsBatteryPresent = true;
sIsMultipleBiometricsSupported = false;
sIsProtectedPackage = false;
}
public static void setIsDemoUser(boolean isDemoUser) {
@@ -188,4 +190,13 @@ public class ShadowUtils {
public static void setIsMultipleBiometricsSupported(boolean isMultipleBiometricsSupported) {
sIsMultipleBiometricsSupported = isMultipleBiometricsSupported;
}
@Implementation
protected static boolean isProtectedPackage(Context context, String packageName) {
return sIsProtectedPackage;
}
public static void setIsProtectedPackage(boolean isProtectedPackage) {
sIsProtectedPackage = isProtectedPackage;
}
}

View File

@@ -17,12 +17,15 @@
package com.android.settings.spa.app.appinfo
import android.app.ActivityManager
import android.app.KeyguardManager
import android.app.settings.SettingsEnums
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.settings.Utils
import com.android.settings.testutils.FakeFeatureFactory
import com.android.settings.testutils.mockAsUser
import com.android.settingslib.spaprivileged.framework.common.activityManager
@@ -31,26 +34,26 @@ import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.MockitoSession
import org.mockito.Mockito.any
import org.mockito.Mockito.doNothing
import org.mockito.Mockito.verify
import org.mockito.Spy
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import org.mockito.quality.Strictness
import org.mockito.Mockito.`when` as whenever
@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
class PackageInfoPresenterTest {
@get:Rule
val mockito: MockitoRule = MockitoJUnit.rule()
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
@@ -63,16 +66,38 @@ class PackageInfoPresenterTest {
@Mock
private lateinit var packageManagers: IPackageManagers
@Mock
private lateinit var keyguardManager: KeyguardManager
private lateinit var mockSession: MockitoSession
private val fakeFeatureFactory = FakeFeatureFactory()
private val metricsFeatureProvider = fakeFeatureFactory.metricsFeatureProvider
private var isUserAuthenticated: Boolean = false
@Before
fun setUp() {
mockSession = ExtendedMockito.mockitoSession()
.initMocks(this)
.mockStatic(Utils::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
context.mockAsUser()
whenever(context.packageManager).thenReturn(packageManager)
whenever(context.activityManager).thenReturn(activityManager)
whenever(context.getSystemService(KeyguardManager::class.java)).thenReturn(keyguardManager)
whenever(Utils.isProtectedPackage(context, PACKAGE_NAME)).thenReturn(false)
}
@After
fun tearDown() {
mockSession.finishMocking()
isUserAuthenticated = false
}
@Test
fun enable() = runTest {
coroutineScope {
@@ -97,10 +122,23 @@ class PackageInfoPresenterTest {
packageInfoPresenter.disable()
}
verifyAction(SettingsEnums.ACTION_SETTINGS_DISABLE_APP)
verify(packageManager).setApplicationEnabledSetting(
PACKAGE_NAME, PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, 0
)
verifyDisablePackage()
}
@Test
fun disable_protectedPackage() = runTest {
mockProtectedPackage()
setAuthPassesAutomatically()
coroutineScope {
val packageInfoPresenter =
PackageInfoPresenter(context, PACKAGE_NAME, USER_ID, this, packageManagers)
packageInfoPresenter.disable()
}
verifyUserAuthenticated()
verifyDisablePackage()
}
@Test
@@ -111,14 +149,22 @@ class PackageInfoPresenterTest {
packageInfoPresenter.startUninstallActivity()
verifyAction(SettingsEnums.ACTION_SETTINGS_UNINSTALL_APP)
val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
verify(context).startActivityAsUser(intentCaptor.capture(), any())
with(intentCaptor.value) {
assertThat(action).isEqualTo(Intent.ACTION_UNINSTALL_PACKAGE)
assertThat(data?.schemeSpecificPart).isEqualTo(PACKAGE_NAME)
assertThat(getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, true)).isEqualTo(false)
}
verifyUninstallPackage()
}
@Test
fun startUninstallActivity_protectedPackage() = runTest {
mockProtectedPackage()
setAuthPassesAutomatically()
doNothing().`when`(context).startActivityAsUser(any(), any())
val packageInfoPresenter =
PackageInfoPresenter(context, PACKAGE_NAME, USER_ID, this, packageManagers)
packageInfoPresenter.startUninstallActivity()
verifyUserAuthenticated()
verifyUninstallPackage()
}
@Test
@@ -143,8 +189,23 @@ class PackageInfoPresenterTest {
packageInfoPresenter.forceStop()
}
verifyAction(SettingsEnums.ACTION_APP_FORCE_STOP)
verify(activityManager).forceStopPackageAsUser(PACKAGE_NAME, USER_ID)
verifyForceStop()
}
@Test
fun forceStop_protectedPackage() = runTest {
mockProtectedPackage()
setAuthPassesAutomatically()
coroutineScope {
val packageInfoPresenter =
PackageInfoPresenter(context, PACKAGE_NAME, USER_ID, this, packageManagers)
packageInfoPresenter.forceStop()
}
verifyUserAuthenticated()
verifyForceStop()
}
@Test
@@ -161,6 +222,46 @@ class PackageInfoPresenterTest {
verify(metricsFeatureProvider).action(context, category, PACKAGE_NAME)
}
private fun verifyDisablePackage() {
verifyAction(SettingsEnums.ACTION_SETTINGS_DISABLE_APP)
verify(packageManager).setApplicationEnabledSetting(
PACKAGE_NAME, PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, 0
)
}
private fun verifyUninstallPackage() {
verifyAction(SettingsEnums.ACTION_SETTINGS_UNINSTALL_APP)
val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
verify(context).startActivityAsUser(intentCaptor.capture(), any())
with(intentCaptor.value) {
assertThat(action).isEqualTo(Intent.ACTION_UNINSTALL_PACKAGE)
assertThat(data?.schemeSpecificPart).isEqualTo(PACKAGE_NAME)
assertThat(getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, true)).isEqualTo(false)
}
}
private fun verifyForceStop() {
verifyAction(SettingsEnums.ACTION_APP_FORCE_STOP)
verify(activityManager).forceStopPackageAsUser(PACKAGE_NAME, USER_ID)
}
private fun setAuthPassesAutomatically() {
whenever(keyguardManager.isKeyguardSecure).thenReturn(mockUserAuthentication())
}
private fun mockUserAuthentication() : Boolean {
isUserAuthenticated = true
return false
}
private fun mockProtectedPackage() {
whenever(Utils.isProtectedPackage(context, PACKAGE_NAME)).thenReturn(true)
}
private fun verifyUserAuthenticated() {
assertThat(isUserAuthenticated).isTrue()
}
private companion object {
const val PACKAGE_NAME = "package.name"
const val USER_ID = 0