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

Commit b472b8f2 authored by Varun Shah's avatar Varun Shah
Browse files

Allow the supervision app to access certain UsageStats APIs.

Allow the active supervision component to access app usage limit APIs.
This includes #registerAppUsageLimitObserver
and #unregisterAppUsageLimitObserver.

Bug: 138681869
Test: atest com.android.server.devicepolicy.DevicePolicyManagerTest#testIsActiveSupervisor
Test: atest AppTimeLimitControllerTests
Test: atest android.app.usage.cts.UsageStatsTest
Change-Id: I3daafc14ccdbf27558c1325b965f2bc6d2dab2f6
parent 76e56e40
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -24,6 +24,10 @@ import java.util.List;
/**
 * Device policy manager local system service interface.
 *
 * Maintenance note: if you need to expose information from DPMS to lower level services such as
 * PM/UM/AM/etc, then exposing it from DevicePolicyManagerInternal is not safe because it may cause
 * lock order inversion. Consider using {@link DevicePolicyCache} instead.
 *
 * @hide Only for use within the system server.
 */
public abstract class DevicePolicyManagerInternal {
@@ -80,6 +84,16 @@ public abstract class DevicePolicyManagerInternal {
     */
    public abstract boolean isActiveAdminWithPolicy(int uid, int reqPolicy);

    /**
     * Checks if an app with given uid is the active supervision admin.
     *
     * <p>This takes the DPMS lock. DO NOT call from PM/UM/AM with their lock held.
     *
     * @param uid App uid.
     * @return true if the uid is the active supervision app.
     */
    public abstract boolean isActiveSupervisionApp(int uid);

    /**
     * Creates an intent to show the admin support dialog to say that an action is disallowed by
     * the device/profile owner.
+4 −4
Original line number Diff line number Diff line
@@ -801,8 +801,8 @@ public final class UsageStatsManager {
     *                       {@link #EXTRA_TIME_USED}. Cannot be {@code null} unless the observer is
     *                       being registered with a {@code timeUsed} equal to or greater than
     *                       {@code timeLimit}.
     * @throws SecurityException if the caller doesn't have both SUSPEND_APPS and OBSERVE_APP_USAGE
     *                           permissions.
     * @throws SecurityException if the caller is neither the active supervision app nor does it
     *                           have both SUSPEND_APPS and OBSERVE_APP_USAGE permissions.
     * @hide
     */
    @SystemApi
@@ -827,8 +827,8 @@ public final class UsageStatsManager {
     * an observer that was already unregistered or never registered will have no effect.
     *
     * @param observerId The id of the observer that was previously registered.
     * @throws SecurityException if the caller doesn't have both SUSPEND_APPS and OBSERVE_APP_USAGE
     *                           permissions.
     * @throws SecurityException if the caller is neither the active supervision app nor does it
     *                         have both SUSPEND_APPS and OBSERVE_APP_USAGE permissions.
     * @hide
     */
    @SystemApi
+22 −0
Original line number Diff line number Diff line
@@ -11320,6 +11320,28 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
            }
        }
        @Override
        public boolean isActiveSupervisionApp(int uid) {
            synchronized (getLockObject()) {
                final ActiveAdmin admin = getActiveAdminWithPolicyForUidLocked(
                        null, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, uid);
                if (admin == null) {
                    return false;
                }
                final String supervisionString = mContext.getResources().getString(
                        com.android.internal.R.string
                                .config_defaultSupervisionProfileOwnerComponent);
                if (supervisionString == null) {
                    return false;
                }
                final ComponentName supervisorComponent = ComponentName.unflattenFromString(
                        supervisionString);
                return admin.info.getComponent().equals(supervisorComponent);
            }
        }
        private void notifyCrossProfileProvidersChanged(int userId, List<String> packages) {
            final List<OnCrossProfileWidgetProvidersChangeListener> listeners;
            synchronized (getLockObject()) {
+15 −0
Original line number Diff line number Diff line
@@ -2650,6 +2650,21 @@ public class DevicePolicyManagerTest extends DpmTestBase {
        verifyStayOnWhilePluggedCleared(false);
    }

    public void testIsActiveSupervisionApp() throws Exception {
        when(mServiceContext.resources
                .getString(R.string.config_defaultSupervisionProfileOwnerComponent))
                .thenReturn(admin1.flattenToString());

        final int PROFILE_USER = 15;
        final int PROFILE_ADMIN = UserHandle.getUid(PROFILE_USER, 19436);
        addManagedProfile(admin1, PROFILE_ADMIN, admin1);
        mContext.binder.callingUid = PROFILE_ADMIN;

        final DevicePolicyManagerInternal dpmi =
                LocalServices.getService(DevicePolicyManagerInternal.class);
        assertTrue(dpmi.isActiveSupervisionApp(PROFILE_ADMIN));
    }

    // Test if lock timeout on managed profile is handled correctly depending on whether profile
    // uses separate challenge.
    public void testSetMaximumTimeToLockProfile() throws Exception {
+12 −8
Original line number Diff line number Diff line
@@ -1729,10 +1729,13 @@ public class UsageStatsService extends SystemService implements
        public void registerAppUsageLimitObserver(int observerId, String[] packages,
                long timeLimitMs, long timeUsedMs, PendingIntent callbackIntent,
                String callingPackage) {
            final int callingUid = Binder.getCallingUid();
            final DevicePolicyManagerInternal dpmInternal = getDpmInternal();
            if (!hasPermissions(callingPackage,
                    Manifest.permission.SUSPEND_APPS, Manifest.permission.OBSERVE_APP_USAGE)) {
                throw new SecurityException("Caller doesn't have both SUSPEND_APPS and "
                        + "OBSERVE_APP_USAGE permissions");
                    Manifest.permission.SUSPEND_APPS, Manifest.permission.OBSERVE_APP_USAGE)
                    && (dpmInternal != null && !dpmInternal.isActiveSupervisionApp(callingUid))) {
                throw new SecurityException("Caller must be the active supervision app or "
                        + "it must have both SUSPEND_APPS and OBSERVE_APP_USAGE permissions");
            }

            if (packages == null || packages.length == 0) {
@@ -1741,7 +1744,6 @@ public class UsageStatsService extends SystemService implements
            if (callbackIntent == null && timeUsedMs < timeLimitMs) {
                throw new NullPointerException("callbackIntent can't be null");
            }
            final int callingUid = Binder.getCallingUid();
            final int userId = UserHandle.getUserId(callingUid);
            final long token = Binder.clearCallingIdentity();
            try {
@@ -1754,13 +1756,15 @@ public class UsageStatsService extends SystemService implements

        @Override
        public void unregisterAppUsageLimitObserver(int observerId, String callingPackage) {
            final int callingUid = Binder.getCallingUid();
            final DevicePolicyManagerInternal dpmInternal = getDpmInternal();
            if (!hasPermissions(callingPackage,
                    Manifest.permission.SUSPEND_APPS, Manifest.permission.OBSERVE_APP_USAGE)) {
                throw new SecurityException("Caller doesn't have both SUSPEND_APPS and "
                        + "OBSERVE_APP_USAGE permissions");
                    Manifest.permission.SUSPEND_APPS, Manifest.permission.OBSERVE_APP_USAGE)
                    && (dpmInternal != null && !dpmInternal.isActiveSupervisionApp(callingUid))) {
                throw new SecurityException("Caller must be the active supervision app or "
                        + "it must have both SUSPEND_APPS and OBSERVE_APP_USAGE permissions");
            }

            final int callingUid = Binder.getCallingUid();
            final int userId = UserHandle.getUserId(callingUid);
            final long token = Binder.clearCallingIdentity();
            try {