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; 18 19 import static android.health.connect.datatypes.MedicalDataSource.validateMedicalDataSourceIds; 20 21 import static com.android.healthfitness.flags.Flags.FLAG_PERSONAL_HEALTH_RECORD; 22 23 import static java.util.Objects.hash; 24 import static java.util.Objects.requireNonNull; 25 26 import android.annotation.FlaggedApi; 27 import android.annotation.NonNull; 28 import android.health.connect.datatypes.FhirVersion; 29 import android.health.connect.datatypes.MedicalDataSource; 30 import android.os.Parcel; 31 import android.os.Parcelable; 32 33 import com.android.healthfitness.flags.Flags; 34 35 import java.util.Set; 36 37 /** 38 * An upsert request for {@link HealthConnectManager#upsertMedicalResources}. 39 * 40 * <p>Medical data is represented using the <a href="https://hl7.org/fhir/">Fast Healthcare 41 * Interoperability Resources (FHIR)</a> standard. 42 */ 43 @FlaggedApi(FLAG_PERSONAL_HEALTH_RECORD) 44 public final class UpsertMedicalResourceRequest implements Parcelable { 45 @NonNull private final String mDataSourceId; 46 @NonNull private final FhirVersion mFhirVersion; 47 @NonNull private final String mData; 48 private int mDataSize; 49 50 @NonNull 51 public static final Creator<UpsertMedicalResourceRequest> CREATOR = 52 new Creator<>() { 53 @Override 54 public UpsertMedicalResourceRequest createFromParcel(Parcel in) { 55 return new UpsertMedicalResourceRequest(in); 56 } 57 58 @Override 59 public UpsertMedicalResourceRequest[] newArray(int size) { 60 return new UpsertMedicalResourceRequest[size]; 61 } 62 }; 63 64 /** 65 * Creates a new instance of {@link UpsertMedicalResourceRequest}. Please see {@link 66 * UpsertMedicalResourceRequest.Builder} for more detailed parameters information. 67 */ UpsertMedicalResourceRequest( @onNull String dataSourceId, @NonNull FhirVersion fhirVersion, @NonNull String data)68 private UpsertMedicalResourceRequest( 69 @NonNull String dataSourceId, @NonNull FhirVersion fhirVersion, @NonNull String data) { 70 requireNonNull(dataSourceId); 71 requireNonNull(fhirVersion); 72 requireNonNull(data); 73 validateMedicalDataSourceIds(Set.of(dataSourceId)); 74 75 mDataSourceId = dataSourceId; 76 mFhirVersion = fhirVersion; 77 mData = data; 78 } 79 UpsertMedicalResourceRequest(@onNull Parcel in)80 private UpsertMedicalResourceRequest(@NonNull Parcel in) { 81 requireNonNull(in); 82 int dataAvailStartPosition = in.dataAvail(); 83 84 mDataSourceId = requireNonNull(in.readString()); 85 validateMedicalDataSourceIds(Set.of(mDataSourceId)); 86 mFhirVersion = 87 requireNonNull( 88 in.readParcelable(FhirVersion.class.getClassLoader(), FhirVersion.class)); 89 mData = requireNonNull(in.readString()); 90 91 if (Flags.phrUpsertFixParcelSizeCalculation()) { 92 mDataSize = dataAvailStartPosition - in.dataAvail(); 93 } else { 94 mDataSize = in.dataSize(); 95 } 96 } 97 98 /** 99 * Returns the unique ID of the existing {@link MedicalDataSource}, to represent where the 100 * {@code data} is coming from. 101 */ 102 @NonNull getDataSourceId()103 public String getDataSourceId() { 104 return mDataSourceId; 105 } 106 107 /** 108 * Returns the FHIR version being used for {@code data}. For the request to succeed this must 109 * match the {@link MedicalDataSource#getFhirVersion()} FHIR version of the {@link 110 * MedicalDataSource} with the provided {@code dataSourceId}. 111 */ 112 @NonNull getFhirVersion()113 public FhirVersion getFhirVersion() { 114 return mFhirVersion; 115 } 116 117 /** Returns the FHIR resource data in JSON representation. */ 118 @NonNull getData()119 public String getData() { 120 return mData; 121 } 122 123 /** 124 * Returns the size of the parcel when the class was created from Parcel. 125 * 126 * @hide 127 */ getDataSize()128 public long getDataSize() { 129 return mDataSize; 130 } 131 132 @Override describeContents()133 public int describeContents() { 134 return 0; 135 } 136 137 @Override writeToParcel(@onNull Parcel dest, int flags)138 public void writeToParcel(@NonNull Parcel dest, int flags) { 139 requireNonNull(dest); 140 dest.writeString(mDataSourceId); 141 dest.writeParcelable(mFhirVersion, 0); 142 dest.writeString(mData); 143 } 144 145 @Override hashCode()146 public int hashCode() { 147 return hash(getDataSourceId(), getFhirVersion(), getData()); 148 } 149 150 @Override equals(Object o)151 public boolean equals(Object o) { 152 if (this == o) return true; 153 if (!(o instanceof UpsertMedicalResourceRequest that)) return false; 154 return getDataSourceId().equals(that.getDataSourceId()) 155 && getFhirVersion().equals(that.getFhirVersion()) 156 && getData().equals(that.getData()); 157 } 158 159 @Override toString()160 public String toString() { 161 StringBuilder sb = new StringBuilder(); 162 sb.append(this.getClass().getSimpleName()).append("{"); 163 sb.append("dataSourceId=").append(mDataSourceId); 164 sb.append(",fhirVersion=").append(mFhirVersion); 165 sb.append(",data=").append(mData); 166 sb.append("}"); 167 return sb.toString(); 168 } 169 170 /** Builder class for {@link UpsertMedicalResourceRequest}. */ 171 public static final class Builder { 172 private String mDataSourceId; 173 private FhirVersion mFhirVersion; 174 private String mData; 175 176 /** 177 * Constructs a new {@link UpsertMedicalResourceRequest.Builder} instance. 178 * 179 * @param dataSourceId The unique identifier of the existing {@link MedicalDataSource}, 180 * representing where the data comes from. 181 * @param fhirVersion The {@link FhirVersion} object that represents the FHIR version being 182 * used for {@code data}. This has to match the FHIR version of the {@link 183 * MedicalDataSource}. 184 * @param data The FHIR resource data in JSON representation. 185 * @throws IllegalArgumentException if the provided {@code dataSourceId} is not a valid ID. 186 */ Builder( @onNull String dataSourceId, @NonNull FhirVersion fhirVersion, @NonNull String data)187 public Builder( 188 @NonNull String dataSourceId, 189 @NonNull FhirVersion fhirVersion, 190 @NonNull String data) { 191 requireNonNull(dataSourceId); 192 requireNonNull(fhirVersion); 193 requireNonNull(data); 194 validateMedicalDataSourceIds(Set.of(dataSourceId)); 195 196 mDataSourceId = dataSourceId; 197 mFhirVersion = fhirVersion; 198 mData = data; 199 } 200 201 /** Constructs a clone of the other {@link UpsertMedicalResourceRequest.Builder}. */ Builder(@onNull Builder other)202 public Builder(@NonNull Builder other) { 203 requireNonNull(other); 204 mDataSourceId = other.mDataSourceId; 205 mFhirVersion = other.mFhirVersion; 206 mData = other.mData; 207 } 208 209 /** Constructs a clone of the other {@link UpsertMedicalResourceRequest} instance. */ Builder(@onNull UpsertMedicalResourceRequest other)210 public Builder(@NonNull UpsertMedicalResourceRequest other) { 211 requireNonNull(other); 212 mDataSourceId = other.getDataSourceId(); 213 mFhirVersion = other.getFhirVersion(); 214 mData = other.getData(); 215 } 216 217 /** 218 * Sets the unique ID of the existing {@link MedicalDataSource}, to represent where the 219 * {@code data} is coming from. 220 * 221 * @throws IllegalArgumentException if the provided {@code dataSourceId} is not a valid ID. 222 */ 223 @NonNull setDataSourceId(@onNull String dataSourceId)224 public Builder setDataSourceId(@NonNull String dataSourceId) { 225 requireNonNull(dataSourceId); 226 validateMedicalDataSourceIds(Set.of(dataSourceId)); 227 mDataSourceId = dataSourceId; 228 return this; 229 } 230 231 /** 232 * Sets the FHIR version being used for {@code data}. For the request to succeed this must 233 * match the {@link MedicalDataSource#getFhirVersion()} FHIR version} of the {@link 234 * MedicalDataSource} with the provided {@code dataSourceId}. 235 */ 236 @NonNull setFhirVersion(@onNull FhirVersion fhirVersion)237 public Builder setFhirVersion(@NonNull FhirVersion fhirVersion) { 238 requireNonNull(fhirVersion); 239 mFhirVersion = fhirVersion; 240 return this; 241 } 242 243 /** Sets the FHIR resource data in JSON format. */ 244 @NonNull setData(@onNull String data)245 public Builder setData(@NonNull String data) { 246 requireNonNull(data); 247 mData = data; 248 return this; 249 } 250 251 /** 252 * Returns a new instance of {@link UpsertMedicalResourceRequest} with the specified 253 * parameters. 254 */ 255 @NonNull build()256 public UpsertMedicalResourceRequest build() { 257 return new UpsertMedicalResourceRequest(mDataSourceId, mFhirVersion, mData); 258 } 259 } 260 } 261