/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.pm import android.app.PropertyInvalidatedCache import android.content.Context import android.content.Intent import android.content.pm.ActivityInfo import android.content.pm.ApplicationInfo import android.content.pm.FallbackCategoryProvider import android.content.pm.FeatureInfo import android.content.pm.ResolveInfo import android.content.pm.ServiceInfo import android.content.pm.Signature import android.content.pm.SigningDetails import android.content.pm.UserInfo import android.content.pm.parsing.result.ParseTypeImpl import android.content.res.Resources import android.hardware.display.DisplayManager import android.os.Build import android.os.Environment import android.os.SystemProperties import android.os.UserHandle import android.os.UserManager import android.os.incremental.IncrementalManager import android.provider.DeviceConfig import android.util.ArrayMap import android.util.ArraySet import android.util.DisplayMetrics import android.util.EventLog import android.view.Display import com.android.dx.mockito.inline.extended.ExtendedMockito import com.android.dx.mockito.inline.extended.ExtendedMockito.any import com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean import com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt import com.android.dx.mockito.inline.extended.ExtendedMockito.anyLong import com.android.dx.mockito.inline.extended.ExtendedMockito.anyString import com.android.dx.mockito.inline.extended.ExtendedMockito.argThat import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn import com.android.dx.mockito.inline.extended.ExtendedMockito.eq import com.android.dx.mockito.inline.extended.ExtendedMockito.spy import com.android.dx.mockito.inline.extended.StaticMockitoSession import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder import com.android.internal.R import com.android.server.LocalManagerRegistry import com.android.server.LocalServices import com.android.server.LockGuard import com.android.server.SystemConfig import com.android.server.SystemServerInitThreadPool import com.android.server.compat.PlatformCompat import com.android.server.extendedtestutils.wheneverStatic import com.android.server.pm.dex.DexManager import com.android.server.pm.dex.DynamicCodeLogger import com.android.server.pm.parsing.PackageParser2 import com.android.server.pm.parsing.pkg.PackageImpl import com.android.server.pm.parsing.pkg.ParsedPackage import com.android.server.pm.permission.PermissionManagerServiceInternal import com.android.server.pm.pkg.AndroidPackage import com.android.server.pm.pkg.parsing.ParsingPackage import com.android.server.pm.pkg.parsing.ParsingPackageUtils import com.android.server.pm.resolution.ComponentResolver import com.android.server.pm.snapshot.PackageDataSnapshot import com.android.server.pm.verify.domain.DomainVerificationManagerInternal import com.android.server.sdksandbox.SdkSandboxManagerLocal import com.android.server.testutils.TestHandler import com.android.server.testutils.mock import com.android.server.testutils.nullable import com.android.server.testutils.whenever import com.android.server.utils.WatchedArrayMap import libcore.util.HexEncoding import org.junit.Assert import org.junit.rules.TestRule import org.junit.runner.Description import org.junit.runners.model.Statement import org.mockito.AdditionalMatchers.or import org.mockito.Mockito import org.mockito.quality.Strictness import java.io.File import java.io.IOException import java.nio.file.Files import java.security.PublicKey import java.security.cert.CertificateException import java.util.Arrays import java.util.Random import java.util.concurrent.FutureTask /** * A utility for mocking behavior of the system and dependencies when testing PackageManagerService * * Create one of these and call [stageNominalSystemState] as a basis for additional behavior in most * tests. */ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { private val random = Random() val mocks = Mocks() // TODO: getBackingApexFile does not handle paths that aren't /apex val apexDirectory = File("/apex") val packageCacheDirectory: File = Files.createTempDirectory("packageCache").toFile() val rootDirectory: File = Files.createTempDirectory("root").toFile() val dataAppDirectory: File = File(Files.createTempDirectory("data").toFile(), "app") val frameworkSignature: SigningDetails = SigningDetails(arrayOf(generateSpySignature()), 3) val systemPartitions: List = redirectScanPartitions(PackageManagerService.SYSTEM_PARTITIONS) val session: StaticMockitoSession /** Tracks temporary files created by this class during the running of a test. */ private val createdFiles = ArrayList() /** Settings that are expected to be added as part of the test */ private val mPendingPackageAdds: MutableList> = ArrayList() /** Settings simulated to be stored on disk */ private val mPreExistingSettings = ArrayMap() /** The active map simulating the in memory storage of Settings */ private val mSettingsMap = WatchedArrayMap() /** The shared libraries on the device */ private lateinit var mSharedLibraries: SharedLibrariesImpl init { PropertyInvalidatedCache.disableForTestMode() val apply = ExtendedMockito.mockitoSession() .strictness(Strictness.LENIENT) .mockStatic(SystemProperties::class.java) .mockStatic(SystemConfig::class.java) .mockStatic(SELinuxMMAC::class.java, Mockito.CALLS_REAL_METHODS) .mockStatic(FallbackCategoryProvider::class.java) .mockStatic(PackageManagerServiceUtils::class.java) .mockStatic(Environment::class.java) .mockStatic(SystemServerInitThreadPool::class.java) .mockStatic(ParsingPackageUtils::class.java, Mockito.CALLS_REAL_METHODS) .mockStatic(LockGuard::class.java) .mockStatic(EventLog::class.java) .mockStatic(LocalServices::class.java) .mockStatic(LocalManagerRegistry::class.java) .mockStatic(DeviceConfig::class.java) .mockStatic(HexEncoding::class.java) .apply(withSession) session = apply.startMocking() whenever(mocks.settings.insertPackageSettingLPw( any(PackageSetting::class.java), any(AndroidPackage::class.java))) { val name: String = (getArgument(0) as PackageSetting).name val pendingAdd = mPendingPackageAdds.firstOrNull { it.first == name } ?: return@whenever null mPendingPackageAdds.remove(pendingAdd) mSettingsMap[name] = pendingAdd.second null } whenever(mocks.settings.addPackageLPw(nullable(), nullable(), nullable(), nullable(), nullable(), nullable(), nullable(), nullable(), nullable(), nullable(), nullable(), nullable(), nullable(), nullable(), nullable(), nullable(), nullable())) { val name: String = getArgument(0) val pendingAdd = mPendingPackageAdds.firstOrNull { it.first == name } ?: return@whenever null mPendingPackageAdds.remove(pendingAdd) mSettingsMap[name] = pendingAdd.second pendingAdd.second } whenever(mocks.settings.packagesLocked).thenReturn(mSettingsMap) whenever(mocks.settings.getPackageLPr(anyString())) { mSettingsMap[getArgument(0)] } whenever(mocks.settings.readLPw(any(), nullable())) { mSettingsMap.putAll(mPreExistingSettings) !mPreExistingSettings.isEmpty() } } /** Collection of mocks used for PackageManagerService tests. */ class Mocks { val lock = PackageManagerTracedLock() val installLock = Any() val injector: PackageManagerServiceInjector = mock() val systemWrapper: PackageManagerServiceInjector.SystemWrapper = mock() val context: Context = mock() val userManagerService: UserManagerService = mock() val componentResolver: ComponentResolver = mock() val permissionManagerInternal: PermissionManagerServiceInternal = mock() val incrementalManager: IncrementalManager = mock() val platformCompat: PlatformCompat = mock() val settings: Settings = mock() val crossProfileIntentFilterHelper: CrossProfileIntentFilterHelper = mock() val resources: Resources = mock() val systemConfig: SystemConfig = mock() val apexManager: ApexManager = mock() val userManagerInternal: UserManagerInternal = mock() val packageParser: PackageParser2 = mock() val keySetManagerService: KeySetManagerService = mock() val packageAbiHelper: PackageAbiHelper = mock() val appsFilterSnapshot: AppsFilterSnapshotImpl = mock() val appsFilter: AppsFilterImpl = mock { whenever(snapshot()) { appsFilterSnapshot } } val dexManager: DexManager = mock() val dynamicCodeLogger: DynamicCodeLogger = mock() val installer: Installer = mock() val displayMetrics: DisplayMetrics = mock() val domainVerificationManagerInternal: DomainVerificationManagerInternal = mock() val handler = TestHandler(null) val defaultAppProvider: DefaultAppProvider = mock() val backgroundHandler = TestHandler(null) val updateOwnershipHelper: UpdateOwnershipHelper = mock() } companion object { private const val DEVICE_PROVISIONING_PACKAGE_NAME = "com.example.android.device.provisioning" private val DEFAULT_AVAILABLE_FEATURES_MAP = ArrayMap() private val DEFAULT_ACTIVE_APEX_INFO_LIST = emptyList() private val DEFAULT_SHARED_LIBRARIES_LIST = ArrayMap() private val DEFAULT_USERS = Arrays.asList( UserInfo(UserHandle.USER_SYSTEM, "primary", "", UserInfo.FLAG_PRIMARY or UserInfo.FLAG_SYSTEM or UserInfo.FLAG_FULL, UserManager.USER_TYPE_FULL_SYSTEM)) public val DEFAULT_VERSION_INFO = Settings.VersionInfo() init { DEFAULT_VERSION_INFO.fingerprint = "abcdef" DEFAULT_VERSION_INFO.sdkVersion = Build.VERSION_CODES.R DEFAULT_VERSION_INFO.databaseVersion = Settings.CURRENT_DATABASE_VERSION } fun addDefaultSharedLibrary(libName: String, libEntry: SystemConfig.SharedLibraryEntry) { DEFAULT_SHARED_LIBRARIES_LIST[libName] = libEntry } } /** * Clean up any potentially dangling state. This should be run at the end of every test to * account for changes to static memory, such as [LocalServices] */ fun cleanup() { createdFiles.forEach(File::delete) createdFiles.clear() mSettingsMap.clear() mPendingPackageAdds.clear() mPreExistingSettings.clear() session.finishMocking() } /** * Run this method to ensure that all expected actions were executed, such as pending * [Settings] adds. */ fun validateFinalState() { if (mPendingPackageAdds.isNotEmpty()) { Assert.fail( "Not all expected settings were added: ${mPendingPackageAdds.map { it.first }}") } } /** * This method stages enough of system startup to execute the PackageManagerService constructor * successfullly. */ @Throws(Exception::class) fun stageNominalSystemState() { whenever(mocks.injector.context).thenReturn(mocks.context) whenever(mocks.injector.lock).thenReturn(mocks.lock) whenever(mocks.injector.installLock).thenReturn(mocks.installLock) whenever(mocks.injector.systemWrapper).thenReturn(mocks.systemWrapper) whenever(mocks.injector.userManagerService).thenReturn(mocks.userManagerService) whenever(mocks.injector.componentResolver).thenReturn(mocks.componentResolver) whenever(mocks.injector.permissionManagerServiceInternal) { mocks.permissionManagerInternal } whenever(mocks.injector.incrementalManager).thenReturn(mocks.incrementalManager) whenever(mocks.injector.compatibility).thenReturn(mocks.platformCompat) whenever(mocks.injector.settings).thenReturn(mocks.settings) whenever(mocks.injector.crossProfileIntentFilterHelper) .thenReturn(mocks.crossProfileIntentFilterHelper) whenever(mocks.injector.dexManager).thenReturn(mocks.dexManager) whenever(mocks.injector.dynamicCodeLogger).thenReturn(mocks.dynamicCodeLogger) whenever(mocks.injector.systemConfig).thenReturn(mocks.systemConfig) whenever(mocks.injector.apexManager).thenReturn(mocks.apexManager) whenever(mocks.injector.scanningCachingPackageParser).thenReturn(mocks.packageParser) whenever(mocks.injector.scanningPackageParser).thenReturn(mocks.packageParser) whenever(mocks.injector.systemPartitions).thenReturn(systemPartitions) whenever(mocks.injector.appsFilter).thenReturn(mocks.appsFilter) whenever(mocks.injector.abiHelper).thenReturn(mocks.packageAbiHelper) whenever(mocks.injector.userManagerInternal).thenReturn(mocks.userManagerInternal) whenever(mocks.injector.installer).thenReturn(mocks.installer) whenever(mocks.injector.displayMetrics).thenReturn(mocks.displayMetrics) whenever(mocks.injector.domainVerificationManagerInternal) .thenReturn(mocks.domainVerificationManagerInternal) whenever(mocks.injector.handler) { mocks.handler } whenever(mocks.injector.defaultAppProvider) { mocks.defaultAppProvider } whenever(mocks.injector.backgroundHandler) { mocks.backgroundHandler } whenever(mocks.injector.updateOwnershipHelper) { mocks.updateOwnershipHelper } wheneverStatic { SystemConfig.getInstance() }.thenReturn(mocks.systemConfig) whenever(mocks.systemConfig.availableFeatures).thenReturn(DEFAULT_AVAILABLE_FEATURES_MAP) whenever(mocks.systemConfig.sharedLibraries).thenReturn(DEFAULT_SHARED_LIBRARIES_LIST) whenever(mocks.systemConfig.defaultVrComponents).thenReturn(ArraySet()) whenever(mocks.systemConfig.hiddenApiWhitelistedApps).thenReturn(ArraySet()) whenever(mocks.systemConfig.appMetadataFilePaths).thenReturn(ArrayMap()) wheneverStatic { SystemProperties.set(anyString(), anyString()) }.thenDoNothing() wheneverStatic { SystemProperties.getBoolean("fw.free_cache_v2", true) }.thenReturn(true) wheneverStatic { Environment.getApexDirectory() }.thenReturn(apexDirectory) wheneverStatic { Environment.getPackageCacheDirectory() }.thenReturn(packageCacheDirectory) wheneverStatic { SystemProperties.digestOf("ro.build.fingerprint") }.thenReturn("cacheName") wheneverStatic { Environment.getRootDirectory() }.thenReturn(rootDirectory) wheneverStatic { SystemServerInitThreadPool.submit(any(Runnable::class.java), anyString()) } .thenAnswer { FutureTask(it.getArgument(0), null) } wheneverStatic { Environment.getDataDirectory() }.thenReturn(dataAppDirectory.parentFile) wheneverStatic { Environment.getDataSystemDirectory() } .thenReturn(File(dataAppDirectory.parentFile, "system")) whenever(mocks.context.resources).thenReturn(mocks.resources) whenever(mocks.resources.getString(R.string.config_deviceProvisioningPackage)) { DEVICE_PROVISIONING_PACKAGE_NAME } whenever(mocks.apexManager.activeApexInfos).thenReturn(DEFAULT_ACTIVE_APEX_INFO_LIST) whenever(mocks.settings.packagesLocked).thenReturn(mSettingsMap) whenever(mocks.settings.internalVersion).thenReturn(DEFAULT_VERSION_INFO) whenever(mocks.settings.keySetManagerService).thenReturn(mocks.keySetManagerService) whenever(mocks.settings.keySetManagerService).thenReturn(mocks.keySetManagerService) whenever(mocks.settings.snapshot()).thenReturn(mocks.settings) whenever(mocks.packageAbiHelper.derivePackageAbi(any(AndroidPackage::class.java), anyBoolean(), anyBoolean(), nullable(), any(File::class.java))) { android.util.Pair(PackageAbiHelper.Abis("", ""), PackageAbiHelper.NativeLibraryPaths("", false, "", "")) } whenever(mocks.userManagerInternal.getUsers(true, false, false)).thenReturn(DEFAULT_USERS) whenever(mocks.userManagerService.userIds).thenReturn(intArrayOf(0)) whenever(mocks.userManagerService.exists(0)).thenReturn(true) whenever(mocks.packageAbiHelper.deriveNativeLibraryPaths(any(AndroidPackage::class.java), anyBoolean(), anyBoolean(), any(File::class.java))) { PackageAbiHelper.NativeLibraryPaths("", false, "", "") } whenever(mocks.injector.bootstrap(any(PackageManagerService::class.java))) { mSharedLibraries = SharedLibrariesImpl( getArgument(0) as PackageManagerService, mocks.injector) } whenever(mocks.injector.sharedLibrariesImpl) { mSharedLibraries } // everything visible by default whenever(mocks.appsFilter.shouldFilterApplication(any(PackageDataSnapshot::class.java), anyInt(), nullable(), nullable(), anyInt())) { false } whenever(mocks.appsFilterSnapshot.shouldFilterApplication( any(PackageDataSnapshot::class.java), anyInt(), nullable(), nullable(), anyInt())) { false } val displayManager: DisplayManager = mock() whenever(mocks.context.getSystemService(DisplayManager::class.java)) .thenReturn(displayManager) val display: Display = mock() whenever(displayManager.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(display) stageFrameworkScan() stageInstallerScan() stageServicesExtensionScan() stageSystemSharedLibraryScan() stagePermissionsControllerScan() stageSupplementalProcessScan() stageInstantAppResolverScan() } /** * This method will stage the parsing and scanning of a package as well as add it to the * [PackageSetting]s read from disk. */ @Throws(Exception::class) fun stageScanExistingPackage( packageName: String, versionCode: Long, parent: File?, withPackage: (PackageImpl) -> PackageImpl = { it }, withSetting: (PackageSettingBuilder) -> PackageSettingBuilder = { it }, withExistingSetting: (PackageSettingBuilder) -> PackageSettingBuilder = { it } ) { val existingSettingBuilderRef = arrayOfNulls(1) stageScanNewPackage(packageName, versionCode, parent, withPackage, withSetting = { settingBuilder -> withSetting(settingBuilder) existingSettingBuilderRef[0] = settingBuilder settingBuilder }) existingSettingBuilderRef[0]?.setPackage(null) val packageSetting = existingSettingBuilderRef[0]?.let { withExistingSetting(it) }!!.build() addPreExistingSetting(packageSetting.name, packageSetting) } /** * This method will stage a [PackageSetting] read from disk, but does not stage any scanning * or parsing of the package. */ fun addPreExistingSetting(packageName: String, packageSetting: PackageSetting) { mPreExistingSettings[packageName] = packageSetting } /** * This method will stage the parsing and scanning of a package but will not add it to the set * of [PackageSetting]s read from disk. */ @Throws(Exception::class) fun stageScanNewPackage( packageName: String, versionCode: Long, parent: File?, withPackage: (PackageImpl) -> PackageImpl = { it }, withSetting: (PackageSettingBuilder) -> PackageSettingBuilder = { it } ) { val pair = createBasicAndroidPackage(parent, packageName, versionCode) val apkPath = pair.first val pkg = withPackage(pair.second) stageParse(apkPath, pkg) val parentFile = apkPath.parentFile val settingBuilder = withSetting(createBasicSettingBuilder(parentFile, pkg)) val packageSetting = settingBuilder.build() stageSettingInsert(packageSetting.name, packageSetting) } /** * Creates a simple package that should reasonably parse for scan operations. This can be used * as a basis for more complicated packages. */ fun createBasicAndroidPackage( parent: File?, packageName: String, versionCode: Long, signingDetails: SigningDetails = createRandomSigningDetails() ): Pair { val apkPath = File(File(parent, packageName), "base.apk") val pkg = PackageImpl.forTesting(packageName, apkPath.parentFile.path) as PackageImpl pkg.signingDetails = signingDetails val result = ParseTypeImpl.forDefaultParsing().success(signingDetails) wheneverStatic { ParsingPackageUtils.getSigningDetails( any(ParseTypeImpl::class.java), eq(pkg), anyBoolean()) } .thenReturn(result) pkg.versionCode = versionCode.toInt() pkg.versionCodeMajor = (versionCode shr 32).toInt() pkg.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT return Pair(apkPath, pkg) } /** * This method will create a spy of a [SigningDetails] object to be used when simulating the * collection of signatures. */ fun createRandomSigningDetails(): SigningDetails { val signingDetails = spy(SigningDetails(arrayOf(generateSpySignature()), SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3)) doReturn(true).whenever(signingDetails).checkCapability( anyString(), anyInt()) doReturn(true).whenever(signingDetails).checkCapability( any(SigningDetails::class.java), anyInt()) return signingDetails } /** * This method will create a basic [PackageSettingBuilder] from an [AndroidPackage] with all of * the necessary parameters to be returned by a simple scan. This can be used as a basis for * more complicated settings. */ fun createBasicSettingBuilder(parentFile: File, pkg: AndroidPackage): PackageSettingBuilder { return createBasicSettingBuilder(parentFile, pkg.packageName, pkg.longVersionCode, pkg.signingDetails) .setPackage(pkg) } /** * This method will create a basic [PackageSettingBuilder] with all of the necessary parameters * to be returned by a simple scan. This can be used as a basis for more complicated settings. */ fun createBasicSettingBuilder( parentFile: File, packageName: String, versionCode: Long, signingDetails: SigningDetails ): PackageSettingBuilder { return PackageSettingBuilder() .setCodePath(parentFile.path) .setName(packageName) .setPVersionCode(versionCode) .setSigningDetails(signingDetails) } fun createBasicApplicationInfo(pkg: ParsingPackage): ApplicationInfo { val applicationInfo: ApplicationInfo = mock() applicationInfo.packageName = pkg.packageName return applicationInfo } fun createBasicActivityInfo( pkg: ParsingPackage, applicationInfo: ApplicationInfo?, className: String? ): ActivityInfo { val activityInfo = ActivityInfo() activityInfo.applicationInfo = applicationInfo activityInfo.packageName = pkg.packageName activityInfo.name = className return activityInfo } fun createBasicServiceInfo( pkg: ParsingPackage, applicationInfo: ApplicationInfo?, className: String? ): ServiceInfo { val serviceInfo = ServiceInfo() serviceInfo.applicationInfo = applicationInfo serviceInfo.packageName = pkg.packageName serviceInfo.name = className return serviceInfo } /** Finds the appropriate partition, if available, based on a scan flag unique to it. */ fun getPartitionFromFlag(scanFlagMask: Int): ScanPartition = systemPartitions.first { (it.scanFlag and scanFlagMask) != 0 } @Throws(Exception::class) private fun stageParse(path: File, parseResult: ParsingPackage): ParsedPackage { val basePath = path.parentFile basePath.mkdirs() path.createNewFile() createdFiles.add(path) val parsedPackage = parseResult.hideAsParsed() as ParsedPackage whenever(mocks.packageParser.parsePackage( or(eq(path), eq(basePath)), anyInt(), anyBoolean())) { parsedPackage } whenever(mocks.packageParser.parsePackage( or(eq(path), eq(basePath)), anyInt(), anyBoolean())) { parsedPackage } return parsedPackage } private fun stageSettingInsert(name: String, setting: PackageSetting): PackageSetting { mPendingPackageAdds.add(Pair(name, setting)) return setting } @Throws(Exception::class) private fun stageFrameworkScan() { val apk = File(File(rootDirectory, "framework"), "framework-res.apk") val frameworkPkg = PackageImpl.forTesting("android", apk.parentFile.path) as PackageImpl val result = ParseTypeImpl.forDefaultParsing().success(frameworkSignature) wheneverStatic { ParsingPackageUtils.getSigningDetails( any(ParseTypeImpl::class.java), eq(frameworkPkg), eq(true)) } .thenReturn(result) stageParse(apk, frameworkPkg) stageSettingInsert("android", PackageSettingBuilder().setCodePath(apk.path).setName( "android").setPackage(frameworkPkg).build()) } @Throws(Exception::class) private fun stageInstantAppResolverScan() { whenever(mocks.resources.getStringArray(R.array.config_ephemeralResolverPackage)) { arrayOf("com.android.test.ephemeral.resolver") } stageScanNewPackage("com.android.test.ephemeral.resolver", 1L, getPartitionFromFlag(PackageManagerService.SCAN_AS_PRODUCT).privAppFolder, withPackage = { pkg: PackageImpl -> val applicationInfo: ApplicationInfo = createBasicApplicationInfo(pkg) whenever(applicationInfo.isPrivilegedApp).thenReturn(true) mockQueryServices(Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE, createBasicServiceInfo(pkg, applicationInfo, "test.EphemeralService")) mockQueryActivities(Intent.ACTION_INSTANT_APP_RESOLVER_SETTINGS, createBasicActivityInfo(pkg, applicationInfo, "test.SettingsActivity")) pkg }, withSetting = { setting: PackageSettingBuilder -> setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM) }) } @Throws(Exception::class) private fun stagePermissionsControllerScan() { stageScanNewPackage("com.android.permissions.controller", 1L, systemPartitions[0].privAppFolder, withPackage = { pkg: PackageImpl -> val applicationInfo: ApplicationInfo = createBasicApplicationInfo(pkg) whenever(applicationInfo.isPrivilegedApp).thenReturn(true) mockQueryActivities(Intent.ACTION_MANAGE_PERMISSIONS, createBasicActivityInfo( pkg, applicationInfo, "test.PermissionActivity")) pkg }, withSetting = { setting: PackageSettingBuilder -> setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM) }) } @Throws(Exception::class) private fun stageSupplementalProcessScan() { stageScanNewPackage("com.android.supplemental.process", 1L, systemPartitions[0].privAppFolder, withPackage = { pkg: PackageImpl -> val applicationInfo: ApplicationInfo = createBasicApplicationInfo(pkg) mockQueryServices(SdkSandboxManagerLocal.SERVICE_INTERFACE, createBasicServiceInfo( pkg, applicationInfo, "SupplementalProcessService")) pkg }, withSetting = { setting: PackageSettingBuilder -> setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM) }) } @Throws(Exception::class) private fun stageSystemSharedLibraryScan() { stageScanNewPackage("android.ext.shared", 1L, systemPartitions[0].appFolder, withPackage = { it.addLibraryName("android.ext.shared") as PackageImpl }, withSetting = { setting: PackageSettingBuilder -> setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM) } ) } @Throws(Exception::class) private fun stageServicesExtensionScan() { whenever(mocks.context.getString(R.string.config_servicesExtensionPackage)) { "com.android.test.services.extension" } stageScanNewPackage("com.android.test.services.extension", 1L, getPartitionFromFlag(PackageManagerService.SCAN_AS_SYSTEM_EXT).privAppFolder, withSetting = { setting: PackageSettingBuilder -> setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM) }) } @Throws(Exception::class) private fun stageInstallerScan() { stageScanNewPackage( "com.android.test.installer", 1L, getPartitionFromFlag(PackageManagerService.SCAN_AS_PRODUCT).privAppFolder, withPackage = { pkg: PackageImpl -> val applicationInfo: ApplicationInfo = createBasicApplicationInfo(pkg) whenever(applicationInfo.isPrivilegedApp).thenReturn(true) val installerActivity: ActivityInfo = createBasicActivityInfo( pkg, applicationInfo, "test.InstallerActivity") mockQueryActivities(Intent.ACTION_INSTALL_PACKAGE, installerActivity) mockQueryActivities(Intent.ACTION_UNINSTALL_PACKAGE, installerActivity) mockQueryActivities(Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE, installerActivity) pkg }, withSetting = { setting: PackageSettingBuilder -> setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM) } ) } private fun mockQueryActivities(action: String, vararg activities: ActivityInfo) { whenever(mocks.componentResolver.queryActivities(any(), argThat { intent: Intent? -> intent != null && (action == intent.action) }, nullable(), anyLong(), anyInt())) { ArrayList(activities.asList().map { info: ActivityInfo? -> ResolveInfo().apply { activityInfo = info } }) } } private fun mockQueryServices(action: String, vararg services: ServiceInfo) { whenever(mocks.componentResolver.queryServices(any(), argThat { intent: Intent? -> intent != null && (action == intent.action) }, nullable(), anyLong(), anyInt())) { ArrayList(services.asList().map { info -> ResolveInfo().apply { serviceInfo = info } }) } } fun generateSpySignature(): Signature { val bytes = ByteArray(32) random.nextBytes(bytes) val signature = spy(Signature(bytes)) try { val mockPublicKey: PublicKey = mock() doReturn(mockPublicKey).whenever(signature).getPublicKey() } catch (e: CertificateException) { throw RuntimeException(e) } return signature } /** Override get*Folder methods to point to temporary local directories */ @Throws(IOException::class) private fun redirectScanPartitions(partitions: List): List { val spiedPartitions: MutableList = ArrayList(partitions.size) for (partition: ScanPartition in partitions) { val spy = spy(partition) val newRoot = Files.createTempDirectory(partition.folder.name).toFile() whenever(spy.overlayFolder).thenReturn(File(newRoot, "overlay")) whenever(spy.appFolder).thenReturn(File(newRoot, "app")) whenever(spy.privAppFolder).thenReturn(File(newRoot, "priv-app")) whenever(spy.folder).thenReturn(newRoot) spiedPartitions.add(spy) } return spiedPartitions } } /** * Sets up a basic [MockSystem] for use in a test method. This will create a MockSystem before the * test method and any [org.junit.Before] annotated methods. It can then be used to access the * MockSystem via the [system] method or the mocks directly via [mocks]. */ class MockSystemRule : TestRule { var mockSystem: MockSystem? = null override fun apply(base: Statement?, description: Description?) = object : Statement() { @Throws(Throwable::class) override fun evaluate() { mockSystem = MockSystem() try { base!!.evaluate() } finally { mockSystem?.cleanup() mockSystem = null } } } /** Fetch the [MockSystem] instance prepared for this test */ fun system(): MockSystem = mockSystem!! /** Fetch the [MockSystem.Mocks] prepared for this test */ fun mocks(): MockSystem.Mocks = mockSystem!!.mocks }