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 59 @SuppressWarnings("NullAway.Init") // TODO(b/317029272): fix this suppression RecordInternal()60 RecordInternal() { 61 Identifier annotation = this.getClass().getAnnotation(Identifier.class); 62 Objects.requireNonNull(annotation); 63 mRecordIdentifier = annotation.recordIdentifier(); 64 } 65 66 @RecordTypeIdentifier.RecordType getRecordType()67 public int getRecordType() { 68 return mRecordIdentifier; 69 } 70 71 /** 72 * Populates self with the data present in {@code parcel}. Reads should be in the same order as 73 * write 74 */ populateUsing(@onNull Parcel parcel)75 public final void populateUsing(@NonNull Parcel parcel) { 76 String uuidString = parcel.readString(); 77 if (uuidString != null && !uuidString.isEmpty()) { 78 mUuid = UUID.fromString(uuidString); 79 } 80 mPackageName = parcel.readString(); 81 mAppName = parcel.readString(); 82 mLastModifiedTime = parcel.readLong(); 83 mClientRecordId = parcel.readString(); 84 mClientRecordVersion = parcel.readLong(); 85 mManufacturer = parcel.readString(); 86 mModel = parcel.readString(); 87 mDeviceType = parcel.readInt(); 88 mRecordingMethod = parcel.readInt(); 89 90 populateRecordFrom(parcel); 91 } 92 93 /** 94 * Populates {@code parcel} with the self information, required to reconstructor this object 95 * during IPC 96 */ 97 @NonNull writeToParcel(@onNull Parcel parcel)98 public final void writeToParcel(@NonNull Parcel parcel) { 99 parcel.writeString(mUuid == null ? "" : mUuid.toString()); 100 parcel.writeString(mPackageName); 101 parcel.writeString(mAppName); 102 parcel.writeLong(mLastModifiedTime); 103 parcel.writeString(mClientRecordId); 104 parcel.writeLong(mClientRecordVersion); 105 parcel.writeString(mManufacturer); 106 parcel.writeString(mModel); 107 parcel.writeInt(mDeviceType); 108 parcel.writeInt(mRecordingMethod); 109 110 populateRecordTo(parcel); 111 } 112 113 @Nullable getUuid()114 public UUID getUuid() { 115 return mUuid; 116 } 117 118 @SuppressWarnings("NullAway") // TODO(b/317029272): fix this suppression 119 @NonNull setUuid(@ullable UUID uuid)120 public RecordInternal<T> setUuid(@Nullable UUID uuid) { 121 this.mUuid = uuid; 122 return this; 123 } 124 125 @SuppressWarnings("NullAway") // TODO(b/317029272): fix this suppression 126 @NonNull setUuid(@ullable String uuid)127 public RecordInternal<T> setUuid(@Nullable String uuid) { 128 if (uuid == null || uuid.isEmpty()) { 129 mUuid = null; 130 return this; 131 } 132 133 mUuid = UUID.fromString(uuid); 134 return this; 135 } 136 137 @Nullable getPackageName()138 public String getPackageName() { 139 return mPackageName; 140 } 141 142 @SuppressWarnings("NullAway") // TODO(b/317029272): fix this suppression 143 @NonNull setPackageName(@ullable String packageName)144 public RecordInternal<T> setPackageName(@Nullable 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 * Populate {@code bundle} with the data required to un-bundle self. This is used suring IPC 307 * transmissions 308 */ populateRecordTo(@onNull Parcel bundle)309 abstract void populateRecordTo(@NonNull Parcel bundle); 310 311 /** 312 * Child class must implement this method and populates itself with the data present in {@code 313 * bundle} 314 */ populateRecordFrom(@onNull Parcel bundle)315 abstract void populateRecordFrom(@NonNull Parcel bundle); 316 } 317