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