Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 254abe92 authored by João Victor Mendes Freire's avatar João Victor Mendes Freire Committed by Android (Google) Code Review
Browse files

Merge "Add supervision packages to ProtectedPackages." into main

parents 7c7918cf 5e4c3332
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -415,3 +415,10 @@ flag {
    description: "Continue parsing packages in system dirs instead of blocking on install completion for each directory."
    bug: "401622806"
}

flag {
    name: "protect_supervision_packages"
    namespace: "supervision"
    description: "Feature flag to enable protecting Supervision packages."
    bug: "404779514"
}
+30 −2
Original line number Diff line number Diff line
@@ -18,10 +18,14 @@ package com.android.server.pm;

import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.role.RoleManager;
import android.content.Context;
import android.content.pm.Flags;
import android.os.Binder;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;

import com.android.internal.R;
@@ -39,6 +43,10 @@ import java.util.Set;
 * to DPMS.
 */
public class ProtectedPackages {
    static final String TAG = "PackageManager";

    private final Context mContext;

    @UserIdInt
    @GuardedBy("this")
    private int mDeviceOwnerUserId;
@@ -60,6 +68,7 @@ public class ProtectedPackages {
    private final SparseArray<Set<String>> mOwnerProtectedPackages = new SparseArray<>();

    public ProtectedPackages(Context context) {
        mContext = context;
        mDeviceProvisioningPackage = context.getResources().getString(
                R.string.config_deviceProvisioningPackage);
    }
@@ -122,8 +131,11 @@ public class ProtectedPackages {
     * can modify its data or package state.
     */
    private synchronized boolean isProtectedPackage(@UserIdInt int userId, String packageName) {
        return packageName != null && (packageName.equals(mDeviceProvisioningPackage)
                || isOwnerProtectedPackage(userId, packageName));
        return packageName != null
                && (packageName.equals(mDeviceProvisioningPackage)
                        || isOwnerProtectedPackage(userId, packageName)
                        || (Flags.protectSupervisionPackages()
                                && isSupervisionPackage(userId, packageName)));
    }

    /**
@@ -175,4 +187,20 @@ public class ProtectedPackages {
        return !TextUtils.isEmpty(mDeviceProvisioningPackage) && Objects.equals(
                mDeviceProvisioningPackage, packageName);
    }

    /** Query the packages with supervision related roles. */
    private boolean isSupervisionPackage(@UserIdInt int userId, String packageName) {
        final RoleManager roleManager = mContext.getSystemService(RoleManager.class);
        if (roleManager == null) {
            Slog.w(TAG, "Failed to get RoleManager. Assuming package isn't role holder.");
            return false;
        }
        return Binder.withCleanCallingIdentity(
                () -> {
                    List<String> roleHolders =
                            roleManager.getRoleHoldersAsUser(
                                    RoleManager.ROLE_SYSTEM_SUPERVISION, UserHandle.of(userId));
                    return roleHolders.contains(packageName);
                });
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ android_test {
        "truth",
        "junit",
        "junit-params",
        "flag-junit",
        "platform-compat-test-rules",
        "ActivityContext",
        "coretests-aidl",
+120 −0
Original line number Diff line number Diff line
/*
 * 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 com.android.server.pm

import android.app.role.RoleManager
import android.content.pm.Flags
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import com.android.server.testutils.any
import com.android.server.testutils.eq
import com.android.server.testutils.whenever
import com.google.common.truth.Truth.assertThat
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.core.IsNot.not
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.mockito.Mock
import org.mockito.MockitoAnnotations

/**
 * Run as {@code atest
 * FrameworksMockingServicesTests:com.android.server.pm.ProtectedPackagesMockedTest}
 */
@RunWith(JUnit4::class)
open class ProtectedPackagesMockedTest {

    @Rule @JvmField val rule = MockSystemRule()

    @get:Rule val setFlagsRule: SetFlagsRule = SetFlagsRule()

    @Mock lateinit var roleManager: RoleManager

    lateinit var protectedPackages: ProtectedPackages

    @Before
    @Throws(Exception::class)
    fun setup() {
        MockitoAnnotations.openMocks(this)
        rule.system().stageNominalSystemState()
        whenever(roleManager.getRoleHoldersAsUser(eq(RoleManager.ROLE_SYSTEM_SUPERVISION), any()))
            .thenReturn(listOf(SUPERVISION_PKG))
        whenever(rule.mocks().context.getSystemService(RoleManager::class.java))
            .thenReturn(roleManager)
        protectedPackages = ProtectedPackages(rule.mocks().context)
    }

    @Test
    @Throws(Exception::class)
    @EnableFlags(Flags.FLAG_PROTECT_SUPERVISION_PACKAGES)
    fun testIsPackageProtected_supevisionPackage_flagEnabled_returnsTrue() {
        val stateProtected =
            protectedPackages.isPackageStateProtected(TEST_USER_ID, SUPERVISION_PKG)
        val dataProtected = protectedPackages.isPackageDataProtected(TEST_USER_ID, SUPERVISION_PKG)

        assertThat(stateProtected).isTrue()
        assertThat(dataProtected).isTrue()
    }

    @Test
    @Throws(Exception::class)
    @EnableFlags(Flags.FLAG_PROTECT_SUPERVISION_PACKAGES)
    fun testIsPackageProtected_regularPackage_flagEnabled_returnsFalse() {
        val stateProtected = protectedPackages.isPackageStateProtected(TEST_USER_ID, TEST_PKG)
        val dataProtected = protectedPackages.isPackageDataProtected(TEST_USER_ID, TEST_PKG)

        assertThat(stateProtected).isFalse()
        assertThat(dataProtected).isFalse()
    }

    @Test
    @Throws(Exception::class)
    @EnableFlags(Flags.FLAG_PROTECT_SUPERVISION_PACKAGES)
    /* This case should not happen. Is is covered here for completeness. */
    fun testIsPackageProtected_missingRoleManager_flagEnabled_returnsFalse() {
        whenever(rule.mocks().context.getSystemService(RoleManager::class.java)).thenReturn(null)

        val stateProtected =
            protectedPackages.isPackageStateProtected(TEST_USER_ID, SUPERVISION_PKG)
        val dataProtected = protectedPackages.isPackageDataProtected(TEST_USER_ID, SUPERVISION_PKG)

        assertThat(stateProtected).isFalse()
        assertThat(dataProtected).isFalse()
    }

    @Test
    @Throws(Exception::class)
    @DisableFlags(Flags.FLAG_PROTECT_SUPERVISION_PACKAGES)
    fun testIsPackageProtected_supevisionPackage_flagDisabled_returnsFalse() {
        val stateProtected =
            protectedPackages.isPackageStateProtected(TEST_USER_ID, SUPERVISION_PKG)
        val dataProtected = protectedPackages.isPackageDataProtected(TEST_USER_ID, SUPERVISION_PKG)

        assertThat(stateProtected).isFalse()
        assertThat(dataProtected).isFalse()
    }

    private companion object {
        const val SUPERVISION_PKG = "com.android.supervision"
        const val TEST_PKG = "com.android.stub"
        const val TEST_USER_ID = 0
    }
}