1 /*
2  * Copyright (C) 2019 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 package com.android.quickstep.util;
17 
18 import android.util.Log;
19 
20 import com.android.systemui.shared.recents.model.Task.TaskKey;
21 
22 import java.util.LinkedHashMap;
23 import java.util.Map;
24 import java.util.function.Predicate;
25 
26 /**
27  * A simple LRU cache for task key entries
28  * @param <V> The type of the value
29  */
30 public class TaskKeyLruCache<V> implements TaskKeyCache<V> {
31 
32     private final MyLinkedHashMap<V> mMap;
33 
TaskKeyLruCache(int maxSize)34     public TaskKeyLruCache(int maxSize) {
35         mMap = new MyLinkedHashMap<>(maxSize);
36     }
37 
38     /**
39      * Removes all entries from the cache
40      */
evictAll()41     public synchronized void evictAll() {
42         mMap.clear();
43     }
44 
45     /**
46      * Removes a particular entry from the cache
47      */
remove(TaskKey key)48     public synchronized void remove(TaskKey key) {
49         mMap.remove(key.id);
50     }
51 
52     /**
53      * Removes all entries matching keyCheck
54      */
removeAll(Predicate<TaskKey> keyCheck)55     public synchronized void removeAll(Predicate<TaskKey> keyCheck) {
56         mMap.entrySet().removeIf(e -> keyCheck.test(e.getValue().mKey));
57     }
58 
59     /**
60      * Gets the entry if it is still valid
61      */
getAndInvalidateIfModified(TaskKey key)62     public synchronized V getAndInvalidateIfModified(TaskKey key) {
63         Entry<V> entry = mMap.get(key.id);
64 
65         if (entry != null && entry.mKey.windowingMode == key.windowingMode
66                 && entry.mKey.lastActiveTime == key.lastActiveTime) {
67             return entry.mValue;
68         } else {
69             remove(key);
70             return null;
71         }
72     }
73 
74     /**
75      * Adds an entry to the cache, optionally evicting the last accessed entry
76      */
put(TaskKey key, V value)77     public final synchronized void put(TaskKey key, V value) {
78         if (key != null && value != null) {
79             mMap.put(key.id, new Entry<>(key, value));
80         } else {
81             Log.e("TaskKeyCache", "Unexpected null key or value: " + key + ", " + value);
82         }
83     }
84 
85     /**
86      * Updates the cache entry if it is already present in the cache
87      */
updateIfAlreadyInCache(int taskId, V data)88     public synchronized void updateIfAlreadyInCache(int taskId, V data) {
89         Entry<V> entry = mMap.get(taskId);
90         if (entry != null) {
91             entry.mValue = data;
92         }
93     }
94 
95     @Override
getMaxSize()96     public int getMaxSize() {
97         return mMap.mMaxSize;
98     }
99 
100     @Override
getSize()101     public int getSize() {
102         return mMap.size();
103     }
104 
105     private static class MyLinkedHashMap<V> extends LinkedHashMap<Integer, Entry<V>> {
106 
107         private final int mMaxSize;
108 
MyLinkedHashMap(int maxSize)109         MyLinkedHashMap(int maxSize) {
110             super(0, 0.75f, true /* accessOrder */);
111             mMaxSize = maxSize;
112         }
113 
114         @Override
removeEldestEntry(Map.Entry<Integer, TaskKeyLruCache.Entry<V>> eldest)115         protected boolean removeEldestEntry(Map.Entry<Integer, TaskKeyLruCache.Entry<V>> eldest) {
116             return size() > mMaxSize;
117         }
118     }
119 }
120