1 /* 2 * Copyright (C) 2022 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 com.android.server.healthconnect.storage.request; 18 19 import android.annotation.NonNull; 20 import android.health.connect.AggregateRecordsResponse; 21 import android.health.connect.AggregateResult; 22 import android.health.connect.TimeRangeFilter; 23 import android.health.connect.aidl.AggregateDataRequestParcel; 24 import android.health.connect.aidl.AggregateDataResponseParcel; 25 import android.health.connect.datatypes.AggregationType; 26 import android.health.connect.internal.datatypes.utils.AggregationTypeIdMapper; 27 import android.util.ArrayMap; 28 29 import com.android.server.healthconnect.storage.TransactionManager; 30 import com.android.server.healthconnect.storage.datatypehelpers.RecordHelper; 31 import com.android.server.healthconnect.storage.utils.RecordHelperProvider; 32 33 import java.time.Duration; 34 import java.time.Period; 35 import java.util.ArrayList; 36 import java.util.List; 37 import java.util.Map; 38 import java.util.Objects; 39 40 /** 41 * Refines aggregate request from what the client sent to a format that makes the most sense for the 42 * TransactionManager. 43 * 44 * @hide 45 */ 46 public final class AggregateTransactionRequest { 47 private final String mPackageName; 48 private final List<AggregateTableRequest> mAggregateTableRequests; 49 private final Period mPeriod; 50 private final Duration mDuration; 51 private final TimeRangeFilter mTimeRangeFilter; 52 AggregateTransactionRequest( @onNull String packageName, @NonNull AggregateDataRequestParcel request, long startDateAccess)53 public AggregateTransactionRequest( 54 @NonNull String packageName, 55 @NonNull AggregateDataRequestParcel request, 56 long startDateAccess) { 57 mPackageName = packageName; 58 mAggregateTableRequests = new ArrayList<>(request.getAggregateIds().length); 59 mPeriod = request.getPeriod(); 60 mDuration = request.getDuration(); 61 mTimeRangeFilter = request.getTimeRangeFilter(); 62 63 final AggregationTypeIdMapper aggregationTypeIdMapper = 64 AggregationTypeIdMapper.getInstance(); 65 for (int id : request.getAggregateIds()) { 66 AggregationType<?> aggregationType = aggregationTypeIdMapper.getAggregationTypeFor(id); 67 List<Integer> recordTypeIds = aggregationType.getApplicableRecordTypeIds(); 68 if (recordTypeIds.size() == 1) { 69 RecordHelper<?> recordHelper = 70 RecordHelperProvider.getRecordHelper(recordTypeIds.get(0)); 71 AggregateTableRequest aggregateTableRequest = 72 recordHelper.getAggregateTableRequest( 73 aggregationType, 74 packageName, 75 request.getPackageFilters(), 76 request.getStartTime(), 77 request.getEndTime(), 78 startDateAccess, 79 request.useLocalTimeFilter()); 80 81 if (mDuration != null || mPeriod != null) { 82 aggregateTableRequest.setGroupBy( 83 recordHelper.getDurationGroupByColumnName(), 84 mPeriod, 85 mDuration, 86 mTimeRangeFilter); 87 } 88 mAggregateTableRequests.add(aggregateTableRequest); 89 } else { 90 throw new UnsupportedOperationException(); 91 } 92 } 93 } 94 95 @NonNull getPackageName()96 public String getPackageName() { 97 return mPackageName; 98 } 99 100 /** 101 * @return Compute and return aggregations 102 */ getAggregateDataResponseParcel()103 public AggregateDataResponseParcel getAggregateDataResponseParcel() { 104 Map<AggregationType<?>, List<AggregateResult<?>>> results = new ArrayMap<>(); 105 for (AggregateTableRequest aggregateTableRequest : mAggregateTableRequests) { 106 // Compute aggregations 107 TransactionManager.getInitialisedInstance() 108 .populateWithAggregation(aggregateTableRequest); 109 results.put( 110 aggregateTableRequest.getAggregationType(), 111 aggregateTableRequest.getAggregateResults()); 112 } 113 114 // Convert DB friendly results to aggregateRecordsResponses 115 int responseSize = 116 mAggregateTableRequests.isEmpty() 117 ? 0 118 : mAggregateTableRequests.get(0).getAggregateResults().size(); 119 List<AggregateRecordsResponse<?>> aggregateRecordsResponses = new ArrayList<>(responseSize); 120 for (int i = 0; i < responseSize; i++) { 121 Map<Integer, AggregateResult<?>> aggregateResultMap = new ArrayMap<>(); 122 for (AggregationType<?> aggregationType : results.keySet()) { 123 aggregateResultMap.put( 124 (AggregationTypeIdMapper.getInstance().getIdFor(aggregationType)), 125 Objects.requireNonNull(results.get(aggregationType)).get(i)); 126 } 127 aggregateRecordsResponses.add(new AggregateRecordsResponse<>(aggregateResultMap)); 128 } 129 130 // Create and return parcel 131 AggregateDataResponseParcel aggregateDataResponseParcel = 132 new AggregateDataResponseParcel(aggregateRecordsResponses); 133 if (mPeriod != null) { 134 aggregateDataResponseParcel.setPeriod(mPeriod, mTimeRangeFilter); 135 } else if (mDuration != null) { 136 aggregateDataResponseParcel.setDuration(mDuration, mTimeRangeFilter); 137 } 138 139 return aggregateDataResponseParcel; 140 } 141 } 142