• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.server.healthconnect.storage.datatypehelpers.aggregation;
18 
19 import static com.android.server.healthconnect.storage.datatypehelpers.IntervalRecordHelper.END_TIME_COLUMN_NAME;
20 import static com.android.server.healthconnect.storage.datatypehelpers.IntervalRecordHelper.LOCAL_DATE_TIME_END_TIME_COLUMN_NAME;
21 import static com.android.server.healthconnect.storage.datatypehelpers.IntervalRecordHelper.LOCAL_DATE_TIME_START_TIME_COLUMN_NAME;
22 import static com.android.server.healthconnect.storage.datatypehelpers.IntervalRecordHelper.START_TIME_COLUMN_NAME;
23 import static com.android.server.healthconnect.storage.datatypehelpers.IntervalRecordHelper.START_ZONE_OFFSET_COLUMN_NAME;
24 import static com.android.server.healthconnect.storage.datatypehelpers.RecordHelper.APP_INFO_ID_COLUMN_NAME;
25 import static com.android.server.healthconnect.storage.datatypehelpers.RecordHelper.LAST_MODIFIED_TIME_COLUMN_NAME;
26 import static com.android.server.healthconnect.storage.datatypehelpers.RecordHelper.UUID_COLUMN_NAME;
27 
28 import android.database.Cursor;
29 
30 import com.android.internal.annotations.VisibleForTesting;
31 import com.android.server.healthconnect.storage.utils.StorageUtils;
32 
33 import java.time.ZoneOffset;
34 import java.util.Map;
35 import java.util.UUID;
36 
37 /**
38  * Represents priority aggregation data.
39  *
40  * @hide
41  */
42 public abstract class AggregationRecordData implements Comparable<AggregationRecordData> {
43     private long mRecordStartTime;
44     private long mRecordEndTime;
45     private int mPriority;
46     private long mLastModifiedTime;
47     private ZoneOffset mStartTimeZoneOffset;
48 
getStartTime()49     long getStartTime() {
50         return mRecordStartTime;
51     }
52 
getEndTime()53     long getEndTime() {
54         return mRecordEndTime;
55     }
56 
getPriority()57     int getPriority() {
58         return mPriority;
59     }
60 
getLastModifiedTime()61     long getLastModifiedTime() {
62         return mLastModifiedTime;
63     }
64 
getStartTimeZoneOffset()65     ZoneOffset getStartTimeZoneOffset() {
66         return mStartTimeZoneOffset;
67     }
68 
readUuid(Cursor cursor)69     protected UUID readUuid(Cursor cursor) {
70         return StorageUtils.getCursorUUID(cursor, UUID_COLUMN_NAME);
71     }
72 
populateAggregationData( Cursor cursor, boolean useLocalTime, Map<Long, Integer> appIdToPriority)73     void populateAggregationData(
74             Cursor cursor, boolean useLocalTime, Map<Long, Integer> appIdToPriority) {
75         mRecordStartTime =
76                 StorageUtils.getCursorLong(
77                         cursor,
78                         useLocalTime
79                                 ? LOCAL_DATE_TIME_START_TIME_COLUMN_NAME
80                                 : START_TIME_COLUMN_NAME);
81         mRecordEndTime =
82                 StorageUtils.getCursorLong(
83                         cursor,
84                         useLocalTime ? LOCAL_DATE_TIME_END_TIME_COLUMN_NAME : END_TIME_COLUMN_NAME);
85         mLastModifiedTime = StorageUtils.getCursorLong(cursor, LAST_MODIFIED_TIME_COLUMN_NAME);
86         mStartTimeZoneOffset = StorageUtils.getZoneOffset(cursor, START_ZONE_OFFSET_COLUMN_NAME);
87         mPriority =
88                 appIdToPriority.getOrDefault(
89                         StorageUtils.getCursorLong(cursor, APP_INFO_ID_COLUMN_NAME),
90                         Integer.MIN_VALUE);
91         populateSpecificAggregationData(cursor, useLocalTime);
92     }
93 
getStartTimestamp()94     AggregationTimestamp getStartTimestamp() {
95         return new AggregationTimestamp(AggregationTimestamp.INTERVAL_START, getStartTime())
96                 .setParentData(this);
97     }
98 
getEndTimestamp()99     AggregationTimestamp getEndTimestamp() {
100         return new AggregationTimestamp(AggregationTimestamp.INTERVAL_END, getEndTime())
101                 .setParentData(this);
102     }
103 
104     @VisibleForTesting
setData( long startTime, long endTime, int priority, long lastModifiedTime)105     AggregationRecordData setData(
106             long startTime, long endTime, int priority, long lastModifiedTime) {
107         mRecordStartTime = startTime;
108         mRecordEndTime = endTime;
109         mPriority = priority;
110         mLastModifiedTime = lastModifiedTime;
111         return this;
112     }
113 
114     /**
115      * Calculates aggregation result given start and end time of the target interval. Implementation
116      * may assume that it's will be called with non overlapping intervals. So (start time, end time)
117      * input intervals of all calls will not overlap.
118      */
getResultOnInterval(long startTime, long endTime)119     abstract double getResultOnInterval(long startTime, long endTime);
120 
populateSpecificAggregationData(Cursor cursor, boolean useLocalTime)121     abstract void populateSpecificAggregationData(Cursor cursor, boolean useLocalTime);
122 
123     @Override
toString()124     public String toString() {
125         return "AggregData{startTime=" + mRecordStartTime + ", endTime=" + mRecordEndTime + "}";
126     }
127 
128     /** Calculates overlap between two intervals */
calculateIntervalOverlapDuration( long intervalStart1, long intervalStart2, long intervalEnd1, long intervalEnd2)129     static long calculateIntervalOverlapDuration(
130             long intervalStart1, long intervalStart2, long intervalEnd1, long intervalEnd2) {
131         return Math.max(
132                 Math.min(intervalEnd1, intervalEnd2) - Math.max(intervalStart1, intervalStart2), 0);
133     }
134 
135     @Override
compareTo(AggregationRecordData o)136     public int compareTo(AggregationRecordData o) {
137         if (this.equals(o)) {
138             return 0;
139         }
140 
141         if (mPriority != o.getPriority()) {
142             return Integer.compare(mPriority, o.getPriority());
143         }
144 
145         // The later the last modified time, the higher priority this record has.
146         if (getLastModifiedTime() != o.getLastModifiedTime()) {
147             return Long.compare(getLastModifiedTime(), o.getLastModifiedTime());
148         }
149 
150         if (getStartTime() != o.getStartTime()) {
151             return Long.compare(getStartTime(), o.getStartTime());
152         }
153 
154         if (getEndTime() != o.getEndTime()) {
155             return Long.compare(getEndTime(), o.getEndTime());
156         }
157 
158         return Double.compare(
159                 getResultOnInterval(getStartTime(), getEndTime()),
160                 o.getResultOnInterval(o.getStartTime(), o.getEndTime()));
161     }
162 }
163