/* * Copyright (C) 2019 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.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.Date; import java.util.Objects; /** * Details for a specific "association" that has been established between an app and companion * device. *
* 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 { /** * A String indicates the selfManaged device is not connected. */ private static final String LAST_TIME_CONNECTED_NONE = "None"; /** * A unique ID of this Association record. * Disclosed to the clients (ie. companion applications) for referring to this record (eg. in * {@code disassociate()} API call). */ private final int mId; private final @UserIdInt int mUserId; private final @NonNull String mPackageName; private final @Nullable MacAddress mDeviceMacAddress; private final @Nullable CharSequence mDisplayName; private final @Nullable String mDeviceProfile; private final @Nullable AssociatedDevice mAssociatedDevice; private final boolean mSelfManaged; private final boolean mNotifyOnDeviceNearby; private final int mSystemDataSyncFlags; /** * Indicates that the association has been revoked (removed), but we keep the association * record for final clean up (e.g. removing the app from the list of the role holders). * * @see CompanionDeviceManager#disassociate(int) */ private final boolean mRevoked; private final long mTimeApprovedMs; /** * A long value indicates the last time connected reported by selfManaged devices * Default value is Long.MAX_VALUE. */ private final long mLastTimeConnectedMs; /** * Creates a new Association. * * @hide */ public AssociationInfo(int id, @UserIdInt int userId, @NonNull String packageName, @Nullable MacAddress macAddress, @Nullable CharSequence displayName, @Nullable String deviceProfile, @Nullable AssociatedDevice associatedDevice, boolean selfManaged, boolean notifyOnDeviceNearby, boolean revoked, long timeApprovedMs, long lastTimeConnectedMs, int systemDataSyncFlags) { if (id <= 0) { throw new IllegalArgumentException("Association ID should be greater than 0"); } if (macAddress == null && displayName == null) { throw new IllegalArgumentException("MAC address and the Display Name must NOT be null " + "at the same time"); } mId = id; mUserId = userId; mPackageName = packageName; mDeviceMacAddress = macAddress; mDisplayName = displayName; mDeviceProfile = deviceProfile; mAssociatedDevice = associatedDevice; mSelfManaged = selfManaged; mNotifyOnDeviceNearby = notifyOnDeviceNearby; mRevoked = revoked; mTimeApprovedMs = timeApprovedMs; mLastTimeConnectedMs = lastTimeConnectedMs; mSystemDataSyncFlags = systemDataSyncFlags; } /** * @return the unique ID of this association record. */ public int getId() { return mId; } /** * @return the ID of the user who "owns" this association. * @hide */ public @UserIdInt int getUserId() { return mUserId; } /** * @return the package name of the app which this association refers to. * @hide */ @SystemApi public @NonNull String getPackageName() { return mPackageName; } /** * @return the MAC address of the device. */ public @Nullable MacAddress getDeviceMacAddress() { return mDeviceMacAddress; } /** @hide */ public @Nullable String getDeviceMacAddressAsString() { return mDeviceMacAddress != null ? mDeviceMacAddress.toString().toUpperCase() : null; } /** * @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 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; } /** * Companion device that was associated. Note that this field is not persisted across sessions. * Device can be one of the following types: * *
* Note that you must call either {@link Builder#setLastTimeConnected(long) * setLastTimeConnected} or {@link Builder#setNotifyOnDeviceNearby(boolean) * setNotifyOnDeviceNearby} before you will be able to call {@link Builder#build() build}. * * This is ensured statically at compile time. * * @hide */ @NonNull public static NonActionableBuilder builder(@NonNull AssociationInfo info) { return new Builder(info); } /** @hide */ public static final class Builder implements NonActionableBuilder { @NonNull private final AssociationInfo mOriginalInfo; private boolean mNotifyOnDeviceNearby; private boolean mRevoked; private long mLastTimeConnectedMs; private int mSystemDataSyncFlags; private Builder(@NonNull AssociationInfo info) { mOriginalInfo = info; mNotifyOnDeviceNearby = info.mNotifyOnDeviceNearby; mRevoked = info.mRevoked; mLastTimeConnectedMs = info.mLastTimeConnectedMs; mSystemDataSyncFlags = info.mSystemDataSyncFlags; } /** @hide */ @Override @NonNull public Builder setLastTimeConnected(long lastTimeConnectedMs) { if (lastTimeConnectedMs < 0) { throw new IllegalArgumentException( "lastTimeConnectedMs must not be negative! (Given " + lastTimeConnectedMs + " )"); } mLastTimeConnectedMs = lastTimeConnectedMs; return this; } /** @hide */ @Override @NonNull public Builder setNotifyOnDeviceNearby(boolean notifyOnDeviceNearby) { mNotifyOnDeviceNearby = notifyOnDeviceNearby; return this; } /** @hide */ @Override @NonNull public Builder setRevoked(boolean revoked) { mRevoked = revoked; return this; } /** @hide */ @Override @NonNull public Builder setSystemDataSyncFlags(int flags) { mSystemDataSyncFlags = flags; return this; } /** @hide */ @NonNull public AssociationInfo build() { return new AssociationInfo( mOriginalInfo.mId, mOriginalInfo.mUserId, mOriginalInfo.mPackageName, mOriginalInfo.mDeviceMacAddress, mOriginalInfo.mDisplayName, mOriginalInfo.mDeviceProfile, mOriginalInfo.mAssociatedDevice, mOriginalInfo.mSelfManaged, mNotifyOnDeviceNearby, mRevoked, mOriginalInfo.mTimeApprovedMs, mLastTimeConnectedMs, mSystemDataSyncFlags ); } } /** * This interface is returned from the * {@link AssociationInfo#builder(android.companion.AssociationInfo) builder} entry point * to indicate that this builder is not yet in a state that can produce a meaningful * {@link AssociationInfo} object that is different from the one originally passed in. * *
* Only by calling one of the setter methods is this builder turned into one where calling * {@link Builder#build() build()} makes sense. * * @hide */ public interface NonActionableBuilder { /** @hide */ @NonNull Builder setNotifyOnDeviceNearby(boolean notifyOnDeviceNearby); /** @hide */ @NonNull Builder setLastTimeConnected(long lastTimeConnectedMs); /** @hide */ @NonNull Builder setRevoked(boolean revoked); /** @hide */ @NonNull Builder setSystemDataSyncFlags(int flags); } }