blob: fc1ae3ca81016c451f6088782109d9cfd6bb0782 [file] [log] [blame]
/*
* Copyright (C) 2025 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 android.app.role.cts
import android.app.role.RoleManager
import android.content.Context
import android.os.Build
import android.os.Process
import android.os.UserHandle
import android.platform.test.annotations.AsbSecurityTest
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SdkSuppress
import androidx.test.platform.app.InstrumentationRegistry
import com.android.compatibility.common.util.SystemUtil
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.CompletableFuture
import java.util.concurrent.Executor
import java.util.concurrent.TimeUnit
import java.util.function.Consumer
import org.junit.After
import org.junit.Assert.fail
import org.junit.Assume.assumeTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
/** Tests {@link RoleManager} security fixes. */
@RunWith(AndroidJUnit4::class)
class RoleManagerSecurityTest {
private var browserRoleHolder: String? = null
@Before
fun setUp() {
saveBrowserRoleHolder()
}
@After
fun tearDown() {
restoreBrowserRoleHolder()
}
private fun saveBrowserRoleHolder() {
val roleHolders: List<String> = getRoleHolders(RoleManager.ROLE_BROWSER, roleManager)
browserRoleHolder = if (roleHolders.isNotEmpty()) roleHolders[0] else null
}
private fun restoreBrowserRoleHolder() {
browserRoleHolder?.let { packageName ->
addRoleHolderAsUser(
RoleManager.ROLE_BROWSER,
packageName,
Process.myUserHandle(),
true,
roleManager,
context.mainExecutor,
)
}
}
private fun getRoleHolders(roleName: String, roleManager: RoleManager): List<String> {
return SystemUtil.callWithShellPermissionIdentity { roleManager.getRoleHolders(roleName) }
}
private fun addRoleHolderAsUser(
roleName: String,
packageName: String,
userHandle: UserHandle,
expectSuccess: Boolean,
roleManager: RoleManager,
executor: Executor,
) {
val future = CallbackFuture()
SystemUtil.runWithShellPermissionIdentity {
roleManager.addRoleHolderAsUser(roleName, packageName, 0, userHandle, executor, future)
}
assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isEqualTo(expectSuccess)
}
@AsbSecurityTest(cveBugId = [379362792])
@SdkSuppress(
minSdkVersion = Build.VERSION_CODES.S,
maxSdkVersion = Build.VERSION_CODES.TIRAMISU,
)
@Test
fun cannotGetDefaultApplicationOnOlderSdk() {
assumeTrue(roleManager.isRoleAvailable(RoleManager.ROLE_BROWSER))
try {
roleManager.getDefaultApplication(RoleManager.ROLE_BROWSER)
} catch (e: NoSuchMethodError) {
// Expected when permission module hasn't been updated
} catch (e: IllegalStateException) {
// Expected when permission module has been updated, and SDK 33 or below
} catch (e: Throwable) {
fail("Missing patch for cveBugId = [379362792]")
}
}
@AsbSecurityTest(cveBugId = [379362792])
@SdkSuppress(
minSdkVersion = Build.VERSION_CODES.S,
maxSdkVersion = Build.VERSION_CODES.TIRAMISU,
)
@Test
fun cannotSetDefaultApplicationOnOlderSdk() {
assumeTrue(roleManager.isRoleAvailable(RoleManager.ROLE_BROWSER))
val future = CallbackFuture()
try {
roleManager.setDefaultApplication(
RoleManager.ROLE_BROWSER,
APP_PACKAGE_NAME,
0,
context.mainExecutor,
future,
)
future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
} catch (e: NoSuchMethodError) {
// Expected when permission module hasn't been updated
} catch (e: IllegalStateException) {
// Expected when permission module has been updated, and SDK 33 or below
} catch (e: Throwable) {
fail("Missing patch for cveBugId = [379362792]")
}
}
private class CallbackFuture : CompletableFuture<Boolean?>(), Consumer<Boolean?> {
override fun accept(successful: Boolean?) {
complete(successful)
}
}
companion object {
private const val TIMEOUT_MILLIS: Long = (15 * 1000).toLong()
private const val APP_PACKAGE_NAME: String = "android.app.role.cts.app"
private val context: Context = InstrumentationRegistry.getInstrumentation().targetContext
private val roleManager: RoleManager = context.getSystemService(RoleManager::class.java)
}
}