QuickSettings: only listen when expanded.

Register for active state updates only when the quick settings
panel is open.

Don't allow a dual-target tile and single-target tile on the same row.

Bug:14133785
Change-Id: I8a5ad3df9b67b5bc3518210d62b705483a422d8e
diff --git a/packages/SystemUI/src/com/android/systemui/qs/GlobalSetting.java b/packages/SystemUI/src/com/android/systemui/qs/GlobalSetting.java
index 1e15b9f..c169df0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/GlobalSetting.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/GlobalSetting.java
@@ -21,10 +21,10 @@
 import android.os.Handler;
 import android.provider.Settings.Global;
 
-import com.android.systemui.statusbar.policy.Disposable;
+import com.android.systemui.statusbar.policy.Listenable;
 
 /** Helper for managing a global setting. **/
-public abstract class GlobalSetting extends ContentObserver implements Disposable {
+public abstract class GlobalSetting extends ContentObserver implements Listenable {
     private final Context mContext;
     private final String mSettingName;
 
@@ -34,8 +34,6 @@
         super(handler);
         mContext = context;
         mSettingName = settingName;
-        mContext.getContentResolver().registerContentObserver(
-                Global.getUriFor(mSettingName), false, this);
     }
 
     public int getValue() {
@@ -47,8 +45,13 @@
     }
 
     @Override
-    public void dispose() {
-        mContext.getContentResolver().unregisterContentObserver(this);
+    public void setListening(boolean listening) {
+        if (listening) {
+            mContext.getContentResolver().registerContentObserver(
+                    Global.getUriFor(mSettingName), false, this);
+        } else {
+            mContext.getContentResolver().unregisterContentObserver(this);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index afb5483..6176eb6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -80,7 +80,10 @@
             showDetail(false /*show*/, mDetailRecord);
         }
         for (TileRecord r : mRecords) {
-            r.tile.setShown(expanded);
+            r.tile.setListening(expanded);
+            if (expanded) {
+                r.tile.refreshState();
+            }
         }
     }
 
@@ -125,6 +128,7 @@
             }
         };
         r.tileView.init(click, clickSecondary);
+        r.tile.refreshState();
         mRecords.add(r);
 
         addView(r.tileView);
@@ -156,24 +160,29 @@
         mCellHeight = (int)(mCellWidth / TILE_ASPECT);
         mLargeCellWidth = (int)(mCellWidth * LARGE_TILE_FACTOR);
         mLargeCellHeight = (int)(mCellHeight * LARGE_TILE_FACTOR);
-        int r = 0;
-        int c = 0;
+        int r = -1;
+        int c = -1;
         int rows = 0;
+        boolean rowIsDual = false;
         for (TileRecord record : mRecords) {
             if (record.tileView.getVisibility() == GONE) continue;
+            // wrap to next column if we've reached the max # of columns
+            // also don't allow dual + single tiles on the same row
+            if (r == -1 || c == (mColumns - 1) || rowIsDual != record.tile.supportsDualTargets()) {
+                r++;
+                c = 0;
+                rowIsDual = record.tile.supportsDualTargets();
+            } else {
+                c++;
+            }
             record.row = r;
             record.col = c;
             rows = r + 1;
-            c++;
-            if (c == mColumns /*end of normal column*/ || r == 0 && c == 2 /*end of 1st column*/) {
-                c = 0;
-                r++;
-            }
         }
 
         for (TileRecord record : mRecords) {
             if (record.tileView.getVisibility() == GONE) continue;
-            record.tileView.setDual(record.row == 0);
+            record.tileView.setDual(record.tile.supportsDualTargets());
             final int cw = record.row == 0 ? mLargeCellWidth : mCellWidth;
             final int ch = record.row == 0 ? mLargeCellHeight : mCellHeight;
             record.tileView.measure(exactly(cw), exactly(ch));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index 05f308d..38496b4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -30,7 +30,7 @@
 import com.android.systemui.qs.QSTile.State;
 import com.android.systemui.statusbar.policy.BluetoothController;
 import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.Disposable;
+import com.android.systemui.statusbar.policy.Listenable;
 import com.android.systemui.statusbar.policy.LocationController;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.RotationLockController;
@@ -47,8 +47,9 @@
  * handleUpdateState.  Callbacks affecting state should use refreshState to trigger another
  * state update pass on tile looper.
  */
-public abstract class QSTile<TState extends State> implements Disposable {
-    private final String TAG = "QSTile." + getClass().getSimpleName();
+public abstract class QSTile<TState extends State> implements Listenable {
+    protected final String TAG = "QSTile." + getClass().getSimpleName();
+    protected static final boolean DEBUG = false;
 
     protected final Host mHost;
     protected final Context mContext;
@@ -69,6 +70,10 @@
         mHandler = new H(host.getLooper());
     }
 
+    public boolean supportsDualTargets() {
+        return false;
+    }
+
     public Host getHost() {
         return mHost;
     }
@@ -111,10 +116,6 @@
         mHandler.obtainMessage(H.USER_SWITCH, newUserId).sendToTarget();
     }
 
-    public void setShown(boolean shown) {
-        mHandler.obtainMessage(H.SHOWN, shown ? 1 : 0, 0).sendToTarget();
-    }
-
     // call only on tile worker looper
 
     private void handleSetCallback(Callback callback) {
@@ -126,10 +127,6 @@
         // optional
     }
 
-    protected void handleShown(boolean shown) {
-        // optional, discouraged
-    }
-
     protected void handleRefreshState(Object arg) {
         handleUpdateState(mTmpState, arg);
         final boolean changed = mTmpState.copyTo(mState);
@@ -161,7 +158,6 @@
         private static final int REFRESH_STATE = 4;
         private static final int SHOW_DETAIL = 5;
         private static final int USER_SWITCH = 6;
-        private static final int SHOWN = 7;
 
         private H(Looper looper) {
             super(looper);
@@ -189,9 +185,6 @@
                 } else if (msg.what == USER_SWITCH) {
                     name = "handleUserSwitch";
                     handleUserSwitch(msg.arg1);
-                } else if (msg.what == SHOWN) {
-                    name = "handleShown";
-                    handleShown(msg.arg1 != 0);
                 }
             } catch (Throwable t) {
                 final String error = "Error in " + name;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java b/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java
index 4debaa9..3ed3d30 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java
@@ -21,10 +21,10 @@
 import android.os.Handler;
 import android.provider.Settings.Secure;
 
-import com.android.systemui.statusbar.policy.Disposable;
+import com.android.systemui.statusbar.policy.Listenable;
 
 /** Helper for managing a secure setting. **/
-public abstract class SecureSetting extends ContentObserver implements Disposable {
+public abstract class SecureSetting extends ContentObserver implements Listenable {
     private final Context mContext;
     private final String mSettingName;
 
@@ -38,8 +38,7 @@
     }
 
     public void rebindForCurrentUser() {
-        mContext.getContentResolver().registerContentObserver(
-                Secure.getUriFor(mSettingName), false, this);
+        setListening(true);
     }
 
     public int getValue() {
@@ -51,8 +50,13 @@
     }
 
     @Override
-    public void dispose() {
-        mContext.getContentResolver().unregisterContentObserver(this);
+    public void setListening(boolean listening) {
+        if (listening) {
+            mContext.getContentResolver().registerContentObserver(
+                    Secure.getUriFor(mSettingName), false, this);
+        } else {
+            mContext.getContentResolver().unregisterContentObserver(this);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index 5fe8422..0c54040 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -39,11 +39,6 @@
                 handleRefreshState(value);
             }
         };
-
-        final IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        mContext.registerReceiver(mReceiver, filter);
-        refreshState();
     }
 
     @Override
@@ -84,9 +79,15 @@
         }
     }
 
-    public void dispose() {
-        mSetting.dispose();
-        mContext.unregisterReceiver(mReceiver);
+    public void setListening(boolean listening) {
+        if (listening) {
+            final IntentFilter filter = new IntentFilter();
+            filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+            mContext.registerReceiver(mReceiver, filter);
+        } else {
+            mContext.unregisterReceiver(mReceiver);
+        }
+        mSetting.setListening(listening);
     }
 
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 60a6047..7a2e2d2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -33,7 +33,11 @@
     public BluetoothTile(Host host) {
         super(host);
         mController = host.getBluetoothController();
-        mController.addStateChangedCallback(mCallback);
+    }
+
+    @Override
+    public boolean supportsDualTargets() {
+        return true;
     }
 
     @Override
@@ -42,8 +46,12 @@
     }
 
     @Override
-    public void dispose() {
-        mController.removeStateChangedCallback(mCallback);
+    public void setListening(boolean listening) {
+        if (listening) {
+            mController.addStateChangedCallback(mCallback);
+        } else {
+            mController.removeStateChangedCallback(mCallback);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java
index 0e9b9a7..dbb112c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java
@@ -51,8 +51,8 @@
     }
 
     @Override
-    public void dispose() {
-        mSetting.dispose();
+    public void setListening(boolean listening) {
+        mSetting.setListening(listening);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index a3eaa2c..58575e4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -35,14 +35,9 @@
 
     private final CastController mController;
 
-    private boolean mShown;
-
     public CastTile(Host host) {
         super(host);
         mController = host.getCastController();
-        if (mController != null) {
-            mController.addCallback(mCallback);
-        }
     }
 
     @Override
@@ -51,9 +46,14 @@
     }
 
     @Override
-    public void dispose() {
+    public void setListening(boolean listening) {
         if (mController == null) return;
-        mController.removeCallback(mCallback);
+        if (listening) {
+            mController.addCallback(mCallback);
+        } else {
+            mController.removeCallback(mCallback);
+        }
+        mController.setDiscovering(listening);
     }
 
     @Override
@@ -64,14 +64,6 @@
     }
 
     @Override
-    protected void handleShown(boolean shown) {
-        if (mShown == shown) return;
-        if (mController == null) return;
-        mShown = shown;
-        mController.setDiscovering(mShown);
-    }
-
-    @Override
     protected void handleClick() {
         mHost.collapsePanels();
         mUiHandler.post(mShowDialog);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 86a4e79..182a0ce 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -38,7 +38,6 @@
     public CellularTile(Host host) {
         super(host);
         mController = host.getNetworkController();
-        mController.addNetworkSignalChangedCallback(mCallback);
     }
 
     @Override
@@ -47,8 +46,12 @@
     }
 
     @Override
-    public void dispose() {
-        mController.removeNetworkSignalChangedCallback(mCallback);
+    public void setListening(boolean listening) {
+        if (listening) {
+            mController.addNetworkSignalChangedCallback(mCallback);
+        } else {
+            mController.removeNetworkSignalChangedCallback(mCallback);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index 66740af..72764e3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -39,8 +39,6 @@
                 handleRefreshState(value);
             }
         };
-
-        refreshState();
     }
 
     @Override
@@ -49,8 +47,8 @@
     }
 
     @Override
-    public void dispose() {
-        mSetting.dispose();
+    public void setListening(boolean listening) {
+        mSetting.setListening(listening);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 1a67afc..0eda922 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -42,7 +42,7 @@
     }
 
     @Override
-    public void dispose() {
+    public void setListening(boolean listening) {
 
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index 176e05c..f40705d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -31,7 +31,6 @@
     public LocationTile(Host host) {
         super(host);
         mController = host.getLocationController();
-        mController.addSettingsChangedCallback(mCallback);
     }
 
     @Override
@@ -39,8 +38,13 @@
         return new BooleanState();
     }
 
-    public void dispose() {
-        mController.removeSettingsChangedCallback(mCallback);
+    @Override
+    public void setListening(boolean listening) {
+        if (listening) {
+            mController.addSettingsChangedCallback(mCallback);
+        } else {
+            mController.removeSettingsChangedCallback(mCallback);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RingerModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RingerModeTile.java
index 36a579c..ebb4cb2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RingerModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RingerModeTile.java
@@ -33,8 +33,6 @@
     public RingerModeTile(Host host) {
         super(host);
         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
-        final IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
-        mContext.registerReceiver(mReceiver, filter);
     }
 
     @Override
@@ -43,8 +41,13 @@
     }
 
     @Override
-    public void dispose() {
-        mContext.unregisterReceiver(mReceiver);
+    public void setListening(boolean listening) {
+        if (listening) {
+            final IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
+            mContext.registerReceiver(mReceiver, filter);
+        } else {
+            mContext.unregisterReceiver(mReceiver);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index d075299..5c5078c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -33,8 +33,6 @@
     public RotationLockTile(Host host) {
         super(host);
         mController = host.getRotationLockController();
-        if (mController == null) return;
-        mController.addRotationLockControllerCallback(mCallback);
     }
 
     @Override
@@ -42,9 +40,13 @@
         return new BooleanState();
     }
 
-    public void dispose() {
+    public void setListening(boolean listening) {
         if (mController == null) return;
-        mController.removeRotationLockControllerCallback(mCallback);
+        if (listening) {
+            mController.addRotationLockControllerCallback(mCallback);
+        } else {
+            mController.removeRotationLockControllerCallback(mCallback);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index e08a6fa..ef7fb89 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -20,6 +20,7 @@
 import android.content.Intent;
 import android.content.res.Resources;
 import android.provider.Settings;
+import android.util.Log;
 
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
@@ -37,7 +38,11 @@
     public WifiTile(Host host) {
         super(host);
         mController = host.getNetworkController();
-        mController.addNetworkSignalChangedCallback(mCallback);
+    }
+
+    @Override
+    public boolean supportsDualTargets() {
+        return true;
     }
 
     @Override
@@ -46,8 +51,12 @@
     }
 
     @Override
-    public void dispose() {
-        mController.removeNetworkSignalChangedCallback(mCallback);
+    public void setListening(boolean listening) {
+        if (listening) {
+            mController.addNetworkSignalChangedCallback(mCallback);
+        } else {
+            mController.removeNetworkSignalChangedCallback(mCallback);
+        }
     }
 
     @Override
@@ -67,8 +76,9 @@
 
     @Override
     protected void handleUpdateState(SignalState state, Object arg) {
-        if (arg == null) return;
         state.visible = true;
+        if (DEBUG) Log.d(TAG, "handleUpdateState arg=" + arg);
+        if (arg == null) return;
         CallbackInfo cb = (CallbackInfo) arg;
 
         boolean wifiConnected = cb.enabled && (cb.wifiSignalIconId > 0) && (cb.enabledDesc != null);
@@ -114,6 +124,18 @@
         boolean activityIn;
         boolean activityOut;
         String wifiSignalContentDescription;
+
+        @Override
+        public String toString() {
+            return new StringBuilder("CallbackInfo[")
+                .append("enabled=").append(enabled)
+                .append(",wifiSignalIconId=").append(wifiSignalIconId)
+                .append(",enabledDesc=").append(enabledDesc)
+                .append(",activityIn=").append(activityIn)
+                .append(",activityOut=").append(activityOut)
+                .append(",wifiSignalContentDescription=").append(wifiSignalContentDescription)
+                .append(']').toString();
+        }
     }
 
     private final NetworkSignalChangedCallback mCallback = new NetworkSignalChangedCallback() {
@@ -121,6 +143,7 @@
         public void onWifiSignalChanged(boolean enabled, int wifiSignalIconId,
                 boolean activityIn, boolean activityOut,
                 String wifiSignalContentDescriptionId, String description) {
+            if (DEBUG) Log.d(TAG, "onWifiSignalChanged enabled=" + enabled);
             final CallbackInfo info = new CallbackInfo();
             info.enabled = enabled;
             info.wifiSignalIconId = wifiSignalIconId;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeTile.java
index 83918e8..dbfef0c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeTile.java
@@ -17,6 +17,7 @@
 package com.android.systemui.qs.tiles;
 
 import android.content.Context;
+import android.util.Log;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -33,7 +34,6 @@
     public ZenModeTile(Host host) {
         super(host);
         mController = host.getZenModeController();
-        mController.addCallback(mCallback);
     }
 
     @Override
@@ -51,8 +51,12 @@
     }
 
     @Override
-    public void dispose() {
-        mController.removeCallback(mCallback);
+    public void setListening(boolean listening) {
+        if (listening) {
+            mController.addCallback(mCallback);
+        } else {
+            mController.removeCallback(mCallback);
+        }
     }
 
     @Override
@@ -77,6 +81,7 @@
     private final ZenModeController.Callback mCallback = new ZenModeController.Callback() {
         @Override
         public void onZenChanged(boolean zen) {
+            if (DEBUG) Log.d(TAG, "onZenChanged " + zen);
             refreshState(zen);
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 1c7119f..5a19881 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -59,6 +59,7 @@
 
     public void addStateChangedCallback(BluetoothStateChangeCallback cb) {
         mChangeCallbacks.add(cb);
+        fireCallback(cb);
     }
 
     @Override
@@ -131,7 +132,11 @@
 
     private void fireCallbacks() {
         for (BluetoothStateChangeCallback cb : mChangeCallbacks) {
-            cb.onBluetoothStateChange(mEnabled);
+            fireCallback(cb);
         }
     }
+
+    private void fireCallback(BluetoothStateChangeCallback cb) {
+        cb.onBluetoothStateChange(mEnabled);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
index 33a85b1..bcd865c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
@@ -28,6 +28,10 @@
     private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
     private final MediaRouter mMediaRouter;
 
+    private boolean mEnabled;
+    private boolean mConnecting;
+    private String mConnectedRouteName;
+
     public CastControllerImpl(Context context) {
         mMediaRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
     }
@@ -35,6 +39,7 @@
     @Override
     public void addCallback(Callback callback) {
         mCallbacks.add(callback);
+        fireStateChanged(callback);
     }
 
     @Override
@@ -76,12 +81,23 @@
         if (connectedRoute != null) {
             connectedRouteName = connectedRoute.getName().toString();
         }
-        fireStateChanged(enabled, connecting, connectedRouteName);
+        synchronized(mCallbacks) {
+            mEnabled = enabled;
+            mConnecting = connecting;
+            mConnectedRouteName = connectedRouteName;
+        }
+        fireStateChanged();
     }
 
-    private void fireStateChanged(boolean enabled, boolean connecting, String connectedRouteName) {
+    private void fireStateChanged() {
         for (Callback callback : mCallbacks) {
-            callback.onStateChanged(enabled, connecting, connectedRouteName);
+            fireStateChanged(callback);
+        }
+    }
+
+    private void fireStateChanged(Callback callback) {
+        synchronized(mCallbacks) {
+            callback.onStateChanged(mEnabled, mConnecting, mConnectedRouteName);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Disposable.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Listenable.java
similarity index 82%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/Disposable.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/policy/Listenable.java
index 158e9c1..4fa59fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Disposable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Listenable.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar.policy;
 
-/** Common interface for items requiring manual cleanup. **/
-public interface Disposable {
-    void dispose();
+/** Common interface for components with an active listening state. **/
+public interface Listenable {
+    void setListening(boolean listening);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
index 9e5ad18..d5b2548 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
@@ -92,6 +92,7 @@
      */
     public void addSettingsChangedCallback(LocationSettingsChangeCallback cb) {
         mSettingsChangeCallbacks.add(cb);
+        locationSettingsChanged(cb);
     }
 
     public void removeSettingsChangedCallback(LocationSettingsChangeCallback cb) {
@@ -204,6 +205,10 @@
         }
     }
 
+    private void locationSettingsChanged(LocationSettingsChangeCallback cb) {
+        cb.onLocationSettingsChanged(isLocationEnabled());
+    }
+
     @Override
     public void onReceive(Context context, Intent intent) {
         final String action = intent.getAction();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java
index 1eb678d..93c4691 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar.policy;
 
-public interface RotationLockController extends Disposable {
+public interface RotationLockController extends Listenable {
     int getRotationLockOrientation();
     boolean isRotationLockAffordanceVisible();
     boolean isRotationLocked();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
index caa07ef..c3bcd94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
@@ -39,12 +39,12 @@
 
     public RotationLockControllerImpl(Context context) {
         mContext = context;
-        RotationPolicy.registerRotationPolicyListener(mContext,
-                mRotationPolicyListener, UserHandle.USER_ALL);
+        setListening(true);
     }
 
     public void addRotationLockControllerCallback(RotationLockControllerCallback callback) {
         mCallbacks.add(callback);
+        notifyChanged(callback);
     }
 
     public void removeRotationLockControllerCallback(RotationLockControllerCallback callback) {
@@ -68,14 +68,23 @@
     }
 
     @Override
-    public void dispose() {
-        RotationPolicy.unregisterRotationPolicyListener(mContext, mRotationPolicyListener);
+    public void setListening(boolean listening) {
+        if (listening) {
+            RotationPolicy.registerRotationPolicyListener(mContext, mRotationPolicyListener,
+                    UserHandle.USER_ALL);
+        } else {
+            RotationPolicy.unregisterRotationPolicyListener(mContext, mRotationPolicyListener);
+        }
     }
 
     private void notifyChanged() {
         for (RotationLockControllerCallback callback : mCallbacks) {
-            callback.onRotationLockStateChanged(RotationPolicy.isRotationLocked(mContext),
-                    RotationPolicy.isRotationLockToggleVisible(mContext));
+            notifyChanged(callback);
         }
     }
+
+    private void notifyChanged(RotationLockControllerCallback callback) {
+        callback.onRotationLockStateChanged(RotationPolicy.isRotationLocked(mContext),
+                RotationPolicy.isRotationLockToggleVisible(mContext));
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index d760f78..adf2935 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -52,6 +52,7 @@
                 fireZenChanged(value != 0);
             }
         };
+        mSetting.setListening(true);
         mNoMan = INotificationManager.Stub.asInterface(
                 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
     }