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 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 * @param <T> State of the SysUI controller. 34 * @param <I> Icon groups of the SysUI controller for a given State. 35 */ 36 public abstract class SignalController<T extends ConnectivityState, I extends IconGroup> { 37 // Save the previous SignalController.States of all SignalControllers for dumps. 38 static final boolean RECORD_HISTORY = true; 39 // If RECORD_HISTORY how many to save, must be a power of 2. 40 static final int HISTORY_SIZE = 64; 41 42 protected static final boolean DEBUG = NetworkControllerImpl.DEBUG; 43 protected static final boolean CHATTY = NetworkControllerImpl.CHATTY; 44 45 protected final String mTag; 46 protected final T mCurrentState; 47 protected final T mLastState; 48 protected final int mTransportType; 49 protected final Context mContext; 50 // The owner of the SignalController (i.e. NetworkController will maintain the following 51 // lists and call notifyListeners whenever the list has changed to ensure everyone 52 // is aware of current state. 53 protected final NetworkControllerImpl mNetworkController; 54 55 private final CallbackHandler mCallbackHandler; 56 57 // Save the previous HISTORY_SIZE states for logging. 58 private final ConnectivityState[] mHistory; 59 // Where to copy the next state into. 60 private int mHistoryIndex; 61 SignalController(String tag, Context context, int type, CallbackHandler callbackHandler, NetworkControllerImpl networkController)62 public SignalController(String tag, Context context, int type, CallbackHandler callbackHandler, 63 NetworkControllerImpl networkController) { 64 mTag = TAG + "." + tag; 65 mNetworkController = networkController; 66 mTransportType = type; 67 mContext = context; 68 mCallbackHandler = callbackHandler; 69 mCurrentState = cleanState(); 70 mLastState = cleanState(); 71 if (RECORD_HISTORY) { 72 mHistory = new ConnectivityState[HISTORY_SIZE]; 73 for (int i = 0; i < HISTORY_SIZE; i++) { 74 mHistory[i] = cleanState(); 75 } 76 } 77 } 78 getState()79 public T getState() { 80 return mCurrentState; 81 } 82 updateConnectivity(BitSet connectedTransports, BitSet validatedTransports)83 void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) { 84 mCurrentState.inetCondition = validatedTransports.get(mTransportType) ? 1 : 0; 85 notifyListenersIfNecessary(); 86 } 87 88 /** 89 * Used at the end of demo mode to clear out any ugly state that it has created. 90 * Since we haven't had any callbacks, then isDirty will not have been triggered, 91 * so we can just take the last good state directly from there. 92 * 93 * Used for demo mode. 94 */ resetLastState()95 public void resetLastState() { 96 mCurrentState.copyFrom(mLastState); 97 } 98 99 /** 100 * Determines if the state of this signal controller has changed and 101 * needs to trigger callbacks related to it. 102 */ isDirty()103 public boolean isDirty() { 104 if (!mLastState.equals(mCurrentState)) { 105 if (DEBUG) { 106 Log.d(mTag, "Change in state from: " + mLastState + "\n" 107 + "\tto: " + mCurrentState); 108 } 109 return true; 110 } 111 return false; 112 } 113 saveLastState()114 void saveLastState() { 115 if (RECORD_HISTORY) { 116 recordLastState(); 117 } 118 // Updates the current time. 119 mCurrentState.time = System.currentTimeMillis(); 120 mLastState.copyFrom(mCurrentState); 121 } 122 123 /** 124 * Gets the signal icon for QS based on current state of connected, enabled, and level. 125 */ getQsCurrentIconId()126 public int getQsCurrentIconId() { 127 if (mCurrentState.connected) { 128 return getIcons().qsIcons[mCurrentState.inetCondition][mCurrentState.level]; 129 } else if (mCurrentState.enabled) { 130 return getIcons().qsDiscState; 131 } else { 132 return getIcons().qsNullState; 133 } 134 } 135 136 /** 137 * Gets the signal icon for SB based on current state of connected, enabled, and level. 138 */ getCurrentIconId()139 public int getCurrentIconId() { 140 if (mCurrentState.connected) { 141 return getIcons().sbIcons[mCurrentState.inetCondition][mCurrentState.level]; 142 } else if (mCurrentState.enabled) { 143 return getIcons().sbDiscState; 144 } else { 145 return getIcons().sbNullState; 146 } 147 } 148 149 /** 150 * Gets the content description id for the signal based on current state of connected and 151 * level. 152 */ getContentDescription()153 public int getContentDescription() { 154 if (mCurrentState.connected) { 155 return getIcons().contentDesc[mCurrentState.level]; 156 } else { 157 return getIcons().discContentDesc; 158 } 159 } 160 notifyListenersIfNecessary()161 void notifyListenersIfNecessary() { 162 if (isDirty()) { 163 saveLastState(); 164 notifyListeners(); 165 } 166 } 167 notifyCallStateChange(IconState statusIcon, int subId)168 protected final void notifyCallStateChange(IconState statusIcon, int subId) { 169 mCallbackHandler.setCallIndicator(statusIcon, subId); 170 } 171 172 /** 173 * Returns the resource if resId is not 0, and an empty string otherwise. 174 */ getTextIfExists(int resId)175 @NonNull CharSequence getTextIfExists(int resId) { 176 return resId != 0 ? mContext.getText(resId) : ""; 177 } 178 getIcons()179 protected I getIcons() { 180 return (I) mCurrentState.iconGroup; 181 } 182 183 /** 184 * Saves the last state of any changes, so we can log the current 185 * and last value of any state data. 186 */ recordLastState()187 protected void recordLastState() { 188 mHistory[mHistoryIndex].copyFrom(mLastState); 189 mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE; 190 } 191 dump(PrintWriter pw)192 void dump(PrintWriter pw) { 193 pw.println(" - " + mTag + " -----"); 194 pw.println(" Current State: " + mCurrentState); 195 if (RECORD_HISTORY) { 196 // Count up the states that actually contain time stamps, and only display those. 197 int size = 0; 198 for (int i = 0; i < HISTORY_SIZE; i++) { 199 if (mHistory[i].time != 0) size++; 200 } 201 // Print out the previous states in ordered number. 202 for (int i = mHistoryIndex + HISTORY_SIZE - 1; 203 i >= mHistoryIndex + HISTORY_SIZE - size; i--) { 204 pw.println(" Previous State(" + (mHistoryIndex + HISTORY_SIZE - i) + "): " 205 + mHistory[i & (HISTORY_SIZE - 1)]); 206 } 207 } 208 } 209 notifyListeners()210 final void notifyListeners() { 211 notifyListeners(mCallbackHandler); 212 } 213 214 /** 215 * Trigger callbacks based on current state. The callbacks should be completely 216 * based on current state, and only need to be called in the scenario where 217 * mCurrentState != mLastState. 218 */ notifyListeners(SignalCallback callback)219 abstract void notifyListeners(SignalCallback callback); 220 221 /** 222 * Generate a blank T. 223 */ cleanState()224 protected abstract T cleanState(); 225 } 226