1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.companion; 18 19 import android.annotation.FlaggedApi; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.RequiresPermission; 23 import android.os.Parcel; 24 import android.os.ParcelUuid; 25 import android.os.Parcelable; 26 import android.provider.OneTimeUseBuilder; 27 28 import java.util.Objects; 29 30 /** 31 * A request for setting the types of device for observing device presence. 32 * 33 * <p>Only supports association id or ParcelUuid and calling app must declare uses-permission 34 * {@link android.Manifest.permission#REQUEST_OBSERVE_DEVICE_UUID_PRESENCE} if using 35 * {@link Builder#setUuid(ParcelUuid)}.</p> 36 * 37 * Calling apps must use either ObservingDevicePresenceRequest.Builder#setUuid(ParcelUuid) or 38 * ObservingDevicePresenceRequest.Builder#setAssociationId(int), but not both. 39 * 40 * @see Builder#setUuid(ParcelUuid) 41 * @see Builder#setAssociationId(int) 42 * @see CompanionDeviceManager#startObservingDevicePresence(ObservingDevicePresenceRequest) 43 */ 44 @FlaggedApi(Flags.FLAG_DEVICE_PRESENCE) 45 public final class ObservingDevicePresenceRequest implements Parcelable { 46 private final int mAssociationId; 47 @Nullable private final ParcelUuid mUuid; 48 49 private static final int PARCEL_UUID_NULL = 0; 50 51 private static final int PARCEL_UUID_NOT_NULL = 1; 52 ObservingDevicePresenceRequest(int associationId, ParcelUuid uuid)53 private ObservingDevicePresenceRequest(int associationId, ParcelUuid uuid) { 54 mAssociationId = associationId; 55 mUuid = uuid; 56 } 57 ObservingDevicePresenceRequest(@onNull Parcel in)58 private ObservingDevicePresenceRequest(@NonNull Parcel in) { 59 mAssociationId = in.readInt(); 60 if (in.readInt() == PARCEL_UUID_NULL) { 61 mUuid = null; 62 } else { 63 mUuid = ParcelUuid.CREATOR.createFromParcel(in); 64 } 65 } 66 67 /** 68 * @return the association id for observing device presence. It will return 69 * {@link DevicePresenceEvent#NO_ASSOCIATION} if using 70 * {@link Builder#setUuid(ParcelUuid)}. 71 */ getAssociationId()72 public int getAssociationId() { 73 return mAssociationId; 74 } 75 76 /** 77 * @return the ParcelUuid for observing device presence. 78 */ 79 @Nullable getUuid()80 public ParcelUuid getUuid() { 81 return mUuid; 82 } 83 84 @Override writeToParcel(@onNull Parcel dest, int flags)85 public void writeToParcel(@NonNull Parcel dest, int flags) { 86 dest.writeInt(mAssociationId); 87 if (mUuid == null) { 88 // Write 0 to the parcel to indicate the ParcelUuid is null. 89 dest.writeInt(PARCEL_UUID_NULL); 90 } else { 91 dest.writeInt(PARCEL_UUID_NOT_NULL); 92 mUuid.writeToParcel(dest, flags); 93 } 94 95 } 96 97 @Override describeContents()98 public int describeContents() { 99 return 0; 100 } 101 102 @NonNull 103 public static final Parcelable.Creator<ObservingDevicePresenceRequest> CREATOR = 104 new Parcelable.Creator<ObservingDevicePresenceRequest>() { 105 @Override 106 public ObservingDevicePresenceRequest[] newArray(int size) { 107 return new ObservingDevicePresenceRequest[size]; 108 } 109 110 @Override 111 public ObservingDevicePresenceRequest createFromParcel(@NonNull Parcel in) { 112 return new ObservingDevicePresenceRequest(in); 113 } 114 }; 115 116 @Override toString()117 public String toString() { 118 return "ObservingDevicePresenceRequest { " 119 + "Association Id= " + mAssociationId + "," 120 + "ParcelUuid= " + mUuid + "}"; 121 } 122 123 @Override equals(@ullable Object o)124 public boolean equals(@Nullable Object o) { 125 if (this == o) return true; 126 if (!(o instanceof ObservingDevicePresenceRequest that)) return false; 127 128 return Objects.equals(mUuid, that.mUuid) && mAssociationId == that.mAssociationId; 129 } 130 131 @Override hashCode()132 public int hashCode() { 133 return Objects.hash(mAssociationId, mUuid); 134 } 135 136 /** 137 * A builder for {@link ObservingDevicePresenceRequest} 138 */ 139 public static final class Builder extends OneTimeUseBuilder<ObservingDevicePresenceRequest> { 140 // Initial the association id to {@link DevicePresenceEvent.NO_ASSOCIATION} 141 // to indicate the value is not set yet. 142 private int mAssociationId = DevicePresenceEvent.NO_ASSOCIATION; 143 private ParcelUuid mUuid; 144 Builder()145 public Builder() {} 146 147 /** 148 * Set the association id to be observed for device presence. 149 * 150 * <p>The provided device must be {@link CompanionDeviceManager#associate associated} 151 * with the calling app before calling this method if using this API. 152 * 153 * Caller must implement a single {@link CompanionDeviceService} which will be bound to and 154 * receive callbacks to 155 * {@link CompanionDeviceService#onDevicePresenceEvent(DevicePresenceEvent)}.</p> 156 * 157 * <p>Calling apps must use either {@link #setUuid(ParcelUuid)} 158 * or this API, but not both.</p> 159 * 160 * @param associationId The association id for observing device presence. 161 */ 162 @NonNull setAssociationId(int associationId)163 public Builder setAssociationId(int associationId) { 164 checkNotUsed(); 165 this.mAssociationId = associationId; 166 return this; 167 } 168 169 /** 170 * Set the ParcelUuid to be observed for device presence. 171 * 172 * <p>It does not require to create the association before calling this API. 173 * This only supports classic Bluetooth scan and caller must implement 174 * a single {@link CompanionDeviceService} which will be bound to and receive callbacks to 175 * {@link CompanionDeviceService#onDevicePresenceEvent(DevicePresenceEvent)}.</p> 176 * 177 * <p>The Uuid should be matching one of the ParcelUuid form 178 * {@link android.bluetooth.BluetoothDevice#getUuids()}</p> 179 * 180 * <p>Calling apps must use either this API or {@link #setAssociationId(int)}, 181 * but not both.</p> 182 * 183 * <p>Calling app must hold the 184 * {@link AssociationRequest#DEVICE_PROFILE_AUTOMOTIVE_PROJECTION} profile.</p> 185 * 186 * @param uuid The ParcelUuid for observing device presence. 187 */ 188 @NonNull 189 @RequiresPermission(allOf = { 190 android.Manifest.permission.REQUEST_OBSERVE_DEVICE_UUID_PRESENCE, 191 android.Manifest.permission.BLUETOOTH_CONNECT, 192 android.Manifest.permission.BLUETOOTH_SCAN 193 }) setUuid(@onNull ParcelUuid uuid)194 public Builder setUuid(@NonNull ParcelUuid uuid) { 195 checkNotUsed(); 196 this.mUuid = uuid; 197 return this; 198 } 199 200 @NonNull 201 @Override build()202 public ObservingDevicePresenceRequest build() { 203 markUsed(); 204 if (mUuid != null && mAssociationId != DevicePresenceEvent.NO_ASSOCIATION) { 205 throw new IllegalStateException("Cannot observe device presence based on " 206 + "both ParcelUuid and association ID. Choose one or the other."); 207 } else if (mUuid == null && mAssociationId <= 0) { 208 throw new IllegalStateException("Must provide either a ParcelUuid or " 209 + "a valid association ID to observe device presence."); 210 } 211 212 return new ObservingDevicePresenceRequest(mAssociationId, mUuid); 213 } 214 } 215 } 216