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

Commit 1e938594 authored by Cintia Martins's avatar Cintia Martins Committed by Android (Google) Code Review
Browse files

Merge "Unsuspend apps when disabling supervision" into main

parents e5bb7dc7 20454ca2
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.annotation.IntDef;
import android.annotation.LongDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SpecialUsers;
import android.annotation.SystemApi;
import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
@@ -300,6 +301,18 @@ public abstract class PackageManagerInternal {
    public abstract String[] setPackagesSuspendedByAdmin(
            @UserIdInt int userId, @NonNull String[] packageNames, boolean suspended);



    /**
     * Unsuspends all packages that were previously suspended by a specific suspending package.
     *
     * @param suspendingPackage The package that originally requested suspension.
     * @param suspendingUserId The user ID that requested suspension.
     * @param affectedUserId The user ID for which to unsuspend the packages.
     */
    public abstract void unsuspendForSuspendingPackage(String suspendingPackage,
            @UserIdInt int suspendingUserId, @SpecialUsers.CanBeALL @UserIdInt int affectedUserId);

    /**
     * Get the information describing the dialog to be shown to the user when they try to launch a
     * suspended application.
+12 −1
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SpecialUsers;
import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.Context;
@@ -700,6 +701,15 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal {
        return snapshot().isSuspendingAnyPackages(PLATFORM_PACKAGE_NAME, suspendingUserId, userId);
    }


    @Override
    @Deprecated
    public void unsuspendForSuspendingPackage(String suspendingPackage,
            @UserIdInt int suspendingUserId, @SpecialUsers.CanBeALL @UserIdInt int affectedUserId) {
        mService.unsuspendForSuspendingPackage(
                snapshot(), suspendingPackage, suspendingUserId, affectedUserId);
    }

    @Override
    @Deprecated
    public final void requestChecksums(@NonNull String packageName, boolean includeSplits,
@@ -716,7 +726,8 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal {
    @Deprecated
    public final boolean isPackageFrozen(@NonNull String packageName,
            int callingUid, int userId) {
        return snapshot().getPackageStartability(mService.getSafeMode(), packageName, callingUid, userId)
        return snapshot().getPackageStartability(mService.getSafeMode(),
                packageName, callingUid, userId)
                == PackageManagerService.PACKAGE_STARTABILITY_FROZEN;
    }

+112 −52
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import android.annotation.UserIdInt;
import android.app.KeyguardManager;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.role.RoleManager;
import android.app.supervision.ISupervisionListener;
import android.app.supervision.ISupervisionManager;
import android.app.supervision.SupervisionManagerInternal;
@@ -45,6 +46,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
import android.os.Binder;
import android.os.Bundle;
@@ -165,9 +167,8 @@ public class SupervisionService extends ISupervisionManager.Stub {
    }

    /**
     * Creates an {@link Intent} that can be used with
     * {@link Context#startActivityForResult(String, Intent, int, Bundle)} to launch the activity to
     * verify supervision credentials.
     * Creates an {@link Intent} that can be used with {@link Context#startActivityForResult(String,
     * Intent, int, Bundle)} to launch the activity to verify supervision credentials.
     *
     * <p>A valid {@link Intent} is always returned if supervision is enabled at the time this
     * method is called, the launched activity still need to perform validity checks as the
@@ -271,8 +272,8 @@ public class SupervisionService extends ISupervisionManager.Stub {
            SupervisionListenerRecord record = mSupervisionListeners.get(listener.asBinder());
            if (record == null) {
                try {
                    mSupervisionListeners.put(listener.asBinder(),
                            new SupervisionListenerRecord(listener, userId));
                    mSupervisionListeners.put(
                            listener.asBinder(), new SupervisionListenerRecord(listener, userId));
                } catch (RemoteException e) {
                    // Binder died, ignore
                }
@@ -380,18 +381,20 @@ public class SupervisionService extends ISupervisionManager.Stub {
                mSupervisionSettings.saveUserData();
            }
        }
        Binder.withCleanCallingIdentity(() -> {
        Binder.withCleanCallingIdentity(
                () -> {
                    updateWebContentFilters(userId, enabled);
            dispatchSupervisionEvent(userId,
                    listener -> listener.onSetSupervisionEnabled(userId, enabled));
                    dispatchSupervisionEvent(
                            userId, listener -> listener.onSetSupervisionEnabled(userId, enabled));
                    if (!enabled) {
                clearDevicePolicies(userId, supervisionAppPackage);
                        clearAllDevicePoliciesAndSuspendedPackages(userId);
                    }
                });
    }

    @NonNull
    private List<ISupervisionListener> getSupervisionAppServiceListeners(@UserIdInt int userId,
    private List<ISupervisionListener> getSupervisionAppServiceListeners(
            @UserIdInt int userId,
            @NonNull RemoteExceptionIgnoringConsumer<ISupervisionListener> action) {
        ArrayList<ISupervisionListener> listeners = new ArrayList<>();
        if (!Flags.enableSupervisionAppService()) {
@@ -409,8 +412,10 @@ public class SupervisionService extends ISupervisionManager.Stub {
            }

            if (binder == null) {
                Slogf.d(SupervisionLog.TAG,
                        "Failed to bind to SupervisionAppService for %s now", targetPackage);
                Slogf.d(
                        SupervisionLog.TAG,
                        "Failed to bind to SupervisionAppService for %s now",
                        targetPackage);

                dispatchSupervisionAppServiceWhenConnected(conn, action);
                continue;
@@ -422,7 +427,8 @@ public class SupervisionService extends ISupervisionManager.Stub {
        return listeners;
    }

    private void dispatchSupervisionEvent(@UserIdInt int userId,
    private void dispatchSupervisionEvent(
            @UserIdInt int userId,
            @NonNull RemoteExceptionIgnoringConsumer<ISupervisionListener> action) {
        ArrayList<ISupervisionListener> listeners = new ArrayList<>();

@@ -430,7 +436,8 @@ public class SupervisionService extends ISupervisionManager.Stub {
        listeners.addAll(getSupervisionAppServiceListeners(userId, action));

        synchronized (getLockObject()) {
            mSupervisionListeners.forEach((binder, record) -> {
            mSupervisionListeners.forEach(
                    (binder, record) -> {
                        if (record.userId == userId || record.userId == UserHandle.USER_ALL) {
                            listeners.add(record.listener);
                        }
@@ -440,12 +447,37 @@ public class SupervisionService extends ISupervisionManager.Stub {
        listeners.forEach(listener -> action.accept(listener));
    }

    private void clearDevicePolicies(
            @UserIdInt int userId, @Nullable String supervisionAppPackage) {
    private void clearAllDevicePoliciesAndSuspendedPackages(@UserIdInt int userId) {
        if (!Flags.enableRemovePoliciesOnSupervisionDisable()) {
            return;
        }

        enforcePermission(MANAGE_ROLE_HOLDERS);
        List<String> supervisionPackages = new ArrayList<>();
        RoleManagerWrapper roleManager = mInjector.getRoleManagerWrapper();
        if (roleManager != null) {
            supervisionPackages.addAll(
                    roleManager.getRoleHoldersAsUser(
                            RoleManager.ROLE_SUPERVISION, UserHandle.of(userId)));
            supervisionPackages.addAll(
                    roleManager.getRoleHoldersAsUser(
                            RoleManager.ROLE_SYSTEM_SUPERVISION, UserHandle.of(userId)));
        }

        for (String supervisionPackage : supervisionPackages) {
            clearDevicePoliciesAndSuspendedPackagesFor(userId, supervisionPackage);
        }
    }

    private void clearDevicePoliciesAndSuspendedPackagesFor(int userId, String supervisionPackage) {
        DevicePolicyManagerInternal dpmi = mInjector.getDpmInternal();
        if (Flags.enableRemovePoliciesOnSupervisionDisable()
                && dpmi != null && supervisionAppPackage != null) {
            dpmi.removePoliciesForAdmins(supervisionAppPackage, userId);
        if (dpmi != null) {
            dpmi.removePoliciesForAdmins(supervisionPackage, userId);
        }

        PackageManagerInternal pmi = mInjector.getPackageManagerInternal();
        if (pmi != null) {
            pmi.unsuspendForSuspendingPackage(supervisionPackage, userId, userId);
        }
    }

@@ -456,8 +488,8 @@ public class SupervisionService extends ISupervisionManager.Stub {
        AppServiceConnection.ConnectionStatusListener connectionListener =
                new AppServiceConnection.ConnectionStatusListener() {
                    @Override
            public void onConnected(@NonNull AppServiceConnection connection,
                    @NonNull IInterface service) {
                    public void onConnected(
                            @NonNull AppServiceConnection connection, @NonNull IInterface service) {
                        try {
                            ISupervisionListener binder = (ISupervisionListener) service;
                            Binder.withCleanCallingIdentity(() -> action.accept(binder));
@@ -496,11 +528,7 @@ public class SupervisionService extends ISupervisionManager.Stub {
            final ContentResolver contentResolver = mInjector.context.getContentResolver();
            final int value = Settings.Secure.getIntForUser(contentResolver, key, userId);
            if (!enabled || value != 1) {
                Settings.Secure.putIntForUser(
                        contentResolver,
                        key,
                        value * -1,
                        userId);
                Settings.Secure.putIntForUser(contentResolver, key, value * -1, userId);
            }
        } catch (Settings.SettingNotFoundException ignored) {
            // Ignore the exception and do not change the value as no value has been set.
@@ -536,7 +564,9 @@ public class SupervisionService extends ISupervisionManager.Stub {
     */
    private ComponentName getSupervisionProfileOwnerComponent() {
        return ComponentName.unflattenFromString(
                mInjector.context.getResources()
                mInjector
                        .context
                        .getResources()
                        .getString(R.string.config_defaultSupervisionProfileOwnerComponent));
    }

@@ -570,7 +600,9 @@ public class SupervisionService extends ISupervisionManager.Stub {
        private AppBindingService mAppBindingService;
        private KeyguardManager mKeyguardManager;
        private PackageManager mPackageManager;
        private PackageManagerInternal mPackageManagerInternal;
        private UserManagerInternal mUserManagerInternal;
        private RoleManagerWrapper mRoleManagerWrapper;

        Injector(Context context) {
            this.context = context;
@@ -612,6 +644,34 @@ public class SupervisionService extends ISupervisionManager.Stub {
            }
            return mUserManagerInternal;
        }

        PackageManagerInternal getPackageManagerInternal() {
            if (mPackageManagerInternal == null) {
                mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
            }
            return mPackageManagerInternal;
        }

        RoleManagerWrapper getRoleManagerWrapper() {
            if (mRoleManagerWrapper == null) {
                final Object roleManager = context.getSystemService(RoleManager.class);
                if (roleManager instanceof RoleManager) {
                    mRoleManagerWrapper = new RoleManagerWrapper() {
                        @Override
                        public List<String> getRoleHoldersAsUser(String roleName, UserHandle user) {
                            return ((RoleManager) roleManager).getRoleHoldersAsUser(roleName, user);
                        }
                    };
                } else {
                    mRoleManagerWrapper = (RoleManagerWrapper) roleManager;
                }
            }
            return mRoleManagerWrapper;
        }
    }

    public interface RoleManagerWrapper {
        List<String> getRoleHoldersAsUser(String roleName, UserHandle user);
    }

    /** Publishes local and binder services and allows the service to act during initialization. */
+67 −10
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.app.Activity
import android.app.KeyguardManager
import android.app.admin.DevicePolicyManager
import android.app.admin.DevicePolicyManagerInternal
import android.app.role.RoleManager
import android.app.supervision.ISupervisionListener
import android.app.supervision.SupervisionRecoveryInfo
import android.app.supervision.SupervisionRecoveryInfo.STATE_PENDING
@@ -30,14 +31,15 @@ import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
import android.os.IBinder
import android.content.pm.PackageManager
import android.content.pm.PackageManagerInternal
import android.content.pm.UserInfo
import android.content.pm.UserInfo.FLAG_FOR_TESTING
import android.content.pm.UserInfo.FLAG_FULL
import android.content.pm.UserInfo.FLAG_MAIN
import android.content.pm.UserInfo.FLAG_SYSTEM
import android.os.Handler
import android.os.IBinder
import android.os.PersistableBundle
import android.os.UserHandle
import android.os.UserHandle.MIN_SECONDARY_USER_ID
@@ -56,6 +58,7 @@ import com.android.server.LocalServices
import com.android.server.SystemService.TargetUser
import com.android.server.pm.UserManagerInternal
import com.android.server.supervision.SupervisionService.ACTION_CONFIRM_SUPERVISION_CREDENTIALS
import com.android.server.supervision.SupervisionService.RoleManagerWrapper
import com.google.common.truth.Truth.assertThat
import java.nio.file.Files
import org.junit.Before
@@ -88,8 +91,9 @@ class SupervisionServiceTest {
    @Mock private lateinit var mockDpmInternal: DevicePolicyManagerInternal
    @Mock private lateinit var mockKeyguardManager: KeyguardManager
    @Mock private lateinit var mockPackageManager: PackageManager
    @Mock private lateinit var mockPackageManagerInternal: PackageManagerInternal
    @Mock private lateinit var mockUserManagerInternal: UserManagerInternal
    @Mock private lateinit var mockSupervisionListener: ISupervisionListener
    @Mock private lateinit var mockRoleManager: SupervisionService.RoleManagerWrapper

    private lateinit var context: Context
    private lateinit var lifecycle: SupervisionService.Lifecycle
@@ -98,7 +102,8 @@ class SupervisionServiceTest {
    @Before
    fun setUp() {
        context = InstrumentationRegistry.getInstrumentation().context
        context = SupervisionContextWrapper(context, mockKeyguardManager, mockPackageManager)
        context = SupervisionContextWrapper(context, mockKeyguardManager, mockPackageManager,
            mockRoleManager)

        LocalServices.removeServiceForTest(DevicePolicyManagerInternal::class.java)
        LocalServices.addService(DevicePolicyManagerInternal::class.java, mockDpmInternal)
@@ -106,6 +111,9 @@ class SupervisionServiceTest {
        LocalServices.removeServiceForTest(UserManagerInternal::class.java)
        LocalServices.addService(UserManagerInternal::class.java, mockUserManagerInternal)

        LocalServices.removeServiceForTest(PackageManagerInternal::class.java)
        LocalServices.addService(PackageManagerInternal::class.java, mockPackageManagerInternal)

        // Creating a temporary folder to enable access to SupervisionSettings.
        SupervisionSettings.getInstance()
            .changeDirForTesting(Files.createTempDirectory("tempSupervisionFolder").toFile())
@@ -114,6 +122,7 @@ class SupervisionServiceTest {
        lifecycle = SupervisionService.Lifecycle(context, service)
        lifecycle.registerProfileOwnerListener()

        //TODO: b/427453821 Remove after converting SupervisionSettings from being a singleton.
        assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
    }

@@ -273,10 +282,18 @@ class SupervisionServiceTest {
    @Test
    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_REMOVE_POLICIES_ON_SUPERVISION_DISABLE)
    fun setSupervisionEnabledForUser_removesPoliciesWhenDisabling() {
        for ((role, packageName) in supervisionRoleHolders) {
            whenever(mockRoleManager.getRoleHoldersAsUser(eq(role), any()))
                .thenReturn(listOf(packageName));
        }

        service.setSupervisionEnabledForUser(USER_ID, false)

        assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
        verify(mockDpmInternal).removePoliciesForAdmins(eq(systemSupervisionPackage), eq(USER_ID))
        for (packageName in supervisionRoleHolders.values) {
            verify(mockPackageManagerInternal)
                .unsuspendForSuspendingPackage(eq(packageName), eq(USER_ID), eq(USER_ID))
        }
    }

    @Test
@@ -491,12 +508,16 @@ class SupervisionServiceTest {
        assertThat(service.mSupervisionListeners).doesNotContainKey(binder)
    }

    private fun setSupervisionEnabledForUser(expectedUserId: Int, enabled: Boolean,
            listeners: Map<Int, Pair<ISupervisionListener, IBinder>>) {
    private fun setSupervisionEnabledForUser(
        expectedUserId: Int,
        enabled: Boolean,
        listeners: Map<Int, Pair<ISupervisionListener, IBinder>>,
    ) {
        service.setSupervisionEnabledForUser(expectedUserId, enabled)
        listeners.forEach { userId, (listener, binder) ->
            when (userId) {
                expectedUserId, UserHandle.USER_ALL -> {
                expectedUserId,
                UserHandle.USER_ALL -> {
                    verify(listener).onSetSupervisionEnabled(eq(expectedUserId), eq(enabled))
                }
                else -> {
@@ -506,6 +527,34 @@ class SupervisionServiceTest {
        }
    }

    @Test
    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_REMOVE_POLICIES_ON_SUPERVISION_DISABLE)
    fun clearPackageSuspensions_unsuspendsSupervisionPackages() {
        assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
        for ((role, packageName) in supervisionRoleHolders) {
            whenever(mockRoleManager.getRoleHoldersAsUser(eq(role), any()))
                .thenReturn(listOf(packageName));
        }

        service.setSupervisionEnabledForUser(USER_ID, false)

        assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
        for (packageName in supervisionRoleHolders.values) {
            verify(mockPackageManagerInternal)
                .unsuspendForSuspendingPackage(eq(packageName), eq(USER_ID), eq(USER_ID))
        }
    }

    @Test
    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_REMOVE_POLICIES_ON_SUPERVISION_DISABLE)
    fun clearPackageSuspensions_noSupervisionPackages_doesNothing() {
        whenever(mockRoleManager.getRoleHoldersAsUser(
            any(), any())).thenReturn(listOf())
        service.setSupervisionEnabledForUser(USER_ID, false)

        verify(mockPackageManagerInternal, never()).unsuspendForSuspendingPackage(any(), any(), any())
    }

    private val systemSupervisionPackage: String
        get() = context.getResources().getString(R.string.config_systemSupervision)

@@ -557,6 +606,10 @@ class SupervisionServiceTest {
        const val SUPERVISING_USER_ID = 10
        const val USER_ICON = "user_icon"
        const val USER_TYPE = "fake_user_type"
        val supervisionRoleHolders = mapOf(
            RoleManager.ROLE_SYSTEM_SUPERVISION to "com.example.supervisionapp1",
            RoleManager.ROLE_SUPERVISION to "com.example.supervisionapp2"
        )
        val userData: Map<Int, Int> =
            mapOf(
                USER_SYSTEM to FLAG_SYSTEM,
@@ -574,14 +627,18 @@ private class SupervisionContextWrapper(
    val context: Context,
    val keyguardManager: KeyguardManager,
    val pkgManager: PackageManager,
    val roleManagerWrapper: RoleManagerWrapper,
) : ContextWrapper(context) {
    val interceptors = mutableListOf<Pair<BroadcastReceiver, IntentFilter>>()

    override fun getSystemService(name: String): Any =
        when (name) {
            KEYGUARD_SERVICE -> keyguardManager
    override fun getSystemService(name: String): Any? {
        var ret =  when (name) {
            Context.KEYGUARD_SERVICE -> keyguardManager
            Context.ROLE_SERVICE -> roleManagerWrapper
            else -> super.getSystemService(name)
        }
        return ret;
    }

    override fun getPackageManager() = pkgManager