• 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 android.health.connect.datatypes.MedicalDataSource.validateMedicalDataSourceIds;
20 import static android.health.connect.datatypes.validation.ValidationUtils.validateIntDefValue;
21 
22 import static com.android.healthfitness.flags.Flags.FLAG_PERSONAL_HEALTH_RECORD;
23 
24 import static java.util.Objects.hash;
25 import static java.util.Objects.requireNonNull;
26 
27 import android.annotation.FlaggedApi;
28 import android.annotation.IntDef;
29 import android.annotation.NonNull;
30 import android.health.connect.MedicalResourceId;
31 import android.os.Parcel;
32 import android.os.Parcelable;
33 
34 import java.lang.annotation.Retention;
35 import java.lang.annotation.RetentionPolicy;
36 import java.util.Set;
37 
38 /**
39  * A class to capture the user's medical data. This is the class used for all medical resource
40  * types.
41  *
42  * <p>The data representation follows the <a href="https://hl7.org/fhir/">Fast Healthcare
43  * Interoperability Resources (FHIR)</a> standard.
44  */
45 @FlaggedApi(FLAG_PERSONAL_HEALTH_RECORD)
46 public final class MedicalResource implements Parcelable {
47 
48     /** Medical resource type labelling data as vaccines. */
49     public static final int MEDICAL_RESOURCE_TYPE_VACCINES = 1;
50 
51     /** Medical resource type labelling data as allergies or intolerances. */
52     public static final int MEDICAL_RESOURCE_TYPE_ALLERGIES_INTOLERANCES = 2;
53 
54     /** Medical resource type labelling data as to do with pregnancy. */
55     public static final int MEDICAL_RESOURCE_TYPE_PREGNANCY = 3;
56 
57     /** Medical resource type labelling data as social history. */
58     public static final int MEDICAL_RESOURCE_TYPE_SOCIAL_HISTORY = 4;
59 
60     /** Medical resource type labelling data as vital signs. */
61     public static final int MEDICAL_RESOURCE_TYPE_VITAL_SIGNS = 5;
62 
63     /** Medical resource type labelling data as results (Laboratory or pathology). */
64     public static final int MEDICAL_RESOURCE_TYPE_LABORATORY_RESULTS = 6;
65 
66     /**
67      * Medical resource type labelling data as medical conditions (clinical condition, problem,
68      * diagnosis etc).
69      */
70     public static final int MEDICAL_RESOURCE_TYPE_CONDITIONS = 7;
71 
72     /** Medical resource type labelling data as procedures (actions taken on or for a patient). */
73     public static final int MEDICAL_RESOURCE_TYPE_PROCEDURES = 8;
74 
75     /** Medical resource type labelling data as medication related. */
76     public static final int MEDICAL_RESOURCE_TYPE_MEDICATIONS = 9;
77 
78     /**
79      * Medical resource type labelling data as related to personal details, including demographic
80      * information such as name, date of birth, and contact details such as address or telephone
81      * numbers.
82      */
83     public static final int MEDICAL_RESOURCE_TYPE_PERSONAL_DETAILS = 10;
84 
85     /**
86      * Medical resource type labelling data as related to practitioners. This is information about
87      * the doctors, nurses, masseurs, physios, etc who have been involved with the user.
88      */
89     public static final int MEDICAL_RESOURCE_TYPE_PRACTITIONER_DETAILS = 11;
90 
91     /**
92      * Medical resource type labelling data as related to an encounter with a practitioner. This
93      * includes visits to healthcare providers and remote encounters such as telephone and
94      * videoconference appointments, and information about the time, location and organization who
95      * is being met.
96      */
97     public static final int MEDICAL_RESOURCE_TYPE_VISITS = 12;
98 
99     /** @hide */
100     @IntDef({
101         MEDICAL_RESOURCE_TYPE_ALLERGIES_INTOLERANCES,
102         MEDICAL_RESOURCE_TYPE_CONDITIONS,
103         MEDICAL_RESOURCE_TYPE_LABORATORY_RESULTS,
104         MEDICAL_RESOURCE_TYPE_MEDICATIONS,
105         MEDICAL_RESOURCE_TYPE_PERSONAL_DETAILS,
106         MEDICAL_RESOURCE_TYPE_PRACTITIONER_DETAILS,
107         MEDICAL_RESOURCE_TYPE_PREGNANCY,
108         MEDICAL_RESOURCE_TYPE_PROCEDURES,
109         MEDICAL_RESOURCE_TYPE_SOCIAL_HISTORY,
110         MEDICAL_RESOURCE_TYPE_VACCINES,
111         MEDICAL_RESOURCE_TYPE_VISITS,
112         MEDICAL_RESOURCE_TYPE_VITAL_SIGNS,
113     })
114     @Retention(RetentionPolicy.SOURCE)
115     public @interface MedicalResourceType {}
116 
117     @MedicalResourceType private final int mType;
118     @NonNull private final MedicalResourceId mId;
119     @NonNull private final String mDataSourceId;
120     @NonNull private final FhirVersion mFhirVersion;
121     @NonNull private final FhirResource mFhirResource;
122 
123     /** @hide */
124     private long mLastModifiedTimestamp;
125 
126     /**
127      * Creates a new instance of {@link MedicalResource} which takes in {@code
128      * lastModifiedTimestamp} as a parameter as well. The {@code lastModifiedTimestamp} is currently
129      * only used internally to ensure D2D merge process, copies over the exact timestamp of when the
130      * {@link MedicalResource} was modified.
131      *
132      * @hide
133      */
MedicalResource( @edicalResourceType int type, @NonNull String dataSourceId, @NonNull FhirVersion fhirVersion, @NonNull FhirResource fhirResource, long lastModifiedTimestamp)134     public MedicalResource(
135             @MedicalResourceType int type,
136             @NonNull String dataSourceId,
137             @NonNull FhirVersion fhirVersion,
138             @NonNull FhirResource fhirResource,
139             long lastModifiedTimestamp) {
140         this(type, dataSourceId, fhirVersion, fhirResource);
141         mLastModifiedTimestamp = lastModifiedTimestamp;
142     }
143 
144     /**
145      * Creates a new instance of {@link MedicalResource}. Please see {@link MedicalResource.Builder}
146      * for more detailed parameters information.
147      */
MedicalResource( @edicalResourceType int type, @NonNull String dataSourceId, @NonNull FhirVersion fhirVersion, @NonNull FhirResource fhirResource)148     private MedicalResource(
149             @MedicalResourceType int type,
150             @NonNull String dataSourceId,
151             @NonNull FhirVersion fhirVersion,
152             @NonNull FhirResource fhirResource) {
153         requireNonNull(dataSourceId);
154         requireNonNull(fhirVersion);
155         requireNonNull(fhirResource);
156         validateMedicalResourceType(type);
157         validateMedicalDataSourceIds(Set.of(dataSourceId));
158 
159         mType = type;
160         mDataSourceId = dataSourceId;
161         mFhirVersion = fhirVersion;
162         mFhirResource = fhirResource;
163         mId = new MedicalResourceId(dataSourceId, fhirResource.getType(), fhirResource.getId());
164     }
165 
166     /**
167      * Constructs this object with the data present in {@code parcel}. It should be in the same
168      * order as {@link MedicalResource#writeToParcel}.
169      */
MedicalResource(@onNull Parcel in)170     private MedicalResource(@NonNull Parcel in) {
171         requireNonNull(in);
172         mType = in.readInt();
173         validateMedicalResourceType(mType);
174         mDataSourceId = requireNonNull(in.readString());
175         validateMedicalDataSourceIds(Set.of(mDataSourceId));
176         mFhirVersion =
177                 requireNonNull(
178                         in.readParcelable(FhirVersion.class.getClassLoader(), FhirVersion.class));
179         mFhirResource =
180                 requireNonNull(
181                         in.readParcelable(FhirResource.class.getClassLoader(), FhirResource.class));
182         mId = new MedicalResourceId(mDataSourceId, mFhirResource.getType(), mFhirResource.getId());
183     }
184 
185     @NonNull
186     public static final Creator<MedicalResource> CREATOR =
187             new Creator<>() {
188                 @Override
189                 public MedicalResource createFromParcel(Parcel in) {
190                     return new MedicalResource(in);
191                 }
192 
193                 @Override
194                 public MedicalResource[] newArray(int size) {
195                     return new MedicalResource[size];
196                 }
197             };
198 
199     /**
200      * Returns the medical resource type, assigned by the Android Health Platform at insertion time.
201      *
202      * <p>For a list of supported types, see the {@link MedicalResource} type constants, such as
203      * {@link #MEDICAL_RESOURCE_TYPE_VACCINES}. Clients should be aware that this list is non
204      * exhaustive and may increase in future releases when additional types will need to be handled.
205      */
206     @MedicalResourceType
getType()207     public int getType() {
208         return mType;
209     }
210 
211     /** Returns the ID of this {@link MedicalResource} as {@link MedicalResourceId}. */
212     @NonNull
getId()213     public MedicalResourceId getId() {
214         return mId;
215     }
216 
217     /** Returns the unique {@link MedicalDataSource} ID of where the data comes from. */
218     @NonNull
getDataSourceId()219     public String getDataSourceId() {
220         return mDataSourceId;
221     }
222 
223     /** Returns the FHIR version being used for {@code mFhirResource} */
224     @NonNull
getFhirVersion()225     public FhirVersion getFhirVersion() {
226         return mFhirVersion;
227     }
228 
229     /** Returns the enclosed {@link FhirResource} object. */
230     @NonNull
getFhirResource()231     public FhirResource getFhirResource() {
232         return mFhirResource;
233     }
234 
235     /**
236      * Returns the last modified timestamp for this {@link MedicalResource}.
237      *
238      * @hide
239      */
getLastModifiedTimestamp()240     public long getLastModifiedTimestamp() {
241         return mLastModifiedTimestamp;
242     }
243 
244     @Override
describeContents()245     public int describeContents() {
246         return 0;
247     }
248 
249     @Override
writeToParcel(@onNull Parcel dest, int flags)250     public void writeToParcel(@NonNull Parcel dest, int flags) {
251         requireNonNull(dest);
252         dest.writeInt(getType());
253         dest.writeString(getDataSourceId());
254         dest.writeParcelable(getFhirVersion(), 0);
255         dest.writeParcelable(getFhirResource(), 0);
256     }
257 
258     /**
259      * Valid set of values for this IntDef. Update this set when add new type or deprecate existing
260      * type.
261      *
262      * @hide
263      */
264     public static final Set<Integer> VALID_TYPES =
265             Set.of(
266                     MEDICAL_RESOURCE_TYPE_ALLERGIES_INTOLERANCES,
267                     MEDICAL_RESOURCE_TYPE_CONDITIONS,
268                     MEDICAL_RESOURCE_TYPE_LABORATORY_RESULTS,
269                     MEDICAL_RESOURCE_TYPE_MEDICATIONS,
270                     MEDICAL_RESOURCE_TYPE_PERSONAL_DETAILS,
271                     MEDICAL_RESOURCE_TYPE_PRACTITIONER_DETAILS,
272                     MEDICAL_RESOURCE_TYPE_PREGNANCY,
273                     MEDICAL_RESOURCE_TYPE_PROCEDURES,
274                     MEDICAL_RESOURCE_TYPE_SOCIAL_HISTORY,
275                     MEDICAL_RESOURCE_TYPE_VACCINES,
276                     MEDICAL_RESOURCE_TYPE_VISITS,
277                     MEDICAL_RESOURCE_TYPE_VITAL_SIGNS);
278 
279     /**
280      * Validates the provided {@code medicalResourceType} is in the {@link
281      * MedicalResource#VALID_TYPES} set.
282      *
283      * <p>Throws {@link IllegalArgumentException} if not.
284      *
285      * @hide
286      */
validateMedicalResourceType(@edicalResourceType int medicalResourceType)287     public static void validateMedicalResourceType(@MedicalResourceType int medicalResourceType) {
288         validateIntDefValue(
289                 medicalResourceType, VALID_TYPES, MedicalResourceType.class.getSimpleName());
290     }
291 
292     @Override
equals(Object o)293     public boolean equals(Object o) {
294         if (this == o) return true;
295         if (!(o instanceof MedicalResource that)) return false;
296         return getType() == that.getType()
297                 && getDataSourceId().equals(that.getDataSourceId())
298                 && getFhirVersion().equals(that.getFhirVersion())
299                 && getFhirResource().equals(that.getFhirResource());
300     }
301 
302     @Override
hashCode()303     public int hashCode() {
304         return hash(getType(), getDataSourceId(), getFhirVersion(), getFhirResource());
305     }
306 
307     @Override
toString()308     public String toString() {
309         StringBuilder sb = new StringBuilder();
310         sb.append(this.getClass().getSimpleName()).append("{");
311         sb.append("type=").append(getType());
312         sb.append(",dataSourceId=").append(getDataSourceId());
313         sb.append(",fhirVersion=").append(getFhirVersion());
314         sb.append(",fhirResource=").append(getFhirResource());
315         sb.append("}");
316         return sb.toString();
317     }
318 
319     /** Builder class for {@link MedicalResource}. */
320     public static final class Builder {
321         @MedicalResourceType private int mType;
322         @NonNull private String mDataSourceId;
323         @NonNull private FhirVersion mFhirVersion;
324         @NonNull private FhirResource mFhirResource;
325 
326         /**
327          * Constructs a new {@link MedicalResource.Builder} instance.
328          *
329          * @param type The medical resource type.
330          * @param dataSourceId The unique {@link MedicalDataSource} ID of where the data comes from.
331          * @param fhirVersion the FHIR version being used for {@code fhirResource}.
332          * @param fhirResource The enclosed {@link FhirResource} object.
333          * @throws IllegalArgumentException if the provided medical resource {@code type} is not a
334          *     valid supported type, or {@code dataSourceId} is not a valid ID.
335          */
Builder( @edicalResourceType int type, @NonNull String dataSourceId, @NonNull FhirVersion fhirVersion, @NonNull FhirResource fhirResource)336         public Builder(
337                 @MedicalResourceType int type,
338                 @NonNull String dataSourceId,
339                 @NonNull FhirVersion fhirVersion,
340                 @NonNull FhirResource fhirResource) {
341             requireNonNull(dataSourceId);
342             requireNonNull(fhirVersion);
343             requireNonNull(fhirResource);
344             validateMedicalResourceType(type);
345             validateMedicalDataSourceIds(Set.of(dataSourceId));
346 
347             mType = type;
348             mDataSourceId = dataSourceId;
349             mFhirVersion = fhirVersion;
350             mFhirResource = fhirResource;
351         }
352 
353         /** Constructs a clone of the other {@link MedicalResource.Builder}. */
Builder(@onNull Builder other)354         public Builder(@NonNull Builder other) {
355             requireNonNull(other);
356             mType = other.mType;
357             mDataSourceId = other.mDataSourceId;
358             mFhirVersion = other.mFhirVersion;
359             mFhirResource = other.mFhirResource;
360         }
361 
362         /** Constructs a clone of the other {@link MedicalResource} instance. */
Builder(@onNull MedicalResource other)363         public Builder(@NonNull MedicalResource other) {
364             requireNonNull(other);
365             mType = other.getType();
366             mDataSourceId = other.getDataSourceId();
367             mFhirVersion = other.getFhirVersion();
368             mFhirResource = other.getFhirResource();
369         }
370 
371         /**
372          * Sets the medical resource type.
373          *
374          * @throws IllegalArgumentException if the provided medical resource {@code type} is not a
375          *     valid supported type.
376          */
377         @NonNull
setType(@edicalResourceType int type)378         public Builder setType(@MedicalResourceType int type) {
379             validateMedicalResourceType(type);
380             mType = type;
381             return this;
382         }
383 
384         /**
385          * Sets the unique {@link MedicalDataSource} ID of where the data comes from.
386          *
387          * @throws IllegalArgumentException if the provided {@code dataSourceId} is not a valid ID.
388          */
389         @NonNull
setDataSourceId(@onNull String dataSourceId)390         public Builder setDataSourceId(@NonNull String dataSourceId) {
391             requireNonNull(dataSourceId);
392             validateMedicalDataSourceIds(Set.of(dataSourceId));
393             mDataSourceId = dataSourceId;
394             return this;
395         }
396 
397         /** Sets the FHIR version being used for {@code fhirResource}. */
398         @NonNull
setFhirVersion(@onNull FhirVersion fhirVersion)399         public Builder setFhirVersion(@NonNull FhirVersion fhirVersion) {
400             requireNonNull(fhirVersion);
401             mFhirVersion = fhirVersion;
402             return this;
403         }
404 
405         /** Sets the enclosed {@link FhirResource} object. */
406         @NonNull
setFhirResource(@onNull FhirResource fhirResource)407         public Builder setFhirResource(@NonNull FhirResource fhirResource) {
408             requireNonNull(fhirResource);
409             mFhirResource = fhirResource;
410             return this;
411         }
412 
413         /** Returns a new instance of {@link MedicalResource} with the specified parameters. */
414         @NonNull
build()415         public MedicalResource build() {
416             return new MedicalResource(mType, mDataSourceId, mFhirVersion, mFhirResource);
417         }
418     }
419 }
420