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

Commit 7918a89b authored by Sergey Nikolaienkov's avatar Sergey Nikolaienkov Committed by Android (Google) Code Review
Browse files

Merge changes from topic "cdm-apis"

* changes:
  Grant Shell CDM permissions
  Check REQUEST_COMPANION_SELF_MANAGED permission in CDM
  Fix usages of CDM APIs in NotificationManagerService(Test)
  Refresh CompanionDeviceManager's API surface
  Add explicit MAC address and display name fields
parents 5f5e11fc 9d95ba09
Loading
Loading
Loading
Loading
+23 −6
Original line number Diff line number Diff line
@@ -9948,6 +9948,16 @@ package android.bluetooth.le {
package android.companion {
  public final class AssociationInfo implements android.os.Parcelable {
    method public int describeContents();
    method @Nullable public android.net.MacAddress getDeviceMacAddress();
    method @Nullable public String getDeviceProfile();
    method @Nullable public CharSequence getDisplayName();
    method public int getId();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.companion.AssociationInfo> CREATOR;
  }
  public final class AssociationRequest implements android.os.Parcelable {
    method public int describeContents();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -9960,6 +9970,7 @@ package android.companion {
    method @NonNull public android.companion.AssociationRequest.Builder addDeviceFilter(@Nullable android.companion.DeviceFilter<?>);
    method @NonNull public android.companion.AssociationRequest build();
    method @NonNull public android.companion.AssociationRequest.Builder setDeviceProfile(@NonNull String);
    method @NonNull public android.companion.AssociationRequest.Builder setDisplayName(@NonNull CharSequence);
    method @NonNull public android.companion.AssociationRequest.Builder setSingleDevice(boolean);
  }
@@ -9995,20 +10006,26 @@ package android.companion {
  }
  public final class CompanionDeviceManager {
    method @RequiresPermission(value=android.Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH, conditional=true) public void associate(@NonNull android.companion.AssociationRequest, @NonNull android.companion.CompanionDeviceManager.Callback, @Nullable android.os.Handler);
    method public void disassociate(@NonNull String);
    method @NonNull public java.util.List<java.lang.String> getAssociations();
    method @RequiresPermission(anyOf={android.Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH, "android.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING", "android.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION"}, conditional=true) public void associate(@NonNull android.companion.AssociationRequest, @NonNull android.companion.CompanionDeviceManager.Callback, @Nullable android.os.Handler);
    method @RequiresPermission(anyOf={android.Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH, "android.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING", "android.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION"}, conditional=true) public void associate(@NonNull android.companion.AssociationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.companion.CompanionDeviceManager.Callback);
    method @Deprecated public void disassociate(@NonNull String);
    method public void disassociate(int);
    method @Deprecated @NonNull public java.util.List<java.lang.String> getAssociations();
    method @NonNull public java.util.List<android.companion.AssociationInfo> getMyAssociations();
    method @Deprecated public boolean hasNotificationAccess(android.content.ComponentName);
    method public void requestNotificationAccess(android.content.ComponentName);
    method @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void startObservingDevicePresence(@NonNull String) throws android.companion.DeviceNotAssociatedException;
    method @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void stopObservingDevicePresence(@NonNull String) throws android.companion.DeviceNotAssociatedException;
    field public static final String EXTRA_DEVICE = "android.companion.extra.DEVICE";
    field public static final String EXTRA_ASSOCIATION = "android.companion.extra.ASSOCIATION";
    field @Deprecated public static final String EXTRA_DEVICE = "android.companion.extra.DEVICE";
  }
  public abstract static class CompanionDeviceManager.Callback {
    ctor public CompanionDeviceManager.Callback();
    method public abstract void onDeviceFound(android.content.IntentSender);
    method public abstract void onFailure(CharSequence);
    method public void onAssociationCreated(@NonNull android.companion.AssociationInfo);
    method public void onAssociationPending(@NonNull android.content.IntentSender);
    method @Deprecated public void onDeviceFound(@NonNull android.content.IntentSender);
    method public abstract void onFailure(@Nullable CharSequence);
  }
  public abstract class CompanionDeviceService extends android.app.Service {
+20 −0
Original line number Diff line number Diff line
@@ -256,6 +256,7 @@ package android {
    field public static final String RENOUNCE_PERMISSIONS = "android.permission.RENOUNCE_PERMISSIONS";
    field public static final String REQUEST_COMPANION_PROFILE_APP_STREAMING = "android.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING";
    field public static final String REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION = "android.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION";
    field public static final String REQUEST_COMPANION_SELF_MANAGED = "android.permission.REQUEST_COMPANION_SELF_MANAGED";
    field public static final String REQUEST_NETWORK_SCORES = "android.permission.REQUEST_NETWORK_SCORES";
    field public static final String REQUEST_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE";
    field public static final String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
@@ -2344,15 +2345,34 @@ package android.bluetooth.le {
package android.companion {
  public final class AssociationInfo implements android.os.Parcelable {
    method @NonNull public String getPackageName();
    method public boolean isSelfManaged();
  }
  public final class AssociationRequest implements android.os.Parcelable {
    method @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED) public boolean isForceConfirmation();
    method @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED) public boolean isSelfManaged();
    field @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING) public static final String DEVICE_PROFILE_APP_STREAMING = "android.app.role.COMPANION_DEVICE_APP_STREAMING";
    field @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION) public static final String DEVICE_PROFILE_AUTOMOTIVE_PROJECTION = "android.app.role.SYSTEM_AUTOMOTIVE_PROJECTION";
  }
  public static final class AssociationRequest.Builder {
    method @NonNull @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED) public android.companion.AssociationRequest.Builder setForceConfirmation(boolean);
    method @NonNull @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED) public android.companion.AssociationRequest.Builder setSelfManaged(boolean);
  }
  public final class CompanionDeviceManager {
    method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public void addOnAssociationsChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.CompanionDeviceManager.OnAssociationsChangedListener);
    method @RequiresPermission(android.Manifest.permission.ASSOCIATE_COMPANION_DEVICES) public void associate(@NonNull String, @NonNull android.net.MacAddress, @NonNull byte[]);
    method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean canPairWithoutPrompt(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
    method @NonNull @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public java.util.List<android.companion.AssociationInfo> getAllAssociations();
    method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean isDeviceAssociatedForWifiConnection(@NonNull String, @NonNull android.net.MacAddress, @NonNull android.os.UserHandle);
    method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public void removeOnAssociationsChangedListener(@NonNull android.companion.CompanionDeviceManager.OnAssociationsChangedListener);
  }
  public static interface CompanionDeviceManager.OnAssociationsChangedListener {
    method public void onAssociationsChanged(@NonNull java.util.List<android.companion.AssociationInfo>);
  }
}
+106 −80
Original line number Diff line number Diff line
@@ -15,29 +15,24 @@
 */
package android.companion;

import static android.companion.DeviceId.TYPE_MAC_ADDRESS;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.UserIdInt;
import android.net.MacAddress;
import android.os.Parcel;
import android.os.Parcelable;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * A record indicating that a device with a given address was confirmed by the user to be
 * associated to a given companion app
 *
 * @hide
 * TODO(b/1979395): un-hide and rename to AssociationInfo when implementing public APIs that use
 *                  this class.
 * Details for a specific "association" that has been established between an app and companion
 * device.
 * <p>
 * An association gives an app the ability to interact with a companion device without needing to
 * acquire broader runtime permissions. An association only exists after the user has confirmed that
 * an app should have access to a companion device.
 */
public final class AssociationInfo implements Parcelable {
    /**
@@ -45,15 +40,16 @@ public final class AssociationInfo implements Parcelable {
     * Disclosed to the clients (ie. companion applications) for referring to this record (eg. in
     * {@code disassociate()} API call).
     */
    private final int mAssociationId;
    private final int mId;

    private final @UserIdInt int mUserId;
    private final @NonNull String mPackageName;

    private final @NonNull List<DeviceId> mDeviceIds;
    private final @Nullable MacAddress mDeviceMacAddress;
    private final @Nullable CharSequence mDisplayName;
    private final @Nullable String mDeviceProfile;

    private final boolean mManagedByCompanionApp;
    private final boolean mSelfManaged;
    private boolean mNotifyOnDeviceNearby;
    private final long mTimeApprovedMs;

@@ -63,23 +59,28 @@ public final class AssociationInfo implements Parcelable {
     *
     * @hide
     */
    public AssociationInfo(int associationId, @UserIdInt int userId, @NonNull String packageName,
            @NonNull List<DeviceId> deviceIds, @Nullable String deviceProfile,
            boolean managedByCompanionApp, boolean notifyOnDeviceNearby, long timeApprovedMs) {
        if (associationId <= 0) {
    public AssociationInfo(int id, @UserIdInt int userId, @NonNull String packageName,
            @Nullable MacAddress macAddress, @Nullable CharSequence displayName,
            @Nullable String deviceProfile, boolean selfManaged, boolean notifyOnDeviceNearby,
            long timeApprovedMs) {
        if (id <= 0) {
            throw new IllegalArgumentException("Association ID should be greater than 0");
        }
        validateDeviceIds(deviceIds);
        if (macAddress == null && displayName == null) {
            throw new IllegalArgumentException("MAC address and the Display Name must NOT be null "
                    + "at the same time");
        }

        mAssociationId = associationId;
        mId = id;

        mUserId = userId;
        mPackageName = packageName;

        mDeviceMacAddress = macAddress;
        mDisplayName = displayName;
        mDeviceProfile = deviceProfile;
        mDeviceIds = new ArrayList<>(deviceIds);

        mManagedByCompanionApp = managedByCompanionApp;
        mSelfManaged = selfManaged;
        mNotifyOnDeviceNearby = notifyOnDeviceNearby;
        mTimeApprovedMs = timeApprovedMs;
    }
@@ -87,55 +88,66 @@ public final class AssociationInfo implements Parcelable {
    /**
     * @return the unique ID of this association record.
     */
    public int getAssociationId() {
        return mAssociationId;
    public int getId() {
        return mId;
    }

    /** @hide */
    public int getUserId() {
    /**
     * @return the ID of the user who "owns" this association.
     * @hide
     */
    public @UserIdInt int getUserId() {
        return mUserId;
    }

    /** @hide */
    /**
     * @return the package name of the app which this association refers to.
     * @hide
     */
    @SystemApi
    public @NonNull String getPackageName() {
        return mPackageName;
    }

    /**
     * @return list of the device's IDs. At any time a device has at least 1 ID.
     * @return the MAC address of the device.
     */
    public @NonNull List<DeviceId> getDeviceIds() {
        return Collections.unmodifiableList(mDeviceIds);
    public @Nullable MacAddress getDeviceMacAddress() {
        return mDeviceMacAddress;
    }

    /**
     * @param type type of the ID.
     * @return ID of the type if the device has such ID, {@code null} otherwise.
     */
    public @Nullable String getIdOfType(@NonNull String type) {
        for (int i = mDeviceIds.size() - 1; i >= 0; i--) {
            final DeviceId id = mDeviceIds.get(i);
            if (Objects.equals(mDeviceIds.get(i).getType(), type)) return id.getValue();
        }
        return null;
    /** @hide */
    public @Nullable String getDeviceMacAddressAsString() {
        return mDeviceMacAddress != null ? mDeviceMacAddress.toString() : null;
    }

    /** @hide */
    public @NonNull String getDeviceMacAddress() {
        return Objects.requireNonNull(getIdOfType(TYPE_MAC_ADDRESS),
                "MAC address of this device is not specified.");
    /**
     * @return the display name of the companion device (optionally) provided by the companion
     * application.
     *
     * @see AssociationRequest.Builder#setDisplayName(CharSequence)
     */
    public @Nullable CharSequence getDisplayName() {
        return mDisplayName;
    }

    /**
     * @return the profile of the device.
     * @return the companion device profile used when establishing this
     *         association, or {@code null} if no specific profile was used.
     * @see AssociationRequest.Builder#setDeviceProfile(String)
     */
    public @Nullable String getDeviceProfile() {
        return mDeviceProfile;
    }

    /** @hide */
    public boolean isManagedByCompanionApp() {
        return mManagedByCompanionApp;
    /**
     * @return whether the association is managed by the companion application it belongs to.
     * @see AssociationRequest.Builder#setSelfManaged(boolean)
     * @hide
     */
    @SystemApi
    public boolean isSelfManaged() {
        return mSelfManaged;
    }

    /**
@@ -161,15 +173,40 @@ public final class AssociationInfo implements Parcelable {
        return mUserId == userId && Objects.equals(mPackageName, packageName);
    }

    /**
     * Utility method for checking if the association represents a device with the given MAC
     * address.
     *
     * @return {@code false} if the association is "self-managed".
     *         {@code false} if the {@code addr} is {@code null} or is not a valid MAC address.
     *         Otherwise - the result of {@link MacAddress#equals(Object)}
     *
     * @hide
     */
    public boolean isLinkedTo(@Nullable String addr) {
        if (mSelfManaged) return false;

        if (addr == null) return false;

        final MacAddress macAddress;
        try {
            macAddress = MacAddress.fromString(addr);
        } catch (IllegalArgumentException e) {
            return false;
        }
        return macAddress.equals(mDeviceMacAddress);
    }

    @Override
    public String toString() {
        return "Association{"
                + "mAssociationId=" + mAssociationId
                + "mId=" + mId
                + ", mUserId=" + mUserId
                + ", mPackageName='" + mPackageName + '\''
                + ", mDeviceIds=" + mDeviceIds
                + ", mDeviceMacAddress=" + mDeviceMacAddress
                + ", mDisplayName='" + mDisplayName + '\''
                + ", mDeviceProfile='" + mDeviceProfile + '\''
                + ", mManagedByCompanionApp=" + mManagedByCompanionApp
                + ", mSelfManaged=" + mSelfManaged
                + ", mNotifyOnDeviceNearby=" + mNotifyOnDeviceNearby
                + ", mTimeApprovedMs=" + new Date(mTimeApprovedMs)
                + '}';
@@ -180,20 +217,21 @@ public final class AssociationInfo implements Parcelable {
        if (this == o) return true;
        if (!(o instanceof AssociationInfo)) return false;
        final AssociationInfo that = (AssociationInfo) o;
        return mAssociationId == that.mAssociationId
        return mId == that.mId
                && mUserId == that.mUserId
                && mManagedByCompanionApp == that.mManagedByCompanionApp
                && mSelfManaged == that.mSelfManaged
                && mNotifyOnDeviceNearby == that.mNotifyOnDeviceNearby
                && mTimeApprovedMs == that.mTimeApprovedMs
                && Objects.equals(mPackageName, that.mPackageName)
                && Objects.equals(mDeviceProfile, that.mDeviceProfile)
                && Objects.equals(mDeviceIds, that.mDeviceIds);
                && Objects.equals(mDeviceMacAddress, that.mDeviceMacAddress)
                && Objects.equals(mDisplayName, that.mDisplayName)
                && Objects.equals(mDeviceProfile, that.mDeviceProfile);
    }

    @Override
    public int hashCode() {
        return Objects.hash(mAssociationId, mUserId, mPackageName, mDeviceIds, mDeviceProfile,
                mManagedByCompanionApp, mNotifyOnDeviceNearby, mTimeApprovedMs);
        return Objects.hash(mId, mUserId, mPackageName, mDeviceMacAddress, mDisplayName,
                mDeviceProfile, mSelfManaged, mNotifyOnDeviceNearby, mTimeApprovedMs);
    }

    @Override
@@ -203,33 +241,36 @@ public final class AssociationInfo implements Parcelable {

    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        dest.writeInt(mAssociationId);
        dest.writeInt(mId);

        dest.writeInt(mUserId);
        dest.writeString(mPackageName);

        dest.writeParcelableList(mDeviceIds, 0);
        dest.writeTypedObject(mDeviceMacAddress, 0);
        dest.writeCharSequence(mDisplayName);
        dest.writeString(mDeviceProfile);

        dest.writeBoolean(mManagedByCompanionApp);
        dest.writeBoolean(mSelfManaged);
        dest.writeBoolean(mNotifyOnDeviceNearby);
        dest.writeLong(mTimeApprovedMs);
    }

    private AssociationInfo(@NonNull Parcel in) {
        mAssociationId = in.readInt();
        mId = in.readInt();

        mUserId = in.readInt();
        mPackageName = in.readString();

        mDeviceIds = in.readParcelableList(new ArrayList<>(), DeviceId.class.getClassLoader());
        mDeviceMacAddress = in.readTypedObject(MacAddress.CREATOR);
        mDisplayName = in.readCharSequence();
        mDeviceProfile = in.readString();

        mManagedByCompanionApp = in.readBoolean();
        mSelfManaged = in.readBoolean();
        mNotifyOnDeviceNearby = in.readBoolean();
        mTimeApprovedMs = in.readLong();
    }

    @NonNull
    public static final Parcelable.Creator<AssociationInfo> CREATOR =
            new Parcelable.Creator<AssociationInfo>() {
        @Override
@@ -242,19 +283,4 @@ public final class AssociationInfo implements Parcelable {
            return new AssociationInfo(in);
        }
    };

    private static void validateDeviceIds(@NonNull List<DeviceId> ids) {
        if (ids.isEmpty()) throw new IllegalArgumentException("Device must have at least 1 id.");

        // Make sure none of the IDs are null, and they all have different types.
        final Set<String> types = new HashSet<>(ids.size());
        for (int i = ids.size() - 1; i >= 0; i--) {
            final DeviceId deviceId = ids.get(i);
            if (deviceId == null) throw new IllegalArgumentException("DeviceId must not be null");
            if (!types.add(deviceId.getType())) {
                throw new IllegalArgumentException(
                        "DeviceId cannot have multiple IDs of the same type");
            }
        }
    }
}
+166 −24

File changed.

Preview size limit exceeded, changes collapsed.

+392 −131

File changed.

Preview size limit exceeded, changes collapsed.

Loading