1 /*
2  * Copyright (C) 2021 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.systemui.util.concurrency;
18 
19 import java.util.ArrayList;
20 import java.util.HashMap;
21 import java.util.List;
22 import java.util.Map;
23 
24 /**
25  * Implementation of {@link MessageRouter}.
26  */
27 public class MessageRouterImpl implements MessageRouter {
28 
29     private final DelayableExecutor mDelayableExecutor;
30 
31     private final Map<Integer, List<Runnable>> mIdMessageCancelers = new HashMap<>();
32     private final Map<Class<Object>, List<Runnable>> mDataMessageCancelers = new HashMap<>();
33     private final Map<Integer, List<SimpleMessageListener>> mSimpleMessageListenerMap =
34             new HashMap<>();
35     private final Map<Class<?>, List<DataMessageListener<Object>>> mDataMessageListenerMap =
36             new HashMap<>();
37 
MessageRouterImpl(DelayableExecutor delayableExecutor)38     public MessageRouterImpl(DelayableExecutor delayableExecutor) {
39         mDelayableExecutor = delayableExecutor;
40     }
41 
42     @Override
sendMessageDelayed(int id, long delayMs)43     public void sendMessageDelayed(int id, long delayMs) {
44         addCanceler(id, mDelayableExecutor.executeDelayed(() -> onMessage(id), delayMs));
45     }
46 
47     @Override
sendMessageDelayed(Object data, long delayMs)48     public void sendMessageDelayed(Object data, long delayMs) {
49         addCanceler((Class<Object>) data.getClass(), mDelayableExecutor.executeDelayed(
50                 () -> onMessage(data), delayMs));
51     }
52 
53     @Override
cancelMessages(int id)54     public void cancelMessages(int id) {
55         synchronized (mIdMessageCancelers) {
56             if (mIdMessageCancelers.containsKey(id)) {
57                 for (Runnable canceler : mIdMessageCancelers.get(id)) {
58                     canceler.run();
59                 }
60                 // Remove, don't clear, otherwise this could look like a memory leak as
61                 // more and more unique message ids are passed in.
62                 mIdMessageCancelers.remove(id);
63             }
64         }
65     }
66 
67     @Override
cancelMessages(Class<T> messageType)68     public <T> void cancelMessages(Class<T> messageType) {
69         synchronized (mDataMessageCancelers) {
70             if (mDataMessageCancelers.containsKey(messageType)) {
71                 for (Runnable canceler : mDataMessageCancelers.get(messageType)) {
72                     canceler.run();
73                 }
74                 // Remove, don't clear, otherwise this could look like a memory leak as
75                 // more and more unique message types are passed in.
76                 mDataMessageCancelers.remove(messageType);
77             }
78         }
79     }
80 
81     @Override
subscribeTo(int id, SimpleMessageListener listener)82     public void subscribeTo(int id, SimpleMessageListener listener) {
83         synchronized (mSimpleMessageListenerMap) {
84             mSimpleMessageListenerMap.putIfAbsent(id, new ArrayList<>());
85             mSimpleMessageListenerMap.get(id).add(listener);
86         }
87     }
88 
89     @Override
subscribeTo(Class<T> messageType, DataMessageListener<T> listener)90     public <T> void subscribeTo(Class<T> messageType, DataMessageListener<T> listener) {
91         synchronized (mDataMessageListenerMap) {
92             mDataMessageListenerMap.putIfAbsent(messageType, new ArrayList<>());
93             mDataMessageListenerMap.get(messageType).add((DataMessageListener<Object>) listener);
94         }
95     }
96 
97     @Override
unsubscribeFrom(int id, SimpleMessageListener listener)98     public void unsubscribeFrom(int id, SimpleMessageListener listener) {
99         synchronized (mSimpleMessageListenerMap) {
100             if (mSimpleMessageListenerMap.containsKey(id)) {
101                 mSimpleMessageListenerMap.get(id).remove(listener);
102             }
103         }
104     }
105 
106     @Override
unsubscribeFrom(Class<T> messageType, DataMessageListener<T> listener)107     public <T> void unsubscribeFrom(Class<T> messageType, DataMessageListener<T> listener) {
108         synchronized (mDataMessageListenerMap) {
109             if (mDataMessageListenerMap.containsKey(messageType)) {
110                 mDataMessageListenerMap.get(messageType).remove(listener);
111             }
112         }
113     }
114 
115     @Override
unsubscribeFrom(SimpleMessageListener listener)116     public void unsubscribeFrom(SimpleMessageListener listener) {
117         synchronized (mSimpleMessageListenerMap) {
118             for (Integer id : mSimpleMessageListenerMap.keySet()) {
119                 mSimpleMessageListenerMap.get(id).remove(listener);
120             }
121         }
122     }
123 
124     @Override
unsubscribeFrom(DataMessageListener<T> listener)125     public <T> void unsubscribeFrom(DataMessageListener<T> listener) {
126         synchronized (mDataMessageListenerMap) {
127             for (Class<?> messageType : mDataMessageListenerMap.keySet()) {
128                 mDataMessageListenerMap.get(messageType).remove(listener);
129             }
130         }
131     }
132 
addCanceler(int id, Runnable canceler)133     private void addCanceler(int id, Runnable canceler) {
134         synchronized (mIdMessageCancelers) {
135             mIdMessageCancelers.putIfAbsent(id, new ArrayList<>());
136             mIdMessageCancelers.get(id).add(canceler);
137         }
138     }
139 
addCanceler(Class<Object> data, Runnable canceler)140     private void addCanceler(Class<Object> data, Runnable canceler) {
141         synchronized (mDataMessageCancelers) {
142             mDataMessageCancelers.putIfAbsent(data, new ArrayList<>());
143             mDataMessageCancelers.get(data).add(canceler);
144         }
145     }
146 
onMessage(int id)147     private void onMessage(int id) {
148         synchronized (mSimpleMessageListenerMap) {
149             if (mSimpleMessageListenerMap.containsKey(id)) {
150                 for (SimpleMessageListener listener : mSimpleMessageListenerMap.get(id)) {
151                     listener.onMessage(id);
152                 }
153             }
154         }
155 
156         synchronized (mIdMessageCancelers) {
157             if (mIdMessageCancelers.containsKey(id) && !mIdMessageCancelers.get(id).isEmpty()) {
158                 mIdMessageCancelers.get(id).remove(0);
159                 if (mIdMessageCancelers.get(id).isEmpty()) {
160                     mIdMessageCancelers.remove(id);
161                 }
162             }
163         }
164     }
165 
onMessage(Object data)166     private void onMessage(Object data) {
167         synchronized (mDataMessageListenerMap) {
168             if (mDataMessageListenerMap.containsKey(data.getClass())) {
169                 for (DataMessageListener<Object> listener : mDataMessageListenerMap.get(
170                         data.getClass())) {
171                     listener.onMessage(data);
172                 }
173             }
174         }
175 
176         synchronized (mDataMessageCancelers) {
177             if (mDataMessageCancelers.containsKey(data.getClass())
178                     && !mDataMessageCancelers.get(data.getClass()).isEmpty()) {
179                 mDataMessageCancelers.get(data.getClass()).remove(0);
180                 if (mDataMessageCancelers.get(data.getClass()).isEmpty()) {
181                     mDataMessageCancelers.remove(data.getClass());
182                 }
183             }
184         }
185     }
186 }
187