• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.policy;
17 
18 import android.content.Context;
19 import android.text.format.DateFormat;
20 import android.util.Log;
21 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
22 
23 import java.io.PrintWriter;
24 import java.util.BitSet;
25 
26 import static com.android.systemui.statusbar.policy.NetworkControllerImpl.TAG;
27 
28 
29 /**
30  * Common base class for handling signal for both wifi and mobile data.
31  */
32 public 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.
37     static final int HISTORY_SIZE = 64;
38 
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.
50     protected final NetworkControllerImpl mNetworkController;
51 
52     private final CallbackHandler mCallbackHandler;
53 
54     // 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 
SignalController(String tag, Context context, int type, CallbackHandler callbackHandler, NetworkControllerImpl networkController)59     public SignalController(String tag, Context context, int type, CallbackHandler callbackHandler,
60             NetworkControllerImpl networkController) {
61         mTag = TAG + "." + tag;
62         mNetworkController = networkController;
63         mTransportType = type;
64         mContext = context;
65         mCallbackHandler = callbackHandler;
66         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 
getState()76     public T getState() {
77         return mCurrentState;
78     }
79 
updateConnectivity(BitSet connectedTransports, BitSet validatedTransports)80     public void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) {
81         mCurrentState.inetCondition = validatedTransports.get(mTransportType) ? 1 : 0;
82         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      */
resetLastState()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      */
isDirty()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 
saveLastState()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      */
getQsCurrentIconId()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      */
getCurrentIconId()136     public int getCurrentIconId() {
137         if (mCurrentState.connected) {
138             return getIcons().mSbIcons[mCurrentState.inetCondition][mCurrentState.level];
139         } else if (mCurrentState.enabled) {
140             return getIcons().mSbDiscState;
141         } 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      */
getContentDescription()150     public int getContentDescription() {
151         if (mCurrentState.connected) {
152             return getIcons().mContentDesc[mCurrentState.level];
153         } else {
154             return getIcons().mDiscContentDesc;
155         }
156     }
157 
notifyListenersIfNecessary()158     public void notifyListenersIfNecessary() {
159         if (isDirty()) {
160             saveLastState();
161             notifyListeners();
162         }
163     }
164 
165     /**
166      * Returns the resource if resId is not 0, and an empty string otherwise.
167      */
getStringIfExists(int resId)168     protected String getStringIfExists(int resId) {
169         return resId != 0 ? mContext.getString(resId) : "";
170     }
171 
getIcons()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      */
recordLastState()180     protected void recordLastState() {
181         mHistory[mHistoryIndex++ & (HISTORY_SIZE - 1)].copyFrom(mLastState);
182     }
183 
dump(PrintWriter pw)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 
notifyListeners()202     public final void notifyListeners() {
203         notifyListeners(mCallbackHandler);
204     }
205 
206     /**
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      */
notifyListeners(SignalCallback callback)211     public abstract void notifyListeners(SignalCallback callback);
212 
213     /**
214      * Generate a blank T.
215      */
cleanState()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 
IconGroup(String name, int[][] sbIcons, int[][] qsIcons, int[] contentDesc, int sbNullState, int qsNullState, int sbDiscState, int qsDiscState, int discContentDesc)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;
245             mQsDiscState = qsDiscState;
246             mDiscContentDesc = discContentDesc;
247         }
248 
249         @Override
toString()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 
copyFrom(State state)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
toString()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 
toString(StringBuilder builder)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(',')
300                     .append("lastModified=").append(DateFormat.format("MM-dd hh:mm:ss", time));
301         }
302 
303         @Override
equals(Object o)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 }
320