1 /* 2 * Copyright (C) 2019 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 package android.companion; 17 18 import android.annotation.FlaggedApi; 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.SuppressLint; 22 import android.annotation.SystemApi; 23 import android.annotation.TestApi; 24 import android.annotation.UserIdInt; 25 import android.graphics.drawable.Icon; 26 import android.net.MacAddress; 27 import android.os.Parcel; 28 import android.os.Parcelable; 29 30 import java.util.Date; 31 import java.util.Objects; 32 33 /** 34 * Details for a specific "association" that has been established between an app and companion 35 * device. 36 * <p> 37 * An association gives an app the ability to interact with a companion device without needing to 38 * acquire broader runtime permissions. An association only exists after the user has confirmed that 39 * an app should have access to a companion device. 40 */ 41 public final class AssociationInfo implements Parcelable { 42 /** 43 * A String indicates the selfManaged device is not connected. 44 */ 45 private static final String LAST_TIME_CONNECTED_NONE = "None"; 46 /** 47 * A unique ID of this Association record. 48 * Disclosed to the clients (i.e. companion applications) for referring to this record (e.g. in 49 * {@code disassociate()} API call). 50 */ 51 private final int mId; 52 @UserIdInt 53 private final int mUserId; 54 @NonNull 55 private final String mPackageName; 56 @Nullable 57 private final MacAddress mDeviceMacAddress; 58 @Nullable 59 private final CharSequence mDisplayName; 60 @Nullable 61 private final String mDeviceProfile; 62 @Nullable 63 private final AssociatedDevice mAssociatedDevice; 64 private final boolean mSelfManaged; 65 private final boolean mNotifyOnDeviceNearby; 66 /** 67 * Indicates that the association has been revoked (removed), but we keep the association 68 * record for final clean up (e.g. removing the app from the list of the role holders). 69 * 70 * @see CompanionDeviceManager#disassociate(int) 71 */ 72 private final boolean mRevoked; 73 /** 74 * Indicates that the association is waiting for its corresponding companion app to be installed 75 * before it can be added to CDM. This is likely because it was restored onto the device from a 76 * backup. 77 */ 78 private final boolean mPending; 79 private final long mTimeApprovedMs; 80 /** 81 * A long value indicates the last time connected reported by selfManaged devices 82 * Default value is Long.MAX_VALUE. 83 */ 84 private final long mLastTimeConnectedMs; 85 private final int mSystemDataSyncFlags; 86 @Nullable 87 private final DeviceId mDeviceId; 88 89 /** 90 * A device icon displayed on a selfManaged association dialog. 91 */ 92 private final Icon mDeviceIcon; 93 94 /** 95 * Creates a new Association. 96 * 97 * @hide 98 */ 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, boolean pending, long timeApprovedMs, long lastTimeConnectedMs, int systemDataSyncFlags, @Nullable Icon deviceIcon, @Nullable DeviceId deviceId)99 public AssociationInfo(int id, @UserIdInt int userId, @NonNull String packageName, 100 @Nullable MacAddress macAddress, @Nullable CharSequence displayName, 101 @Nullable String deviceProfile, @Nullable AssociatedDevice associatedDevice, 102 boolean selfManaged, boolean notifyOnDeviceNearby, boolean revoked, boolean pending, 103 long timeApprovedMs, long lastTimeConnectedMs, int systemDataSyncFlags, 104 @Nullable Icon deviceIcon, @Nullable DeviceId deviceId) { 105 if (id <= 0) { 106 throw new IllegalArgumentException("Association ID should be greater than 0"); 107 } 108 if (macAddress == null && displayName == null) { 109 throw new IllegalArgumentException("MAC address and the Display Name must NOT be null " 110 + "at the same time"); 111 } 112 113 mId = id; 114 mUserId = userId; 115 mPackageName = packageName; 116 mDeviceMacAddress = macAddress; 117 mDisplayName = displayName; 118 mDeviceProfile = deviceProfile; 119 mAssociatedDevice = associatedDevice; 120 mSelfManaged = selfManaged; 121 mNotifyOnDeviceNearby = notifyOnDeviceNearby; 122 mRevoked = revoked; 123 mPending = pending; 124 mTimeApprovedMs = timeApprovedMs; 125 mLastTimeConnectedMs = lastTimeConnectedMs; 126 mSystemDataSyncFlags = systemDataSyncFlags; 127 mDeviceIcon = deviceIcon; 128 mDeviceId = deviceId; 129 } 130 131 /** 132 * @return the unique ID of this association record. 133 */ getId()134 public int getId() { 135 return mId; 136 } 137 138 /** 139 * @return the ID of the user who "owns" this association. 140 * @hide 141 */ 142 @UserIdInt getUserId()143 public int getUserId() { 144 return mUserId; 145 } 146 147 /** 148 * @return the package name of the app which this association refers to. 149 * @hide 150 */ 151 @SystemApi 152 @NonNull getPackageName()153 public String getPackageName() { 154 return mPackageName; 155 } 156 157 /** 158 * @return the {@link DeviceId} of this association. 159 * @see CompanionDeviceManager#setDeviceId(int, DeviceId) 160 */ 161 @FlaggedApi(Flags.FLAG_ASSOCIATION_TAG) 162 @Nullable getDeviceId()163 public DeviceId getDeviceId() { 164 return mDeviceId; 165 } 166 167 /** 168 * @return the MAC address of the device. 169 */ 170 @Nullable getDeviceMacAddress()171 public MacAddress getDeviceMacAddress() { 172 return mDeviceMacAddress; 173 } 174 175 /** @hide */ 176 @Nullable getDeviceMacAddressAsString()177 public String getDeviceMacAddressAsString() { 178 return mDeviceMacAddress != null ? mDeviceMacAddress.toString().toUpperCase() : null; 179 } 180 181 /** 182 * @return the display name of the companion device (optionally) provided by the companion 183 * application. 184 * 185 * @see AssociationRequest.Builder#setDisplayName(CharSequence) 186 */ 187 @Nullable getDisplayName()188 public CharSequence getDisplayName() { 189 return mDisplayName; 190 } 191 192 /** 193 * @return the companion device profile used when establishing this 194 * association, or {@code null} if no specific profile was used. 195 * @see AssociationRequest.Builder#setDeviceProfile(String) 196 */ 197 @Nullable getDeviceProfile()198 public String getDeviceProfile() { 199 return mDeviceProfile; 200 } 201 202 /** 203 * Companion device that was associated. Note that this field is not persisted across sessions. 204 * Device can be one of the following types: 205 * 206 * <ul> 207 * <li>for classic Bluetooth - {@link AssociatedDevice#getBluetoothDevice()}</li> 208 * <li>for Bluetooth LE - {@link AssociatedDevice#getBleDevice()}</li> 209 * <li>for WiFi - {@link AssociatedDevice#getWifiDevice()}</li> 210 * </ul> 211 * 212 * @return the companion device that was associated, or {@code null} if the device is 213 * self-managed or this association info was retrieved from persistent storage. 214 */ 215 @Nullable getAssociatedDevice()216 public AssociatedDevice getAssociatedDevice() { 217 return mAssociatedDevice; 218 } 219 220 /** 221 * @return whether the association is managed by the companion application it belongs to. 222 * @see AssociationRequest.Builder#setSelfManaged(boolean) 223 */ 224 @SuppressLint("UnflaggedApi") // promoting from @SystemApi isSelfManaged()225 public boolean isSelfManaged() { 226 return mSelfManaged; 227 } 228 229 /** @hide */ isNotifyOnDeviceNearby()230 public boolean isNotifyOnDeviceNearby() { 231 return mNotifyOnDeviceNearby; 232 } 233 234 /** @hide */ getTimeApprovedMs()235 public long getTimeApprovedMs() { 236 return mTimeApprovedMs; 237 } 238 239 /** @hide */ belongsToPackage(@serIdInt int userId, String packageName)240 public boolean belongsToPackage(@UserIdInt int userId, String packageName) { 241 return mUserId == userId && Objects.equals(mPackageName, packageName); 242 } 243 244 /** 245 * @return if the association has been revoked (removed). 246 * @hide 247 */ isRevoked()248 public boolean isRevoked() { 249 return mRevoked; 250 } 251 252 /** 253 * @return true if the association is waiting for its corresponding app to be installed 254 * before it can be added to CDM. 255 * @hide 256 */ isPending()257 public boolean isPending() { 258 return mPending; 259 } 260 261 /** 262 * @return true if the association is not revoked nor pending 263 * @hide 264 */ isActive()265 public boolean isActive() { 266 return !mRevoked && !mPending; 267 } 268 269 /** 270 * @return the last time self reported disconnected for selfManaged only. 271 * @hide 272 */ getLastTimeConnectedMs()273 public long getLastTimeConnectedMs() { 274 return mLastTimeConnectedMs; 275 } 276 277 /** 278 * @return Enabled system data sync flags set via 279 * {@link CompanionDeviceManager#enableSystemDataSyncForTypes(int, int)} (int, int)} and 280 * {@link CompanionDeviceManager#disableSystemDataSyncForTypes(int, int)} (int, int)}. 281 * Or by default all flags are 1 (enabled). 282 */ getSystemDataSyncFlags()283 public int getSystemDataSyncFlags() { 284 return mSystemDataSyncFlags; 285 } 286 287 /** 288 * Get the device icon of the associated device. The device icon represents the device type. 289 * 290 * @return the device icon with size 24dp x 24dp. 291 * If the associated device has no icon set, it returns {@code null}. 292 * 293 * @see AssociationRequest.Builder#setDeviceIcon(Icon) 294 */ 295 @FlaggedApi(Flags.FLAG_ASSOCIATION_DEVICE_ICON) 296 @Nullable getDeviceIcon()297 public Icon getDeviceIcon() { 298 return mDeviceIcon; 299 } 300 301 /** 302 * Utility method for checking if the association represents a device with the given MAC 303 * address. 304 * 305 * @return {@code false} if the association is "self-managed". 306 * {@code false} if the {@code addr} is {@code null} or is not a valid MAC address. 307 * Otherwise - the result of {@link MacAddress#equals(Object)} 308 * 309 * @hide 310 */ isLinkedTo(@ullable String addr)311 public boolean isLinkedTo(@Nullable String addr) { 312 if (mSelfManaged) return false; 313 314 if (addr == null) return false; 315 316 final MacAddress macAddress; 317 try { 318 macAddress = MacAddress.fromString(addr); 319 } catch (IllegalArgumentException e) { 320 return false; 321 } 322 return macAddress.equals(mDeviceMacAddress); 323 } 324 325 /** 326 * Utility method to be used by CdmService only. 327 * 328 * @return whether CdmService should bind the companion application that "owns" this association 329 * when the device is present. 330 * 331 * @hide 332 */ shouldBindWhenPresent()333 public boolean shouldBindWhenPresent() { 334 return mNotifyOnDeviceNearby || mSelfManaged; 335 } 336 337 /** @hide */ 338 @NonNull toShortString()339 public String toShortString() { 340 final StringBuilder sb = new StringBuilder(); 341 sb.append("id=").append(mId); 342 if (mDeviceMacAddress != null) { 343 sb.append(", addr=").append(getDeviceMacAddressAsString()); 344 } 345 if (mSelfManaged) { 346 sb.append(", self-managed"); 347 } 348 sb.append(", pkg=u").append(mUserId).append('/').append(mPackageName); 349 return sb.toString(); 350 } 351 352 @Override toString()353 public String toString() { 354 return "Association{" 355 + "mId=" + mId 356 + ", mUserId=" + mUserId 357 + ", mPackageName='" + mPackageName + '\'' 358 + ", mDeviceMacAddress=" + mDeviceMacAddress 359 + ", mDisplayName='" + mDisplayName + '\'' 360 + ", mDeviceProfile='" + mDeviceProfile + '\'' 361 + ", mSelfManaged=" + mSelfManaged 362 + ", mAssociatedDevice=" + mAssociatedDevice 363 + ", mNotifyOnDeviceNearby=" + mNotifyOnDeviceNearby 364 + ", mRevoked=" + mRevoked 365 + ", mPending=" + mPending 366 + ", mTimeApprovedMs=" + new Date(mTimeApprovedMs) 367 + ", mLastTimeConnectedMs=" + ( 368 mLastTimeConnectedMs == Long.MAX_VALUE 369 ? LAST_TIME_CONNECTED_NONE : new Date(mLastTimeConnectedMs)) 370 + ", mSystemDataSyncFlags=" + mSystemDataSyncFlags 371 + ", mDeviceId='" + mDeviceId 372 + '}'; 373 } 374 375 @Override equals(Object o)376 public boolean equals(Object o) { 377 if (this == o) return true; 378 if (!(o instanceof AssociationInfo)) return false; 379 final AssociationInfo that = (AssociationInfo) o; 380 381 return mId == that.mId 382 && mUserId == that.mUserId 383 && mSelfManaged == that.mSelfManaged 384 && mNotifyOnDeviceNearby == that.mNotifyOnDeviceNearby 385 && mRevoked == that.mRevoked 386 && mPending == that.mPending 387 && mTimeApprovedMs == that.mTimeApprovedMs 388 && mLastTimeConnectedMs == that.mLastTimeConnectedMs 389 && Objects.equals(mPackageName, that.mPackageName) 390 && Objects.equals(mDeviceMacAddress, that.mDeviceMacAddress) 391 && Objects.equals(mDisplayName, that.mDisplayName) 392 && Objects.equals(mDeviceProfile, that.mDeviceProfile) 393 && Objects.equals(mAssociatedDevice, that.mAssociatedDevice) 394 && mSystemDataSyncFlags == that.mSystemDataSyncFlags 395 && isSameIcon(mDeviceIcon, that.mDeviceIcon) 396 && Objects.equals(mDeviceId, that.mDeviceId); 397 } 398 isSameIcon(Icon iconA, Icon iconB)399 private boolean isSameIcon(Icon iconA, Icon iconB) { 400 // Because we've already rescaled and converted both icons to bitmaps, 401 // we can now directly compare them by bitmap. 402 return (iconA == null && iconB == null) 403 || (iconA != null && iconB != null && iconA.getBitmap().sameAs(iconB.getBitmap())); 404 } 405 406 @Override hashCode()407 public int hashCode() { 408 return Objects.hash(mId, mUserId, mPackageName, mDeviceMacAddress, mDisplayName, 409 mDeviceProfile, mAssociatedDevice, mSelfManaged, mNotifyOnDeviceNearby, mRevoked, 410 mPending, mTimeApprovedMs, mLastTimeConnectedMs, mSystemDataSyncFlags, mDeviceIcon, 411 mDeviceId); 412 } 413 414 @Override describeContents()415 public int describeContents() { 416 return 0; 417 } 418 419 @Override writeToParcel(@onNull Parcel dest, int flags)420 public void writeToParcel(@NonNull Parcel dest, int flags) { 421 dest.writeInt(mId); 422 dest.writeInt(mUserId); 423 dest.writeString(mPackageName); 424 dest.writeTypedObject(mDeviceMacAddress, 0); 425 dest.writeCharSequence(mDisplayName); 426 dest.writeString(mDeviceProfile); 427 dest.writeTypedObject(mAssociatedDevice, 0); 428 dest.writeBoolean(mSelfManaged); 429 dest.writeBoolean(mNotifyOnDeviceNearby); 430 dest.writeBoolean(mRevoked); 431 dest.writeBoolean(mPending); 432 dest.writeLong(mTimeApprovedMs); 433 dest.writeLong(mLastTimeConnectedMs); 434 dest.writeInt(mSystemDataSyncFlags); 435 if (Flags.associationDeviceIcon() && mDeviceIcon != null) { 436 dest.writeInt(1); 437 mDeviceIcon.writeToParcel(dest, flags); 438 } else { 439 dest.writeInt(0); 440 } 441 442 if (Flags.associationTag() && mDeviceId != null) { 443 dest.writeInt(1); 444 dest.writeTypedObject(mDeviceId, flags); 445 } else { 446 dest.writeInt(0); 447 } 448 } 449 AssociationInfo(@onNull Parcel in)450 private AssociationInfo(@NonNull Parcel in) { 451 mId = in.readInt(); 452 mUserId = in.readInt(); 453 mPackageName = in.readString(); 454 mDeviceMacAddress = in.readTypedObject(MacAddress.CREATOR); 455 mDisplayName = in.readCharSequence(); 456 mDeviceProfile = in.readString(); 457 mAssociatedDevice = in.readTypedObject(AssociatedDevice.CREATOR); 458 mSelfManaged = in.readBoolean(); 459 mNotifyOnDeviceNearby = in.readBoolean(); 460 mRevoked = in.readBoolean(); 461 mPending = in.readBoolean(); 462 mTimeApprovedMs = in.readLong(); 463 mLastTimeConnectedMs = in.readLong(); 464 mSystemDataSyncFlags = in.readInt(); 465 int deviceIcon = in.readInt(); 466 if (Flags.associationDeviceIcon() && deviceIcon == 1) { 467 mDeviceIcon = Icon.CREATOR.createFromParcel(in); 468 } else { 469 mDeviceIcon = null; 470 } 471 int deviceId = in.readInt(); 472 if (Flags.associationTag() && deviceId == 1) { 473 mDeviceId = in.readTypedObject(DeviceId.CREATOR); 474 } else { 475 mDeviceId = null; 476 } 477 } 478 479 @NonNull 480 public static final Parcelable.Creator<AssociationInfo> CREATOR = 481 new Parcelable.Creator<AssociationInfo>() { 482 @Override 483 public AssociationInfo[] newArray(int size) { 484 return new AssociationInfo[size]; 485 } 486 487 @Override 488 public AssociationInfo createFromParcel(@NonNull Parcel in) { 489 return new AssociationInfo(in); 490 } 491 }; 492 493 /** 494 * Builder for {@link AssociationInfo} 495 * 496 * @hide 497 */ 498 @FlaggedApi(Flags.FLAG_NEW_ASSOCIATION_BUILDER) 499 @TestApi 500 public static final class Builder { 501 private final int mId; 502 private final int mUserId; 503 private final String mPackageName; 504 private MacAddress mDeviceMacAddress; 505 private CharSequence mDisplayName; 506 private String mDeviceProfile; 507 private AssociatedDevice mAssociatedDevice; 508 private boolean mSelfManaged; 509 private boolean mNotifyOnDeviceNearby; 510 private boolean mRevoked; 511 private boolean mPending; 512 private long mTimeApprovedMs; 513 private long mLastTimeConnectedMs; 514 private int mSystemDataSyncFlags; 515 private Icon mDeviceIcon; 516 private DeviceId mDeviceId; 517 518 /** @hide */ 519 @TestApi Builder(int id, int userId, @NonNull String packageName)520 public Builder(int id, int userId, @NonNull String packageName) { 521 mId = id; 522 mUserId = userId; 523 mPackageName = packageName; 524 } 525 526 /** @hide */ 527 @TestApi Builder(@onNull AssociationInfo info)528 public Builder(@NonNull AssociationInfo info) { 529 mId = info.mId; 530 mUserId = info.mUserId; 531 mPackageName = info.mPackageName; 532 mDeviceMacAddress = info.mDeviceMacAddress; 533 mDisplayName = info.mDisplayName; 534 mDeviceProfile = info.mDeviceProfile; 535 mAssociatedDevice = info.mAssociatedDevice; 536 mSelfManaged = info.mSelfManaged; 537 mNotifyOnDeviceNearby = info.mNotifyOnDeviceNearby; 538 mRevoked = info.mRevoked; 539 mPending = info.mPending; 540 mTimeApprovedMs = info.mTimeApprovedMs; 541 mLastTimeConnectedMs = info.mLastTimeConnectedMs; 542 mSystemDataSyncFlags = info.mSystemDataSyncFlags; 543 mDeviceIcon = info.mDeviceIcon; 544 mDeviceId = info.mDeviceId; 545 } 546 547 /** 548 * This builder is used specifically to create a new association to be restored to a device 549 * that is potentially using a different user ID from the backed-up device. 550 * 551 * @hide 552 */ Builder(int id, int userId, @NonNull String packageName, AssociationInfo info)553 public Builder(int id, int userId, @NonNull String packageName, AssociationInfo info) { 554 mId = id; 555 mUserId = userId; 556 mPackageName = packageName; 557 mDeviceMacAddress = info.mDeviceMacAddress; 558 mDisplayName = info.mDisplayName; 559 mDeviceProfile = info.mDeviceProfile; 560 mAssociatedDevice = info.mAssociatedDevice; 561 mSelfManaged = info.mSelfManaged; 562 mNotifyOnDeviceNearby = info.mNotifyOnDeviceNearby; 563 mRevoked = info.mRevoked; 564 mPending = info.mPending; 565 mTimeApprovedMs = info.mTimeApprovedMs; 566 mLastTimeConnectedMs = info.mLastTimeConnectedMs; 567 mSystemDataSyncFlags = info.mSystemDataSyncFlags; 568 mDeviceIcon = info.mDeviceIcon; 569 mDeviceId = info.mDeviceId; 570 } 571 572 /** @hide */ 573 @FlaggedApi(Flags.FLAG_ASSOCIATION_TAG) 574 @TestApi 575 @NonNull setDeviceId(@ullable DeviceId deviceId)576 public Builder setDeviceId(@Nullable DeviceId deviceId) { 577 mDeviceId = deviceId; 578 return this; 579 } 580 581 /** @hide */ 582 @TestApi 583 @NonNull setDeviceMacAddress(@ullable MacAddress deviceMacAddress)584 public Builder setDeviceMacAddress(@Nullable MacAddress deviceMacAddress) { 585 mDeviceMacAddress = deviceMacAddress; 586 return this; 587 } 588 589 /** @hide */ 590 @TestApi 591 @NonNull setDisplayName(@ullable CharSequence displayName)592 public Builder setDisplayName(@Nullable CharSequence displayName) { 593 mDisplayName = displayName; 594 return this; 595 } 596 597 /** @hide */ 598 @TestApi 599 @NonNull setDeviceProfile(@ullable String deviceProfile)600 public Builder setDeviceProfile(@Nullable String deviceProfile) { 601 mDeviceProfile = deviceProfile; 602 return this; 603 } 604 605 /** @hide */ 606 @TestApi 607 @NonNull setAssociatedDevice(@ullable AssociatedDevice associatedDevice)608 public Builder setAssociatedDevice(@Nullable AssociatedDevice associatedDevice) { 609 mAssociatedDevice = associatedDevice; 610 return this; 611 } 612 613 /** @hide */ 614 @TestApi 615 @NonNull setSelfManaged(boolean selfManaged)616 public Builder setSelfManaged(boolean selfManaged) { 617 mSelfManaged = selfManaged; 618 return this; 619 } 620 621 /** @hide */ 622 @TestApi 623 @NonNull 624 @SuppressLint("MissingGetterMatchingBuilder") setNotifyOnDeviceNearby(boolean notifyOnDeviceNearby)625 public Builder setNotifyOnDeviceNearby(boolean notifyOnDeviceNearby) { 626 mNotifyOnDeviceNearby = notifyOnDeviceNearby; 627 return this; 628 } 629 630 /** @hide */ 631 @TestApi 632 @NonNull 633 @SuppressLint("MissingGetterMatchingBuilder") setRevoked(boolean revoked)634 public Builder setRevoked(boolean revoked) { 635 mRevoked = revoked; 636 return this; 637 } 638 639 /** @hide */ 640 @NonNull 641 @SuppressLint("MissingGetterMatchingBuilder") setPending(boolean pending)642 public Builder setPending(boolean pending) { 643 mPending = pending; 644 return this; 645 } 646 647 /** @hide */ 648 @TestApi 649 @NonNull 650 @SuppressLint("MissingGetterMatchingBuilder") setTimeApproved(long timeApprovedMs)651 public Builder setTimeApproved(long timeApprovedMs) { 652 if (timeApprovedMs < 0) { 653 throw new IllegalArgumentException("timeApprovedMs must be positive. Was given (" 654 + timeApprovedMs + ")"); 655 } 656 mTimeApprovedMs = timeApprovedMs; 657 return this; 658 } 659 660 /** @hide */ 661 @TestApi 662 @NonNull 663 @SuppressLint("MissingGetterMatchingBuilder") setLastTimeConnected(long lastTimeConnectedMs)664 public Builder setLastTimeConnected(long lastTimeConnectedMs) { 665 if (lastTimeConnectedMs < 0) { 666 throw new IllegalArgumentException( 667 "lastTimeConnectedMs must not be negative! (Given " + lastTimeConnectedMs 668 + " )"); 669 } 670 mLastTimeConnectedMs = lastTimeConnectedMs; 671 return this; 672 } 673 674 /** @hide */ 675 @TestApi 676 @NonNull setSystemDataSyncFlags(int flags)677 public Builder setSystemDataSyncFlags(int flags) { 678 mSystemDataSyncFlags = flags; 679 return this; 680 } 681 682 /** @hide */ 683 @TestApi 684 @NonNull 685 @SuppressLint("MissingGetterMatchingBuilder") 686 @FlaggedApi(Flags.FLAG_ASSOCIATION_DEVICE_ICON) setDeviceIcon(@ullable Icon deviceIcon)687 public Builder setDeviceIcon(@Nullable Icon deviceIcon) { 688 mDeviceIcon = deviceIcon; 689 return this; 690 } 691 692 /** @hide */ 693 @TestApi 694 @NonNull build()695 public AssociationInfo build() { 696 if (mId <= 0) { 697 throw new IllegalArgumentException("Association ID should be greater than 0"); 698 } 699 if (mDeviceMacAddress == null && mDisplayName == null) { 700 throw new IllegalArgumentException("MAC address and the display name must NOT be " 701 + "null at the same time"); 702 } 703 return new AssociationInfo( 704 mId, 705 mUserId, 706 mPackageName, 707 mDeviceMacAddress, 708 mDisplayName, 709 mDeviceProfile, 710 mAssociatedDevice, 711 mSelfManaged, 712 mNotifyOnDeviceNearby, 713 mRevoked, 714 mPending, 715 mTimeApprovedMs, 716 mLastTimeConnectedMs, 717 mSystemDataSyncFlags, 718 mDeviceIcon, 719 mDeviceId 720 ); 721 } 722 } 723 } 724