/* * Copyright (C) 2024 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.systemconfig import android.content.Context import android.content.pm.FeatureInfo import android.util.ArrayMap import android.util.Xml import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry import com.android.server.SystemConfig import com.google.common.truth.Truth.assertThat import org.junit.runner.RunWith import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder import org.junit.runners.JUnit4 @SmallTest @RunWith(JUnit4::class) class SystemConfigReadOnlyFeaturesTest { companion object { private const val FEATURE_ONE = "feature.test.1" private const val FEATURE_TWO = "feature.test.2" private const val FEATURE_RUNTIME_AVAILABLE_TEMPLATE = """ """ private const val FEATURE_RUNTIME_DISABLED_TEMPLATE = """ """ fun featureInfo(featureName: String) = FeatureInfo().apply { name = featureName } } private val context: Context = InstrumentationRegistry.getInstrumentation().context @get:Rule val tempFolder = TemporaryFolder(context.filesDir) private val injector = TestInjector() private var uniqueCounter = 0 @Test fun empty() { assertFeatures().isEmpty() } @Test fun readOnlyEnabled() { addReadOnlyEnabledFeature(FEATURE_ONE) addReadOnlyEnabledFeature(FEATURE_TWO) assertFeatures().containsAtLeast(FEATURE_ONE, FEATURE_TWO) } @Test fun readOnlyAndRuntimeEnabled() { addReadOnlyEnabledFeature(FEATURE_ONE) addRuntimeEnabledFeature(FEATURE_TWO) // No issues with matching availability. assertFeatures().containsAtLeast(FEATURE_ONE, FEATURE_TWO) } @Test fun readOnlyEnabledRuntimeDisabled() { addReadOnlyEnabledFeature(FEATURE_ONE) addRuntimeDisabledFeature(FEATURE_ONE) // Read-only feature availability should take precedence. assertFeatures().contains(FEATURE_ONE) } @Test fun readOnlyDisabled() { addReadOnlyDisabledFeature(FEATURE_ONE) assertFeatures().doesNotContain(FEATURE_ONE) } @Test fun readOnlyAndRuntimeDisabled() { addReadOnlyDisabledFeature(FEATURE_ONE) addRuntimeDisabledFeature(FEATURE_ONE) // No issues with matching (un)availability. assertFeatures().doesNotContain(FEATURE_ONE) } @Test fun readOnlyDisabledRuntimeEnabled() { addReadOnlyDisabledFeature(FEATURE_ONE) addRuntimeEnabledFeature(FEATURE_ONE) addRuntimeEnabledFeature(FEATURE_TWO) // Read-only feature (un)availability should take precedence. assertFeatures().doesNotContain(FEATURE_ONE) assertFeatures().contains(FEATURE_TWO) } fun addReadOnlyEnabledFeature(featureName: String) { injector.readOnlyEnabledFeatures[featureName] = featureInfo(featureName) } fun addReadOnlyDisabledFeature(featureName: String) { injector.readOnlyDisabledFeatures.add(featureName) } fun addRuntimeEnabledFeature(featureName: String) { FEATURE_RUNTIME_AVAILABLE_TEMPLATE.format(featureName).write() } fun addRuntimeDisabledFeature(featureName: String) { FEATURE_RUNTIME_DISABLED_TEMPLATE.format(featureName).write() } private fun String.write() = tempFolder.root.resolve("${uniqueCounter++}.xml") .writeText(this.trimIndent()) private fun assertFeatures() = assertThat(availableFeatures().keys) private fun availableFeatures() = SystemConfig(false, injector).apply { val parser = Xml.newPullParser() readPermissions(parser, tempFolder.root, /*Grant all permission flags*/ 0.inv()) }.let { it.availableFeatures } internal class TestInjector() : SystemConfig.Injector() { val readOnlyEnabledFeatures = ArrayMap() val readOnlyDisabledFeatures = mutableSetOf() override fun isReadOnlySystemEnabledFeature(featureName: String, version: Int): Boolean { return readOnlyEnabledFeatures.containsKey(featureName) } override fun isReadOnlySystemDisabledFeature(featureName: String, version: Int): Boolean { return readOnlyDisabledFeatures.contains(featureName) } override fun getReadOnlySystemEnabledFeatures(): ArrayMap { return readOnlyEnabledFeatures } } }