Add support for auto-adding tiles

hotspot, color inversion, data saver, and work profiles should add
themselves when they first become applicable.

Also refactor the availability flow a little bit.

Change-Id: Iaed89059540a98fefd4d26ce69edc0dde987b992
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index e2b3777..1471622 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -44,6 +44,10 @@
         Key.DND_NONE_SELECTED,
         Key.DND_FAVORITE_ZEN,
         Key.TV_PICTURE_IN_PICTURE_ONBOARDING_SHOWN,
+        Key.QS_HOTSPOT_ADDED,
+        Key.QS_DATA_SAVER_ADDED,
+        Key.QS_INVERT_COLORS_ADDED,
+        Key.QS_WORK_ADDED,
     })
     public @interface Key {
         String OVERVIEW_SEARCH_APP_WIDGET_ID = "searchAppWidgetId";
@@ -60,6 +64,10 @@
         String DND_NONE_SELECTED = "DndNoneSelected";
         String DND_FAVORITE_ZEN = "DndFavoriteZen";
         String TV_PICTURE_IN_PICTURE_ONBOARDING_SHOWN = "TvPictureInPictureOnboardingShown";
+        String QS_HOTSPOT_ADDED = "QsHotspotAdded";
+        String QS_DATA_SAVER_ADDED = "QsDataSaverAdded";
+        String QS_INVERT_COLORS_ADDED = "QsInvertColorsAdded";
+        String QS_WORK_ADDED = "QsWorkAdded";
     }
 
     public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index d79f4d4..e363b76 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -32,6 +32,7 @@
 import com.android.settingslib.RestrictedLockUtils;
 import com.android.systemui.qs.QSTile.State;
 import com.android.systemui.qs.external.TileServices;
+import com.android.systemui.statusbar.phone.ManagedProfileController;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BluetoothController;
 import com.android.systemui.statusbar.policy.CastController;
@@ -114,6 +115,15 @@
         return null; // optional
     }
 
+    /**
+     * Is a startup check whether this device currently supports this tile.
+     * Should not be used to conditionally hide tiles.  Only checked on tile
+     * creation or whether should be shown in edit screen.
+     */
+    public boolean isAvailable() {
+        return true;
+    }
+
     public interface DetailAdapter {
         CharSequence getTitle();
         Boolean getToggleState();
@@ -392,6 +402,7 @@
         TileServices getTileServices();
         DisplayController getDisplayController();
         void removeTile(String tileSpec);
+        ManagedProfileController getManagedProfileController();
 
 
         public interface Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 29f8af2..2a10c0b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -55,14 +55,14 @@
     private void addSystemTiles(QSTileHost host) {
         boolean hasColorMod = host.getDisplayController().isEnabled();
         String possible = mContext.getString(R.string.quick_settings_tiles_default)
-                + ",hotspot,inversion,saver" + (hasColorMod ? ",colors" : "");
+                + ",hotspot,inversion,saver,work" + (hasColorMod ? ",colors" : "");
         String[] possibleTiles = possible.split(",");
         final Handler qsHandler = new Handler(host.getLooper());
         final Handler mainHandler = new Handler(Looper.getMainLooper());
         for (int i = 0; i < possibleTiles.length; i++) {
             final String spec = possibleTiles[i];
             final QSTile<?> tile = host.createTile(spec);
-            if (tile == null) {
+            if (tile == null || !tile.isAvailable()) {
                 continue;
             }
             tile.setListening(true);
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 1dce053..c4b7944 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -146,8 +146,9 @@
         }
     }
 
-    public static boolean isSupported(Host host) {
-        return host.getBluetoothController().isBluetoothSupported();
+    @Override
+    public boolean isAvailable() {
+        return mController.isBluetoothSupported();
     }
 
     private final BluetoothController.Callback mCallback = new BluetoothController.Callback() {
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 c3a2ebe..15617c7f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -136,6 +136,11 @@
         return MetricsEvent.QS_CELLULAR;
     }
 
+    @Override
+    public boolean isAvailable() {
+        return mController.hasMobileDataFeature();
+    }
+
     // Remove the period from the network name
     public static String removeTrailingPeriod(String string) {
         if (string == null) return null;
@@ -146,10 +151,6 @@
         return string;
     }
 
-    public static boolean isSupported(Host host) {
-        return host.getNetworkController().hasMobileDataFeature();
-    }
-
     private static final class CallbackInfo {
         boolean enabled;
         boolean wifiEnabled;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 58872ec..29ca06b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -179,6 +179,11 @@
         }
     }
 
+    @Override
+    public boolean isAvailable() {
+        return isVisible(mContext);
+    }
+
     private final OnSharedPreferenceChangeListener mPrefListener
             = new OnSharedPreferenceChangeListener() {
         @Override
@@ -206,10 +211,6 @@
         }
     };
 
-    public static boolean isSupported(Host host) {
-        return isVisible(host.getContext());
-    }
-
     private final class DndDetailAdapter implements DetailAdapter, OnAttachStateChangeListener {
 
         @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 ac4dfd5..7a58f15 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -181,6 +181,11 @@
         }
     }
 
+    @Override
+    public boolean isAvailable() {
+        return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
+    }
+
     private static String removeDoubleQuotes(String string) {
         if (string == null) return null;
         final int length = string.length();
@@ -190,10 +195,6 @@
         return string;
     }
 
-    public static boolean isSupported(Host host) {
-        return host.getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
-    }
-
     protected static final class CallbackInfo {
         boolean enabled;
         boolean connected;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index a94973c..053a98a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -16,40 +16,25 @@
 
 package com.android.systemui.qs.tiles;
 
-import android.app.ActivityManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.UserInfo;
-import android.os.UserHandle;
-import android.os.UserManager;
-
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.MetricsProto.MetricsEvent;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
-
-import java.util.LinkedList;
-import java.util.List;
+import com.android.systemui.statusbar.phone.ManagedProfileController;
 
 /** Quick settings tile: Work profile on/off */
-public class WorkModeTile extends QSTile<QSTile.BooleanState> {
+public class WorkModeTile extends QSTile<QSTile.BooleanState> implements
+        ManagedProfileController.Callback {
     private final AnimationIcon mEnable =
             new AnimationIcon(R.drawable.ic_signal_workmode_enable_animation);
     private final AnimationIcon mDisable =
             new AnimationIcon(R.drawable.ic_signal_workmode_disable_animation);
 
-    private boolean mListening;
-
-    private UserManager mUserManager;
-    private List<UserInfo> mProfiles;
+    private final ManagedProfileController mProfileController;
 
     public WorkModeTile(Host host) {
         super(host);
-        mUserManager = UserManager.get(mContext);
-        mProfiles = new LinkedList<UserInfo>();
-        reloadManagedProfiles(UserHandle.USER_CURRENT);
+        mProfileController = host.getManagedProfileController();
     }
 
     @Override
@@ -58,58 +43,41 @@
     }
 
     @Override
+    public void setListening(boolean listening) {
+        if (listening) {
+            mProfileController.addCallback(this);
+        } else {
+            mProfileController.removeCallback(this);
+        }
+    }
+
+    @Override
     public void handleClick() {
         MetricsLogger.action(mContext, getMetricsCategory(), !mState.value);
-        setWorkModeEnabled(!mState.value);
+        mProfileController.setWorkModeEnabled(!mState.value);
     }
 
-    private void reloadManagedProfiles(int userHandle) {
-        synchronized (mProfiles) {
-            mProfiles.clear();
-
-            if (userHandle == UserHandle.USER_CURRENT) {
-                userHandle = ActivityManager.getCurrentUser();
-            }
-            for (UserInfo ui : mUserManager.getEnabledProfiles(userHandle)) {
-                if (ui.isManagedProfile()) {
-                    mProfiles.add(ui);
-                }
-            }
-        }
+    @Override
+    public boolean isAvailable() {
+        return mProfileController.hasActiveProfile();
     }
 
-    private boolean hasActiveProfile() {
-        synchronized (mProfiles) {
-            return mProfiles.size() > 0;
-        }
+    @Override
+    public void onManagedProfileChanged() {
+        refreshState(mProfileController.isWorkModeEnabled());
     }
 
-    private boolean isWorkModeEnabled() {
-        synchronized (mProfiles) {
-            for (UserInfo ui : mProfiles) {
-                if (ui.isQuietModeEnabled()) {
-                    return false;
-                }
-            }
-            return true;
-        }
-    }
-
-    private void refreshQuietModeState(boolean backgroundRefresh) {
-        refreshState(isWorkModeEnabled());
+    @Override
+    public void onManagedProfileRemoved() {
+        mHost.removeTile(getTileSpec());
     }
 
     @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
-        if (!hasActiveProfile()) {
-            mHost.removeTile(getTileSpec());
-            return;
-        }
-
         if (arg instanceof Boolean) {
             state.value = (Boolean) arg;
         } else {
-            state.value = isWorkModeEnabled();
+            state.value = mProfileController.isWorkModeEnabled();
         }
 
         state.label = mContext.getString(R.string.quick_settings_work_mode_label);
@@ -137,64 +105,4 @@
             return mContext.getString(R.string.accessibility_quick_settings_work_mode_changed_off);
         }
     }
-
-    @Override
-    public void setListening(boolean listening) {
-        if (mListening == listening) {
-            return;
-        }
-        mListening = listening;
-        if (listening) {
-            reloadManagedProfiles(UserHandle.USER_CURRENT);
-
-            final IntentFilter filter = new IntentFilter();
-            filter.addAction(Intent.ACTION_USER_SWITCHED);
-            filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
-            filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
-            filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED);
-            mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, null);
-        } else {
-            mContext.unregisterReceiver(mReceiver);
-        }
-    }
-
-    private void setWorkModeEnabled(boolean enabled) {
-        synchronized (mProfiles) {
-            for (UserInfo ui : mProfiles) {
-                mUserManager.setQuietModeEnabled(ui.id, !enabled);
-            }
-        }
-    }
-
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            final int targetUser;
-            final boolean isBackgroundRefresh;
-            switch (action) {
-                case Intent.ACTION_USER_SWITCHED:
-                    targetUser = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
-                            UserHandle.USER_CURRENT);
-                    isBackgroundRefresh = true;
-                    break;
-                case Intent.ACTION_MANAGED_PROFILE_ADDED:
-                case Intent.ACTION_MANAGED_PROFILE_REMOVED:
-                    targetUser = UserHandle.USER_CURRENT;
-                    isBackgroundRefresh = true;
-                    break;
-                case Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED:
-                    targetUser = UserHandle.USER_CURRENT;
-                    isBackgroundRefresh = false;
-                    break;
-               default:
-                   targetUser = UserHandle.USER_NULL;
-                   isBackgroundRefresh = false;
-            }
-            if (targetUser != UserHandle.USER_NULL) {
-                reloadManagedProfiles(targetUser);
-                refreshQuietModeState(isBackgroundRefresh);
-            }
-        }
-    };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
new file mode 100644
index 0000000..b742479
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.content.Context;
+import android.os.Handler;
+import android.provider.Settings.Secure;
+import com.android.systemui.Prefs;
+import com.android.systemui.Prefs.Key;
+import com.android.systemui.qs.SecureSetting;
+import com.android.systemui.statusbar.policy.DataSaverController;
+import com.android.systemui.statusbar.policy.DataSaverController.Listener;
+import com.android.systemui.statusbar.policy.HotspotController;
+import com.android.systemui.statusbar.policy.HotspotController.Callback;
+
+/**
+ * Manages which tiles should be automatically added to QS.
+ */
+public class AutoTileManager {
+
+    private final Context mContext;
+    private final QSTileHost mHost;
+    private final Handler mHandler;
+
+    public AutoTileManager(Context context, QSTileHost host) {
+        mContext = context;
+        mHost = host;
+        mHandler = new Handler(mHost.getLooper());
+        if (!Prefs.getBoolean(context, Key.QS_HOTSPOT_ADDED, false)) {
+            host.getHotspotController().addCallback(mHotspotCallback);
+        }
+        if (!Prefs.getBoolean(context, Key.QS_DATA_SAVER_ADDED, false)) {
+            host.getNetworkController().getDataSaverController().addListener(mDataSaverListener);
+        }
+        if (!Prefs.getBoolean(context, Key.QS_INVERT_COLORS_ADDED, false)) {
+            mColorsSetting = new SecureSetting(mContext, mHandler,
+                    Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED) {
+                @Override
+                protected void handleValueChanged(int value, boolean observedChange) {
+                    if (value != 0) {
+                        mHost.addTile("inversion");
+                        Prefs.putBoolean(mContext, Key.QS_INVERT_COLORS_ADDED, true);
+                        mHandler.post(new Runnable() {
+                            @Override
+                            public void run() {
+                                mColorsSetting.setListening(false);
+                            }
+                        });
+                    }
+                }
+            };
+            mColorsSetting.setListening(true);
+        }
+        if (!Prefs.getBoolean(context, Key.QS_WORK_ADDED, false)) {
+            host.getManagedProfileController().addCallback(mProfileCallback);
+        }
+    }
+
+    public void destroy() {
+        // TODO: Remove any registered listeners.
+    }
+
+    private final ManagedProfileController.Callback mProfileCallback =
+            new ManagedProfileController.Callback() {
+                @Override
+                public void onManagedProfileChanged() {
+                    if (mHost.getManagedProfileController().hasActiveProfile()) {
+                        mHost.addTile("work");
+                        Prefs.putBoolean(mContext, Key.QS_WORK_ADDED, true);
+                        mHandler.post(new Runnable() {
+                            @Override
+                            public void run() {
+                                mHost.getManagedProfileController().removeCallback(
+                                        mProfileCallback);
+                            }
+                        });
+                    }
+                }
+
+                @Override
+                public void onManagedProfileRemoved() {
+                }
+            };
+
+    private SecureSetting mColorsSetting;
+
+    private final DataSaverController.Listener mDataSaverListener = new Listener() {
+        @Override
+        public void onDataSaverChanged(boolean isDataSaving) {
+            if (isDataSaving) {
+                mHost.addTile("saver");
+                Prefs.putBoolean(mContext, Key.QS_DATA_SAVER_ADDED, true);
+                mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        mHost.getNetworkController().getDataSaverController().remListener(
+                                mDataSaverListener);
+                    }
+                });
+            }
+        }
+    };
+
+    private final HotspotController.Callback mHotspotCallback = new Callback() {
+        @Override
+        public void onHotspotChanged(boolean enabled) {
+            if (enabled) {
+                mHost.addTile("hotspot");
+                Prefs.putBoolean(mContext, Key.QS_HOTSPOT_ADDED, true);
+                mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        mHost.getHotspotController().removeCallback(mHotspotCallback);
+                    }
+                });
+            }
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileController.java
new file mode 100644
index 0000000..63ee0c0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileController.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+public class ManagedProfileController {
+
+    private final List<Callback> mCallbacks = new ArrayList<>();
+
+    private final Context mContext;
+    private final UserManager mUserManager;
+    private final LinkedList<UserInfo> mProfiles;
+    private boolean mListening;
+    private int mCurrentUser;
+
+    public ManagedProfileController(QSTileHost host) {
+        mContext = host.getContext();
+        mUserManager = UserManager.get(mContext);
+        mProfiles = new LinkedList<UserInfo>();
+    }
+
+    public void addCallback(Callback callback) {
+        mCallbacks.add(callback);
+        if (mCallbacks.size() == 1) {
+            setListening(true);
+        }
+        callback.onManagedProfileChanged();
+    }
+
+    public void removeCallback(Callback callback) {
+        if (mCallbacks.remove(callback) && mCallbacks.size() == 0) {
+            setListening(false);
+        }
+    }
+
+    public void setWorkModeEnabled(boolean enabled) {
+        synchronized (mProfiles) {
+            for (UserInfo ui : mProfiles) {
+                mUserManager.setQuietModeEnabled(ui.id, !enabled);
+            }
+        }
+    }
+
+    private void reloadManagedProfiles() {
+        synchronized (mProfiles) {
+            boolean hadProfile = mProfiles.size() > 0;
+            int user = ActivityManager.getCurrentUser();
+            mProfiles.clear();
+
+            for (UserInfo ui : mUserManager.getEnabledProfiles(user)) {
+                if (ui.isManagedProfile()) {
+                    mProfiles.add(ui);
+                }
+            }
+            if (mProfiles.size() == 0 && hadProfile && (user == mCurrentUser)) {
+                for (Callback callback : mCallbacks) {
+                    callback.onManagedProfileRemoved();
+                }
+            }
+            mCurrentUser = user;
+        }
+    }
+
+    public boolean hasActiveProfile() {
+        if (!mListening) reloadManagedProfiles();
+        synchronized (mProfiles) {
+            return mProfiles.size() > 0;
+        }
+    }
+
+    public boolean isWorkModeEnabled() {
+        if (!mListening) reloadManagedProfiles();
+        synchronized (mProfiles) {
+            for (UserInfo ui : mProfiles) {
+                if (ui.isQuietModeEnabled()) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+
+    private void setListening(boolean listening) {
+        mListening = listening;
+        if (listening) {
+            reloadManagedProfiles();
+
+            final IntentFilter filter = new IntentFilter();
+            filter.addAction(Intent.ACTION_USER_SWITCHED);
+            filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
+            filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
+            filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED);
+            mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, null);
+        } else {
+            mContext.unregisterReceiver(mReceiver);
+        }
+    }
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            reloadManagedProfiles();
+            for (Callback callback : mCallbacks) {
+                callback.onManagedProfileChanged();
+            }
+        }
+    };
+
+    public interface Callback {
+        void onManagedProfileChanged();
+        void onManagedProfileRemoved();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 4393e75..da2c20d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -109,6 +109,8 @@
 
     private final List<Callback> mCallbacks = new ArrayList<>();
     private final DisplayController mDisplayController;
+    private final AutoTileManager mAutoTiles;
+    private final ManagedProfileController mProfileController;
     private View mHeader;
 
     public QSTileHost(Context context, PhoneStatusBar statusBar,
@@ -136,6 +138,7 @@
         mBattery = battery;
         mIconController = iconController;
         mDisplayController = new DisplayController(mContext);
+        mProfileController = new ManagedProfileController(this);
 
         final HandlerThread ht = new HandlerThread(QSTileHost.class.getSimpleName(),
                 Process.THREAD_PRIORITY_BACKGROUND);
@@ -144,6 +147,7 @@
 
         mServices = new TileServices(this, mLooper);
 
+        mAutoTiles = new AutoTileManager(context, this);
         TunerService.get(mContext).addTunable(this, TILES_SETTING);
     }
 
@@ -156,6 +160,7 @@
     }
 
     public void destroy() {
+        mAutoTiles.destroy();
         TunerService.get(mContext).removeTunable(this);
     }
 
@@ -290,6 +295,10 @@
         return mDisplayController;
     }
 
+    public ManagedProfileController getManagedProfileController() {
+        return mProfileController;
+    }
+
     @Override
     public void onTuningChanged(String key, String newValue) {
         if (!TILES_SETTING.equals(key)) {
@@ -315,7 +324,7 @@
                 if (DEBUG) Log.d(TAG, "Creating tile: " + tileSpec);
                 try {
                     QSTile<?> tile = createTile(tileSpec);
-                    if (tile != null) {
+                    if (tile != null && tile.isAvailable()) {
                         tile.setTileSpec(tileSpec);
                         newTiles.put(tileSpec, tile);
                     }
@@ -341,6 +350,16 @@
                 TextUtils.join(",", specs), ActivityManager.getCurrentUser());
     }
 
+    public void addTile(String spec) {
+        if (mTileSpecs.contains(spec)) {
+            return;
+        }
+        ArrayList<String> specs = new ArrayList<>(mTileSpecs);
+        specs.add(spec);
+        Settings.Secure.putStringForUser(mContext.getContentResolver(), TILES_SETTING,
+                TextUtils.join(",", specs), ActivityManager.getCurrentUser());
+    }
+
     public void addTile(ComponentName tile) {
         List<String> newSpecs = new ArrayList<>(mTileSpecs);
         newSpecs.add(0, CustomTile.toSpec(tile));
@@ -387,14 +406,10 @@
     }
 
     public QSTile<?> createTile(String tileSpec) {
-        if (tileSpec.equals("wifi")) return WifiTile.isSupported(this)
-                ? new WifiTile(this) : null;
-        else if (tileSpec.equals("bt")) return BluetoothTile.isSupported(this)
-                ? new BluetoothTile(this) : null;
-        else if (tileSpec.equals("cell")) return CellularTile.isSupported(this)
-                ? new CellularTile(this) : null;
-        else if (tileSpec.equals("dnd")) return DndTile.isSupported(this)
-                ? new DndTile(this) : null;
+        if (tileSpec.equals("wifi")) return new WifiTile(this);
+        else if (tileSpec.equals("bt")) return new BluetoothTile(this);
+        else if (tileSpec.equals("cell")) return new CellularTile(this);
+        else if (tileSpec.equals("dnd")) return new DndTile(this);
         else if (tileSpec.equals("inversion")) return new ColorInversionTile(this);
         else if (tileSpec.equals("airplane")) return new AirplaneModeTile(this);
         else if (tileSpec.equals("work")) return new WorkModeTile(this);