| /* |
| * Copyright (C) 2019 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.om |
| |
| import android.content.om.OverlayInfo |
| import android.content.om.OverlayableInfo |
| import android.content.pm.ApplicationInfo |
| import android.content.pm.PackageInfo |
| import android.os.Process |
| import org.junit.Rule |
| import org.junit.Test |
| import org.junit.rules.ExpectedException |
| |
| class OverlayActorEnforcerTests { |
| companion object { |
| private const val NAMESPACE = "testnamespace" |
| private const val ACTOR_NAME = "testactor" |
| private const val ACTOR_PKG_NAME = "com.test.actor.one" |
| private const val OVERLAYABLE_NAME = "TestOverlayable" |
| private const val UID = 3536 |
| private const val USER_ID = 55 |
| } |
| |
| @get:Rule |
| val expectedException = ExpectedException.none()!! |
| |
| @Test |
| fun isRoot() { |
| verify(callingUid = Process.ROOT_UID) |
| } |
| |
| @Test(expected = SecurityException::class) |
| fun isShell() { |
| verify(callingUid = Process.SHELL_UID) |
| } |
| |
| @Test |
| fun isSystem() { |
| verify(callingUid = Process.SYSTEM_UID) |
| } |
| |
| @Test(expected = SecurityException::class) |
| fun noOverlayable_noTarget() { |
| verify(targetOverlayableName = null) |
| } |
| |
| @Test |
| fun noOverlayable_noTarget_withPermission() { |
| verify(targetOverlayableName = null, hasPermission = true) |
| } |
| |
| @Test(expected = SecurityException::class) |
| fun noOverlayable_withTarget() { |
| verify(targetOverlayableName = OVERLAYABLE_NAME) |
| } |
| |
| @Test(expected = SecurityException::class) |
| fun withOverlayable_noTarget() { |
| verify( |
| targetOverlayableName = null, |
| overlayableInfo = OverlayableInfo(OVERLAYABLE_NAME, null) |
| ) |
| } |
| |
| @Test(expected = SecurityException::class) |
| fun withOverlayable_noActor() { |
| verify( |
| overlayableInfo = OverlayableInfo(OVERLAYABLE_NAME, null) |
| ) |
| } |
| |
| @Test |
| fun withOverlayable_noActor_withPermission() { |
| verify( |
| hasPermission = true, |
| overlayableInfo = OverlayableInfo(OVERLAYABLE_NAME, null) |
| ) |
| } |
| |
| @Test(expected = SecurityException::class) |
| fun withOverlayable_withActor_notActor() { |
| verify( |
| isActor = false, |
| overlayableInfo = OverlayableInfo(OVERLAYABLE_NAME, |
| "overlay://$NAMESPACE/$ACTOR_NAME") |
| ) |
| } |
| |
| @Test(expected = SecurityException::class) |
| fun withOverlayable_withActor_isActor_notPreInstalled() { |
| verify( |
| isActor = true, |
| isPreInstalled = false, |
| overlayableInfo = OverlayableInfo(OVERLAYABLE_NAME, |
| "overlay://$NAMESPACE/$ACTOR_NAME") |
| ) |
| } |
| |
| @Test |
| fun withOverlayable_withActor_isActor_isPreInstalled() { |
| verify( |
| isActor = true, |
| isPreInstalled = true, |
| overlayableInfo = OverlayableInfo(OVERLAYABLE_NAME, |
| "overlay://$NAMESPACE/$ACTOR_NAME") |
| ) |
| } |
| |
| @Test(expected = SecurityException::class) |
| fun withOverlayable_invalidActor() { |
| verify( |
| isActor = true, |
| isPreInstalled = true, |
| overlayableInfo = OverlayableInfo(OVERLAYABLE_NAME, "notValidActor") |
| ) |
| } |
| |
| private fun verify( |
| isActor: Boolean = false, |
| isPreInstalled: Boolean = false, |
| hasPermission: Boolean = false, |
| overlayableInfo: OverlayableInfo? = null, |
| callingUid: Int = UID, |
| targetOverlayableName: String? = OVERLAYABLE_NAME |
| ) { |
| val callback = MockCallback( |
| isActor = isActor, |
| isPreInstalled = isPreInstalled, |
| hasPermission = hasPermission, |
| overlayableInfo = overlayableInfo |
| ) |
| |
| val overlayInfo = overlayInfo(targetOverlayableName) |
| OverlayActorEnforcer(callback) |
| .enforceActor(overlayInfo, "test", callingUid, USER_ID) |
| } |
| |
| private fun overlayInfo(targetOverlayableName: String?) = OverlayInfo("com.test.overlay", |
| "com.test.target", targetOverlayableName, null, "/path", OverlayInfo.STATE_UNKNOWN, 0, |
| 0, false) |
| |
| private class MockCallback( |
| private val isActor: Boolean = false, |
| private val isPreInstalled: Boolean = false, |
| private val hasPermission: Boolean = false, |
| private val overlayableInfo: OverlayableInfo? = null, |
| private vararg val packageNames: String = arrayOf("com.test.actor.one") |
| ) : OverlayActorEnforcer.VerifyCallback { |
| |
| override fun getNamedActors() = if (isActor) { |
| mapOf(NAMESPACE to mapOf(ACTOR_NAME to ACTOR_PKG_NAME)) |
| } else { |
| emptyMap() |
| } |
| |
| override fun getOverlayableForTarget( |
| packageName: String, |
| targetOverlayableName: String?, |
| userId: Int |
| ) = overlayableInfo |
| |
| override fun getPackagesForUid(uid: Int) = when (uid) { |
| UID -> packageNames |
| else -> null |
| } |
| |
| override fun getPackageInfo(packageName: String, userId: Int) = PackageInfo().apply { |
| applicationInfo = ApplicationInfo().apply { |
| flags = if (isPreInstalled) ApplicationInfo.FLAG_SYSTEM else 0 |
| } |
| } |
| |
| override fun doesTargetDefineOverlayable(targetPackageName: String?, userId: Int): Boolean { |
| return overlayableInfo != null |
| } |
| |
| override fun enforcePermission(permission: String?, message: String?) { |
| if (!hasPermission) { |
| throw SecurityException() |
| } |
| } |
| } |
| } |