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