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

Commit 5737941c authored by Guojing Yuan's avatar Guojing Yuan Committed by Android (Google) Code Review
Browse files

Merge "Update role grant flow to be synchronized"

parents 0f2d50e2 f3f0b6c5
Loading
Loading
Loading
Loading
+10 −15
Original line number Diff line number Diff line
@@ -363,13 +363,6 @@ public class CompanionDeviceActivity extends FragmentActivity implements
        mCdmServiceReceiver.send(RESULT_CODE_ASSOCIATION_APPROVED, data);
    }

    private void onAssociationCreated(@NonNull AssociationInfo association) {
        if (DEBUG) Log.i(TAG, "onAssociationCreated(), association=" + association);

        // Don't need to notify the app, CdmService has already done that. Just finish.
        setResultAndFinish(association, RESULT_OK);
    }

    private void cancel(boolean discoveryTimeout, boolean userRejected) {
        if (DEBUG) {
            Log.i(TAG, "cancel(), discoveryTimeout="
@@ -413,7 +406,9 @@ public class CompanionDeviceActivity extends FragmentActivity implements
    }

    private void setResultAndFinish(@Nullable AssociationInfo association, int resultCode) {
        if (DEBUG) Log.i(TAG, "setResultAndFinish(), association=" + association);
        Log.i(TAG, "setResultAndFinish(), association="
                + (association == null ? "null" : association)
                + "resultCode=" + resultCode);

        final Intent data = new Intent();
        if (association != null) {
@@ -652,14 +647,14 @@ public class CompanionDeviceActivity extends FragmentActivity implements
            new ResultReceiver(Handler.getMain()) {
                @Override
                protected void onReceiveResult(int resultCode, Bundle data) {
                    if (resultCode != RESULT_CODE_ASSOCIATION_CREATED) {
                        throw new RuntimeException("Unknown result code: " + resultCode);
                    }

                    final AssociationInfo association = data.getParcelable(EXTRA_ASSOCIATION);
                    if (resultCode == RESULT_CODE_ASSOCIATION_CREATED) {
                        final AssociationInfo association = data.getParcelable(
                                EXTRA_ASSOCIATION, AssociationInfo.class);
                        requireNonNull(association);

                    onAssociationCreated(association);
                        setResultAndFinish(association, RESULT_OK);
                    } else {
                        setResultAndFinish(null, resultCode);
                    }
                }
            };

+102 −22
Original line number Diff line number Diff line
@@ -23,8 +23,10 @@ import static android.companion.CompanionDeviceManager.COMPANION_DEVICE_DISCOVER
import static android.content.ComponentName.createRelative;

import static com.android.server.companion.CompanionDeviceManagerService.DEBUG;
import static com.android.server.companion.MetricUtils.logCreateAssociation;
import static com.android.server.companion.PackageUtils.enforceUsesCompanionDeviceFeature;
import static com.android.server.companion.PermissionsUtils.enforcePermissionsForAssociation;
import static com.android.server.companion.RolesUtils.addRoleHolderForAssociation;
import static com.android.server.companion.RolesUtils.isRoleHolder;
import static com.android.server.companion.Utils.prepareForIpc;

@@ -35,8 +37,10 @@ import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.app.PendingIntent;
import android.companion.AssociatedDevice;
import android.companion.AssociationInfo;
import android.companion.AssociationRequest;
import android.companion.CompanionDeviceManager;
import android.companion.IAssociationRequestCallback;
import android.content.ComponentName;
import android.content.Context;
@@ -87,7 +91,7 @@ import java.util.Set;
 * required.
 *
 * If the user's approval is NOT required: an {@link AssociationRequestsProcessor} invokes
 * {@link #createAssociationAndNotifyApplication(AssociationRequest, String, int, MacAddress, IAssociationRequestCallback)}
 * {@link #createAssociationAndNotifyApplication(AssociationRequest, String, int, MacAddress, IAssociationRequestCallback, ResultReceiver)}
 * which after calling to  {@link CompanionDeviceManagerService} to create an association, notifies
 * the requester via
 * {@link android.companion.CompanionDeviceManager.Callback#onAssociationCreated(AssociationInfo)}.
@@ -99,7 +103,7 @@ import java.util.Set;
 * from the Approval UI in via {@link #mOnRequestConfirmationReceiver} and invokes
 * {@link #processAssociationRequestApproval(AssociationRequest, IAssociationRequestCallback, ResultReceiver, MacAddress)}
 * which one more time checks that the packages holds all necessary permissions before proceeding to
 * {@link #createAssociationAndNotifyApplication(AssociationRequest, String, int, MacAddress, IAssociationRequestCallback)}.
 * {@link #createAssociationAndNotifyApplication(AssociationRequest, String, int, MacAddress, IAssociationRequestCallback, ResultReceiver)}.
 *
 * @see #processNewAssociationRequest(AssociationRequest, String, int, IAssociationRequestCallback)
 * @see #processAssociationRequestApproval(AssociationRequest, IAssociationRequestCallback,
@@ -132,10 +136,10 @@ class AssociationRequestsProcessor {
    private final @NonNull Context mContext;
    private final @NonNull CompanionDeviceManagerService mService;
    private final @NonNull PackageManagerInternal mPackageManager;
    private final @NonNull AssociationStore mAssociationStore;
    private final @NonNull AssociationStoreImpl mAssociationStore;

    AssociationRequestsProcessor(@NonNull CompanionDeviceManagerService service,
            @NonNull AssociationStore associationStore) {
            @NonNull AssociationStoreImpl associationStore) {
        mContext = service.getContext();
        mService = service;
        mPackageManager = service.mPackageManagerInternal;
@@ -174,7 +178,7 @@ class AssociationRequestsProcessor {
                && !willAddRoleHolder(request, packageName, userId)) {
            // 2a. Create association right away.
            createAssociationAndNotifyApplication(request, packageName, userId,
                    /*macAddress*/ null, callback);
                    /* macAddress */ null, callback, /* resultReceiver */ null);
            return;
        }

@@ -253,34 +257,110 @@ class AssociationRequestsProcessor {
        }

        // 2. Create association and notify the application.
        final AssociationInfo association = createAssociationAndNotifyApplication(
                request, packageName, userId, macAddress, callback);

        // 3. Send the association back the Approval Activity, so that it can report back to the app
        // via Activity.setResult().
        final Bundle data = new Bundle();
        data.putParcelable(EXTRA_ASSOCIATION, association);
        resultReceiver.send(RESULT_CODE_ASSOCIATION_CREATED, data);
        createAssociationAndNotifyApplication(request, packageName, userId, macAddress, callback,
                resultReceiver);
    }

    private AssociationInfo createAssociationAndNotifyApplication(
    private void createAssociationAndNotifyApplication(
            @NonNull AssociationRequest request, @NonNull String packageName, @UserIdInt int userId,
            @Nullable MacAddress macAddress, @NonNull IAssociationRequestCallback callback) {
        final AssociationInfo association;
            @Nullable MacAddress macAddress, @NonNull IAssociationRequestCallback callback,
            @NonNull ResultReceiver resultReceiver) {
        final long callingIdentity = Binder.clearCallingIdentity();
        try {
            association = mService.createAssociation(userId, packageName, macAddress,
            createAssociation(userId, packageName, macAddress,
                    request.getDisplayName(), request.getDeviceProfile(),
                    request.getAssociatedDevice(), request.isSelfManaged());
                    request.getAssociatedDevice(), request.isSelfManaged(),
                    callback, resultReceiver);
        } finally {
            Binder.restoreCallingIdentity(callingIdentity);
        }
    }

    public void createAssociation(@UserIdInt int userId, @NonNull String packageName,
            @Nullable MacAddress macAddress, @Nullable CharSequence displayName,
            @Nullable String deviceProfile, @Nullable AssociatedDevice associatedDevice,
            boolean selfManaged, @Nullable IAssociationRequestCallback callback,
            @Nullable ResultReceiver resultReceiver) {
        final int id = mService.getNewAssociationIdForPackage(userId, packageName);
        final long timestamp = System.currentTimeMillis();

        final AssociationInfo association = new AssociationInfo(id, userId, packageName,
                macAddress, displayName, deviceProfile, associatedDevice, selfManaged,
                /* notifyOnDeviceNearby */ false, /* revoked */ false, timestamp, Long.MAX_VALUE);

        if (deviceProfile != null) {
            // If the "Device Profile" is specified, make the companion application a holder of the
            // corresponding role.
            addRoleHolderForAssociation(mService.getContext(), association, success -> {
                if (success) {
                    addAssociationToStore(association, deviceProfile);

                    sendCallbackAndFinish(association, callback, resultReceiver);
                } else {
                    Slog.e(TAG, "Failed to add u" + userId + "\\" + packageName
                            + " to the list of " + deviceProfile + " holders.");

                    sendCallbackAndFinish(null, callback, resultReceiver);
                }
            });
        } else {
            addAssociationToStore(association, null);

            sendCallbackAndFinish(association, callback, resultReceiver);
        }

        // Don't need to update the mRevokedAssociationsPendingRoleHolderRemoval since
        // maybeRemoveRoleHolderForAssociation in PackageInactivityListener will handle the case
        // that there are other devices with the same profile, so the role holder won't be removed.
    }

    private void addAssociationToStore(@NonNull AssociationInfo association,
            @Nullable String deviceProfile) {
        Slog.i(TAG, "New CDM association created=" + association);

        mAssociationStore.addAssociation(association);

        mService.updateSpecialAccessPermissionForAssociatedPackage(association);

        logCreateAssociation(deviceProfile);
    }

    private void sendCallbackAndFinish(@Nullable AssociationInfo association,
            @Nullable IAssociationRequestCallback callback,
            @Nullable ResultReceiver resultReceiver) {
        if (association != null) {
            // Send the association back via the app's callback
            if (callback != null) {
                try {
                    callback.onAssociationCreated(association);
        } catch (RemoteException ignore) { }
                } catch (RemoteException ignore) {
                }
            }

        return association;
            // Send the association back to CompanionDeviceActivity, so that it can report
            // back to the app via Activity.setResult().
            if (resultReceiver != null) {
                final Bundle data = new Bundle();
                data.putParcelable(EXTRA_ASSOCIATION, association);
                resultReceiver.send(RESULT_CODE_ASSOCIATION_CREATED, data);
            }
        } else {
            // Send the association back via the app's callback
            if (callback != null) {
                try {
                    // TODO: update to INTERNAL_ERROR once it's added.
                    callback.onFailure(CompanionDeviceManager.REASON_CANCELED);
                } catch (RemoteException ignore) {
                }
            }

            // Send the association back to CompanionDeviceActivity, so that it can report
            // back to the app via Activity.setResult().
            if (resultReceiver != null) {
                final Bundle data = new Bundle();
                resultReceiver.send(CompanionDeviceManager.RESULT_INTERNAL_ERROR, data);
            }
        }
    }

    private boolean willAddRoleHolder(@NonNull AssociationRequest request,
+9 −44
Original line number Diff line number Diff line
@@ -28,7 +28,6 @@ import static com.android.internal.util.CollectionUtils.any;
import static com.android.internal.util.Preconditions.checkState;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.server.companion.AssociationStore.CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED;
import static com.android.server.companion.MetricUtils.logCreateAssociation;
import static com.android.server.companion.MetricUtils.logRemoveAssociation;
import static com.android.server.companion.PackageUtils.enforceUsesCompanionDeviceFeature;
import static com.android.server.companion.PackageUtils.getPackageInfo;
@@ -38,7 +37,6 @@ import static com.android.server.companion.PermissionsUtils.enforceCallerCanMana
import static com.android.server.companion.PermissionsUtils.enforceCallerIsSystemOr;
import static com.android.server.companion.PermissionsUtils.enforceCallerIsSystemOrCanInteractWithUserId;
import static com.android.server.companion.PermissionsUtils.sanitizeWithCallerChecks;
import static com.android.server.companion.RolesUtils.addRoleHolderForAssociation;
import static com.android.server.companion.RolesUtils.removeRoleHolderForAssociation;

import static java.util.Objects.requireNonNull;
@@ -55,7 +53,6 @@ import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.companion.AssociatedDevice;
import android.companion.AssociationInfo;
import android.companion.AssociationRequest;
import android.companion.DeviceNotAssociatedException;
@@ -819,7 +816,8 @@ public class CompanionDeviceManagerService extends SystemService {
            getContext().enforceCallingOrSelfPermission(
                    android.Manifest.permission.ASSOCIATE_COMPANION_DEVICES, "createAssociation");

            legacyCreateAssociation(userId, macAddress, packageName, null);
            final MacAddress macAddressObj = MacAddress.fromString(macAddress);
            createNewAssociation(userId, packageName, macAddressObj, null, null, false);
        }

        private void checkCanCallNotificationApi(String callingPackage) {
@@ -871,45 +869,12 @@ public class CompanionDeviceManagerService extends SystemService {
        }
    }

    /**
     * @deprecated use
     * {@link #createAssociation(int, String, MacAddress, CharSequence, String, AssociatedDevice,
     * boolean)}
     */
    @Deprecated
    void legacyCreateAssociation(@UserIdInt int userId, @NonNull String deviceMacAddress,
            @NonNull String packageName, @Nullable String deviceProfile) {
        final MacAddress macAddress = MacAddress.fromString(deviceMacAddress);
        createAssociation(userId, packageName, macAddress, null, deviceProfile, null, false);
    }

    AssociationInfo createAssociation(@UserIdInt int userId, @NonNull String packageName,
    void createNewAssociation(@UserIdInt int userId, @NonNull String packageName,
            @Nullable MacAddress macAddress, @Nullable CharSequence displayName,
            @Nullable String deviceProfile, @Nullable AssociatedDevice associatedDevice,
            boolean selfManaged) {
        final int id = getNewAssociationIdForPackage(userId, packageName);
        final long timestamp = System.currentTimeMillis();

        final AssociationInfo association = new AssociationInfo(id, userId, packageName,
                macAddress, displayName, deviceProfile, associatedDevice, selfManaged,
                /* notifyOnDeviceNearby */ false, /* revoked */ false, timestamp, Long.MAX_VALUE);
        Slog.i(TAG, "New CDM association created=" + association);
        mAssociationStore.addAssociation(association);

        // If the "Device Profile" is specified, make the companion application a holder of the
        // corresponding role.
        if (deviceProfile != null) {
            addRoleHolderForAssociation(getContext(), association);
        }

        updateSpecialAccessPermissionForAssociatedPackage(association);
        logCreateAssociation(deviceProfile);

        // Don't need to update the mRevokedAssociationsPendingRoleHolderRemoval since
        // maybeRemoveRoleHolderForAssociation in PackageInactivityListener will handle the case
        // that there are other devices with the same profile, so the role holder won't be removed.

        return association;
            @Nullable String deviceProfile, boolean isSelfManaged) {
        mAssociationRequestsProcessor.createAssociation(userId, packageName, macAddress,
                displayName, deviceProfile, /* associatedDevice */ null, isSelfManaged,
                /* callback */ null, /* resultReceiver */ null);
    }

    @NonNull
@@ -946,7 +911,7 @@ public class CompanionDeviceManagerService extends SystemService {
        return usedIdsForPackage;
    }

    private int getNewAssociationIdForPackage(@UserIdInt int userId, @NonNull String packageName) {
    int getNewAssociationIdForPackage(@UserIdInt int userId, @NonNull String packageName) {
        synchronized (mPreviouslyUsedIds) {
            // First: collect all IDs currently in use for this user's Associations.
            final SparseBooleanArray usedIds = new SparseBooleanArray();
@@ -1170,7 +1135,7 @@ public class CompanionDeviceManagerService extends SystemService {
        }
    }

    private void updateSpecialAccessPermissionForAssociatedPackage(AssociationInfo association) {
    void updateSpecialAccessPermissionForAssociatedPackage(AssociationInfo association) {
        final PackageInfo packageInfo =
                getPackageInfo(getContext(), association.getUserId(), association.getPackageName());

+4 −1
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.companion;

import android.companion.AssociationInfo;
import android.net.MacAddress;
import android.os.Binder;
import android.os.ShellCommand;

@@ -63,7 +64,9 @@ class CompanionDeviceShellCommand extends ShellCommand {
                    int userId = getNextIntArgRequired();
                    String packageName = getNextArgRequired();
                    String address = getNextArgRequired();
                    mService.legacyCreateAssociation(userId, address, packageName, null);
                    final MacAddress macAddress = MacAddress.fromString(address);
                    mService.createNewAssociation(userId, packageName, macAddress,
                            null, null, false);
                }
                break;

+4 −7
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import android.util.Log;
import android.util.Slog;

import java.util.List;
import java.util.function.Consumer;

/** Utility methods for accessing {@link RoleManager} APIs. */
@SuppressLint("LongLogTag")
@@ -46,7 +47,8 @@ final class RolesUtils {
    }

    static void addRoleHolderForAssociation(
            @NonNull Context context, @NonNull AssociationInfo associationInfo) {
            @NonNull Context context, @NonNull AssociationInfo associationInfo,
            @NonNull Consumer<Boolean> roleGrantResult) {
        if (DEBUG) {
            Log.d(TAG, "addRoleHolderForAssociation() associationInfo=" + associationInfo);
        }
@@ -62,12 +64,7 @@ final class RolesUtils {

        roleManager.addRoleHolderAsUser(deviceProfile, packageName,
                MANAGE_HOLDERS_FLAG_DONT_KILL_APP, userHandle, context.getMainExecutor(),
                success -> {
                    if (!success) {
                        Slog.e(TAG, "Failed to add u" + userId + "\\" + packageName
                                + " to the list of " + deviceProfile + " holders.");
                    }
                });
                roleGrantResult);
    }

    static void removeRoleHolderForAssociation(