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.changelog; 18 19 import android.annotation.NonNull; 20 import android.health.connect.HealthConnectManager; 21 import android.health.connect.aidl.DeletedLogsParcel; 22 import android.health.connect.aidl.RecordsParcel; 23 import android.health.connect.datatypes.Metadata; 24 import android.health.connect.datatypes.Record; 25 import android.health.connect.internal.datatypes.RecordInternal; 26 import android.health.connect.internal.datatypes.utils.InternalExternalRecordConverter; 27 import android.os.Parcel; 28 import android.os.Parcelable; 29 30 import java.time.Instant; 31 import java.util.ArrayList; 32 import java.util.List; 33 import java.util.Objects; 34 35 /** 36 * Response class for {@link HealthConnectManager#getChangeLogs} This is the response to clients 37 * fetching changes. 38 */ 39 public final class ChangeLogsResponse implements Parcelable { 40 private final List<Record> mUpsertedRecords; 41 private final List<DeletedLog> mDeletedLogs; 42 private final String mNextChangesToken; 43 private final boolean mHasMorePages; 44 45 /** 46 * Response for {@link HealthConnectManager#getChangeLogs}. 47 * 48 * @hide 49 */ ChangeLogsResponse( @onNull RecordsParcel upsertedRecords, @NonNull List<DeletedLog> deletedLogs, @NonNull String nextChangesToken, boolean hasMorePages)50 public ChangeLogsResponse( 51 @NonNull RecordsParcel upsertedRecords, 52 @NonNull List<DeletedLog> deletedLogs, 53 @NonNull String nextChangesToken, 54 boolean hasMorePages) { 55 Objects.requireNonNull(upsertedRecords); 56 Objects.requireNonNull(deletedLogs); 57 Objects.requireNonNull(nextChangesToken); 58 59 mUpsertedRecords = 60 InternalExternalRecordConverter.getInstance() 61 .getExternalRecords(upsertedRecords.getRecords()); 62 mDeletedLogs = deletedLogs; 63 mNextChangesToken = nextChangesToken; 64 mHasMorePages = hasMorePages; 65 } 66 ChangeLogsResponse(Parcel in)67 private ChangeLogsResponse(Parcel in) { 68 mUpsertedRecords = 69 InternalExternalRecordConverter.getInstance() 70 .getExternalRecords( 71 in.readParcelable( 72 RecordsParcel.class.getClassLoader(), 73 RecordsParcel.class) 74 .getRecords()); 75 mDeletedLogs = 76 in.readParcelable(DeletedLogsParcel.class.getClassLoader(), DeletedLogsParcel.class) 77 .getDeletedLogs(); 78 mNextChangesToken = in.readString(); 79 mHasMorePages = in.readBoolean(); 80 } 81 82 @NonNull 83 public static final Creator<ChangeLogsResponse> CREATOR = 84 new Creator<>() { 85 @Override 86 public ChangeLogsResponse createFromParcel(Parcel in) { 87 return new ChangeLogsResponse(in); 88 } 89 90 @Override 91 public ChangeLogsResponse[] newArray(int size) { 92 return new ChangeLogsResponse[size]; 93 } 94 }; 95 96 /** 97 * Returns records that have been updated or inserted post the time when the given token was 98 * generated. 99 * 100 * <p>Clients can use the last modified time of the record to check when the record was 101 * modified. 102 */ 103 @NonNull getUpsertedRecords()104 public List<Record> getUpsertedRecords() { 105 return mUpsertedRecords; 106 } 107 108 /** 109 * Returns delete logs for records that have been deleted post the time when the token was 110 * requested from {@link HealthConnectManager#getChangeLogToken}. 111 * 112 * <p>This contains record ids of deleted records and the timestamps when the records were 113 * deleted. 114 */ 115 @NonNull getDeletedLogs()116 public List<DeletedLog> getDeletedLogs() { 117 return mDeletedLogs; 118 } 119 120 /** Returns token for future reads using {@link HealthConnectManager#getChangeLogs}. */ 121 @NonNull getNextChangesToken()122 public String getNextChangesToken() { 123 return mNextChangesToken; 124 } 125 126 /** Returns whether there are more pages available for read. */ hasMorePages()127 public boolean hasMorePages() { 128 return mHasMorePages; 129 } 130 131 @Override describeContents()132 public int describeContents() { 133 return 0; 134 } 135 136 @Override writeToParcel(@onNull Parcel dest, int flags)137 public void writeToParcel(@NonNull Parcel dest, int flags) { 138 List<RecordInternal<?>> recordInternal = new ArrayList<>(); 139 for (Record record : mUpsertedRecords) { 140 recordInternal.add(record.toRecordInternal()); 141 } 142 dest.writeParcelable(new RecordsParcel(recordInternal), 0); 143 dest.writeParcelable(new DeletedLogsParcel(mDeletedLogs), 0); 144 dest.writeString(mNextChangesToken); 145 dest.writeBoolean(mHasMorePages); 146 } 147 148 /** 149 * A change log holds the {@link Metadata#getId()} of a deleted Record. For privacy, only unique 150 * identifiers of deleted records are returned. 151 * 152 * <p>Clients holding copies of data from Health Connect should keep a copy of these unique 153 * identifiers along with their contents. When receiving a {@link DeletedLog} in {@link 154 * ChangeLogsResponse}, use the identifiers to delete copy of the data. 155 */ 156 public static final class DeletedLog { 157 private final String mDeletedRecordId; 158 private final Instant mDeletedTime; 159 DeletedLog(@onNull String deletedRecordId, long deletedTime)160 public DeletedLog(@NonNull String deletedRecordId, long deletedTime) { 161 Objects.requireNonNull(deletedRecordId); 162 mDeletedRecordId = deletedRecordId; 163 mDeletedTime = Instant.ofEpochMilli(deletedTime); 164 } 165 166 /** Returns record id of the record deleted. */ 167 @NonNull getDeletedRecordId()168 public String getDeletedRecordId() { 169 return mDeletedRecordId; 170 } 171 172 /** Returns timestamp when the record was deleted. */ 173 @NonNull getDeletedTime()174 public Instant getDeletedTime() { 175 return mDeletedTime; 176 } 177 } 178 } 179