1 /*
2  * Copyright (C) 2015 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.systemui.statusbar.connectivity;
17 
18 import android.os.Handler;
19 import android.os.Looper;
20 import android.os.Message;
21 import android.telephony.SubscriptionInfo;
22 
23 import com.android.internal.annotations.VisibleForTesting;
24 import com.android.systemui.dagger.SysUISingleton;
25 import com.android.systemui.dagger.qualifiers.Main;
26 import com.android.systemui.statusbar.connectivity.NetworkController.EmergencyListener;
27 
28 import java.io.PrintWriter;
29 import java.text.SimpleDateFormat;
30 import java.util.ArrayList;
31 import java.util.List;
32 
33 import javax.inject.Inject;
34 
35 /**
36  * Implements network listeners and forwards the calls along onto other listeners but on
37  * the current or specified Looper.
38  */
39 @SysUISingleton
40 public class CallbackHandler extends Handler implements EmergencyListener, SignalCallback {
41     private static final String TAG = "CallbackHandler";
42     private static final int MSG_EMERGENCE_CHANGED           = 0;
43     private static final int MSG_SUBS_CHANGED                = 1;
44     private static final int MSG_NO_SIM_VISIBLE_CHANGED      = 2;
45     private static final int MSG_ETHERNET_CHANGED            = 3;
46     private static final int MSG_AIRPLANE_MODE_CHANGED       = 4;
47     private static final int MSG_MOBILE_DATA_ENABLED_CHANGED = 5;
48     private static final int MSG_ADD_REMOVE_EMERGENCY        = 6;
49     private static final int MSG_ADD_REMOVE_SIGNAL           = 7;
50     private static final int HISTORY_SIZE = 64;
51     private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
52 
53     // All the callbacks.
54     private final ArrayList<EmergencyListener> mEmergencyListeners = new ArrayList<>();
55     private final ArrayList<SignalCallback> mSignalCallbacks = new ArrayList<>();
56 
57     // Save the previous HISTORY_SIZE states for logging.
58     private final String[] mHistory = new String[HISTORY_SIZE];
59     // Where to copy the next state into.
60     private int mHistoryIndex;
61     private String mLastCallback;
62 
63     @Inject
64     @VisibleForTesting
CallbackHandler(@ain Looper looper)65     CallbackHandler(@Main Looper looper) {
66         super(looper);
67     }
68 
69     @Override
70     @SuppressWarnings("unchecked")
handleMessage(Message msg)71     public void handleMessage(Message msg) {
72         switch (msg.what) {
73             case MSG_EMERGENCE_CHANGED:
74                 for (EmergencyListener listener : mEmergencyListeners) {
75                     listener.setEmergencyCallsOnly(msg.arg1 != 0);
76                 }
77                 break;
78             case MSG_SUBS_CHANGED:
79                 for (SignalCallback signalCluster : mSignalCallbacks) {
80                     signalCluster.setSubs((List<SubscriptionInfo>) msg.obj);
81                 }
82                 break;
83             case MSG_NO_SIM_VISIBLE_CHANGED:
84                 for (SignalCallback signalCluster : mSignalCallbacks) {
85                     signalCluster.setNoSims(msg.arg1 != 0, msg.arg2 != 0);
86                 }
87                 break;
88             case MSG_ETHERNET_CHANGED:
89                 for (SignalCallback signalCluster : mSignalCallbacks) {
90                     signalCluster.setEthernetIndicators((IconState) msg.obj);
91                 }
92                 break;
93             case MSG_AIRPLANE_MODE_CHANGED:
94                 for (SignalCallback signalCluster : mSignalCallbacks) {
95                     signalCluster.setIsAirplaneMode((IconState) msg.obj);
96                 }
97                 break;
98             case MSG_MOBILE_DATA_ENABLED_CHANGED:
99                 for (SignalCallback signalCluster : mSignalCallbacks) {
100                     signalCluster.setMobileDataEnabled(msg.arg1 != 0);
101                 }
102                 break;
103             case MSG_ADD_REMOVE_EMERGENCY:
104                 if (msg.arg1 != 0) {
105                     mEmergencyListeners.add((EmergencyListener) msg.obj);
106                 } else {
107                     mEmergencyListeners.remove((EmergencyListener) msg.obj);
108                 }
109                 break;
110             case MSG_ADD_REMOVE_SIGNAL:
111                 if (msg.arg1 != 0) {
112                     mSignalCallbacks.add((SignalCallback) msg.obj);
113                 } else {
114                     mSignalCallbacks.remove((SignalCallback) msg.obj);
115                 }
116                 break;
117         }
118     }
119 
120     @Override
setWifiIndicators(final WifiIndicators indicators)121     public void setWifiIndicators(final WifiIndicators indicators) {
122         String log = new StringBuilder()
123                 .append(SSDF.format(System.currentTimeMillis())).append(",")
124                 .append(indicators)
125                 .toString();
126         recordLastCallback(log);
127         post(() -> {
128             for (SignalCallback callback : mSignalCallbacks) {
129                 callback.setWifiIndicators(indicators);
130             }
131         });
132     }
133 
134     @Override
setMobileDataIndicators(final MobileDataIndicators indicators)135     public void setMobileDataIndicators(final MobileDataIndicators indicators) {
136         String log = new StringBuilder()
137                 .append(SSDF.format(System.currentTimeMillis())).append(",")
138                 .append(indicators)
139                 .toString();
140         recordLastCallback(log);
141         post(() -> {
142             for (SignalCallback signalCluster : mSignalCallbacks) {
143                 signalCluster.setMobileDataIndicators(indicators);
144             }
145         });
146     }
147 
148     @Override
setConnectivityStatus(boolean noDefaultNetwork, boolean noValidatedNetwork, boolean noNetworksAvailable)149     public void setConnectivityStatus(boolean noDefaultNetwork, boolean noValidatedNetwork,
150                 boolean noNetworksAvailable) {
151         String currentCallback = new StringBuilder()
152                 .append("setConnectivityStatus: ")
153                 .append("noDefaultNetwork=").append(noDefaultNetwork).append(",")
154                 .append("noValidatedNetwork=").append(noValidatedNetwork).append(",")
155                 .append("noNetworksAvailable=").append(noNetworksAvailable)
156                 .toString();
157         if (!currentCallback.equals(mLastCallback)) {
158             mLastCallback = currentCallback;
159             String log = new StringBuilder()
160                     .append(SSDF.format(System.currentTimeMillis())).append(",")
161                     .append(currentCallback).append(",")
162                     .toString();
163             recordLastCallback(log);
164         }
165         post(() -> {
166             for (SignalCallback signalCluster : mSignalCallbacks) {
167                 signalCluster.setConnectivityStatus(
168                         noDefaultNetwork, noValidatedNetwork, noNetworksAvailable);
169             }
170         });
171     }
172 
173     @Override
setCallIndicator(IconState statusIcon, int subId)174     public void setCallIndicator(IconState statusIcon, int subId) {
175         String currentCallback = new StringBuilder()
176                 .append("setCallIndicator: ")
177                 .append("statusIcon=").append(statusIcon).append(",")
178                 .append("subId=").append(subId)
179                 .toString();
180         if (!currentCallback.equals(mLastCallback)) {
181             mLastCallback = currentCallback;
182             String log = new StringBuilder()
183                     .append(SSDF.format(System.currentTimeMillis())).append(",")
184                     .append(currentCallback).append(",")
185                     .toString();
186             recordLastCallback(log);
187         }
188         post(() -> {
189             for (SignalCallback signalCluster : mSignalCallbacks) {
190                 signalCluster.setCallIndicator(statusIcon, subId);
191             }
192         });
193     }
194 
195     @Override
setSubs(List<SubscriptionInfo> subs)196     public void setSubs(List<SubscriptionInfo> subs) {
197         String currentCallback = new StringBuilder()
198                 .append("setSubs: ")
199                 .append("subs=").append(subs == null ? "" : subs.toString())
200                 .toString();
201         if (!currentCallback.equals(mLastCallback)) {
202             mLastCallback = currentCallback;
203             String log = new StringBuilder()
204                     .append(SSDF.format(System.currentTimeMillis())).append(",")
205                     .append(currentCallback).append(",")
206                     .toString();
207             recordLastCallback(log);
208         }
209         obtainMessage(MSG_SUBS_CHANGED, subs).sendToTarget();
210     }
211 
212     @Override
setNoSims(boolean show, boolean simDetected)213     public void setNoSims(boolean show, boolean simDetected) {
214         obtainMessage(MSG_NO_SIM_VISIBLE_CHANGED, show ? 1 : 0, simDetected ? 1 : 0).sendToTarget();
215     }
216 
217     @Override
setMobileDataEnabled(boolean enabled)218     public void setMobileDataEnabled(boolean enabled) {
219         obtainMessage(MSG_MOBILE_DATA_ENABLED_CHANGED, enabled ? 1 : 0, 0).sendToTarget();
220     }
221 
222     @Override
setEmergencyCallsOnly(boolean emergencyOnly)223     public void setEmergencyCallsOnly(boolean emergencyOnly) {
224         obtainMessage(MSG_EMERGENCE_CHANGED, emergencyOnly ? 1 : 0, 0).sendToTarget();
225     }
226 
227     @Override
setEthernetIndicators(IconState icon)228     public void setEthernetIndicators(IconState icon) {
229         String log = new StringBuilder()
230                 .append(SSDF.format(System.currentTimeMillis())).append(",")
231                 .append("setEthernetIndicators: ")
232                 .append("icon=").append(icon)
233                 .toString();
234         recordLastCallback(log);
235         obtainMessage(MSG_ETHERNET_CHANGED, icon).sendToTarget();
236     }
237 
238     @Override
setIsAirplaneMode(IconState icon)239     public void setIsAirplaneMode(IconState icon) {
240         String currentCallback = new StringBuilder()
241                 .append("setIsAirplaneMode: ")
242                 .append("icon=").append(icon)
243                 .toString();
244         if (!currentCallback.equals(mLastCallback)) {
245             mLastCallback = currentCallback;
246             String log = new StringBuilder()
247                     .append(SSDF.format(System.currentTimeMillis())).append(",")
248                     .append(currentCallback).append(",")
249                     .toString();
250             recordLastCallback(log);
251         }
252         obtainMessage(MSG_AIRPLANE_MODE_CHANGED, icon).sendToTarget();
253     }
254 
setListening(EmergencyListener listener, boolean listening)255     void setListening(EmergencyListener listener, boolean listening) {
256         obtainMessage(MSG_ADD_REMOVE_EMERGENCY, listening ? 1 : 0, 0, listener).sendToTarget();
257     }
258 
setListening(SignalCallback listener, boolean listening)259     void setListening(SignalCallback listener, boolean listening) {
260         obtainMessage(MSG_ADD_REMOVE_SIGNAL, listening ? 1 : 0, 0, listener).sendToTarget();
261     }
262 
recordLastCallback(String callback)263     protected void recordLastCallback(String callback) {
264         mHistory[mHistoryIndex] = callback;
265         mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE;
266     }
267 
268     /**
269      * Dump the Callback logs
270      */
dump(PrintWriter pw)271     public void dump(PrintWriter pw) {
272         pw.println("  - CallbackHandler -----");
273         int size = 0;
274         for (int i = 0; i < HISTORY_SIZE; i++) {
275             if (mHistory[i] != null) {
276                 size++;
277             }
278         }
279         // Print out the previous states in ordered number.
280         for (int i = mHistoryIndex + HISTORY_SIZE - 1;
281                 i >= mHistoryIndex + HISTORY_SIZE - size; i--) {
282             pw.println("  Previous Callback(" + (mHistoryIndex + HISTORY_SIZE - i) + "): "
283                     + mHistory[i & (HISTORY_SIZE - 1)]);
284         }
285     }
286 
287 }
288