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