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