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