1 /*
2  * Copyright (C) 2018 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.car.internal;
18 
19 import android.util.SparseArray;
20 
21 import java.util.Collection;
22 import java.util.Collections;
23 import java.util.HashMap;
24 import java.util.Map;
25 
26 /**
27  * Represent listeners for a property grouped by their rate.
28  * T is a type of EventListener such as CarPropertyEventCallback
29  * in {@link android.car.hardware.property.CarPropertyManager}
30  * @param <T>
31  * @hide
32  */
33 public class CarRatedFloatListeners<T> {
34     private static final float NANOSECOND_PER_SECOND = 1000 * 1000 * 1000;
35     private final Map<T, Float> mListenersToRate = new HashMap<>(4);
36 
37     private final Map<T, Long> mListenersUpdateTime = new HashMap<>(4);
38 
39     private float mUpdateRate;
40 
41     // key: areaId, value: lastUpdateTime in nanosecond
42     protected SparseArray<Long> mAreaIdToLastUpdateTime = new SparseArray<>();
43 
CarRatedFloatListeners(float rate)44     protected CarRatedFloatListeners(float rate) {
45         mUpdateRate = rate;
46     }
47 
48     /** Check listener */
contains(T listener)49     public boolean contains(T listener) {
50         return mListenersToRate.containsKey(listener);
51     }
52     /** Return current rate after updating */
getRate()53     public float getRate() {
54         return mUpdateRate;
55     }
56 
57     /**
58      * Remove given listener from the list and update rate if necessary.
59      *
60      * @param listener
61      * @return true if rate was updated. Otherwise, returns false.
62      */
remove(T listener)63     public boolean remove(T listener) {
64         mListenersToRate.remove(listener);
65         mListenersUpdateTime.remove(listener);
66         if (mListenersToRate.isEmpty()) {
67             return false;
68         }
69         Float updateRate = Collections.max(mListenersToRate.values());
70         if (updateRate != mUpdateRate) {
71             mUpdateRate = updateRate;
72             return true;
73         }
74         return false;
75     }
76 
isEmpty()77     public boolean isEmpty() {
78         return mListenersToRate.isEmpty();
79     }
80 
81     /**
82      * Add given listener to the list and update rate if necessary.
83      *
84      * @param listener if null, add part is skipped.
85      * @param updateRate
86      * @return true if rate was updated. Otherwise, returns false.
87      */
addAndUpdateRate(T listener, float updateRate)88     public boolean addAndUpdateRate(T listener, float updateRate) {
89         Float oldUpdateRate = mListenersToRate.put(listener, updateRate);
90         mListenersUpdateTime.put(listener, 0L);
91         if (mUpdateRate < updateRate) {
92             mUpdateRate = updateRate;
93             return true;
94         } else if (oldUpdateRate != null && oldUpdateRate == mUpdateRate) {
95             Float newUpdateRate = Collections.max(mListenersToRate.values());
96             if (newUpdateRate != mUpdateRate) {
97                 mUpdateRate = newUpdateRate;
98                 return true;
99             }
100         }
101         return false;
102     }
103 
104     /**
105      * Check whether listener should be notified by events.
106      *
107      * @param listener
108      * @param eventTimeStamp
109      * @return true if listener need to be notified.
110      */
needUpdateForSelectedListener(T listener, long eventTimeStamp)111     public boolean needUpdateForSelectedListener(T listener, long eventTimeStamp) {
112         Long nextUpdateTime = mListenersUpdateTime.get(listener);
113         Float updateRate = mListenersToRate.get(listener);
114         /** Update ON_CHANGE property. */
115         if (updateRate == 0) {
116             return true;
117         }
118         if (nextUpdateTime <= eventTimeStamp) {
119             Float cycle = NANOSECOND_PER_SECOND / updateRate;
120             nextUpdateTime = eventTimeStamp + cycle.longValue();
121             mListenersUpdateTime.put(listener, nextUpdateTime);
122             return true;
123         }
124         return false;
125     }
126 
127     /**
128      * @param areaId AreaId in CarPropertyValue
129      * @param eventTime TimeStamp in CarPropertyValue
130      * @return true if eventTime is greater than the last event time for the same areaId.
131      */
needUpdateForAreaId(int areaId, long eventTime)132     public boolean needUpdateForAreaId(int areaId, long eventTime) {
133         long lastUpdateTime = mAreaIdToLastUpdateTime.get(areaId, 0L);
134         if (eventTime >= lastUpdateTime) {
135             mAreaIdToLastUpdateTime.put(areaId, eventTime);
136             return true;
137         }
138         return false;
139     }
140 
141 
getListeners()142     public Collection<T> getListeners() {
143         return mListenersToRate.keySet();
144     }
145 }
146 
147