• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.NonNull;
19 import android.annotation.Nullable;
20 import android.annotation.SystemApi;
21 import android.annotation.UserIdInt;
22 import android.net.MacAddress;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 
26 import java.util.Date;
27 import java.util.Objects;
28 
29 /**
30  * Details for a specific "association" that has been established between an app and companion
31  * device.
32  * <p>
33  * An association gives an app the ability to interact with a companion device without needing to
34  * acquire broader runtime permissions. An association only exists after the user has confirmed that
35  * an app should have access to a companion device.
36  */
37 public final class AssociationInfo implements Parcelable {
38     /**
39      * A String indicates the selfManaged device is not connected.
40      */
41     private static final String LAST_TIME_CONNECTED_NONE = "None";
42     /**
43      * A unique ID of this Association record.
44      * Disclosed to the clients (ie. companion applications) for referring to this record (eg. in
45      * {@code disassociate()} API call).
46      */
47     private final int mId;
48 
49     private final @UserIdInt int mUserId;
50     private final @NonNull String mPackageName;
51 
52     private final @Nullable MacAddress mDeviceMacAddress;
53     private final @Nullable CharSequence mDisplayName;
54     private final @Nullable String mDeviceProfile;
55 
56     private final boolean mSelfManaged;
57     private final boolean mNotifyOnDeviceNearby;
58 
59     /**
60      * Indicates that the association has been revoked (removed), but we keep the association
61      * record for final clean up (e.g. removing the app from the list of the role holders).
62      *
63      * @see CompanionDeviceManager#disassociate(int)
64      */
65     private final boolean mRevoked;
66     private final long mTimeApprovedMs;
67     /**
68      * A long value indicates the last time connected reported by selfManaged devices
69      * Default value is Long.MAX_VALUE.
70      */
71     private final long mLastTimeConnectedMs;
72 
73     /**
74      * Creates a new Association.
75      * Only to be used by the CompanionDeviceManagerService.
76      *
77      * @hide
78      */
AssociationInfo(int id, @UserIdInt int userId, @NonNull String packageName, @Nullable MacAddress macAddress, @Nullable CharSequence displayName, @Nullable String deviceProfile, boolean selfManaged, boolean notifyOnDeviceNearby, boolean revoked, long timeApprovedMs, long lastTimeConnectedMs)79     public AssociationInfo(int id, @UserIdInt int userId, @NonNull String packageName,
80             @Nullable MacAddress macAddress, @Nullable CharSequence displayName,
81             @Nullable String deviceProfile, boolean selfManaged, boolean notifyOnDeviceNearby,
82             boolean revoked, long timeApprovedMs, long lastTimeConnectedMs) {
83         if (id <= 0) {
84             throw new IllegalArgumentException("Association ID should be greater than 0");
85         }
86         if (macAddress == null && displayName == null) {
87             throw new IllegalArgumentException("MAC address and the Display Name must NOT be null "
88                     + "at the same time");
89         }
90 
91         mId = id;
92 
93         mUserId = userId;
94         mPackageName = packageName;
95 
96         mDeviceMacAddress = macAddress;
97         mDisplayName = displayName;
98         mDeviceProfile = deviceProfile;
99 
100         mSelfManaged = selfManaged;
101         mNotifyOnDeviceNearby = notifyOnDeviceNearby;
102         mRevoked = revoked;
103         mTimeApprovedMs = timeApprovedMs;
104         mLastTimeConnectedMs = lastTimeConnectedMs;
105     }
106 
107     /**
108      * @return the unique ID of this association record.
109      */
getId()110     public int getId() {
111         return mId;
112     }
113 
114     /**
115      * @return the ID of the user who "owns" this association.
116      * @hide
117      */
getUserId()118     public @UserIdInt int getUserId() {
119         return mUserId;
120     }
121 
122     /**
123      * @return the package name of the app which this association refers to.
124      * @hide
125      */
126     @SystemApi
getPackageName()127     public @NonNull String getPackageName() {
128         return mPackageName;
129     }
130 
131     /**
132      * @return the MAC address of the device.
133      */
getDeviceMacAddress()134     public @Nullable MacAddress getDeviceMacAddress() {
135         return mDeviceMacAddress;
136     }
137 
138     /** @hide */
getDeviceMacAddressAsString()139     public @Nullable String getDeviceMacAddressAsString() {
140         return mDeviceMacAddress != null ? mDeviceMacAddress.toString().toUpperCase() : null;
141     }
142 
143     /**
144      * @return the display name of the companion device (optionally) provided by the companion
145      * application.
146      *
147      * @see AssociationRequest.Builder#setDisplayName(CharSequence)
148      */
getDisplayName()149     public @Nullable CharSequence getDisplayName() {
150         return mDisplayName;
151     }
152 
153     /**
154      * @return the companion device profile used when establishing this
155      *         association, or {@code null} if no specific profile was used.
156      * @see AssociationRequest.Builder#setDeviceProfile(String)
157      */
getDeviceProfile()158     public @Nullable String getDeviceProfile() {
159         return mDeviceProfile;
160     }
161 
162     /**
163      * @return whether the association is managed by the companion application it belongs to.
164      * @see AssociationRequest.Builder#setSelfManaged(boolean)
165      * @hide
166      */
167     @SystemApi
isSelfManaged()168     public boolean isSelfManaged() {
169         return mSelfManaged;
170     }
171 
172     /** @hide */
isNotifyOnDeviceNearby()173     public boolean isNotifyOnDeviceNearby() {
174         return mNotifyOnDeviceNearby;
175     }
176 
177     /** @hide */
getTimeApprovedMs()178     public long getTimeApprovedMs() {
179         return mTimeApprovedMs;
180     }
181 
182     /** @hide */
belongsToPackage(@serIdInt int userId, String packageName)183     public boolean belongsToPackage(@UserIdInt int userId, String packageName) {
184         return mUserId == userId && Objects.equals(mPackageName, packageName);
185     }
186 
187     /**
188      * @return if the association has been revoked (removed).
189      * @hide
190      */
isRevoked()191     public boolean isRevoked() {
192         return mRevoked;
193     }
194 
195     /**
196      * @return the last time self reported disconnected for selfManaged only.
197      * @hide
198      */
getLastTimeConnectedMs()199     public Long getLastTimeConnectedMs() {
200         return mLastTimeConnectedMs;
201     }
202 
203     /**
204      * Utility method for checking if the association represents a device with the given MAC
205      * address.
206      *
207      * @return {@code false} if the association is "self-managed".
208      *         {@code false} if the {@code addr} is {@code null} or is not a valid MAC address.
209      *         Otherwise - the result of {@link MacAddress#equals(Object)}
210      *
211      * @hide
212      */
isLinkedTo(@ullable String addr)213     public boolean isLinkedTo(@Nullable String addr) {
214         if (mSelfManaged) return false;
215 
216         if (addr == null) return false;
217 
218         final MacAddress macAddress;
219         try {
220             macAddress = MacAddress.fromString(addr);
221         } catch (IllegalArgumentException e) {
222             return false;
223         }
224         return macAddress.equals(mDeviceMacAddress);
225     }
226 
227     /**
228      * Utility method to be used by CdmService only.
229      *
230      * @return whether CdmService should bind the companion application that "owns" this association
231      *         when the device is present.
232      *
233      * @hide
234      */
shouldBindWhenPresent()235     public boolean shouldBindWhenPresent() {
236         return mNotifyOnDeviceNearby || mSelfManaged;
237     }
238 
239     /** @hide */
toShortString()240     public @NonNull String toShortString() {
241         final StringBuilder sb = new StringBuilder();
242         sb.append("id=").append(mId);
243         if (mDeviceMacAddress != null) {
244             sb.append(", addr=").append(getDeviceMacAddressAsString());
245         }
246         if (mSelfManaged) {
247             sb.append(", self-managed");
248         }
249         sb.append(", pkg=u").append(mUserId).append('/').append(mPackageName);
250         return sb.toString();
251     }
252 
253     @Override
toString()254     public String toString() {
255         return "Association{"
256                 + "mId=" + mId
257                 + ", mUserId=" + mUserId
258                 + ", mPackageName='" + mPackageName + '\''
259                 + ", mDeviceMacAddress=" + mDeviceMacAddress
260                 + ", mDisplayName='" + mDisplayName + '\''
261                 + ", mDeviceProfile='" + mDeviceProfile + '\''
262                 + ", mSelfManaged=" + mSelfManaged
263                 + ", mNotifyOnDeviceNearby=" + mNotifyOnDeviceNearby
264                 + ", mRevoked=" + mRevoked
265                 + ", mTimeApprovedMs=" + new Date(mTimeApprovedMs)
266                 + ", mLastTimeConnectedMs=" + (
267                     mLastTimeConnectedMs == Long.MAX_VALUE
268                         ? LAST_TIME_CONNECTED_NONE : new Date(mLastTimeConnectedMs))
269                 + '}';
270     }
271 
272     @Override
equals(Object o)273     public boolean equals(Object o) {
274         if (this == o) return true;
275         if (!(o instanceof AssociationInfo)) return false;
276         final AssociationInfo that = (AssociationInfo) o;
277         return mId == that.mId
278                 && mUserId == that.mUserId
279                 && mSelfManaged == that.mSelfManaged
280                 && mNotifyOnDeviceNearby == that.mNotifyOnDeviceNearby
281                 && mRevoked == that.mRevoked
282                 && mTimeApprovedMs == that.mTimeApprovedMs
283                 && mLastTimeConnectedMs == that.mLastTimeConnectedMs
284                 && Objects.equals(mPackageName, that.mPackageName)
285                 && Objects.equals(mDeviceMacAddress, that.mDeviceMacAddress)
286                 && Objects.equals(mDisplayName, that.mDisplayName)
287                 && Objects.equals(mDeviceProfile, that.mDeviceProfile);
288     }
289 
290     @Override
hashCode()291     public int hashCode() {
292         return Objects.hash(mId, mUserId, mPackageName, mDeviceMacAddress, mDisplayName,
293                 mDeviceProfile, mSelfManaged, mNotifyOnDeviceNearby, mRevoked, mTimeApprovedMs,
294                 mLastTimeConnectedMs);
295     }
296 
297     @Override
describeContents()298     public int describeContents() {
299         return 0;
300     }
301 
302     @Override
writeToParcel(@onNull Parcel dest, int flags)303     public void writeToParcel(@NonNull Parcel dest, int flags) {
304         dest.writeInt(mId);
305 
306         dest.writeInt(mUserId);
307         dest.writeString(mPackageName);
308 
309         dest.writeTypedObject(mDeviceMacAddress, 0);
310         dest.writeCharSequence(mDisplayName);
311         dest.writeString(mDeviceProfile);
312 
313         dest.writeBoolean(mSelfManaged);
314         dest.writeBoolean(mNotifyOnDeviceNearby);
315         dest.writeBoolean(mRevoked);
316         dest.writeLong(mTimeApprovedMs);
317         dest.writeLong(mLastTimeConnectedMs);
318     }
319 
AssociationInfo(@onNull Parcel in)320     private AssociationInfo(@NonNull Parcel in) {
321         mId = in.readInt();
322 
323         mUserId = in.readInt();
324         mPackageName = in.readString();
325 
326         mDeviceMacAddress = in.readTypedObject(MacAddress.CREATOR);
327         mDisplayName = in.readCharSequence();
328         mDeviceProfile = in.readString();
329 
330         mSelfManaged = in.readBoolean();
331         mNotifyOnDeviceNearby = in.readBoolean();
332         mRevoked = in.readBoolean();
333         mTimeApprovedMs = in.readLong();
334         mLastTimeConnectedMs = in.readLong();
335     }
336 
337     @NonNull
338     public static final Parcelable.Creator<AssociationInfo> CREATOR =
339             new Parcelable.Creator<AssociationInfo>() {
340         @Override
341         public AssociationInfo[] newArray(int size) {
342             return new AssociationInfo[size];
343         }
344 
345         @Override
346         public AssociationInfo createFromParcel(@NonNull Parcel in) {
347             return new AssociationInfo(in);
348         }
349     };
350 
351     /**
352      * Use this method to obtain a builder that you can use to create a copy of the
353      * given {@link AssociationInfo} with modified values of {@code mLastTimeConnected}
354      * or {@code mNotifyOnDeviceNearby}.
355      * <p>
356      *     Note that you <b>must</b> call either {@link Builder#setLastTimeConnected(long)
357      *     setLastTimeConnected} or {@link Builder#setNotifyOnDeviceNearby(boolean)
358      *     setNotifyOnDeviceNearby} before you will be able to call {@link Builder#build() build}.
359      *
360      *     This is ensured statically at compile time.
361      *
362      * @hide
363      */
364     @NonNull
builder(@onNull AssociationInfo info)365     public static NonActionableBuilder builder(@NonNull AssociationInfo info) {
366         return new Builder(info);
367     }
368 
369     /**
370      * @hide
371      */
372     public static final class Builder implements NonActionableBuilder {
373         @NonNull
374         private final AssociationInfo mOriginalInfo;
375         private boolean mNotifyOnDeviceNearby;
376         private boolean mRevoked;
377         private long mLastTimeConnectedMs;
378 
Builder(@onNull AssociationInfo info)379         private Builder(@NonNull AssociationInfo info) {
380             mOriginalInfo = info;
381             mNotifyOnDeviceNearby = info.mNotifyOnDeviceNearby;
382             mRevoked = info.mRevoked;
383             mLastTimeConnectedMs = info.mLastTimeConnectedMs;
384         }
385 
386         /**
387          * Should only be used by the CompanionDeviceManagerService.
388          * @hide
389          */
390         @Override
391         @NonNull
setLastTimeConnected(long lastTimeConnectedMs)392         public Builder setLastTimeConnected(long lastTimeConnectedMs) {
393             if (lastTimeConnectedMs < 0) {
394                 throw new IllegalArgumentException(
395                         "lastTimeConnectedMs must not be negative! (Given " + lastTimeConnectedMs
396                                 + " )");
397             }
398             mLastTimeConnectedMs = lastTimeConnectedMs;
399             return this;
400         }
401 
402         /**
403          * Should only be used by the CompanionDeviceManagerService.
404          * @hide
405          */
406         @Override
407         @NonNull
setNotifyOnDeviceNearby(boolean notifyOnDeviceNearby)408         public Builder setNotifyOnDeviceNearby(boolean notifyOnDeviceNearby) {
409             mNotifyOnDeviceNearby = notifyOnDeviceNearby;
410             return this;
411         }
412 
413         /**
414          * Should only be used by the CompanionDeviceManagerService.
415          * @hide
416          */
417         @Override
418         @NonNull
setRevoked(boolean revoked)419         public Builder setRevoked(boolean revoked) {
420             mRevoked = revoked;
421             return this;
422         }
423 
424         /**
425          * @hide
426          */
427         @NonNull
build()428         public AssociationInfo build() {
429             return new AssociationInfo(
430                     mOriginalInfo.mId,
431                     mOriginalInfo.mUserId,
432                     mOriginalInfo.mPackageName,
433                     mOriginalInfo.mDeviceMacAddress,
434                     mOriginalInfo.mDisplayName,
435                     mOriginalInfo.mDeviceProfile,
436                     mOriginalInfo.mSelfManaged,
437                     mNotifyOnDeviceNearby,
438                     mRevoked,
439                     mOriginalInfo.mTimeApprovedMs,
440                     mLastTimeConnectedMs
441             );
442         }
443     }
444 
445     /**
446      * This interface is returned from the
447      * {@link AssociationInfo#builder(android.companion.AssociationInfo) builder} entry point
448      * to indicate that this builder is not yet in a state that can produce a meaningful
449      * {@link AssociationInfo} object that is different from the one originally passed in.
450      *
451      * <p>
452      * Only by calling one of the setter methods is this builder turned into one where calling
453      * {@link Builder#build() build()} makes sense.
454      *
455      * @hide
456      */
457     public interface NonActionableBuilder {
458         /**
459          * Should only be used by the CompanionDeviceManagerService.
460          * @hide
461          */
462         @NonNull
setNotifyOnDeviceNearby(boolean notifyOnDeviceNearby)463         Builder setNotifyOnDeviceNearby(boolean notifyOnDeviceNearby);
464 
465         /**
466          * Should only be used by the CompanionDeviceManagerService.
467          * @hide
468          */
469         @NonNull
setLastTimeConnected(long lastTimeConnectedMs)470         Builder setLastTimeConnected(long lastTimeConnectedMs);
471 
472         /**
473          * Should only be used by the CompanionDeviceManagerService.
474          * @hide
475          */
476         @NonNull
setRevoked(boolean revoked)477         Builder setRevoked(boolean revoked);
478     }
479 }
480