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.connectivity; 17 18 import static com.android.systemui.statusbar.connectivity.NetworkControllerImpl.TAG; 19 20 import android.annotation.NonNull; 21 import android.content.Context; 22 import android.util.Log; 23 24 import com.android.settingslib.SignalIcon.IconGroup; 25 import com.android.systemui.dump.DumpsysTableLogger; 26 27 import java.io.PrintWriter; 28 import java.util.ArrayList; 29 import java.util.BitSet; 30 import java.util.List; 31 32 /** 33 * Common base class for handling signal for both wifi and mobile data. 34 * 35 * @param <T> State of the SysUI controller. 36 * @param <I> Icon groups of the SysUI controller for a given State. 37 * 38 * @deprecated "Use Recommended Architecture classes instead: MobileIconsInteractor, WifiInteractor, 39 * AirplaneModeInteractor, and EthernetInteractor 40 */ 41 @Deprecated 42 public abstract class SignalController<T extends ConnectivityState, I extends IconGroup> { 43 // Save the previous SignalController.States of all SignalControllers for dumps. 44 static final boolean RECORD_HISTORY = true; 45 // If RECORD_HISTORY how many to save, must be a power of 2. 46 static final int HISTORY_SIZE = 64; 47 48 protected static final boolean DEBUG = NetworkControllerImpl.DEBUG; 49 protected static final boolean CHATTY = NetworkControllerImpl.CHATTY; 50 51 protected final String mTag; 52 protected final T mCurrentState; 53 protected final T mLastState; 54 protected final int mTransportType; 55 protected final Context mContext; 56 // The owner of the SignalController (i.e. NetworkController) will maintain the following 57 // lists and call notifyListeners whenever the list has changed to ensure everyone 58 // is aware of current state. 59 protected final NetworkControllerImpl mNetworkController; 60 61 private final CallbackHandler mCallbackHandler; 62 63 // Save the previous HISTORY_SIZE states for logging. 64 private final ConnectivityState[] mHistory; 65 // Where to copy the next state into. 66 private int mHistoryIndex; 67 SignalController(String tag, Context context, int type, CallbackHandler callbackHandler, NetworkControllerImpl networkController)68 public SignalController(String tag, Context context, int type, CallbackHandler callbackHandler, 69 NetworkControllerImpl networkController) { 70 mTag = TAG + "." + tag; 71 mNetworkController = networkController; 72 mTransportType = type; 73 mContext = context; 74 mCallbackHandler = callbackHandler; 75 mCurrentState = cleanState(); 76 mLastState = cleanState(); 77 if (RECORD_HISTORY) { 78 mHistory = new ConnectivityState[HISTORY_SIZE]; 79 for (int i = 0; i < HISTORY_SIZE; i++) { 80 mHistory[i] = cleanState(); 81 } 82 } 83 } 84 getState()85 public T getState() { 86 return mCurrentState; 87 } 88 updateConnectivity(BitSet connectedTransports, BitSet validatedTransports)89 void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) { 90 mCurrentState.inetCondition = validatedTransports.get(mTransportType) ? 1 : 0; 91 notifyListenersIfNecessary(); 92 } 93 94 /** 95 * Used at the end of demo mode to clear out any ugly state that it has created. 96 * Since we haven't had any callbacks, then isDirty will not have been triggered, 97 * so we can just take the last good state directly from there. 98 * 99 * Used for demo mode. 100 */ resetLastState()101 public void resetLastState() { 102 mCurrentState.copyFrom(mLastState); 103 } 104 105 /** 106 * Determines if the state of this signal controller has changed and 107 * needs to trigger callbacks related to it. 108 */ isDirty()109 boolean isDirty() { 110 if (!mLastState.equals(mCurrentState)) { 111 if (DEBUG) { 112 Log.d(mTag, "Change in state from: " + mLastState + "\n" 113 + "\tto: " + mCurrentState); 114 } 115 return true; 116 } 117 return false; 118 } 119 saveLastState()120 void saveLastState() { 121 if (RECORD_HISTORY) { 122 recordLastState(); 123 } 124 // Updates the current time. 125 mCurrentState.time = System.currentTimeMillis(); 126 mLastState.copyFrom(mCurrentState); 127 } 128 129 /** 130 * Gets the signal icon for QS based on current state of connected, enabled, and level. 131 */ getQsCurrentIconId()132 public int getQsCurrentIconId() { 133 if (mCurrentState.connected) { 134 return getIcons().qsIcons[mCurrentState.inetCondition][mCurrentState.level]; 135 } else if (mCurrentState.enabled) { 136 return getIcons().qsDiscState; 137 } else { 138 return getIcons().qsNullState; 139 } 140 } 141 142 /** 143 * Gets the signal icon for SB based on current state of connected, enabled, and level. 144 */ getCurrentIconId()145 public int getCurrentIconId() { 146 if (mCurrentState.connected) { 147 return getIcons().sbIcons[mCurrentState.inetCondition][mCurrentState.level]; 148 } else if (mCurrentState.enabled) { 149 return getIcons().sbDiscState; 150 } else { 151 return getIcons().sbNullState; 152 } 153 } 154 155 /** 156 * Gets the content description id for the signal based on current state of connected and 157 * level. 158 */ getContentDescription()159 public int getContentDescription() { 160 if (mCurrentState.connected) { 161 return getIcons().contentDesc[mCurrentState.level]; 162 } else { 163 return getIcons().discContentDesc; 164 } 165 } 166 notifyListenersIfNecessary()167 void notifyListenersIfNecessary() { 168 if (isDirty()) { 169 saveLastState(); 170 notifyListeners(); 171 } 172 } 173 174 /** 175 * Returns the resource if resId is not 0, and an empty string otherwise. 176 */ getTextIfExists(int resId)177 @NonNull CharSequence getTextIfExists(int resId) { 178 return resId != 0 ? mContext.getText(resId) : ""; 179 } 180 getIcons()181 protected I getIcons() { 182 return (I) mCurrentState.iconGroup; 183 } 184 185 /** 186 * Saves the last state of any changes, so we can log the current 187 * and last value of any state data. 188 */ recordLastState()189 protected void recordLastState() { 190 mHistory[mHistoryIndex].copyFrom(mLastState); 191 mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE; 192 } 193 dump(PrintWriter pw)194 void dump(PrintWriter pw) { 195 pw.println(" - " + mTag + " -----"); 196 pw.println(" Current State: " + mCurrentState); 197 if (RECORD_HISTORY) { 198 List<ConnectivityState> history = getOrderedHistoryExcludingCurrentState(); 199 for (int i = 0; i < history.size(); i++) { 200 pw.println(" Previous State(" + (i + 1) + "): " + mHistory[i]); 201 } 202 } 203 } 204 205 /** 206 * mHistory is a ring, so use this method to get the time-ordered (from youngest to oldest) 207 * list of historical states. Filters out any state whose `time` is `0`. 208 * 209 * For ease of compatibility, this list returns JUST the historical states, not the current 210 * state which has yet to be copied into the history 211 * 212 * @see #getOrderedHistory() 213 * @return historical states, ordered from newest to oldest 214 */ getOrderedHistoryExcludingCurrentState()215 List<ConnectivityState> getOrderedHistoryExcludingCurrentState() { 216 ArrayList<ConnectivityState> history = new ArrayList<>(); 217 218 // Count up the states that actually contain time stamps, and only display those. 219 int size = 0; 220 for (int i = 0; i < HISTORY_SIZE; i++) { 221 if (mHistory[i].time != 0) size++; 222 } 223 // Print out the previous states in ordered number. 224 for (int i = mHistoryIndex + HISTORY_SIZE - 1; 225 i >= mHistoryIndex + HISTORY_SIZE - size; i--) { 226 history.add(mHistory[i & (HISTORY_SIZE - 1)]); 227 } 228 229 return history; 230 } 231 232 /** 233 * Get the ordered history states, including the current yet-to-be-copied state. Useful for 234 * logging 235 * 236 * @see #getOrderedHistoryExcludingCurrentState() 237 * @return [currentState, historicalState...] array 238 */ getOrderedHistory()239 List<ConnectivityState> getOrderedHistory() { 240 ArrayList<ConnectivityState> history = new ArrayList<>(); 241 // Start with the current state 242 history.add(mCurrentState); 243 history.addAll(getOrderedHistoryExcludingCurrentState()); 244 return history; 245 } 246 dumpTableData(PrintWriter pw)247 void dumpTableData(PrintWriter pw) { 248 List<List<String>> tableData = new ArrayList<List<String>>(); 249 List<ConnectivityState> history = getOrderedHistory(); 250 for (int i = 0; i < history.size(); i++) { 251 tableData.add(history.get(i).tableData()); 252 } 253 254 DumpsysTableLogger logger = 255 new DumpsysTableLogger(mTag, mCurrentState.tableColumns(), tableData); 256 257 logger.printTableData(pw); 258 } 259 notifyListeners()260 final void notifyListeners() { 261 notifyListeners(mCallbackHandler); 262 } 263 264 /** 265 * Trigger callbacks based on current state. The callbacks should be completely 266 * based on current state, and only need to be called in the scenario where 267 * mCurrentState != mLastState. 268 */ notifyListeners(SignalCallback callback)269 abstract void notifyListeners(SignalCallback callback); 270 271 /** 272 * Generate a blank T. 273 */ cleanState()274 protected abstract T cleanState(); 275 } 276