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