Merge "[SPA] Add biometric authentication for package modification" into udc-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
06151a58c6
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user