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.datatypes.DataOrigin; 22 import android.health.connect.datatypes.Record; 23 import android.health.connect.datatypes.RecordTypeIdentifier; 24 import android.health.connect.internal.datatypes.utils.RecordMapper; 25 import android.os.Parcel; 26 import android.os.Parcelable; 27 import android.util.ArraySet; 28 29 import java.util.ArrayList; 30 import java.util.Arrays; 31 import java.util.List; 32 import java.util.Objects; 33 import java.util.Set; 34 import java.util.stream.Collectors; 35 36 /** 37 * A class to request changelog token using {@link HealthConnectManager#getChangeLogToken} 38 * 39 * @see HealthConnectManager#getChangeLogToken 40 */ 41 public final class ChangeLogTokenRequest implements Parcelable { 42 private final Set<DataOrigin> mDataOriginFilters; 43 private final Set<Class<? extends Record>> mRecordTypes; 44 45 /** 46 * @param dataOriginFilters list of package names to filter the data 47 * @param recordTypes list of records for which change log is required 48 */ ChangeLogTokenRequest( @onNull Set<DataOrigin> dataOriginFilters, @NonNull Set<Class<? extends Record>> recordTypes)49 private ChangeLogTokenRequest( 50 @NonNull Set<DataOrigin> dataOriginFilters, 51 @NonNull Set<Class<? extends Record>> recordTypes) { 52 Objects.requireNonNull(recordTypes); 53 Objects.requireNonNull(dataOriginFilters); 54 55 mDataOriginFilters = dataOriginFilters; 56 mRecordTypes = recordTypes; 57 } 58 ChangeLogTokenRequest(@onNull Parcel in)59 private ChangeLogTokenRequest(@NonNull Parcel in) { 60 RecordMapper recordMapper = RecordMapper.getInstance(); 61 Set<Class<? extends Record>> recordTypes = new ArraySet<>(); 62 for (@RecordTypeIdentifier.RecordType int recordType : in.createIntArray()) { 63 recordTypes.add(recordMapper.getRecordIdToExternalRecordClassMap().get(recordType)); 64 } 65 mRecordTypes = recordTypes; 66 Set<DataOrigin> dataOrigin = new ArraySet<>(); 67 for (String packageName : in.createStringArrayList()) { 68 dataOrigin.add(new DataOrigin.Builder().setPackageName(packageName).build()); 69 } 70 mDataOriginFilters = dataOrigin; 71 } 72 73 @NonNull 74 public static final Creator<ChangeLogTokenRequest> CREATOR = 75 new Creator<ChangeLogTokenRequest>() { 76 @Override 77 public ChangeLogTokenRequest createFromParcel(@NonNull Parcel in) { 78 return new ChangeLogTokenRequest(in); 79 } 80 81 @Override 82 public ChangeLogTokenRequest[] newArray(int size) { 83 return new ChangeLogTokenRequest[size]; 84 } 85 }; 86 87 /** Returns list of package names corresponding to which the logs are required */ 88 @NonNull getDataOriginFilters()89 public Set<DataOrigin> getDataOriginFilters() { 90 return mDataOriginFilters; 91 } 92 93 /** Returns list of record classes for which the logs are to be fetched */ 94 @NonNull getRecordTypes()95 public Set<Class<? extends Record>> getRecordTypes() { 96 return mRecordTypes; 97 } 98 99 /** 100 * Returns List of Record types for which logs are to be fetched 101 * 102 * @hide 103 */ 104 @NonNull getRecordTypesArray()105 public int[] getRecordTypesArray() { 106 return getRecordTypesAsInteger(); 107 } 108 109 /** 110 * Returns List of Record types for which logs are to be fetched 111 * 112 * @hide 113 */ 114 @NonNull getRecordTypesList()115 public List<Integer> getRecordTypesList() { 116 return Arrays.stream(getRecordTypesAsInteger()).boxed().collect(Collectors.toList()); 117 } 118 119 /** 120 * Returns list of package names corresponding to which the logs are required 121 * 122 * @hide 123 */ 124 @NonNull getPackageNamesToFilter()125 public List<String> getPackageNamesToFilter() { 126 return getPackageNames(); 127 } 128 129 @Override describeContents()130 public int describeContents() { 131 return 0; 132 } 133 134 @Override writeToParcel(@onNull Parcel dest, int flags)135 public void writeToParcel(@NonNull Parcel dest, int flags) { 136 dest.writeIntArray(getRecordTypesAsInteger()); 137 dest.writeStringList(getPackageNames()); 138 } 139 140 @NonNull getRecordTypesAsInteger()141 private int[] getRecordTypesAsInteger() { 142 int[] recordTypes = new int[mRecordTypes.size()]; 143 int index = 0; 144 for (Class<? extends Record> recordClass : mRecordTypes) { 145 recordTypes[index++] = RecordMapper.getInstance().getRecordType(recordClass); 146 } 147 return recordTypes; 148 } 149 150 @NonNull getPackageNames()151 private List<String> getPackageNames() { 152 List<String> packageNamesToFilter = new ArrayList<>(mDataOriginFilters.size()); 153 mDataOriginFilters.forEach( 154 (dataOrigin) -> packageNamesToFilter.add(dataOrigin.getPackageName())); 155 return packageNamesToFilter; 156 } 157 158 /** Builder for {@link ChangeLogTokenRequest} */ 159 public static final class Builder { 160 private final Set<Class<? extends Record>> mRecordTypes = new ArraySet<>(); 161 private final Set<DataOrigin> mDataOriginFilters = new ArraySet<>(); 162 163 /** 164 * @param recordType type of record for which change log is required. If not set includes 165 * all record types 166 */ 167 @NonNull addRecordType(@onNull Class<? extends Record> recordType)168 public Builder addRecordType(@NonNull Class<? extends Record> recordType) { 169 Objects.requireNonNull(recordType); 170 171 mRecordTypes.add(recordType); 172 return this; 173 } 174 175 /** 176 * @param dataOriginFilter list of package names on which to filter the data. 177 * <p>If not set logs from all the sources will be returned 178 */ 179 @NonNull addDataOriginFilter(@onNull DataOrigin dataOriginFilter)180 public Builder addDataOriginFilter(@NonNull DataOrigin dataOriginFilter) { 181 Objects.requireNonNull(dataOriginFilter); 182 183 mDataOriginFilters.add(dataOriginFilter); 184 return this; 185 } 186 187 /** 188 * Returns Object of {@link ChangeLogTokenRequest} 189 * 190 * @throws IllegalArgumentException if record types are empty 191 */ 192 @NonNull build()193 public ChangeLogTokenRequest build() { 194 return new ChangeLogTokenRequest(mDataOriginFilters, mRecordTypes); 195 } 196 } 197 } 198