1 /* 2 * Copyright (C) 2017 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 static com.android.internal.util.CollectionUtils.emptyIfNull; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.StringDef; 24 import android.compat.annotation.UnsupportedAppUsage; 25 import android.os.Build; 26 import android.os.Parcel; 27 import android.os.Parcelable; 28 import android.provider.OneTimeUseBuilder; 29 30 import com.android.internal.util.ArrayUtils; 31 import com.android.internal.util.DataClass; 32 33 import java.util.ArrayList; 34 import java.util.List; 35 import java.util.Objects; 36 37 /** 38 * A request for the user to select a companion device to associate with. 39 * 40 * You can optionally set {@link Builder#addDeviceFilter filters} for which devices to show to the 41 * user to select from. 42 * The exact type and fields of the filter you can set depend on the 43 * medium type. See {@link Builder}'s static factory methods for specific protocols that are 44 * supported. 45 * 46 * You can also set {@link Builder#setSingleDevice single device} to request a popup with single 47 * device to be shown instead of a list to choose from 48 */ 49 @DataClass( 50 genToString = true, 51 genEqualsHashCode = true, 52 genHiddenGetters = true, 53 genParcelable = true, 54 genHiddenConstructor = true, 55 genBuilder = false) 56 public final class AssociationRequest implements Parcelable { 57 58 private static final String LOG_TAG = AssociationRequest.class.getSimpleName(); 59 60 /** 61 * Device profile: watch. 62 * 63 * If specified, the current request may have a modified UI to highlight that the device being 64 * set up is a specific kind of device, and some extra permissions may be granted to the app 65 * as a result. 66 * 67 * Using it requires declaring uses-permission 68 * {@link android.Manifest.permission#REQUEST_COMPANION_PROFILE_WATCH} in the manifest. 69 * 70 * <a href="{@docRoot}about/versions/12/features#cdm-profiles">Learn more</a> 71 * about device profiles. 72 * 73 * @see AssociationRequest.Builder#setDeviceProfile 74 */ 75 public static final String DEVICE_PROFILE_WATCH = "android.app.role.COMPANION_DEVICE_WATCH"; 76 77 /** @hide */ 78 @StringDef(value = { DEVICE_PROFILE_WATCH }) 79 public @interface DeviceProfile {} 80 81 /** 82 * Whether only a single device should match the provided filter. 83 * 84 * When scanning for a single device with a specifc {@link BluetoothDeviceFilter} mac 85 * address, bonded devices are also searched among. This allows to obtain the necessary app 86 * privileges even if the device is already paired. 87 */ 88 private boolean mSingleDevice = false; 89 90 /** 91 * If set, only devices matching either of the given filters will be shown to the user 92 */ 93 @DataClass.PluralOf("deviceFilter") 94 private @NonNull List<DeviceFilter<?>> mDeviceFilters = new ArrayList<>(); 95 96 /** 97 * If set, association will be requested as a corresponding kind of device 98 */ 99 private @Nullable @DeviceProfile String mDeviceProfile = null; 100 101 /** 102 * The app package making the request. 103 * 104 * Populated by the system. 105 * 106 * @hide 107 */ 108 private @Nullable String mCallingPackage = null; 109 110 /** 111 * The user-readable description of the device profile's privileges. 112 * 113 * Populated by the system. 114 * 115 * @hide 116 */ 117 private @Nullable String mDeviceProfilePrivilegesDescription = null; 118 119 /** 120 * The time at which his request was created 121 * 122 * @hide 123 */ 124 private long mCreationTime; 125 126 /** 127 * Whether the user-prompt may be skipped once the device is found. 128 * 129 * Populated by the system. 130 * 131 * @hide 132 */ 133 private boolean mSkipPrompt = false; 134 onConstructed()135 private void onConstructed() { 136 mCreationTime = System.currentTimeMillis(); 137 } 138 139 /** @hide */ setCallingPackage(@onNull String pkg)140 public void setCallingPackage(@NonNull String pkg) { 141 mCallingPackage = pkg; 142 } 143 144 /** @hide */ setDeviceProfilePrivilegesDescription(@onNull String desc)145 public void setDeviceProfilePrivilegesDescription(@NonNull String desc) { 146 mDeviceProfilePrivilegesDescription = desc; 147 } 148 149 /** @hide */ setSkipPrompt(boolean value)150 public void setSkipPrompt(boolean value) { 151 mSkipPrompt = true; 152 } 153 154 /** @hide */ 155 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) isSingleDevice()156 public boolean isSingleDevice() { 157 return mSingleDevice; 158 } 159 160 /** @hide */ 161 @NonNull 162 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) getDeviceFilters()163 public List<DeviceFilter<?>> getDeviceFilters() { 164 return mDeviceFilters; 165 } 166 167 /** 168 * A builder for {@link AssociationRequest} 169 */ 170 public static final class Builder extends OneTimeUseBuilder<AssociationRequest> { 171 private boolean mSingleDevice = false; 172 @Nullable private ArrayList<DeviceFilter<?>> mDeviceFilters = null; 173 private @Nullable String mDeviceProfile = null; 174 Builder()175 public Builder() {} 176 177 /** 178 * Whether only a single device should match the provided filter. 179 * 180 * When scanning for a single device with a specifc {@link BluetoothDeviceFilter} mac 181 * address, bonded devices are also searched among. This allows to obtain the necessary app 182 * privileges even if the device is already paired. 183 * 184 * @param singleDevice if true, scanning for a device will stop as soon as at least one 185 * fitting device is found 186 */ 187 @NonNull setSingleDevice(boolean singleDevice)188 public Builder setSingleDevice(boolean singleDevice) { 189 checkNotUsed(); 190 this.mSingleDevice = singleDevice; 191 return this; 192 } 193 194 /** 195 * @param deviceFilter if set, only devices matching the given filter will be shown to the 196 * user 197 */ 198 @NonNull addDeviceFilter(@ullable DeviceFilter<?> deviceFilter)199 public Builder addDeviceFilter(@Nullable DeviceFilter<?> deviceFilter) { 200 checkNotUsed(); 201 if (deviceFilter != null) { 202 mDeviceFilters = ArrayUtils.add(mDeviceFilters, deviceFilter); 203 } 204 return this; 205 } 206 207 /** 208 * If set, association will be requested as a corresponding kind of device 209 */ 210 @NonNull setDeviceProfile(@onNull @eviceProfile String deviceProfile)211 public Builder setDeviceProfile(@NonNull @DeviceProfile String deviceProfile) { 212 checkNotUsed(); 213 mDeviceProfile = deviceProfile; 214 return this; 215 } 216 217 /** @inheritDoc */ 218 @NonNull 219 @Override build()220 public AssociationRequest build() { 221 markUsed(); 222 return new AssociationRequest( 223 mSingleDevice, emptyIfNull(mDeviceFilters), 224 mDeviceProfile, null, null, -1L, false); 225 } 226 } 227 228 229 230 231 232 // Code below generated by codegen v1.0.22. 233 // 234 // DO NOT MODIFY! 235 // CHECKSTYLE:OFF Generated code 236 // 237 // To regenerate run: 238 // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/companion/AssociationRequest.java 239 // 240 // To exclude the generated code from IntelliJ auto-formatting enable (one-time): 241 // Settings > Editor > Code Style > Formatter Control 242 //@formatter:off 243 244 245 /** 246 * Creates a new AssociationRequest. 247 * 248 * @param singleDevice 249 * Whether only a single device should match the provided filter. 250 * 251 * When scanning for a single device with a specifc {@link BluetoothDeviceFilter} mac 252 * address, bonded devices are also searched among. This allows to obtain the necessary app 253 * privileges even if the device is already paired. 254 * @param deviceFilters 255 * If set, only devices matching either of the given filters will be shown to the user 256 * @param deviceProfile 257 * If set, association will be requested as a corresponding kind of device 258 * @param callingPackage 259 * The app package making the request. 260 * 261 * Populated by the system. 262 * @param deviceProfilePrivilegesDescription 263 * The user-readable description of the device profile's privileges. 264 * 265 * Populated by the system. 266 * @param creationTime 267 * The time at which his request was created 268 * @param skipPrompt 269 * Whether the user-prompt may be skipped once the device is found. 270 * 271 * Populated by the system. 272 * @hide 273 */ 274 @DataClass.Generated.Member AssociationRequest( boolean singleDevice, @NonNull List<DeviceFilter<?>> deviceFilters, @Nullable @DeviceProfile String deviceProfile, @Nullable String callingPackage, @Nullable String deviceProfilePrivilegesDescription, long creationTime, boolean skipPrompt)275 public AssociationRequest( 276 boolean singleDevice, 277 @NonNull List<DeviceFilter<?>> deviceFilters, 278 @Nullable @DeviceProfile String deviceProfile, 279 @Nullable String callingPackage, 280 @Nullable String deviceProfilePrivilegesDescription, 281 long creationTime, 282 boolean skipPrompt) { 283 this.mSingleDevice = singleDevice; 284 this.mDeviceFilters = deviceFilters; 285 com.android.internal.util.AnnotationValidations.validate( 286 NonNull.class, null, mDeviceFilters); 287 this.mDeviceProfile = deviceProfile; 288 com.android.internal.util.AnnotationValidations.validate( 289 DeviceProfile.class, null, mDeviceProfile); 290 this.mCallingPackage = callingPackage; 291 this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription; 292 this.mCreationTime = creationTime; 293 this.mSkipPrompt = skipPrompt; 294 295 onConstructed(); 296 } 297 298 /** 299 * If set, association will be requested as a corresponding kind of device 300 * 301 * @hide 302 */ 303 @DataClass.Generated.Member getDeviceProfile()304 public @Nullable @DeviceProfile String getDeviceProfile() { 305 return mDeviceProfile; 306 } 307 308 /** 309 * The app package making the request. 310 * 311 * Populated by the system. 312 * 313 * @hide 314 */ 315 @DataClass.Generated.Member getCallingPackage()316 public @Nullable String getCallingPackage() { 317 return mCallingPackage; 318 } 319 320 /** 321 * The user-readable description of the device profile's privileges. 322 * 323 * Populated by the system. 324 * 325 * @hide 326 */ 327 @DataClass.Generated.Member getDeviceProfilePrivilegesDescription()328 public @Nullable String getDeviceProfilePrivilegesDescription() { 329 return mDeviceProfilePrivilegesDescription; 330 } 331 332 /** 333 * The time at which his request was created 334 * 335 * @hide 336 */ 337 @DataClass.Generated.Member getCreationTime()338 public long getCreationTime() { 339 return mCreationTime; 340 } 341 342 /** 343 * Whether the user-prompt may be skipped once the device is found. 344 * 345 * Populated by the system. 346 * 347 * @hide 348 */ 349 @DataClass.Generated.Member isSkipPrompt()350 public boolean isSkipPrompt() { 351 return mSkipPrompt; 352 } 353 354 @Override 355 @DataClass.Generated.Member toString()356 public String toString() { 357 // You can override field toString logic by defining methods like: 358 // String fieldNameToString() { ... } 359 360 return "AssociationRequest { " + 361 "singleDevice = " + mSingleDevice + ", " + 362 "deviceFilters = " + mDeviceFilters + ", " + 363 "deviceProfile = " + mDeviceProfile + ", " + 364 "callingPackage = " + mCallingPackage + ", " + 365 "deviceProfilePrivilegesDescription = " + mDeviceProfilePrivilegesDescription + ", " + 366 "creationTime = " + mCreationTime + ", " + 367 "skipPrompt = " + mSkipPrompt + 368 " }"; 369 } 370 371 @Override 372 @DataClass.Generated.Member equals(@ullable Object o)373 public boolean equals(@Nullable Object o) { 374 // You can override field equality logic by defining either of the methods like: 375 // boolean fieldNameEquals(AssociationRequest other) { ... } 376 // boolean fieldNameEquals(FieldType otherValue) { ... } 377 378 if (this == o) return true; 379 if (o == null || getClass() != o.getClass()) return false; 380 @SuppressWarnings("unchecked") 381 AssociationRequest that = (AssociationRequest) o; 382 //noinspection PointlessBooleanExpression 383 return true 384 && mSingleDevice == that.mSingleDevice 385 && Objects.equals(mDeviceFilters, that.mDeviceFilters) 386 && Objects.equals(mDeviceProfile, that.mDeviceProfile) 387 && Objects.equals(mCallingPackage, that.mCallingPackage) 388 && Objects.equals(mDeviceProfilePrivilegesDescription, that.mDeviceProfilePrivilegesDescription) 389 && mCreationTime == that.mCreationTime 390 && mSkipPrompt == that.mSkipPrompt; 391 } 392 393 @Override 394 @DataClass.Generated.Member hashCode()395 public int hashCode() { 396 // You can override field hashCode logic by defining methods like: 397 // int fieldNameHashCode() { ... } 398 399 int _hash = 1; 400 _hash = 31 * _hash + Boolean.hashCode(mSingleDevice); 401 _hash = 31 * _hash + Objects.hashCode(mDeviceFilters); 402 _hash = 31 * _hash + Objects.hashCode(mDeviceProfile); 403 _hash = 31 * _hash + Objects.hashCode(mCallingPackage); 404 _hash = 31 * _hash + Objects.hashCode(mDeviceProfilePrivilegesDescription); 405 _hash = 31 * _hash + Long.hashCode(mCreationTime); 406 _hash = 31 * _hash + Boolean.hashCode(mSkipPrompt); 407 return _hash; 408 } 409 410 @Override 411 @DataClass.Generated.Member writeToParcel(@onNull Parcel dest, int flags)412 public void writeToParcel(@NonNull Parcel dest, int flags) { 413 // You can override field parcelling by defining methods like: 414 // void parcelFieldName(Parcel dest, int flags) { ... } 415 416 byte flg = 0; 417 if (mSingleDevice) flg |= 0x1; 418 if (mSkipPrompt) flg |= 0x40; 419 if (mDeviceProfile != null) flg |= 0x4; 420 if (mCallingPackage != null) flg |= 0x8; 421 if (mDeviceProfilePrivilegesDescription != null) flg |= 0x10; 422 dest.writeByte(flg); 423 dest.writeParcelableList(mDeviceFilters, flags); 424 if (mDeviceProfile != null) dest.writeString(mDeviceProfile); 425 if (mCallingPackage != null) dest.writeString(mCallingPackage); 426 if (mDeviceProfilePrivilegesDescription != null) dest.writeString(mDeviceProfilePrivilegesDescription); 427 dest.writeLong(mCreationTime); 428 } 429 430 @Override 431 @DataClass.Generated.Member describeContents()432 public int describeContents() { return 0; } 433 434 /** @hide */ 435 @SuppressWarnings({"unchecked", "RedundantCast"}) 436 @DataClass.Generated.Member AssociationRequest(@onNull Parcel in)437 /* package-private */ AssociationRequest(@NonNull Parcel in) { 438 // You can override field unparcelling by defining methods like: 439 // static FieldType unparcelFieldName(Parcel in) { ... } 440 441 byte flg = in.readByte(); 442 boolean singleDevice = (flg & 0x1) != 0; 443 boolean skipPrompt = (flg & 0x40) != 0; 444 List<DeviceFilter<?>> deviceFilters = new ArrayList<>(); 445 in.readParcelableList(deviceFilters, DeviceFilter.class.getClassLoader()); 446 String deviceProfile = (flg & 0x4) == 0 ? null : in.readString(); 447 String callingPackage = (flg & 0x8) == 0 ? null : in.readString(); 448 String deviceProfilePrivilegesDescription = (flg & 0x10) == 0 ? null : in.readString(); 449 long creationTime = in.readLong(); 450 451 this.mSingleDevice = singleDevice; 452 this.mDeviceFilters = deviceFilters; 453 com.android.internal.util.AnnotationValidations.validate( 454 NonNull.class, null, mDeviceFilters); 455 this.mDeviceProfile = deviceProfile; 456 com.android.internal.util.AnnotationValidations.validate( 457 DeviceProfile.class, null, mDeviceProfile); 458 this.mCallingPackage = callingPackage; 459 this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription; 460 this.mCreationTime = creationTime; 461 this.mSkipPrompt = skipPrompt; 462 463 onConstructed(); 464 } 465 466 @DataClass.Generated.Member 467 public static final @NonNull Parcelable.Creator<AssociationRequest> CREATOR 468 = new Parcelable.Creator<AssociationRequest>() { 469 @Override 470 public AssociationRequest[] newArray(int size) { 471 return new AssociationRequest[size]; 472 } 473 474 @Override 475 public AssociationRequest createFromParcel(@NonNull Parcel in) { 476 return new AssociationRequest(in); 477 } 478 }; 479 480 @DataClass.Generated( 481 time = 1615252862756L, 482 codegenVersion = "1.0.22", 483 sourceFile = "frameworks/base/core/java/android/companion/AssociationRequest.java", 484 inputSignatures = "private static final java.lang.String LOG_TAG\npublic static final java.lang.String DEVICE_PROFILE_WATCH\nprivate boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\nprivate long mCreationTime\nprivate boolean mSkipPrompt\nprivate void onConstructed()\npublic void setCallingPackage(java.lang.String)\npublic void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic void setSkipPrompt(boolean)\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false)") 485 @Deprecated __metadata()486 private void __metadata() {} 487 488 489 //@formatter:on 490 // End of generated code 491 492 } 493