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

Commit 3c8b0b82 authored by Sergey Nikolaienkov's avatar Sergey Nikolaienkov
Browse files

Add explicit MAC address and display name fields

Replace List<List>mDeviceIds field in AssociationInfo with explicit
MacAddress mDeviceMacAddress and CharSequence mDeviceDisplayName fields.

Bug: 194301022
Test: atest CompanionDeviceManagerTest
Test: atest FrameworksUiServicesTests:NotificationManagerServiceTest
Change-Id: Ie8ae1c1154d3b53b1df8d612f116b514c99dc95c
parent 27c311d4
Loading
Loading
Loading
Loading
+60 −72
Original line number Diff line number Diff line
@@ -15,29 +15,22 @@
 */
package android.companion;

import static android.companion.DeviceId.TYPE_MAC_ADDRESS;

import android.annotation.NonNull;
import android.annotation.Nullable;
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.
 * TODO(b/1979395): un-hide.
 */
public final class AssociationInfo implements Parcelable {
    /**
@@ -45,15 +38,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 +57,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,8 +86,8 @@ 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 */
@@ -102,28 +101,25 @@ public final class AssociationInfo implements Parcelable {
    }

    /**
     * @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 device (only "self-managed" association are expected to
     * specify a non-null display name of the device).
     *
     * @see #isSelfManaged()
     */
    public @Nullable CharSequence getDisplayName() {
        return mDisplayName;
    }

    /**
@@ -133,9 +129,12 @@ public final class AssociationInfo implements Parcelable {
        return mDeviceProfile;
    }

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

    /**
@@ -164,12 +163,13 @@ public final class AssociationInfo implements Parcelable {
    @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 +180,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,29 +204,31 @@ 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();
    }
@@ -242,19 +245,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");
            }
        }
    }
}
+0 −127
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.companion;

import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;

import java.util.Objects;

/**
 * The class represents free-form ID of a companion device.
 *
 * Since companion devices may have multiple IDs of different type at the same time
 * (eg. a MAC address and a Serial Number), this class not only stores the ID itself, it also stores
 * the type of the ID.
 * Both the type of the ID and its actual value are represented as {@link String}-s.
 *
 * Examples of device IDs:
 *  - "mac_address: f0:18:98:b3:fd:2e"
 *  - "ip_address: 128.121.35.200"
 *  - "imei: 352932100034923 / 44"
 *  - "serial_number: 96141FFAZ000B7"
 *  - "meid_hex: 35293210003492"
 *  - "meid_dic: 08918 92240 0001 3548"
 *
 * @hide
 * TODO(b/1979395): un-hide when implementing public APIs that use this class.
 */
public final class DeviceId implements Parcelable {
    public static final String TYPE_MAC_ADDRESS = "mac_address";

    private final @NonNull String mType;
    private final @NonNull String mValue;

    /**
     * @param type type of the ID. Non-empty. Max length - 16 characters.
     * @param value the ID. Non-empty. Max length - 48 characters.
     * @throws IllegalArgumentException if either {@param type} or {@param value} is empty or
     *         exceeds its max allowed length.
     */
    public DeviceId(@NonNull String type, @NonNull String value) {
        if (type.isEmpty() || value.isEmpty()) {
            throw new IllegalArgumentException("'type' and 'value' should not be empty");
        }
        this.mType = type;
        this.mValue = value;
    }

    /**
     * @return the type of the ID.
     */
    public @NonNull String getType() {
        return mType;
    }

    /**
     * @return the ID.
     */
    public @NonNull String getValue() {
        return mValue;
    }

    @Override
    public String toString() {
        return "DeviceId{"
                + "type='" + mType + '\''
                + ", value='" + mValue + '\''
                + '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof DeviceId)) return false;
        DeviceId deviceId = (DeviceId) o;
        return Objects.equals(mType, deviceId.mType) && Objects.equals(mValue,
                deviceId.mValue);
    }

    @Override
    public int hashCode() {
        return Objects.hash(mType, mValue);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        dest.writeString(mType);
        dest.writeString(mValue);
    }

    private DeviceId(@NonNull Parcel in) {
        mType = in.readString();
        mValue = in.readString();
    }

    public static final @NonNull Creator<DeviceId> CREATOR = new Creator<DeviceId>() {
        @Override
        public DeviceId createFromParcel(@NonNull Parcel in) {
            return new DeviceId(in);
        }

        @Override
        public DeviceId[] newArray(int size) {
            return new DeviceId[size];
        }
    };
}
+9 −10
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@ package com.android.server.companion;

import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES;
import static android.bluetooth.le.ScanSettings.SCAN_MODE_BALANCED;
import static android.companion.DeviceId.TYPE_MAC_ADDRESS;
import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;

@@ -58,7 +57,6 @@ import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.companion.AssociationInfo;
import android.companion.AssociationRequest;
import android.companion.DeviceId;
import android.companion.DeviceNotAssociatedException;
import android.companion.ICompanionDeviceManager;
import android.companion.IFindDeviceCallback;
@@ -74,6 +72,7 @@ import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
import android.net.MacAddress;
import android.net.NetworkPolicyManager;
import android.os.Binder;
import android.os.Environment;
@@ -117,7 +116,6 @@ import java.io.PrintWriter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
@@ -351,7 +349,7 @@ public class CompanionDeviceManagerService extends SystemService {
            }
            return new ArrayList<>(map(
                    getAllAssociations(userId, callingPackage),
                    a -> a.getDeviceMacAddress()));
                    AssociationInfo::getDeviceMacAddressAsString));
        }

        @Override
@@ -623,7 +621,8 @@ public class CompanionDeviceManagerService extends SystemService {
                getNewAssociationIdForPackage(userId, packageName),
                userId,
                packageName,
                Arrays.asList(new DeviceId(TYPE_MAC_ADDRESS, deviceMacAddress)),
                MacAddress.fromString(deviceMacAddress),
                null,
                deviceProfile,
                /* managedByCompanionApp */false,
                /* notifyOnDeviceNearby */ false ,
@@ -649,7 +648,7 @@ public class CompanionDeviceManagerService extends SystemService {
            // First: collect all IDs currently in use for this user's Associations.
            final SparseBooleanArray usedIds = new SparseBooleanArray();
            for (AssociationInfo it : getAllAssociations(userId)) {
                usedIds.put(it.getAssociationId(), true);
                usedIds.put(it.getId(), true);
            }

            // Second: collect all IDs that have been previously used for this package (and user).
@@ -680,7 +679,7 @@ public class CompanionDeviceManagerService extends SystemService {
                    && Objects.equals(it.getDeviceMacAddress(), deviceMacAddress);
            if (match) {
                onAssociationPreRemove(it);
                markIdAsPreviouslyUsedForPackage(it.getAssociationId(), userId, packageName);
                markIdAsPreviouslyUsedForPackage(it.getId(), userId, packageName);
            }
            return match;
        }), userId);
@@ -780,7 +779,7 @@ public class CompanionDeviceManagerService extends SystemService {

        exemptFromAutoRevoke(packageInfo.packageName, packageInfo.applicationInfo.uid);

        if (!association.isManagedByCompanionApp()) {
        if (!association.isSelfManaged()) {
            if (mCurrentlyConnectedDevices.contains(association.getDeviceMacAddress())) {
                grantDeviceProfile(association);
            }
@@ -1224,10 +1223,10 @@ public class CompanionDeviceManagerService extends SystemService {
        ArrayList<ScanFilter> result = new ArrayList<>();
        ArraySet<String> addressesSeen = new ArraySet<>();
        for (AssociationInfo association : getAllAssociations()) {
            if (association.isManagedByCompanionApp()) {
            if (association.isSelfManaged()) {
                continue;
            }
            String address = association.getDeviceMacAddress();
            String address = association.getDeviceMacAddressAsString();
            if (addressesSeen.contains(address)) {
                continue;
            }
+2 −2
Original line number Diff line number Diff line
@@ -67,7 +67,7 @@ public class CompanionDevicePresenceController {
            Slog.i(LOG_TAG,
                    "Sending onDeviceAppeared to " + association.getPackageName() + ")");
            primaryConnector.run(
                    service -> service.onDeviceAppeared(association.getDeviceMacAddress()));
                    s -> s.onDeviceAppeared(association.getDeviceMacAddressAsString()));
        }
    }

@@ -78,7 +78,7 @@ public class CompanionDevicePresenceController {
            Slog.i(LOG_TAG,
                    "Sending onDeviceDisappeared to " + association.getPackageName() + ")");
            primaryConnector.run(
                    service -> service.onDeviceDisappeared(association.getDeviceMacAddress()));
                    s -> s.onDeviceDisappeared(association.getDeviceMacAddressAsString()));
        }
    }

+47 −58

File changed.

Preview size limit exceeded, changes collapsed.