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