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