1 /* 2 * Copyright (C) 2023 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.internal.datatypes; 18 19 import static android.health.connect.Constants.DEFAULT_INT; 20 import static android.health.connect.Constants.DEFAULT_LONG; 21 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.health.connect.datatypes.DataOrigin; 25 import android.health.connect.datatypes.Device; 26 import android.health.connect.datatypes.Identifier; 27 import android.health.connect.datatypes.Metadata; 28 import android.health.connect.datatypes.Record; 29 import android.health.connect.datatypes.RecordTypeIdentifier; 30 import android.os.Parcel; 31 32 import java.time.Instant; 33 import java.time.LocalDate; 34 import java.util.Objects; 35 import java.util.UUID; 36 37 /** 38 * Base class for all health connect datatype records. 39 * 40 * @hide 41 */ 42 public abstract class RecordInternal<T extends Record> { 43 private final int mRecordIdentifier; 44 private UUID mUuid; 45 private String mPackageName; 46 private String mAppName; 47 private long mLastModifiedTime = DEFAULT_LONG; 48 private String mClientRecordId; 49 private long mClientRecordVersion = DEFAULT_LONG; 50 private String mManufacturer; 51 private String mModel; 52 private int mDeviceType; 53 private long mDeviceInfoId = DEFAULT_LONG; 54 private long mAppInfoId = DEFAULT_LONG; 55 private int mRowId = DEFAULT_INT; 56 57 @Metadata.RecordingMethod private int mRecordingMethod; 58 RecordInternal()59 RecordInternal() { 60 Identifier annotation = this.getClass().getAnnotation(Identifier.class); 61 Objects.requireNonNull(annotation); 62 mRecordIdentifier = annotation.recordIdentifier(); 63 } 64 65 @RecordTypeIdentifier.RecordType getRecordType()66 public int getRecordType() { 67 return mRecordIdentifier; 68 } 69 70 /** 71 * Populates self with the data present in {@code parcel}. Reads should be in the same order as 72 * write 73 */ populateUsing(@onNull Parcel parcel)74 public final void populateUsing(@NonNull Parcel parcel) { 75 String uuidString = parcel.readString(); 76 if (uuidString != null && !uuidString.isEmpty()) { 77 mUuid = UUID.fromString(uuidString); 78 } 79 mPackageName = parcel.readString(); 80 mAppName = parcel.readString(); 81 mLastModifiedTime = parcel.readLong(); 82 mClientRecordId = parcel.readString(); 83 mClientRecordVersion = parcel.readLong(); 84 mManufacturer = parcel.readString(); 85 mModel = parcel.readString(); 86 mDeviceType = parcel.readInt(); 87 mRecordingMethod = parcel.readInt(); 88 89 populateRecordFrom(parcel); 90 } 91 92 /** 93 * Populates {@code parcel} with the self information, required to reconstructor this object 94 * during IPC 95 */ 96 @NonNull writeToParcel(@onNull Parcel parcel)97 public final void writeToParcel(@NonNull Parcel parcel) { 98 parcel.writeString(mUuid == null ? "" : mUuid.toString()); 99 parcel.writeString(mPackageName); 100 parcel.writeString(mAppName); 101 parcel.writeLong(mLastModifiedTime); 102 parcel.writeString(mClientRecordId); 103 parcel.writeLong(mClientRecordVersion); 104 parcel.writeString(mManufacturer); 105 parcel.writeString(mModel); 106 parcel.writeInt(mDeviceType); 107 parcel.writeInt(mRecordingMethod); 108 109 populateRecordTo(parcel); 110 } 111 112 @Nullable getUuid()113 public UUID getUuid() { 114 return mUuid; 115 } 116 117 @NonNull setUuid(@ullable UUID uuid)118 public RecordInternal<T> setUuid(@Nullable UUID uuid) { 119 this.mUuid = uuid; 120 return this; 121 } 122 123 @NonNull setUuid(@ullable String uuid)124 public RecordInternal<T> setUuid(@Nullable String uuid) { 125 if (uuid == null || uuid.isEmpty()) { 126 mUuid = null; 127 return this; 128 } 129 130 mUuid = UUID.fromString(uuid); 131 return this; 132 } 133 134 @Nullable getPackageName()135 public String getPackageName() { 136 return mPackageName; 137 } 138 139 @NonNull setPackageName(@ullable String packageName)140 public RecordInternal<T> setPackageName(@Nullable String packageName) { 141 this.mPackageName = packageName; 142 return this; 143 } 144 145 /** Gets row id of this record. */ getRowId()146 public int getRowId() { 147 return mRowId; 148 } 149 150 /** Sets the row id for this record. */ setRowId(int rowId)151 public RecordInternal<T> setRowId(int rowId) { 152 mRowId = rowId; 153 return this; 154 } 155 156 /** 157 * Returns an application name associated with this record. Currently, it is used for AppInfo 158 * generation when inserting a record. May be {@code null}, in which case the app name may be 159 * missing in AppInfo. 160 */ 161 @Nullable getAppName()162 public String getAppName() { 163 return mAppName; 164 } 165 166 /** Sets the application name for this record. */ 167 @NonNull setAppName(@ullable String appName)168 public RecordInternal<T> setAppName(@Nullable String appName) { 169 mAppName = appName; 170 return this; 171 } 172 getLastModifiedTime()173 public long getLastModifiedTime() { 174 return mLastModifiedTime; 175 } 176 177 @NonNull setLastModifiedTime(long lastModifiedTime)178 public RecordInternal<T> setLastModifiedTime(long lastModifiedTime) { 179 this.mLastModifiedTime = lastModifiedTime; 180 return this; 181 } 182 183 @Nullable getClientRecordId()184 public String getClientRecordId() { 185 return mClientRecordId; 186 } 187 188 @NonNull setClientRecordId(@ullable String clientRecordId)189 public RecordInternal<T> setClientRecordId(@Nullable String clientRecordId) { 190 this.mClientRecordId = clientRecordId; 191 return this; 192 } 193 getClientRecordVersion()194 public long getClientRecordVersion() { 195 return mClientRecordVersion; 196 } 197 198 @NonNull setClientRecordVersion(long clientRecordVersion)199 public RecordInternal<T> setClientRecordVersion(long clientRecordVersion) { 200 this.mClientRecordVersion = clientRecordVersion; 201 return this; 202 } 203 204 @Nullable getManufacturer()205 public String getManufacturer() { 206 return mManufacturer; 207 } 208 209 @NonNull setManufacturer(@ullable String manufacturer)210 public RecordInternal<T> setManufacturer(@Nullable String manufacturer) { 211 this.mManufacturer = manufacturer; 212 return this; 213 } 214 215 @Nullable getModel()216 public String getModel() { 217 return mModel; 218 } 219 220 @NonNull setModel(@ullable String model)221 public RecordInternal<T> setModel(@Nullable String model) { 222 this.mModel = model; 223 return this; 224 } 225 226 @Device.DeviceType getDeviceType()227 public int getDeviceType() { 228 return mDeviceType; 229 } 230 231 @NonNull setDeviceType(@evice.DeviceType int deviceType)232 public RecordInternal<T> setDeviceType(@Device.DeviceType int deviceType) { 233 this.mDeviceType = deviceType; 234 return this; 235 } 236 getDeviceInfoId()237 public long getDeviceInfoId() { 238 return mDeviceInfoId; 239 } 240 241 @NonNull setDeviceInfoId(long deviceInfoId)242 public RecordInternal<T> setDeviceInfoId(long deviceInfoId) { 243 this.mDeviceInfoId = deviceInfoId; 244 return this; 245 } 246 getAppInfoId()247 public long getAppInfoId() { 248 return mAppInfoId; 249 } 250 251 @NonNull setAppInfoId(long appInfoId)252 public RecordInternal<T> setAppInfoId(long appInfoId) { 253 this.mAppInfoId = appInfoId; 254 return this; 255 } 256 257 /** Returns recording method which indicates how data was recorded for the {@link Record} */ 258 @Metadata.RecordingMethod getRecordingMethod()259 public int getRecordingMethod() { 260 return mRecordingMethod; 261 } 262 263 /** Sets Recording method to know how data was recorded for the {@link Record} */ 264 @NonNull setRecordingMethod(@etadata.RecordingMethod int recordingMethod)265 public RecordInternal<T> setRecordingMethod(@Metadata.RecordingMethod int recordingMethod) { 266 this.mRecordingMethod = recordingMethod; 267 return this; 268 } 269 270 /** Child class must implement this method and return an external record for this record */ toExternalRecord()271 public abstract T toExternalRecord(); 272 273 @NonNull buildMetaData()274 Metadata buildMetaData() { 275 return new Metadata.Builder() 276 .setClientRecordId(getClientRecordId()) 277 .setClientRecordVersion(getClientRecordVersion()) 278 .setDataOrigin(new DataOrigin.Builder().setPackageName(getPackageName()).build()) 279 .setId(getUuid() == null ? null : getUuid().toString()) 280 .setLastModifiedTime(Instant.ofEpochMilli(getLastModifiedTime())) 281 .setRecordingMethod(getRecordingMethod()) 282 .setDevice( 283 new Device.Builder() 284 .setManufacturer(getManufacturer()) 285 .setType(getDeviceType()) 286 .setModel(getModel()) 287 .build()) 288 .build(); 289 } 290 291 /** 292 * @return the {@link LocalDate} object of this activity start time. 293 */ getLocalDate()294 public abstract LocalDate getLocalDate(); 295 296 /** 297 * Populate {@code bundle} with the data required to un-bundle self. This is used suring IPC 298 * transmissions 299 */ populateRecordTo(@onNull Parcel bundle)300 abstract void populateRecordTo(@NonNull Parcel bundle); 301 302 /** 303 * Child class must implement this method and populates itself with the data present in {@code 304 * bundle} 305 */ populateRecordFrom(@onNull Parcel bundle)306 abstract void populateRecordFrom(@NonNull Parcel bundle); 307 } 308