blob: 9ec30d43ac755a3818e7ece8c59664fd0765a9f6 [file] [log] [blame]
Jason Monkda68f592015-01-07 10:55:58 -05001/*
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 */
16package com.android.systemui.statusbar.policy;
17
Gus Prevasab336792018-11-14 13:52:20 -050018import static com.android.systemui.statusbar.policy.NetworkControllerImpl.TAG;
19
Jason Monkda68f592015-01-07 10:55:58 -050020import android.content.Context;
21import android.text.format.DateFormat;
22import android.util.Log;
Gus Prevasab336792018-11-14 13:52:20 -050023
Jason Monke06b0652016-03-02 16:35:27 -050024import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
Jason Monkda68f592015-01-07 10:55:58 -050025
Jason Monkda68f592015-01-07 10:55:58 -050026import java.io.PrintWriter;
Jason Monk33f8ae72015-05-08 10:45:15 -040027import java.util.BitSet;
Jason Monkda68f592015-01-07 10:55:58 -050028
29
30/**
31 * Common base class for handling signal for both wifi and mobile data.
32 */
33public abstract class SignalController<T extends SignalController.State,
34 I extends SignalController.IconGroup> {
35 // Save the previous SignalController.States of all SignalControllers for dumps.
36 static final boolean RECORD_HISTORY = true;
37 // If RECORD_HISTORY how many to save, must be a power of 2.
Jason Monk7f9623f2015-02-11 09:03:02 -050038 static final int HISTORY_SIZE = 64;
Jason Monkda68f592015-01-07 10:55:58 -050039
40 protected static final boolean DEBUG = NetworkControllerImpl.DEBUG;
41 protected static final boolean CHATTY = NetworkControllerImpl.CHATTY;
42
43 protected final String mTag;
44 protected final T mCurrentState;
45 protected final T mLastState;
46 protected final int mTransportType;
47 protected final Context mContext;
48 // The owner of the SignalController (i.e. NetworkController will maintain the following
49 // lists and call notifyListeners whenever the list has changed to ensure everyone
50 // is aware of current state.
Jason Monkda68f592015-01-07 10:55:58 -050051 protected final NetworkControllerImpl mNetworkController;
52
Jason Monke06b0652016-03-02 16:35:27 -050053 private final CallbackHandler mCallbackHandler;
Jason Monk07b75fe2015-05-14 16:47:03 -040054
Jason Monkda68f592015-01-07 10:55:58 -050055 // Save the previous HISTORY_SIZE states for logging.
56 private final State[] mHistory;
57 // Where to copy the next state into.
58 private int mHistoryIndex;
59
Jason Monk07b75fe2015-05-14 16:47:03 -040060 public SignalController(String tag, Context context, int type, CallbackHandler callbackHandler,
61 NetworkControllerImpl networkController) {
Jason Monkda68f592015-01-07 10:55:58 -050062 mTag = TAG + "." + tag;
63 mNetworkController = networkController;
64 mTransportType = type;
65 mContext = context;
Jason Monk07b75fe2015-05-14 16:47:03 -040066 mCallbackHandler = callbackHandler;
Jason Monkda68f592015-01-07 10:55:58 -050067 mCurrentState = cleanState();
68 mLastState = cleanState();
69 if (RECORD_HISTORY) {
70 mHistory = new State[HISTORY_SIZE];
71 for (int i = 0; i < HISTORY_SIZE; i++) {
72 mHistory[i] = cleanState();
73 }
74 }
75 }
76
77 public T getState() {
78 return mCurrentState;
79 }
80
Jason Monk33f8ae72015-05-08 10:45:15 -040081 public void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) {
82 mCurrentState.inetCondition = validatedTransports.get(mTransportType) ? 1 : 0;
Jason Monkda68f592015-01-07 10:55:58 -050083 notifyListenersIfNecessary();
84 }
85
86 /**
87 * Used at the end of demo mode to clear out any ugly state that it has created.
88 * Since we haven't had any callbacks, then isDirty will not have been triggered,
89 * so we can just take the last good state directly from there.
90 *
91 * Used for demo mode.
92 */
93 public void resetLastState() {
94 mCurrentState.copyFrom(mLastState);
95 }
96
97 /**
98 * Determines if the state of this signal controller has changed and
99 * needs to trigger callbacks related to it.
100 */
101 public boolean isDirty() {
102 if (!mLastState.equals(mCurrentState)) {
103 if (DEBUG) {
104 Log.d(mTag, "Change in state from: " + mLastState + "\n"
105 + "\tto: " + mCurrentState);
106 }
107 return true;
108 }
109 return false;
110 }
111
112 public void saveLastState() {
113 if (RECORD_HISTORY) {
114 recordLastState();
115 }
116 // Updates the current time.
117 mCurrentState.time = System.currentTimeMillis();
118 mLastState.copyFrom(mCurrentState);
119 }
120
121 /**
122 * Gets the signal icon for QS based on current state of connected, enabled, and level.
123 */
124 public int getQsCurrentIconId() {
125 if (mCurrentState.connected) {
126 return getIcons().mQsIcons[mCurrentState.inetCondition][mCurrentState.level];
127 } else if (mCurrentState.enabled) {
128 return getIcons().mQsDiscState;
129 } else {
130 return getIcons().mQsNullState;
131 }
132 }
133
134 /**
135 * Gets the signal icon for SB based on current state of connected, enabled, and level.
136 */
137 public int getCurrentIconId() {
138 if (mCurrentState.connected) {
Andrew Flynn2fdbe122015-06-01 16:34:21 -0400139 return getIcons().mSbIcons[mCurrentState.inetCondition][mCurrentState.level];
Jason Monkda68f592015-01-07 10:55:58 -0500140 } else if (mCurrentState.enabled) {
Andrew Flynn2fdbe122015-06-01 16:34:21 -0400141 return getIcons().mSbDiscState;
Jason Monkda68f592015-01-07 10:55:58 -0500142 } else {
143 return getIcons().mSbNullState;
144 }
145 }
146
147 /**
148 * Gets the content description id for the signal based on current state of connected and
149 * level.
150 */
151 public int getContentDescription() {
152 if (mCurrentState.connected) {
153 return getIcons().mContentDesc[mCurrentState.level];
154 } else {
155 return getIcons().mDiscContentDesc;
156 }
157 }
158
159 public void notifyListenersIfNecessary() {
160 if (isDirty()) {
161 saveLastState();
162 notifyListeners();
Jason Monkda68f592015-01-07 10:55:58 -0500163 }
164 }
165
166 /**
167 * Returns the resource if resId is not 0, and an empty string otherwise.
168 */
169 protected String getStringIfExists(int resId) {
170 return resId != 0 ? mContext.getString(resId) : "";
171 }
172
173 protected I getIcons() {
174 return (I) mCurrentState.iconGroup;
175 }
176
177 /**
178 * Saves the last state of any changes, so we can log the current
179 * and last value of any state data.
180 */
181 protected void recordLastState() {
182 mHistory[mHistoryIndex++ & (HISTORY_SIZE - 1)].copyFrom(mLastState);
183 }
184
185 public void dump(PrintWriter pw) {
186 pw.println(" - " + mTag + " -----");
187 pw.println(" Current State: " + mCurrentState);
188 if (RECORD_HISTORY) {
189 // Count up the states that actually contain time stamps, and only display those.
190 int size = 0;
191 for (int i = 0; i < HISTORY_SIZE; i++) {
192 if (mHistory[i].time != 0) size++;
193 }
194 // Print out the previous states in ordered number.
195 for (int i = mHistoryIndex + HISTORY_SIZE - 1;
196 i >= mHistoryIndex + HISTORY_SIZE - size; i--) {
197 pw.println(" Previous State(" + (mHistoryIndex + HISTORY_SIZE - i) + "): "
198 + mHistory[i & (HISTORY_SIZE - 1)]);
199 }
200 }
201 }
202
Jason Monke06b0652016-03-02 16:35:27 -0500203 public final void notifyListeners() {
204 notifyListeners(mCallbackHandler);
205 }
206
Jason Monkda68f592015-01-07 10:55:58 -0500207 /**
208 * Trigger callbacks based on current state. The callbacks should be completely
209 * based on current state, and only need to be called in the scenario where
210 * mCurrentState != mLastState.
211 */
Jason Monke06b0652016-03-02 16:35:27 -0500212 public abstract void notifyListeners(SignalCallback callback);
Jason Monkda68f592015-01-07 10:55:58 -0500213
214 /**
215 * Generate a blank T.
216 */
217 protected abstract T cleanState();
218
219 /*
220 * Holds icons for a given state. Arrays are generally indexed as inet
221 * state (full connectivity or not) first, and second dimension as
222 * signal strength.
223 */
224 static class IconGroup {
225 final int[][] mSbIcons;
226 final int[][] mQsIcons;
227 final int[] mContentDesc;
228 final int mSbNullState;
229 final int mQsNullState;
230 final int mSbDiscState;
231 final int mQsDiscState;
232 final int mDiscContentDesc;
233 // For logging.
234 final String mName;
235
236 public IconGroup(String name, int[][] sbIcons, int[][] qsIcons, int[] contentDesc,
237 int sbNullState, int qsNullState, int sbDiscState, int qsDiscState,
238 int discContentDesc) {
239 mName = name;
240 mSbIcons = sbIcons;
241 mQsIcons = qsIcons;
242 mContentDesc = contentDesc;
243 mSbNullState = sbNullState;
244 mQsNullState = qsNullState;
245 mSbDiscState = sbDiscState;
Jason Monkda68f592015-01-07 10:55:58 -0500246 mQsDiscState = qsDiscState;
247 mDiscContentDesc = discContentDesc;
248 }
249
250 @Override
251 public String toString() {
252 return "IconGroup(" + mName + ")";
253 }
254 }
255
256 static class State {
257 boolean connected;
258 boolean enabled;
259 boolean activityIn;
260 boolean activityOut;
261 int level;
262 IconGroup iconGroup;
263 int inetCondition;
264 int rssi; // Only for logging.
265
266 // Not used for comparison, just used for logging.
267 long time;
268
269 public void copyFrom(State state) {
270 connected = state.connected;
271 enabled = state.enabled;
272 level = state.level;
273 iconGroup = state.iconGroup;
274 inetCondition = state.inetCondition;
275 activityIn = state.activityIn;
276 activityOut = state.activityOut;
277 rssi = state.rssi;
278 time = state.time;
279 }
280
281 @Override
282 public String toString() {
283 if (time != 0) {
284 StringBuilder builder = new StringBuilder();
285 toString(builder);
286 return builder.toString();
287 } else {
288 return "Empty " + getClass().getSimpleName();
289 }
290 }
291
292 protected void toString(StringBuilder builder) {
293 builder.append("connected=").append(connected).append(',')
294 .append("enabled=").append(enabled).append(',')
295 .append("level=").append(level).append(',')
296 .append("inetCondition=").append(inetCondition).append(',')
297 .append("iconGroup=").append(iconGroup).append(',')
298 .append("activityIn=").append(activityIn).append(',')
299 .append("activityOut=").append(activityOut).append(',')
300 .append("rssi=").append(rssi).append(',')
Evan Laird528437d2017-08-01 10:16:06 -0400301 .append("lastModified=").append(DateFormat.format("MM-dd HH:mm:ss", time));
Jason Monkda68f592015-01-07 10:55:58 -0500302 }
303
304 @Override
305 public boolean equals(Object o) {
306 if (!o.getClass().equals(getClass())) {
307 return false;
308 }
309 State other = (State) o;
310 return other.connected == connected
311 && other.enabled == enabled
312 && other.level == level
313 && other.inetCondition == inetCondition
314 && other.iconGroup == iconGroup
315 && other.activityIn == activityIn
316 && other.activityOut == activityOut
317 && other.rssi == rssi;
318 }
319 }
320}