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

Commit ce0fe9b6 authored by Antoan Angelov's avatar Antoan Angelov Committed by Android (Google) Code Review
Browse files

Merge "Add profile owner transfer functionality."

parents 5607d0b7 08841efc
Loading
Loading
Loading
Loading
+40 −0
Original line number Diff line number Diff line
@@ -8753,4 +8753,44 @@ public class DevicePolicyManager {
            throw re.rethrowFromSystemServer();
        }
    }

    //TODO STOPSHIP Add link to onTransferComplete callback when implemented.
    /**
     * Transfers the current administrator. All policies from the current administrator are
     * migrated to the new administrator. The whole operation is atomic - the transfer is either
     * complete or not done at all.
     *
     * Depending on the current administrator (device owner, profile owner, corporate owned
     * profile owner), you have the following expected behaviour:
     * <ul>
     *     <li>A device owner can only be transferred to a new device owner</li>
     *     <li>A profile owner can only be transferred to a new profile owner</li>
     *     <li>A corporate owned managed profile can have two cases:
     *          <ul>
     *              <li>If the device owner and profile owner are the same package,
     *              both will be transferred.</li>
     *              <li>If the device owner and profile owner are different packages,
     *              and if this method is called from the profile owner, only the profile owner
     *              is transferred. Similarly, if it is called from the device owner, only
     *              the device owner is transferred.</li>
     *          </ul>
     *     </li>
     * </ul>
     *
     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
     * @param target Which {@link DeviceAdminReceiver} we want the new administrator to be.
     * @param bundle Parameters - This bundle allows the current administrator to pass data to the
     *               new administrator. The parameters will be received in the
     *               onTransferComplete callback.
     * @hide
     */
    public void transferOwner(@NonNull ComponentName admin, @NonNull ComponentName target,
            PersistableBundle bundle) {
        throwIfParentInstance("transferOwner");
        try {
            mService.transferOwner(admin, target, bundle);
        } catch (RemoteException re) {
            throw re.rethrowFromSystemServer();
        }
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -378,4 +378,5 @@ interface IDevicePolicyManager {
    boolean isLogoutEnabled();

    List<String> getDisallowedSystemApps(in ComponentName admin, int userId, String provisioningAction);
    void transferOwner(in ComponentName admin, in ComponentName target, in PersistableBundle bundle);
}
+3 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ package com.android.server.devicepolicy;

import android.app.admin.IDevicePolicyManager;
import android.content.ComponentName;
import android.os.PersistableBundle;

import com.android.internal.R;
import com.android.server.SystemService;
@@ -57,4 +58,6 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub {
    abstract void handleStopUser(int userId);
    
    public void setSystemSetting(ComponentName who, String setting, String value){}

    public void transferOwner(ComponentName admin, ComponentName target, PersistableBundle bundle) {}
}
+109 −20
Original line number Diff line number Diff line
@@ -751,7 +751,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
        private static final String TAG_IS_LOGOUT_ENABLED = "is_logout_enabled";

        final DeviceAdminInfo info;
        DeviceAdminInfo info;


        static final int DEF_PASSWORD_HISTORY_LENGTH = 0;
@@ -1402,6 +1402,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
            return userRestrictions;
        }

        public void transfer(DeviceAdminInfo deviceAdminInfo) {
            if (hasParentActiveAdmin()) {
                parentAdmin.info = deviceAdminInfo;
            }
            info = deviceAdminInfo;
        }

        void dump(String prefix, PrintWriter pw) {
            pw.print(prefix); pw.print("uid="); pw.println(getUid());
            pw.print(prefix); pw.print("testOnlyAdmin=");
@@ -2541,7 +2548,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {


    public DeviceAdminInfo findAdmin(ComponentName adminName, int userHandle,
            boolean throwForMissiongPermission) {
            boolean throwForMissingPermission) {
        if (!mHasFeature) {
            return null;
        }
@@ -2564,7 +2571,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
            final String message = "DeviceAdminReceiver " + adminName + " must be protected with "
                    + permission.BIND_DEVICE_ADMIN;
            Slog.w(LOG_TAG, message);
            if (throwForMissiongPermission &&
            if (throwForMissingPermission &&
                    ai.applicationInfo.targetSdkVersion > Build.VERSION_CODES.M) {
                throw new IllegalArgumentException(message);
            }
@@ -2889,7 +2896,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
                    try {
                        DeviceAdminInfo dai = findAdmin(
                                ComponentName.unflattenFromString(name), userHandle,
                                /* throwForMissionPermission= */ false);
                                /* throwForMissingPermission= */ false);
                        if (VERBOSE_LOG
                                && (UserHandle.getUserId(dai.getActivityInfo().applicationInfo.uid)
                                != userHandle)) {
@@ -3293,19 +3300,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {

        DevicePolicyData policy = getUserData(userHandle);
        DeviceAdminInfo info = findAdmin(adminReceiver, userHandle,
                /* throwForMissionPermission= */ true);
        if (info == null) {
            throw new IllegalArgumentException("Bad admin: " + adminReceiver);
        }
        if (!info.getActivityInfo().applicationInfo.isInternal()) {
            throw new IllegalArgumentException("Only apps in internal storage can be active admin: "
                    + adminReceiver);
        }
        if (info.getActivityInfo().applicationInfo.isInstantApp()) {
            throw new IllegalArgumentException("Instant apps cannot be device admins: "
                    + adminReceiver);
        }
                /* throwForMissingPermission= */ true);
        synchronized (this) {
            checkActiveAdminPrecondition(adminReceiver, info, policy);
            long ident = mInjector.binderClearCallingIdentity();
            try {
                final ActiveAdmin existingAdmin
@@ -3313,10 +3310,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
                if (!refreshing && existingAdmin != null) {
                    throw new IllegalArgumentException("Admin is already added");
                }
                if (policy.mRemovingAdmins.contains(adminReceiver)) {
                    throw new IllegalArgumentException(
                            "Trying to set an admin which is being removed");
                }
                ActiveAdmin newAdmin = new ActiveAdmin(info, /* parent */ false);
                newAdmin.testOnlyAdmin =
                        (existingAdmin != null) ? existingAdmin.testOnlyAdmin
@@ -3346,6 +3339,46 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        }
    }

    private void transferActiveAdminUncheckedLocked(ComponentName incomingReceiver,
            ComponentName outgoingReceiver, int userHandle) {
        final DevicePolicyData policy = getUserData(userHandle);
        final DeviceAdminInfo incomingDeviceInfo = findAdmin(incomingReceiver, userHandle,
            /* throwForMissingPermission= */ true);
        final ActiveAdmin adminToTransfer = policy.mAdminMap.get(outgoingReceiver);
        final int oldAdminUid = adminToTransfer.getUid();

        adminToTransfer.transfer(incomingDeviceInfo);
        policy.mAdminMap.remove(outgoingReceiver);
        policy.mAdminMap.put(incomingReceiver, adminToTransfer);
        if (policy.mPasswordOwner == oldAdminUid) {
            policy.mPasswordOwner = adminToTransfer.getUid();
        }

        saveSettingsLocked(userHandle);
        //TODO: Make sure we revert back when we detect a failure.
        sendAdminCommandLocked(adminToTransfer, DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED,
                null, null);
    }

    private void checkActiveAdminPrecondition(ComponentName adminReceiver, DeviceAdminInfo info,
            DevicePolicyData policy) {
        if (info == null) {
            throw new IllegalArgumentException("Bad admin: " + adminReceiver);
        }
        if (!info.getActivityInfo().applicationInfo.isInternal()) {
            throw new IllegalArgumentException("Only apps in internal storage can be active admin: "
                    + adminReceiver);
        }
        if (info.getActivityInfo().applicationInfo.isInstantApp()) {
            throw new IllegalArgumentException("Instant apps cannot be device admins: "
                    + adminReceiver);
        }
        if (policy.mRemovingAdmins.contains(adminReceiver)) {
            throw new IllegalArgumentException(
                    "Trying to set an admin which is being removed");
        }
    }

    @Override
    public boolean isAdminActive(ComponentName adminReceiver, int userHandle) {
        if (!mHasFeature) {
@@ -11538,4 +11571,60 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        return new ArrayList<>(
                mOverlayPackagesProvider.getNonRequiredApps(admin, userId, provisioningAction));
    }

    //TODO: Add callback information to the javadoc once it is completed.
    //TODO: Make transferOwner atomic.
    @Override
    public void transferOwner(ComponentName admin, ComponentName target, PersistableBundle bundle) {
        if (!mHasFeature) {
            return;
        }

        Preconditions.checkNotNull(admin, "Admin cannot be null.");
        Preconditions.checkNotNull(target, "Target cannot be null.");

        enforceProfileOrDeviceOwner(admin);

        if (admin.equals(target)) {
            throw new IllegalArgumentException("Provided administrator and target are "
                    + "the same object.");
        }

        if (admin.getPackageName().equals(target.getPackageName())) {
            throw new IllegalArgumentException("Provided administrator and target have "
                    + "the same package name.");
        }

        final int callingUserId = mInjector.userHandleGetCallingUserId();
        final DevicePolicyData policy = getUserData(callingUserId);
        final DeviceAdminInfo incomingDeviceInfo = findAdmin(target, callingUserId,
                /* throwForMissingPermission= */ true);
        checkActiveAdminPrecondition(target, incomingDeviceInfo, policy);

        final long id = mInjector.binderClearCallingIdentity();
        try {
            //STOPSHIP add support for COMP, DO, edge cases when device is rebooted/work mode off,
            //transfer callbacks and broadcast
            if (isProfileOwner(admin, callingUserId)) {
                transferProfileOwner(admin, target, callingUserId);
            }
        } finally {
            mInjector.binderRestoreCallingIdentity(id);
        }
    }

    /**
     * Transfers the profile owner for user with id profileOwnerUserId from admin to target.
     */
    private void transferProfileOwner(ComponentName admin, ComponentName target,
            int profileOwnerUserId) {
        synchronized (this) {
            transferActiveAdminUncheckedLocked(target, admin, profileOwnerUserId);
            mOwners.transferProfileOwner(target, profileOwnerUserId);
            Slog.i(LOG_TAG, "Profile owner set: " + target + " on user " + profileOwnerUserId);
            mOwners.writeProfileOwner(profileOwnerUserId);
            mDeviceAdminServiceController.startServiceForOwner(
                    target.getPackageName(), profileOwnerUserId, "transfer-profile-owner");
        }
    }
}
+11 −0
Original line number Diff line number Diff line
@@ -277,6 +277,17 @@ class Owners {
        }
    }

    void transferProfileOwner(ComponentName target, int userId) {
        synchronized (mLock) {
            final OwnerInfo ownerInfo = mProfileOwners.get(userId);
            final OwnerInfo newOwnerInfo = new OwnerInfo(target.getPackageName(), target,
                    ownerInfo.userRestrictionsMigrated, ownerInfo.remoteBugreportUri,
                    ownerInfo.remoteBugreportHash);
            mProfileOwners.put(userId, newOwnerInfo);
            pushToPackageManagerLocked();
        }
    }

    ComponentName getProfileOwnerComponent(int userId) {
        synchronized (mLock) {
            OwnerInfo profileOwner = mProfileOwners.get(userId);