• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.health.connect.datatypes;
18 
19 import static com.android.healthfitness.flags.Flags.FLAG_PERSONAL_HEALTH_RECORD;
20 
21 import static java.util.Objects.hash;
22 import static java.util.Objects.requireNonNull;
23 
24 import android.annotation.FlaggedApi;
25 import android.annotation.NonNull;
26 import android.annotation.Nullable;
27 import android.net.Uri;
28 import android.os.Parcel;
29 import android.os.Parcelable;
30 
31 import java.time.Instant;
32 import java.util.HashSet;
33 import java.util.Objects;
34 import java.util.Set;
35 import java.util.UUID;
36 
37 /**
38  * Captures the data source information of medical data. All {@link MedicalResource}s are associated
39  * with a {@code MedicalDataSource}.
40  *
41  * <p>The medical data is represented using the <a href="https://hl7.org/fhir/">Fast Healthcare
42  * Interoperability Resources (FHIR)</a> standard.
43  */
44 @FlaggedApi(FLAG_PERSONAL_HEALTH_RECORD)
45 public final class MedicalDataSource implements Parcelable {
46     @NonNull private final String mId;
47     @NonNull private final String mPackageName;
48     @NonNull private final Uri mFhirBaseUri;
49     @NonNull private final String mDisplayName;
50     @NonNull private final FhirVersion mFhirVersion;
51     @Nullable private final Instant mLastDataUpdateTime;
52 
53     @NonNull
54     public static final Creator<MedicalDataSource> CREATOR =
55             new Creator<MedicalDataSource>() {
56                 @NonNull
57                 @Override
58                 public MedicalDataSource createFromParcel(@NonNull Parcel in) {
59                     return new MedicalDataSource(in);
60                 }
61 
62                 @NonNull
63                 @Override
64                 public MedicalDataSource[] newArray(int size) {
65                     return new MedicalDataSource[size];
66                 }
67             };
68 
69     /**
70      * Creates a new instance of {@link MedicalDataSource}. Please see {@link
71      * MedicalDataSource.Builder} for more detailed parameters information.
72      */
MedicalDataSource( @onNull String id, @NonNull String packageName, @NonNull Uri fhirBaseUri, @NonNull String displayName, @NonNull FhirVersion fhirVersion, @Nullable Instant lastDataUpdateTime)73     private MedicalDataSource(
74             @NonNull String id,
75             @NonNull String packageName,
76             @NonNull Uri fhirBaseUri,
77             @NonNull String displayName,
78             @NonNull FhirVersion fhirVersion,
79             @Nullable Instant lastDataUpdateTime) {
80         requireNonNull(id);
81         requireNonNull(packageName);
82         requireNonNull(fhirBaseUri);
83         requireNonNull(displayName);
84         requireNonNull(fhirVersion);
85 
86         mId = id;
87         mPackageName = packageName;
88         mFhirBaseUri = fhirBaseUri;
89         mDisplayName = displayName;
90         mFhirVersion = fhirVersion;
91         mLastDataUpdateTime = lastDataUpdateTime;
92     }
93 
MedicalDataSource(@onNull Parcel in)94     private MedicalDataSource(@NonNull Parcel in) {
95         requireNonNull(in);
96         mId = requireNonNull(in.readString());
97         mPackageName = requireNonNull(in.readString());
98         mFhirBaseUri = requireNonNull(in.readParcelable(Uri.class.getClassLoader(), Uri.class));
99         mDisplayName = requireNonNull(in.readString());
100         mFhirVersion =
101                 requireNonNull(
102                         in.readParcelable(FhirVersion.class.getClassLoader(), FhirVersion.class));
103         long lastDataUpdateTimeMillis = in.readLong();
104         mLastDataUpdateTime =
105                 lastDataUpdateTimeMillis == 0
106                         ? null
107                         : Instant.ofEpochMilli(lastDataUpdateTimeMillis);
108     }
109 
110     /** Returns the unique identifier, assigned by the Android Health Platform at insertion time. */
111     @NonNull
getId()112     public String getId() {
113         return mId;
114     }
115 
116     /** Returns the corresponding package name of the owning app. */
117     @NonNull
getPackageName()118     public String getPackageName() {
119         return mPackageName;
120     }
121 
122     /** Returns the display name. */
123     @NonNull
getDisplayName()124     public String getDisplayName() {
125         return mDisplayName;
126     }
127 
128     /** Returns the FHIR version of {@link MedicalResource}s from this source. */
129     @NonNull
getFhirVersion()130     public FhirVersion getFhirVersion() {
131         return mFhirVersion;
132     }
133 
134     /**
135      * Returns the time {@link MedicalResource}s linked to this data source were last updated, or
136      * {@code null} if the data source has no linked resources.
137      *
138      * <p>This time is based on resources that currently exist in HealthConnect, so does not reflect
139      * data deletion.
140      */
141     @Nullable
getLastDataUpdateTime()142     public Instant getLastDataUpdateTime() {
143         return mLastDataUpdateTime;
144     }
145 
146     /** Returns the FHIR base URI, where data written for this data source came from. */
147     @NonNull
getFhirBaseUri()148     public Uri getFhirBaseUri() {
149         return mFhirBaseUri;
150     }
151 
152     @Override
describeContents()153     public int describeContents() {
154         return 0;
155     }
156 
157     @Override
writeToParcel(@onNull Parcel dest, int flags)158     public void writeToParcel(@NonNull Parcel dest, int flags) {
159         dest.writeString(mId);
160         dest.writeString(mPackageName);
161         dest.writeParcelable(mFhirBaseUri, 0);
162         dest.writeString(mDisplayName);
163         dest.writeParcelable(mFhirVersion, 0);
164         dest.writeLong(mLastDataUpdateTime == null ? 0 : mLastDataUpdateTime.toEpochMilli());
165     }
166 
167     /**
168      * Validates all of the provided {@code ids} are valid.
169      *
170      * <p>Throws {@link IllegalArgumentException} with all invalid IDs if not.
171      *
172      * @hide
173      */
validateMedicalDataSourceIds(@onNull Set<String> ids)174     public static Set<UUID> validateMedicalDataSourceIds(@NonNull Set<String> ids) {
175         Set<String> invalidIds = new HashSet<>();
176         Set<UUID> uuids = new HashSet<>();
177         for (String id : ids) {
178             try {
179                 uuids.add(UUID.fromString(id));
180             } catch (IllegalArgumentException e) {
181                 invalidIds.add(id);
182             }
183         }
184         if (!invalidIds.isEmpty()) {
185             throw new IllegalArgumentException("Invalid data source ID(s): " + invalidIds);
186         }
187         return uuids;
188     }
189 
190     /** Indicates whether some other object is "equal to" this one. */
191     @Override
equals(@ullable Object o)192     public boolean equals(@Nullable Object o) {
193         if (this == o) return true;
194         if (!(o instanceof MedicalDataSource that)) return false;
195         return getId().equals(that.getId())
196                 && getPackageName().equals(that.getPackageName())
197                 && getFhirBaseUri().equals(that.getFhirBaseUri())
198                 && getDisplayName().equals(that.getDisplayName())
199                 && getFhirVersion().equals(that.getFhirVersion())
200                 && Objects.equals(getLastDataUpdateTime(), that.getLastDataUpdateTime());
201     }
202 
203     /** Returns a hash code value for the object. */
204     @Override
hashCode()205     public int hashCode() {
206         return hash(
207                 getId(),
208                 getPackageName(),
209                 getFhirBaseUri(),
210                 getDisplayName(),
211                 getFhirVersion(),
212                 getLastDataUpdateTime());
213     }
214 
215     /** Returns a string representation of this {@link MedicalDataSource}. */
216     @Override
toString()217     public String toString() {
218         StringBuilder sb = new StringBuilder();
219         sb.append(this.getClass().getSimpleName()).append("{");
220         sb.append("id=").append(getId());
221         sb.append(",packageName=").append(getPackageName());
222         sb.append(",fhirBaseUri=").append(getFhirBaseUri());
223         sb.append(",displayName=").append(getDisplayName());
224         sb.append(",fhirVersion=").append(getFhirVersion());
225         sb.append(",lastDataUpdateTime=").append(getLastDataUpdateTime());
226         sb.append("}");
227         return sb.toString();
228     }
229 
230     /** Builder class for {@link MedicalDataSource}. */
231     public static final class Builder {
232         @NonNull private String mId;
233         @NonNull private String mPackageName;
234         @NonNull private Uri mFhirBaseUri;
235         @NonNull private String mDisplayName;
236         @NonNull private FhirVersion mFhirVersion;
237         @Nullable private Instant mLastDataUpdateTime;
238 
239         /**
240          * Constructs a new {@link MedicalDataSource.Builder} instance.
241          *
242          * @param id The unique identifier of this data source.
243          * @param packageName The package name of the owning app.
244          * @param fhirBaseUri The FHIR base URI of the data source.
245          * @param displayName The display name that describes the data source.
246          * @param fhirVersion The FHIR version of {@link MedicalResource}s linked to this source.
247          */
Builder( @onNull String id, @NonNull String packageName, @NonNull Uri fhirBaseUri, @NonNull String displayName, @NonNull FhirVersion fhirVersion)248         public Builder(
249                 @NonNull String id,
250                 @NonNull String packageName,
251                 @NonNull Uri fhirBaseUri,
252                 @NonNull String displayName,
253                 @NonNull FhirVersion fhirVersion) {
254             requireNonNull(id);
255             requireNonNull(packageName);
256             requireNonNull(fhirBaseUri);
257             requireNonNull(displayName);
258             requireNonNull(fhirVersion);
259 
260             mId = id;
261             mPackageName = packageName;
262             mFhirBaseUri = fhirBaseUri;
263             mDisplayName = displayName;
264             mFhirVersion = fhirVersion;
265         }
266 
267         /** Constructs a clone of the other {@link MedicalDataSource.Builder}. */
Builder(@onNull Builder other)268         public Builder(@NonNull Builder other) {
269             requireNonNull(other);
270             mId = other.mId;
271             mPackageName = other.mPackageName;
272             mFhirBaseUri = other.mFhirBaseUri;
273             mDisplayName = other.mDisplayName;
274             mFhirVersion = other.mFhirVersion;
275             mLastDataUpdateTime = other.mLastDataUpdateTime;
276         }
277 
278         /** Constructs a clone of the other {@link MedicalDataSource} instance. */
Builder(@onNull MedicalDataSource other)279         public Builder(@NonNull MedicalDataSource other) {
280             requireNonNull(other);
281             mId = other.getId();
282             mPackageName = other.getPackageName();
283             mFhirBaseUri = other.getFhirBaseUri();
284             mDisplayName = other.getDisplayName();
285             mFhirVersion = other.getFhirVersion();
286             mLastDataUpdateTime = other.getLastDataUpdateTime();
287         }
288 
289         /** Sets unique identifier of this data source. */
290         @NonNull
setId(@onNull String id)291         public Builder setId(@NonNull String id) {
292             requireNonNull(id);
293             mId = id;
294             return this;
295         }
296 
297         /**
298          * Sets the package name of the contributing package. Auto-populated by the platform at
299          * source creation time.
300          */
301         @NonNull
setPackageName(@onNull String packageName)302         public Builder setPackageName(@NonNull String packageName) {
303             requireNonNull(packageName);
304             mPackageName = packageName;
305             return this;
306         }
307 
308         /** Sets the FHIR base URI of this data source. */
309         @NonNull
setFhirBaseUri(@onNull Uri fhirBaseUri)310         public Builder setFhirBaseUri(@NonNull Uri fhirBaseUri) {
311             requireNonNull(fhirBaseUri);
312             mFhirBaseUri = fhirBaseUri;
313             return this;
314         }
315 
316         /** Sets the display name that describes this data source. */
317         @NonNull
setDisplayName(@onNull String displayName)318         public Builder setDisplayName(@NonNull String displayName) {
319             requireNonNull(displayName);
320             mDisplayName = displayName;
321             return this;
322         }
323 
324         /** Sets the FHIR version of {@link MedicalResource}s linked to this source. */
325         @NonNull
setFhirVersion(@onNull FhirVersion fhirVersion)326         public Builder setFhirVersion(@NonNull FhirVersion fhirVersion) {
327             requireNonNull(fhirVersion);
328             mFhirVersion = fhirVersion;
329             return this;
330         }
331 
332         /** Sets the time {@link MedicalResource}s linked to this data source were last updated. */
333         @NonNull
setLastDataUpdateTime(@ullable Instant lastDataUpdateTime)334         public Builder setLastDataUpdateTime(@Nullable Instant lastDataUpdateTime) {
335             mLastDataUpdateTime = lastDataUpdateTime;
336             return this;
337         }
338 
339         /** Returns a new instance of {@link MedicalDataSource} with the specified parameters. */
340         @NonNull
build()341         public MedicalDataSource build() {
342             return new MedicalDataSource(
343                     mId,
344                     mPackageName,
345                     mFhirBaseUri,
346                     mDisplayName,
347                     mFhirVersion,
348                     mLastDataUpdateTime);
349         }
350     }
351 }
352