Move out test utilities to a Testables library

Test: runtest --path frameworks/base/tests/testablets/tests
        && runtest systemui

Change-Id: Ideef4aef5f26136b1741c556b9be5884f38842a0
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index bb7e19d..4dfaf45 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -30,6 +30,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.PluginDependencyProvider;
 import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.plugins.PluginManagerImpl;
 import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
 import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
 import com.android.systemui.statusbar.phone.ManagedProfileController;
@@ -73,6 +74,7 @@
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
 import com.android.systemui.tuner.TunerService;
+import com.android.systemui.tuner.TunerServiceImpl;
 import com.android.systemui.util.leak.GarbageMonitor;
 import com.android.systemui.util.leak.LeakDetector;
 import com.android.systemui.util.leak.LeakReporter;
@@ -200,7 +202,7 @@
                 new DeviceProvisionedControllerImpl(mContext));
 
         mProviders.put(PluginManager.class, () ->
-                new PluginManager(mContext));
+                new PluginManagerImpl(mContext));
 
         mProviders.put(AssistManager.class, () ->
                 new AssistManager(getDependency(DeviceProvisionedController.class), mContext));
@@ -223,7 +225,7 @@
                 getDependency(LeakReporter.class)));
 
         mProviders.put(TunerService.class, () ->
-                new TunerService(mContext));
+                new TunerServiceImpl(mContext));
 
         mProviders.put(StatusBarWindowManager.class, () ->
                 new StatusBarWindowManager(mContext));
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java
index 79f78c9..07ac52d 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java
@@ -62,10 +62,10 @@
     final PluginHandler mPluginHandler;
     private final boolean isDebuggable;
     private final PackageManager mPm;
-    private final PluginManager mManager;
+    private final PluginManagerImpl mManager;
 
     PluginInstanceManager(Context context, String action, PluginListener<T> listener,
-            boolean allowMultiple, Looper looper, VersionInfo version, PluginManager manager) {
+            boolean allowMultiple, Looper looper, VersionInfo version, PluginManagerImpl manager) {
         this(context, context.getPackageManager(), action, listener, allowMultiple, looper, version,
                 manager, Build.IS_DEBUGGABLE);
     }
@@ -73,7 +73,7 @@
     @VisibleForTesting
     PluginInstanceManager(Context context, PackageManager pm, String action,
             PluginListener<T> listener, boolean allowMultiple, Looper looper, VersionInfo version,
-            PluginManager manager, boolean debuggable) {
+            PluginManagerImpl manager, boolean debuggable) {
         mMainHandler = new MainHandler(Looper.getMainLooper());
         mPluginHandler = new PluginHandler(looper);
         mManager = manager;
@@ -346,7 +346,7 @@
                                 .setContentText("Check to see if an OTA is available.\n"
                                         + e.getMessage());
                     }
-                    Intent i = new Intent(PluginManager.DISABLE_PLUGIN).setData(
+                    Intent i = new Intent(PluginManagerImpl.DISABLE_PLUGIN).setData(
                             Uri.parse("package://" + component.flattenToString()));
                     PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i, 0);
                     nb.addAction(new Action.Builder(null, "Disable plugin", pi).build());
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginManager.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginManager.java
index 9ad862d..298eaf1 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginManager.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 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
@@ -14,276 +14,33 @@
 
 package com.android.systemui.plugins;
 
-import android.app.Notification;
-import android.app.Notification.Action;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.SystemProperties;
-import android.os.UserHandle;
 import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.ArraySet;
 
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.systemui.Dependency;
-import com.android.systemui.plugins.PluginInstanceManager.PluginContextWrapper;
-import com.android.systemui.plugins.PluginInstanceManager.PluginInfo;
 import com.android.systemui.plugins.annotations.ProvidesInterface;
 
-import dalvik.system.PathClassLoader;
+public interface PluginManager {
 
-import java.lang.Thread.UncaughtExceptionHandler;
-import java.util.Map;
-
-/**
- * @see Plugin
- */
-public class PluginManager extends BroadcastReceiver {
-
-    public static final String PLUGIN_CHANGED = "com.android.systemui.action.PLUGIN_CHANGED";
-
-    static final String DISABLE_PLUGIN = "com.android.systemui.action.DISABLE_PLUGIN";
+    String PLUGIN_CHANGED = "com.android.systemui.action.PLUGIN_CHANGED";
 
     // must be one of the channels created in NotificationChannels.java
-    static final String NOTIFICATION_CHANNEL_ID = "ALR";
+    String NOTIFICATION_CHANNEL_ID = "ALR";
 
-    private static PluginManager sInstance;
+    <T extends Plugin> T getOneShotPlugin(Class<T> cls);
+    <T extends Plugin> T getOneShotPlugin(String action, Class<?> cls);
 
-    private final HandlerThread mBackgroundThread;
-    private final ArrayMap<PluginListener<?>, PluginInstanceManager> mPluginMap
-            = new ArrayMap<>();
-    private final Map<String, ClassLoader> mClassLoaders = new ArrayMap<>();
-    private final ArraySet<String> mOneShotPackages = new ArraySet<>();
-    private final Context mContext;
-    private final PluginInstanceManagerFactory mFactory;
-    private final boolean isDebuggable;
-    private final PluginPrefs mPluginPrefs;
-    private ClassLoaderFilter mParentClassLoader;
-    private boolean mListening;
-    private boolean mHasOneShot;
+    <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls);
+    <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls,
+            boolean allowMultiple);
+    <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
+            Class<?> cls);
+    <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
+            Class cls, boolean allowMultiple);
 
-    public PluginManager(Context context) {
-        this(context, new PluginInstanceManagerFactory(),
-                Build.IS_DEBUGGABLE, Thread.getDefaultUncaughtExceptionHandler());
-    }
+    void removePluginListener(PluginListener<?> listener);
 
-    @VisibleForTesting
-    PluginManager(Context context, PluginInstanceManagerFactory factory, boolean debuggable,
-            UncaughtExceptionHandler defaultHandler) {
-        mContext = context;
-        mFactory = factory;
-        mBackgroundThread = new HandlerThread("Plugins");
-        mBackgroundThread.start();
-        isDebuggable = debuggable;
-        mPluginPrefs = new PluginPrefs(mContext);
+    <T> boolean dependsOn(Plugin p, Class<T> cls);
 
-        PluginExceptionHandler uncaughtExceptionHandler = new PluginExceptionHandler(
-                defaultHandler);
-        Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler);
-        if (isDebuggable) {
-            new Handler(mBackgroundThread.getLooper()).post(() -> {
-                // Plugin dependencies that don't have another good home can go here, but
-                // dependencies that have better places to init can happen elsewhere.
-                Dependency.get(PluginDependencyProvider.class)
-                        .allowPluginDependency(ActivityStarter.class);
-            });
-        }
-    }
-
-    public <T extends Plugin> T getOneShotPlugin(Class<T> cls) {
-        ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
-        if (info == null) {
-            throw new RuntimeException(cls + " doesn't provide an interface");
-        }
-        if (TextUtils.isEmpty(info.action())) {
-            throw new RuntimeException(cls + " doesn't provide an action");
-        }
-        return getOneShotPlugin(info.action(), cls);
-    }
-
-    public <T extends Plugin> T getOneShotPlugin(String action, Class<?> cls) {
-        if (!isDebuggable) {
-            // Never ever ever allow these on production builds, they are only for prototyping.
-            return null;
-        }
-        if (Looper.myLooper() != Looper.getMainLooper()) {
-            throw new RuntimeException("Must be called from UI thread");
-        }
-        PluginInstanceManager<T> p = mFactory.createPluginInstanceManager(mContext, action, null,
-                false, mBackgroundThread.getLooper(), cls, this);
-        mPluginPrefs.addAction(action);
-        PluginInfo<T> info = p.getPlugin();
-        if (info != null) {
-            mOneShotPackages.add(info.mPackage);
-            mHasOneShot = true;
-            startListening();
-            return info.mPlugin;
-        }
-        return null;
-    }
-
-    public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls) {
-        addPluginListener(listener, cls, false);
-    }
-
-    public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls,
-            boolean allowMultiple) {
-        addPluginListener(getAction(cls), listener, cls, allowMultiple);
-    }
-
-    public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
-            Class<?> cls) {
-        addPluginListener(action, listener, cls, false);
-    }
-
-    public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
-            Class cls, boolean allowMultiple) {
-        if (!isDebuggable) {
-            // Never ever ever allow these on production builds, they are only for prototyping.
-            return;
-        }
-        mPluginPrefs.addAction(action);
-        PluginInstanceManager p = mFactory.createPluginInstanceManager(mContext, action, listener,
-                allowMultiple, mBackgroundThread.getLooper(), cls, this);
-        p.loadAll();
-        mPluginMap.put(listener, p);
-        startListening();
-    }
-
-    public void removePluginListener(PluginListener<?> listener) {
-        if (!isDebuggable) {
-            // Never ever ever allow these on production builds, they are only for prototyping.
-            return;
-        }
-        if (!mPluginMap.containsKey(listener)) return;
-        mPluginMap.remove(listener).destroy();
-        stopListening();
-    }
-
-    private void startListening() {
-        if (mListening) return;
-        mListening = true;
-        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
-        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        filter.addAction(PLUGIN_CHANGED);
-        filter.addAction(DISABLE_PLUGIN);
-        filter.addDataScheme("package");
-        mContext.registerReceiver(this, filter);
-        filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
-        mContext.registerReceiver(this, filter);
-    }
-
-    private void stopListening() {
-        // Never stop listening if a one-shot is present.
-        if (!mListening || mHasOneShot) return;
-        mListening = false;
-        mContext.unregisterReceiver(this);
-    }
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
-            for (PluginInstanceManager manager : mPluginMap.values()) {
-                manager.loadAll();
-            }
-        } else if (DISABLE_PLUGIN.equals(intent.getAction())) {
-            Uri uri = intent.getData();
-            ComponentName component = ComponentName.unflattenFromString(
-                    uri.toString().substring(10));
-            mContext.getPackageManager().setComponentEnabledSetting(component,
-                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
-                    PackageManager.DONT_KILL_APP);
-            mContext.getSystemService(NotificationManager.class).cancel(component.getClassName(),
-                    SystemMessage.NOTE_PLUGIN);
-        } else {
-            Uri data = intent.getData();
-            String pkg = data.getEncodedSchemeSpecificPart();
-            if (mOneShotPackages.contains(pkg)) {
-                int icon = mContext.getResources().getIdentifier("tuner", "drawable",
-                        mContext.getPackageName());
-                int color = Resources.getSystem().getIdentifier(
-                        "system_notification_accent_color", "color", "android");
-                String label = pkg;
-                try {
-                    PackageManager pm = mContext.getPackageManager();
-                    label = pm.getApplicationInfo(pkg, 0).loadLabel(pm).toString();
-                } catch (NameNotFoundException e) {
-                }
-                // Localization not required as this will never ever appear in a user build.
-                final Notification.Builder nb =
-                        new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
-                                .setSmallIcon(icon)
-                                .setWhen(0)
-                                .setShowWhen(false)
-                                .setPriority(Notification.PRIORITY_MAX)
-                                .setVisibility(Notification.VISIBILITY_PUBLIC)
-                                .setColor(mContext.getColor(color))
-                                .setContentTitle("Plugin \"" + label + "\" has updated")
-                                .setContentText("Restart SysUI for changes to take effect.");
-                Intent i = new Intent("com.android.systemui.action.RESTART").setData(
-                            Uri.parse("package://" + pkg));
-                PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i, 0);
-                nb.addAction(new Action.Builder(null, "Restart SysUI", pi).build());
-                mContext.getSystemService(NotificationManager.class).notifyAsUser(pkg,
-                        SystemMessage.NOTE_PLUGIN, nb.build(), UserHandle.ALL);
-            }
-            clearClassLoader(pkg);
-            if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
-                for (PluginInstanceManager manager : mPluginMap.values()) {
-                    manager.onPackageChange(pkg);
-                }
-            } else {
-                for (PluginInstanceManager manager : mPluginMap.values()) {
-                    manager.onPackageRemoved(pkg);
-                }
-            }
-        }
-    }
-
-    public ClassLoader getClassLoader(String sourceDir, String pkg) {
-        if (mClassLoaders.containsKey(pkg)) {
-            return mClassLoaders.get(pkg);
-        }
-        ClassLoader classLoader = new PathClassLoader(sourceDir, getParentClassLoader());
-        mClassLoaders.put(pkg, classLoader);
-        return classLoader;
-    }
-
-    private void clearClassLoader(String pkg) {
-        mClassLoaders.remove(pkg);
-    }
-
-    ClassLoader getParentClassLoader() {
-        if (mParentClassLoader == null) {
-            // Lazily load this so it doesn't have any effect on devices without plugins.
-            mParentClassLoader = new ClassLoaderFilter(getClass().getClassLoader(),
-                    "com.android.systemui.plugin");
-        }
-        return mParentClassLoader;
-    }
-
-    public Context getContext(ApplicationInfo info, String pkg) throws NameNotFoundException {
-        ClassLoader classLoader = getClassLoader(info.sourceDir, pkg);
-        return new PluginContextWrapper(mContext.createApplicationContext(info, 0), classLoader);
-    }
-
-    public static <P> String getAction(Class<P> cls) {
+    static <P> String getAction(Class<P> cls) {
         ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
         if (info == null) {
             throw new RuntimeException(cls + " doesn't provide an interface");
@@ -293,82 +50,4 @@
         }
         return info.action();
     }
-
-    public <T> boolean dependsOn(Plugin p, Class<T> cls) {
-        for (int i = 0; i < mPluginMap.size(); i++) {
-            if (mPluginMap.valueAt(i).dependsOn(p, cls)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @VisibleForTesting
-    public static class PluginInstanceManagerFactory {
-        public <T extends Plugin> PluginInstanceManager createPluginInstanceManager(Context context,
-                String action, PluginListener<T> listener, boolean allowMultiple, Looper looper,
-                Class<?> cls, PluginManager manager) {
-            return new PluginInstanceManager(context, action, listener, allowMultiple, looper,
-                    new VersionInfo().addClass(cls), manager);
-        }
-    }
-
-    // This allows plugins to include any libraries or copied code they want by only including
-    // classes from the plugin library.
-    private static class ClassLoaderFilter extends ClassLoader {
-        private final String mPackage;
-        private final ClassLoader mBase;
-
-        public ClassLoaderFilter(ClassLoader base, String pkg) {
-            super(ClassLoader.getSystemClassLoader());
-            mBase = base;
-            mPackage = pkg;
-        }
-
-        @Override
-        protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
-            if (!name.startsWith(mPackage)) super.loadClass(name, resolve);
-            return mBase.loadClass(name);
-        }
-    }
-
-    private class PluginExceptionHandler implements UncaughtExceptionHandler {
-        private final UncaughtExceptionHandler mHandler;
-
-        private PluginExceptionHandler(UncaughtExceptionHandler handler) {
-            mHandler = handler;
-        }
-
-        @Override
-        public void uncaughtException(Thread thread, Throwable throwable) {
-            if (SystemProperties.getBoolean("plugin.debugging", false)) {
-                mHandler.uncaughtException(thread, throwable);
-                return;
-            }
-            // Search for and disable plugins that may have been involved in this crash.
-            boolean disabledAny = checkStack(throwable);
-            if (!disabledAny) {
-                // We couldn't find any plugins involved in this crash, just to be safe
-                // disable all the plugins, so we can be sure that SysUI is running as
-                // best as possible.
-                for (PluginInstanceManager manager : mPluginMap.values()) {
-                    manager.disableAll();
-                }
-            }
-
-            // Run the normal exception handler so we can crash and cleanup our state.
-            mHandler.uncaughtException(thread, throwable);
-        }
-
-        private boolean checkStack(Throwable throwable) {
-            if (throwable == null) return false;
-            boolean disabledAny = false;
-            for (StackTraceElement element : throwable.getStackTrace()) {
-                for (PluginInstanceManager manager : mPluginMap.values()) {
-                    disabledAny |= manager.checkAndDisable(element.getClassName());
-                }
-            }
-            return disabledAny | checkStack(throwable.getCause());
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java
new file mode 100644
index 0000000..1fb6c87
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java
@@ -0,0 +1,358 @@
+/*
+ * 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.plugins;
+
+import android.app.Notification;
+import android.app.Notification.Action;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.systemui.Dependency;
+import com.android.systemui.plugins.PluginInstanceManager.PluginContextWrapper;
+import com.android.systemui.plugins.PluginInstanceManager.PluginInfo;
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+import dalvik.system.PathClassLoader;
+
+import java.lang.Thread.UncaughtExceptionHandler;
+import java.util.Map;
+
+/**
+ * @see Plugin
+ */
+public class PluginManagerImpl extends BroadcastReceiver implements PluginManager {
+
+    static final String DISABLE_PLUGIN = "com.android.systemui.action.DISABLE_PLUGIN";
+
+    private static PluginManager sInstance;
+
+    private final HandlerThread mBackgroundThread;
+    private final ArrayMap<PluginListener<?>, PluginInstanceManager> mPluginMap
+            = new ArrayMap<>();
+    private final Map<String, ClassLoader> mClassLoaders = new ArrayMap<>();
+    private final ArraySet<String> mOneShotPackages = new ArraySet<>();
+    private final Context mContext;
+    private final PluginInstanceManagerFactory mFactory;
+    private final boolean isDebuggable;
+    private final PluginPrefs mPluginPrefs;
+    private ClassLoaderFilter mParentClassLoader;
+    private boolean mListening;
+    private boolean mHasOneShot;
+
+    public PluginManagerImpl(Context context) {
+        this(context, new PluginInstanceManagerFactory(),
+                Build.IS_DEBUGGABLE, Thread.getDefaultUncaughtExceptionHandler());
+    }
+
+    @VisibleForTesting
+    PluginManagerImpl(Context context, PluginInstanceManagerFactory factory, boolean debuggable,
+            UncaughtExceptionHandler defaultHandler) {
+        mContext = context;
+        mFactory = factory;
+        mBackgroundThread = new HandlerThread("Plugins");
+        mBackgroundThread.start();
+        isDebuggable = debuggable;
+        mPluginPrefs = new PluginPrefs(mContext);
+
+        PluginExceptionHandler uncaughtExceptionHandler = new PluginExceptionHandler(
+                defaultHandler);
+        Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler);
+        if (isDebuggable) {
+            new Handler(mBackgroundThread.getLooper()).post(() -> {
+                // Plugin dependencies that don't have another good home can go here, but
+                // dependencies that have better places to init can happen elsewhere.
+                Dependency.get(PluginDependencyProvider.class)
+                        .allowPluginDependency(ActivityStarter.class);
+            });
+        }
+    }
+
+    public <T extends Plugin> T getOneShotPlugin(Class<T> cls) {
+        ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
+        if (info == null) {
+            throw new RuntimeException(cls + " doesn't provide an interface");
+        }
+        if (TextUtils.isEmpty(info.action())) {
+            throw new RuntimeException(cls + " doesn't provide an action");
+        }
+        return getOneShotPlugin(info.action(), cls);
+    }
+
+    public <T extends Plugin> T getOneShotPlugin(String action, Class<?> cls) {
+        if (!isDebuggable) {
+            // Never ever ever allow these on production builds, they are only for prototyping.
+            return null;
+        }
+        if (Looper.myLooper() != Looper.getMainLooper()) {
+            throw new RuntimeException("Must be called from UI thread");
+        }
+        PluginInstanceManager<T> p = mFactory.createPluginInstanceManager(mContext, action, null,
+                false, mBackgroundThread.getLooper(), cls, this);
+        mPluginPrefs.addAction(action);
+        PluginInfo<T> info = p.getPlugin();
+        if (info != null) {
+            mOneShotPackages.add(info.mPackage);
+            mHasOneShot = true;
+            startListening();
+            return info.mPlugin;
+        }
+        return null;
+    }
+
+    public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls) {
+        addPluginListener(listener, cls, false);
+    }
+
+    public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls,
+            boolean allowMultiple) {
+        addPluginListener(PluginManager.getAction(cls), listener, cls, allowMultiple);
+    }
+
+    public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
+            Class<?> cls) {
+        addPluginListener(action, listener, cls, false);
+    }
+
+    public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
+            Class cls, boolean allowMultiple) {
+        if (!isDebuggable) {
+            // Never ever ever allow these on production builds, they are only for prototyping.
+            return;
+        }
+        mPluginPrefs.addAction(action);
+        PluginInstanceManager p = mFactory.createPluginInstanceManager(mContext, action, listener,
+                allowMultiple, mBackgroundThread.getLooper(), cls, this);
+        p.loadAll();
+        mPluginMap.put(listener, p);
+        startListening();
+    }
+
+    public void removePluginListener(PluginListener<?> listener) {
+        if (!isDebuggable) {
+            // Never ever ever allow these on production builds, they are only for prototyping.
+            return;
+        }
+        if (!mPluginMap.containsKey(listener)) return;
+        mPluginMap.remove(listener).destroy();
+        stopListening();
+    }
+
+    private void startListening() {
+        if (mListening) return;
+        mListening = true;
+        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        filter.addAction(PLUGIN_CHANGED);
+        filter.addAction(DISABLE_PLUGIN);
+        filter.addDataScheme("package");
+        mContext.registerReceiver(this, filter);
+        filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
+        mContext.registerReceiver(this, filter);
+    }
+
+    private void stopListening() {
+        // Never stop listening if a one-shot is present.
+        if (!mListening || mHasOneShot) return;
+        mListening = false;
+        mContext.unregisterReceiver(this);
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
+            for (PluginInstanceManager manager : mPluginMap.values()) {
+                manager.loadAll();
+            }
+        } else if (DISABLE_PLUGIN.equals(intent.getAction())) {
+            Uri uri = intent.getData();
+            ComponentName component = ComponentName.unflattenFromString(
+                    uri.toString().substring(10));
+            mContext.getPackageManager().setComponentEnabledSetting(component,
+                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                    PackageManager.DONT_KILL_APP);
+            mContext.getSystemService(NotificationManager.class).cancel(component.getClassName(),
+                    SystemMessage.NOTE_PLUGIN);
+        } else {
+            Uri data = intent.getData();
+            String pkg = data.getEncodedSchemeSpecificPart();
+            if (mOneShotPackages.contains(pkg)) {
+                int icon = mContext.getResources().getIdentifier("tuner", "drawable",
+                        mContext.getPackageName());
+                int color = Resources.getSystem().getIdentifier(
+                        "system_notification_accent_color", "color", "android");
+                String label = pkg;
+                try {
+                    PackageManager pm = mContext.getPackageManager();
+                    label = pm.getApplicationInfo(pkg, 0).loadLabel(pm).toString();
+                } catch (NameNotFoundException e) {
+                }
+                // Localization not required as this will never ever appear in a user build.
+                final Notification.Builder nb =
+                        new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
+                                .setSmallIcon(icon)
+                                .setWhen(0)
+                                .setShowWhen(false)
+                                .setPriority(Notification.PRIORITY_MAX)
+                                .setVisibility(Notification.VISIBILITY_PUBLIC)
+                                .setColor(mContext.getColor(color))
+                                .setContentTitle("Plugin \"" + label + "\" has updated")
+                                .setContentText("Restart SysUI for changes to take effect.");
+                Intent i = new Intent("com.android.systemui.action.RESTART").setData(
+                            Uri.parse("package://" + pkg));
+                PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i, 0);
+                nb.addAction(new Action.Builder(null, "Restart SysUI", pi).build());
+                mContext.getSystemService(NotificationManager.class).notifyAsUser(pkg,
+                        SystemMessage.NOTE_PLUGIN, nb.build(), UserHandle.ALL);
+            }
+            clearClassLoader(pkg);
+            if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
+                for (PluginInstanceManager manager : mPluginMap.values()) {
+                    manager.onPackageChange(pkg);
+                }
+            } else {
+                for (PluginInstanceManager manager : mPluginMap.values()) {
+                    manager.onPackageRemoved(pkg);
+                }
+            }
+        }
+    }
+
+    public ClassLoader getClassLoader(String sourceDir, String pkg) {
+        if (mClassLoaders.containsKey(pkg)) {
+            return mClassLoaders.get(pkg);
+        }
+        ClassLoader classLoader = new PathClassLoader(sourceDir, getParentClassLoader());
+        mClassLoaders.put(pkg, classLoader);
+        return classLoader;
+    }
+
+    private void clearClassLoader(String pkg) {
+        mClassLoaders.remove(pkg);
+    }
+
+    ClassLoader getParentClassLoader() {
+        if (mParentClassLoader == null) {
+            // Lazily load this so it doesn't have any effect on devices without plugins.
+            mParentClassLoader = new ClassLoaderFilter(getClass().getClassLoader(),
+                    "com.android.systemui.plugin");
+        }
+        return mParentClassLoader;
+    }
+
+    public Context getContext(ApplicationInfo info, String pkg) throws NameNotFoundException {
+        ClassLoader classLoader = getClassLoader(info.sourceDir, pkg);
+        return new PluginContextWrapper(mContext.createApplicationContext(info, 0), classLoader);
+    }
+
+    public <T> boolean dependsOn(Plugin p, Class<T> cls) {
+        for (int i = 0; i < mPluginMap.size(); i++) {
+            if (mPluginMap.valueAt(i).dependsOn(p, cls)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @VisibleForTesting
+    public static class PluginInstanceManagerFactory {
+        public <T extends Plugin> PluginInstanceManager createPluginInstanceManager(Context context,
+                String action, PluginListener<T> listener, boolean allowMultiple, Looper looper,
+                Class<?> cls, PluginManagerImpl manager) {
+            return new PluginInstanceManager(context, action, listener, allowMultiple, looper,
+                    new VersionInfo().addClass(cls), manager);
+        }
+    }
+
+    // This allows plugins to include any libraries or copied code they want by only including
+    // classes from the plugin library.
+    private static class ClassLoaderFilter extends ClassLoader {
+        private final String mPackage;
+        private final ClassLoader mBase;
+
+        public ClassLoaderFilter(ClassLoader base, String pkg) {
+            super(ClassLoader.getSystemClassLoader());
+            mBase = base;
+            mPackage = pkg;
+        }
+
+        @Override
+        protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+            if (!name.startsWith(mPackage)) super.loadClass(name, resolve);
+            return mBase.loadClass(name);
+        }
+    }
+
+    private class PluginExceptionHandler implements UncaughtExceptionHandler {
+        private final UncaughtExceptionHandler mHandler;
+
+        private PluginExceptionHandler(UncaughtExceptionHandler handler) {
+            mHandler = handler;
+        }
+
+        @Override
+        public void uncaughtException(Thread thread, Throwable throwable) {
+            if (SystemProperties.getBoolean("plugin.debugging", false)) {
+                mHandler.uncaughtException(thread, throwable);
+                return;
+            }
+            // Search for and disable plugins that may have been involved in this crash.
+            boolean disabledAny = checkStack(throwable);
+            if (!disabledAny) {
+                // We couldn't find any plugins involved in this crash, just to be safe
+                // disable all the plugins, so we can be sure that SysUI is running as
+                // best as possible.
+                for (PluginInstanceManager manager : mPluginMap.values()) {
+                    manager.disableAll();
+                }
+            }
+
+            // Run the normal exception handler so we can crash and cleanup our state.
+            mHandler.uncaughtException(thread, throwable);
+        }
+
+        private boolean checkStack(Throwable throwable) {
+            if (throwable == null) return false;
+            boolean disabledAny = false;
+            for (StackTraceElement element : throwable.getStackTrace()) {
+                for (PluginInstanceManager manager : mPluginMap.values()) {
+                    disabledAny |= manager.checkAndDisable(element.getClassName());
+                }
+            }
+            return disabledAny | checkStack(throwable.getCause());
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
index 7c4f2ee..369ce69 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
@@ -1,226 +1,94 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2017 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
+ * 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
+ * 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.tuner;
 
 import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.UserInfo;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
 import android.os.UserHandle;
-import android.os.UserManager;
 import android.provider.Settings;
-import android.provider.Settings.Secure;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.ArraySet;
 
 import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;
 import com.android.systemui.DemoMode;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.SysUiServiceProvider;
-import com.android.systemui.SystemUI;
-import com.android.systemui.SystemUIApplication;
-import com.android.systemui.settings.CurrentUserTracker;
-import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
-import com.android.systemui.util.leak.LeakDetector;
 
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Set;
-
-
-public class TunerService {
+public abstract class TunerService {
 
     public static final String ACTION_CLEAR = "com.android.systemui.action.CLEAR_TUNER";
 
-    private static final String TUNER_VERSION = "sysui_tuner_version";
+    public abstract void clearAll();
+    public abstract void destroy();
 
-    private static final int CURRENT_TUNER_VERSION = 1;
+    public abstract String getValue(String setting);
+    public abstract int getValue(String setting, int def);
+    public abstract String getValue(String setting, String def);
 
-    private final Observer mObserver = new Observer();
-    // Map of Uris we listen on to their settings keys.
-    private final ArrayMap<Uri, String> mListeningUris = new ArrayMap<>();
-    // Map of settings keys to the listener.
-    private final HashMap<String, Set<Tunable>> mTunableLookup = new HashMap<>();
-    // Set of all tunables, used for leak detection.
-    private final HashSet<Tunable> mTunables = LeakDetector.ENABLED ? new HashSet<>() : null;
-    private final Context mContext;
+    public abstract void setValue(String setting, String value);
+    public abstract void setValue(String setting, int value);
 
-    private ContentResolver mContentResolver;
-    private int mCurrentUser;
-    private CurrentUserTracker mUserTracker;
+    public abstract void addTunable(Tunable tunable, String... keys);
+    public abstract void removeTunable(Tunable tunable);
 
-    public TunerService(Context context) {
-        mContext = context;
-        mContentResolver = mContext.getContentResolver();
+    public interface Tunable {
+        void onTuningChanged(String key, String newValue);
+    }
 
-        for (UserInfo user : UserManager.get(mContext).getUsers()) {
-            mCurrentUser = user.getUserHandle().getIdentifier();
-            if (getValue(TUNER_VERSION, 0) != CURRENT_TUNER_VERSION) {
-                upgradeTuner(getValue(TUNER_VERSION, 0), CURRENT_TUNER_VERSION);
+    private static Context userContext(Context context) {
+        try {
+            return context.createPackageContextAsUser(context.getPackageName(), 0,
+                    new UserHandle(ActivityManager.getCurrentUser()));
+        } catch (NameNotFoundException e) {
+            return context;
+        }
+    }
+
+    public static final void setTunerEnabled(Context context, boolean enabled) {
+        userContext(context).getPackageManager().setComponentEnabledSetting(
+                new ComponentName(context, TunerActivity.class),
+                enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+                        : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                PackageManager.DONT_KILL_APP);
+
+        userContext(context).getPackageManager().setComponentEnabledSetting(
+                new ComponentName(context, TunerActivity.ACTIVITY_ALIAS_NAME),
+                enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+                        : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                PackageManager.DONT_KILL_APP);
+    }
+
+    public static final boolean isTunerEnabled(Context context) {
+        return userContext(context).getPackageManager().getComponentEnabledSetting(
+                new ComponentName(context, TunerActivity.class))
+                == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+    }
+
+    public static class ClearReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (ACTION_CLEAR.equals(intent.getAction())) {
+                Dependency.get(TunerService.class).clearAll();
             }
         }
-
-        mCurrentUser = ActivityManager.getCurrentUser();
-        mUserTracker = new CurrentUserTracker(mContext) {
-            @Override
-            public void onUserSwitched(int newUserId) {
-                mCurrentUser = newUserId;
-                reloadAll();
-                reregisterAll();
-            }
-        };
-        mUserTracker.startTracking();
-    }
-
-    public void destroy() {
-        mUserTracker.stopTracking();
-    }
-
-    private void upgradeTuner(int oldVersion, int newVersion) {
-        if (oldVersion < 1) {
-            String blacklistStr = getValue(StatusBarIconController.ICON_BLACKLIST);
-            if (blacklistStr != null) {
-                ArraySet<String> iconBlacklist =
-                        StatusBarIconController.getIconBlacklist(blacklistStr);
-
-                iconBlacklist.add("rotate");
-                iconBlacklist.add("headset");
-
-                Settings.Secure.putStringForUser(mContentResolver,
-                        StatusBarIconController.ICON_BLACKLIST,
-                        TextUtils.join(",", iconBlacklist), mCurrentUser);
-            }
-        }
-        setValue(TUNER_VERSION, newVersion);
-    }
-
-    public String getValue(String setting) {
-        return Settings.Secure.getStringForUser(mContentResolver, setting, mCurrentUser);
-    }
-
-    public void setValue(String setting, String value) {
-         Settings.Secure.putStringForUser(mContentResolver, setting, value, mCurrentUser);
-    }
-
-    public int getValue(String setting, int def) {
-        return Settings.Secure.getIntForUser(mContentResolver, setting, def, mCurrentUser);
-    }
-
-    public String getValue(String setting, String def) {
-        String ret = Secure.getStringForUser(mContentResolver, setting, mCurrentUser);
-        if (ret == null) return def;
-        return ret;
-    }
-
-    public void setValue(String setting, int value) {
-         Settings.Secure.putIntForUser(mContentResolver, setting, value, mCurrentUser);
-    }
-
-    public void addTunable(Tunable tunable, String... keys) {
-        for (String key : keys) {
-            addTunable(tunable, key);
-        }
-    }
-
-    private void addTunable(Tunable tunable, String key) {
-        if (!mTunableLookup.containsKey(key)) {
-            mTunableLookup.put(key, new ArraySet<Tunable>());
-        }
-        mTunableLookup.get(key).add(tunable);
-        if (LeakDetector.ENABLED) {
-            mTunables.add(tunable);
-            Dependency.get(LeakDetector.class).trackCollection(mTunables, "TunerService.mTunables");
-        }
-        Uri uri = Settings.Secure.getUriFor(key);
-        if (!mListeningUris.containsKey(uri)) {
-            mListeningUris.put(uri, key);
-            mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser);
-        }
-        // Send the first state.
-        String value = Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser);
-        tunable.onTuningChanged(key, value);
-    }
-
-    public void removeTunable(Tunable tunable) {
-        for (Set<Tunable> list : mTunableLookup.values()) {
-            list.remove(tunable);
-        }
-        if (LeakDetector.ENABLED) {
-            mTunables.remove(tunable);
-        }
-    }
-
-    protected void reregisterAll() {
-        if (mListeningUris.size() == 0) {
-            return;
-        }
-        mContentResolver.unregisterContentObserver(mObserver);
-        for (Uri uri : mListeningUris.keySet()) {
-            mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser);
-        }
-    }
-
-    public void reloadSetting(Uri uri) {
-        String key = mListeningUris.get(uri);
-        Set<Tunable> tunables = mTunableLookup.get(key);
-        if (tunables == null) {
-            return;
-        }
-        String value = Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser);
-        for (Tunable tunable : tunables) {
-            tunable.onTuningChanged(key, value);
-        }
-    }
-
-    private void reloadAll() {
-        for (String key : mTunableLookup.keySet()) {
-            String value = Settings.Secure.getStringForUser(mContentResolver, key,
-                    mCurrentUser);
-            for (Tunable tunable : mTunableLookup.get(key)) {
-                tunable.onTuningChanged(key, value);
-            }
-        }
-    }
-
-    public void clearAll() {
-        // A couple special cases.
-        Settings.Global.putString(mContentResolver, DemoMode.DEMO_MODE_ALLOWED, null);
-        Settings.System.putString(mContentResolver,
-                SHOW_BATTERY_PERCENT, null);
-        Intent intent = new Intent(DemoMode.ACTION_DEMO);
-        intent.putExtra(DemoMode.EXTRA_COMMAND, DemoMode.COMMAND_EXIT);
-        mContext.sendBroadcast(intent);
-
-        for (String key : mTunableLookup.keySet()) {
-            Settings.Secure.putString(mContentResolver, key, null);
-        }
     }
 
     public static final void showResetRequest(final Context context, final Runnable onDisabled) {
@@ -247,59 +115,4 @@
         });
         dialog.show();
     }
-
-    public static final void setTunerEnabled(Context context, boolean enabled) {
-        userContext(context).getPackageManager().setComponentEnabledSetting(
-                new ComponentName(context, TunerActivity.class),
-                enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
-                        : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
-                        PackageManager.DONT_KILL_APP);
-
-        userContext(context).getPackageManager().setComponentEnabledSetting(
-                new ComponentName(context, TunerActivity.ACTIVITY_ALIAS_NAME),
-                enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
-                        : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
-                PackageManager.DONT_KILL_APP);
-    }
-
-    public static final boolean isTunerEnabled(Context context) {
-        return userContext(context).getPackageManager().getComponentEnabledSetting(
-                new ComponentName(context, TunerActivity.class))
-                == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-    }
-
-    private static Context userContext(Context context) {
-        try {
-            return context.createPackageContextAsUser(context.getPackageName(), 0,
-                    new UserHandle(ActivityManager.getCurrentUser()));
-        } catch (NameNotFoundException e) {
-            return context;
-        }
-    }
-
-    private class Observer extends ContentObserver {
-        public Observer() {
-            super(new Handler(Looper.getMainLooper()));
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri, int userId) {
-            if (userId == ActivityManager.getCurrentUser()) {
-                reloadSetting(uri);
-            }
-        }
-    }
-
-    public interface Tunable {
-        void onTuningChanged(String key, String newValue);
-    }
-
-    public static class ClearReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (ACTION_CLEAR.equals(intent.getAction())) {
-                Dependency.get(TunerService.class).clearAll();
-            }
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
new file mode 100644
index 0000000..8e584bc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2015 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.tuner;
+
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.UserInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.provider.Settings.Secure;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.systemui.DemoMode;
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.SystemUI;
+import com.android.systemui.SystemUIApplication;
+import com.android.systemui.settings.CurrentUserTracker;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.util.leak.LeakDetector;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+
+public class TunerServiceImpl extends TunerService {
+
+    private static final String TUNER_VERSION = "sysui_tuner_version";
+
+    private static final int CURRENT_TUNER_VERSION = 1;
+
+    private final Observer mObserver = new Observer();
+    // Map of Uris we listen on to their settings keys.
+    private final ArrayMap<Uri, String> mListeningUris = new ArrayMap<>();
+    // Map of settings keys to the listener.
+    private final HashMap<String, Set<Tunable>> mTunableLookup = new HashMap<>();
+    // Set of all tunables, used for leak detection.
+    private final HashSet<Tunable> mTunables = LeakDetector.ENABLED ? new HashSet<>() : null;
+    private final Context mContext;
+
+    private ContentResolver mContentResolver;
+    private int mCurrentUser;
+    private CurrentUserTracker mUserTracker;
+
+    public TunerServiceImpl(Context context) {
+        mContext = context;
+        mContentResolver = mContext.getContentResolver();
+
+        for (UserInfo user : UserManager.get(mContext).getUsers()) {
+            mCurrentUser = user.getUserHandle().getIdentifier();
+            if (getValue(TUNER_VERSION, 0) != CURRENT_TUNER_VERSION) {
+                upgradeTuner(getValue(TUNER_VERSION, 0), CURRENT_TUNER_VERSION);
+            }
+        }
+
+        mCurrentUser = ActivityManager.getCurrentUser();
+        mUserTracker = new CurrentUserTracker(mContext) {
+            @Override
+            public void onUserSwitched(int newUserId) {
+                mCurrentUser = newUserId;
+                reloadAll();
+                reregisterAll();
+            }
+        };
+        mUserTracker.startTracking();
+    }
+
+    @Override
+    public void destroy() {
+        mUserTracker.stopTracking();
+    }
+
+    private void upgradeTuner(int oldVersion, int newVersion) {
+        if (oldVersion < 1) {
+            String blacklistStr = getValue(StatusBarIconController.ICON_BLACKLIST);
+            if (blacklistStr != null) {
+                ArraySet<String> iconBlacklist =
+                        StatusBarIconController.getIconBlacklist(blacklistStr);
+
+                iconBlacklist.add("rotate");
+                iconBlacklist.add("headset");
+
+                Settings.Secure.putStringForUser(mContentResolver,
+                        StatusBarIconController.ICON_BLACKLIST,
+                        TextUtils.join(",", iconBlacklist), mCurrentUser);
+            }
+        }
+        setValue(TUNER_VERSION, newVersion);
+    }
+
+    @Override
+    public String getValue(String setting) {
+        return Settings.Secure.getStringForUser(mContentResolver, setting, mCurrentUser);
+    }
+
+    @Override
+    public void setValue(String setting, String value) {
+         Settings.Secure.putStringForUser(mContentResolver, setting, value, mCurrentUser);
+    }
+
+    @Override
+    public int getValue(String setting, int def) {
+        return Settings.Secure.getIntForUser(mContentResolver, setting, def, mCurrentUser);
+    }
+
+    @Override
+    public String getValue(String setting, String def) {
+        String ret = Secure.getStringForUser(mContentResolver, setting, mCurrentUser);
+        if (ret == null) return def;
+        return ret;
+    }
+
+    @Override
+    public void setValue(String setting, int value) {
+         Settings.Secure.putIntForUser(mContentResolver, setting, value, mCurrentUser);
+    }
+
+    @Override
+    public void addTunable(Tunable tunable, String... keys) {
+        for (String key : keys) {
+            addTunable(tunable, key);
+        }
+    }
+
+    private void addTunable(Tunable tunable, String key) {
+        if (!mTunableLookup.containsKey(key)) {
+            mTunableLookup.put(key, new ArraySet<Tunable>());
+        }
+        mTunableLookup.get(key).add(tunable);
+        if (LeakDetector.ENABLED) {
+            mTunables.add(tunable);
+            Dependency.get(LeakDetector.class).trackCollection(mTunables, "TunerService.mTunables");
+        }
+        Uri uri = Settings.Secure.getUriFor(key);
+        if (!mListeningUris.containsKey(uri)) {
+            mListeningUris.put(uri, key);
+            mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser);
+        }
+        // Send the first state.
+        String value = Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser);
+        tunable.onTuningChanged(key, value);
+    }
+
+    @Override
+    public void removeTunable(Tunable tunable) {
+        for (Set<Tunable> list : mTunableLookup.values()) {
+            list.remove(tunable);
+        }
+        if (LeakDetector.ENABLED) {
+            mTunables.remove(tunable);
+        }
+    }
+
+    protected void reregisterAll() {
+        if (mListeningUris.size() == 0) {
+            return;
+        }
+        mContentResolver.unregisterContentObserver(mObserver);
+        for (Uri uri : mListeningUris.keySet()) {
+            mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser);
+        }
+    }
+
+    private void reloadSetting(Uri uri) {
+        String key = mListeningUris.get(uri);
+        Set<Tunable> tunables = mTunableLookup.get(key);
+        if (tunables == null) {
+            return;
+        }
+        String value = Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser);
+        for (Tunable tunable : tunables) {
+            tunable.onTuningChanged(key, value);
+        }
+    }
+
+    private void reloadAll() {
+        for (String key : mTunableLookup.keySet()) {
+            String value = Settings.Secure.getStringForUser(mContentResolver, key,
+                    mCurrentUser);
+            for (Tunable tunable : mTunableLookup.get(key)) {
+                tunable.onTuningChanged(key, value);
+            }
+        }
+    }
+
+    @Override
+    public void clearAll() {
+        // A couple special cases.
+        Settings.Global.putString(mContentResolver, DemoMode.DEMO_MODE_ALLOWED, null);
+        Intent intent = new Intent(DemoMode.ACTION_DEMO);
+        intent.putExtra(DemoMode.EXTRA_COMMAND, DemoMode.COMMAND_EXIT);
+        mContext.sendBroadcast(intent);
+
+        for (String key : mTunableLookup.keySet()) {
+            Settings.Secure.putString(mContentResolver, key, null);
+        }
+    }
+
+    private class Observer extends ContentObserver {
+        public Observer() {
+            super(new Handler(Looper.getMainLooper()));
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri, int userId) {
+            if (userId == ActivityManager.getCurrentUser()) {
+                reloadSetting(uri);
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/LayoutInflaterBuilder.java b/packages/SystemUI/src/com/android/systemui/util/LayoutInflaterBuilder.java
deleted file mode 100644
index 5cfe677..0000000
--- a/packages/SystemUI/src/com/android/systemui/util/LayoutInflaterBuilder.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * 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.util;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Builder class to create a {@link LayoutInflater} with various properties.
- *
- * Call any desired configuration methods on the Builder and then use
- * {@link Builder#build} to create the LayoutInflater. This is an alternative to directly using
- * {@link LayoutInflater#setFilter} and {@link LayoutInflater#setFactory}.
- * @hide for use by framework
- */
-public class LayoutInflaterBuilder {
-    private static final String TAG = "LayoutInflaterBuilder";
-
-    private Context mFromContext;
-    private Context mTargetContext;
-    private Map<String, String> mReplaceMap;
-    private Set<Class> mDisallowedClasses;
-    private LayoutInflater mBuiltInflater;
-
-    /**
-     * Creates a new Builder which will construct a LayoutInflater.
-     *
-     * @param fromContext This context's LayoutInflater will be cloned by the Builder using
-     * {@link LayoutInflater#cloneInContext}. By default, the new LayoutInflater will point at
-     * this same Context.
-     */
-    public LayoutInflaterBuilder(@NonNull Context fromContext) {
-        mFromContext = fromContext;
-        mTargetContext = fromContext;
-        mReplaceMap = null;
-        mDisallowedClasses = null;
-        mBuiltInflater = null;
-    }
-
-    /**
-     * Instructs the Builder to point the LayoutInflater at a different Context.
-     *
-     * @param targetContext Context to be provided to
-     * {@link LayoutInflater#cloneInContext(Context)}.
-     * @return Builder object post-modification.
-     */
-    public LayoutInflaterBuilder target(@NonNull Context targetContext) {
-        assertIfAlreadyBuilt();
-        mTargetContext = targetContext;
-        return this;
-    }
-
-    /**
-     * Instructs the Builder to configure the LayoutInflater such that all instances
-     * of one {@link View} will be replaced with instances of another during inflation.
-     *
-     * @param from Instances of this class will be replaced during inflation.
-     * @param to Instances of this class will be inflated as replacements.
-     * @return Builder object post-modification.
-     */
-    public LayoutInflaterBuilder replace(@NonNull Class from, @NonNull Class to) {
-        return replace(from.getName(), to);
-    }
-
-    /**
-     * Instructs the Builder to configure the LayoutInflater such that all instances
-     * of one {@link View} will be replaced with instances of another during inflation.
-     *
-     * @param tag Instances of this tag will be replaced during inflation.
-     * @param to Instances of this class will be inflated as replacements.
-     * @return Builder object post-modification.
-     */
-    public LayoutInflaterBuilder replace(@NonNull String tag, @NonNull Class to) {
-        assertIfAlreadyBuilt();
-        if (mReplaceMap == null) {
-            mReplaceMap = new ArrayMap<String, String>();
-        }
-        mReplaceMap.put(tag, to.getName());
-        return this;
-    }
-
-    /**
-     * Instructs the Builder to configure the LayoutInflater such that any attempt to inflate
-     * a {@link View} of a given type will throw a {@link InflateException}.
-     *
-     * @param disallowedClass The Class type that will be disallowed.
-     * @return Builder object post-modification.
-     */
-    public LayoutInflaterBuilder disallow(@NonNull Class disallowedClass) {
-        assertIfAlreadyBuilt();
-        if (mDisallowedClasses == null) {
-            mDisallowedClasses = new ArraySet<Class>();
-        }
-        mDisallowedClasses.add(disallowedClass);
-        return this;
-    }
-
-    /**
-     * Builds and returns the LayoutInflater.  Afterwards, this Builder can no longer can be
-     * used, all future calls on the Builder will throw {@link AssertionError}.
-     */
-    public LayoutInflater build() {
-        assertIfAlreadyBuilt();
-        mBuiltInflater =
-                LayoutInflater.from(mFromContext).cloneInContext(mTargetContext);
-        setFactoryIfNeeded(mBuiltInflater);
-        setFilterIfNeeded(mBuiltInflater);
-        return mBuiltInflater;
-    }
-
-    private void assertIfAlreadyBuilt() {
-        if (mBuiltInflater != null) {
-            throw new AssertionError("Cannot use this Builder after build() has been called.");
-        }
-    }
-
-    private void setFactoryIfNeeded(LayoutInflater inflater) {
-        if (mReplaceMap == null) {
-            return;
-        }
-        inflater.setFactory(
-                new LayoutInflater.Factory() {
-                    @Override
-                    public View onCreateView(String name, Context context, AttributeSet attrs) {
-                        String replacingClassName = mReplaceMap.get(name);
-                        if (replacingClassName != null) {
-                            try {
-                                return inflater.createView(replacingClassName, null, attrs);
-                            } catch (ClassNotFoundException e) {
-                                Log.e(TAG, "Could not replace " + name
-                                        + " with " + replacingClassName
-                                        + ", Exception: ", e);
-                            }
-                        }
-                        return null;
-                    }
-                });
-    }
-
-    private void setFilterIfNeeded(LayoutInflater inflater) {
-        if (mDisallowedClasses == null) {
-            return;
-        }
-        inflater.setFilter(
-                new LayoutInflater.Filter() {
-                    @Override
-                    public boolean onLoadClass(Class clazz) {
-                        return !mDisallowedClasses.contains(clazz);
-                    }
-                });
-    }
-}
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index 760d875..576299f 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -49,7 +49,8 @@
     mockito-updated-target-minus-junit4 \
     SystemUI-proto \
     SystemUI-tags \
-    legacy-android-test
+    legacy-android-test \
+    testables
 
 LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common android.car
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
index 27955ec..c297ae8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
@@ -39,21 +39,21 @@
     @Test
     public void testClassDependency() {
         FlashlightController f = mock(FlashlightController.class);
-        injectTestDependency(FlashlightController.class, f);
+        mDependency.injectTestDependency(FlashlightController.class, f);
         Assert.assertEquals(f, Dependency.get(FlashlightController.class));
     }
 
     @Test
     public void testStringDependency() {
         Looper l = Looper.getMainLooper();
-        injectTestDependency(Dependency.BG_LOOPER, l);
+        mDependency.injectTestDependency(Dependency.BG_LOOPER, l);
         assertEquals(l, Dependency.get(Dependency.BG_LOOPER));
     }
 
     @Test
     public void testDump() {
         Dumpable d = mock(Dumpable.class);
-        injectTestDependency(DUMPABLE, d);
+        mDependency.injectTestDependency(DUMPABLE, d);
         Dependency.get(DUMPABLE);
         mDependency.dump(null, mock(PrintWriter.class), null);
         verify(d).dump(eq(null), any(), eq(null));
@@ -62,7 +62,7 @@
     @Test
     public void testConfigurationChanged() {
         ConfigurationChangedReceiver d = mock(ConfigurationChangedReceiver.class);
-        injectTestDependency(CONFIGURATION_CHANGED_RECEIVER, d);
+        mDependency.injectTestDependency(CONFIGURATION_CHANGED_RECEIVER, d);
         Dependency.get(CONFIGURATION_CHANGED_RECEIVER);
         mDependency.onConfigurationChanged(null);
         verify(d).onConfigurationChanged(eq(null));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/FragmentTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/FragmentTestCase.java
deleted file mode 100644
index 1678d92..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/FragmentTestCase.java
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * 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;
-
-import android.annotation.Nullable;
-import android.app.Fragment;
-import android.app.FragmentController;
-import android.app.FragmentHostCallback;
-import android.app.FragmentManagerNonConfig;
-import android.graphics.PixelFormat;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Parcelable;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
-import android.widget.FrameLayout;
-
-import com.android.systemui.utils.TestableLooper;
-import com.android.systemui.utils.ViewUtils;
-import com.android.systemui.utils.leaks.LeakCheckedTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * Base class for fragment class tests.  Just adding one for any fragment will push it through
- * general lifecycle events and ensure no basic leaks are happening.  This class also implements
- * the host for subclasses, so they can push it into desired states and do any unit testing
- * required.
- */
-public abstract class FragmentTestCase extends LeakCheckedTest {
-
-    private static final int VIEW_ID = 42;
-    private final Class<? extends Fragment> mCls;
-    private Handler mHandler;
-    private FrameLayout mView;
-    protected FragmentController mFragments;
-    protected Fragment mFragment;
-
-    public FragmentTestCase(Class<? extends Fragment> cls) {
-        mCls = cls;
-    }
-
-    @Before
-    public void setupFragment() throws Exception {
-        mView = new FrameLayout(mContext);
-        mView.setId(VIEW_ID);
-
-        TestableLooper.get(this).runWithLooper(() -> {
-            mHandler = new Handler();
-
-            mFragment = mCls.newInstance();
-            mFragments = FragmentController.createController(new HostCallbacks());
-            mFragments.attachHost(null);
-            mFragments.getFragmentManager().beginTransaction()
-                    .replace(VIEW_ID, mFragment)
-                    .commit();
-        });
-    }
-
-    private String hex(Looper looper) {
-        return Integer.toHexString(System.identityHashCode(looper));
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        if (mFragments != null) {
-            // Set mFragments to null to let it know not to destroy.
-            TestableLooper.get(this).runWithLooper(() -> mFragments.dispatchDestroy());
-        }
-    }
-
-    @Test
-    public void testCreateDestroy() {
-        mFragments.dispatchCreate();
-        processAllMessages();
-        destroyFragments();
-    }
-
-    @Test
-    public void testStartStop() {
-        mFragments.dispatchStart();
-        processAllMessages();
-        mFragments.dispatchStop();
-        processAllMessages();
-    }
-
-    @Test
-    public void testResumePause() {
-        mFragments.dispatchResume();
-        processAllMessages();
-        mFragments.dispatchPause();
-        processAllMessages();
-    }
-
-    @Test
-    public void testAttachDetach() {
-        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
-                LayoutParams.TYPE_SYSTEM_ALERT,
-                0, PixelFormat.TRANSLUCENT);
-        mFragments.dispatchResume();
-        processAllMessages();
-        attachFragmentToWindow();
-        detachFragmentToWindow();
-        mFragments.dispatchPause();
-        processAllMessages();
-    }
-
-    @Test
-    public void testRecreate() {
-        mFragments.dispatchResume();
-        processAllMessages();
-        mFragments.dispatchPause();
-        Parcelable p = mFragments.saveAllState();
-        mFragments.dispatchDestroy();
-
-        mFragments = FragmentController.createController(new HostCallbacks());
-        mFragments.attachHost(null);
-        mFragments.restoreAllState(p, (FragmentManagerNonConfig) null);
-        mFragments.dispatchResume();
-        processAllMessages();
-    }
-
-    @Test
-    public void testMultipleResumes() {
-        mFragments.dispatchResume();
-        processAllMessages();
-        mFragments.dispatchStop();
-        processAllMessages();
-        mFragments.dispatchResume();
-        processAllMessages();
-    }
-
-    protected void attachFragmentToWindow() {
-        ViewUtils.attachView(mView);
-        TestableLooper.get(this).processMessages(1);
-    }
-
-    protected void detachFragmentToWindow() {
-        ViewUtils.detachView(mView);
-        TestableLooper.get(this).processMessages(1);
-    }
-
-    protected void destroyFragments() {
-        mFragments.dispatchDestroy();
-        processAllMessages();
-        mFragments = null;
-    }
-
-    protected void processAllMessages() {
-        TestableLooper.get(this).processAllMessages();
-    }
-
-    private View findViewById(int id) {
-        return mView.findViewById(id);
-    }
-
-    private class HostCallbacks extends FragmentHostCallback<FragmentTestCase> {
-        public HostCallbacks() {
-            super(mContext, FragmentTestCase.this.mHandler, 0);
-        }
-
-        @Override
-        public FragmentTestCase onGetHost() {
-            return FragmentTestCase.this;
-        }
-
-        @Override
-        public void onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
-        }
-
-        @Override
-        public boolean onShouldSaveFragmentState(Fragment fragment) {
-            return true; // True for now.
-        }
-
-        @Override
-        public LayoutInflater onGetLayoutInflater() {
-            return LayoutInflater.from(mContext);
-        }
-
-        @Override
-        public boolean onUseFragmentManagerInflaterFactory() {
-            return true;
-        }
-
-        @Override
-        public boolean onHasWindowAnimations() {
-            return false;
-        }
-
-        @Override
-        public int onGetWindowAnimations() {
-            return 0;
-        }
-
-        @Override
-        public void onAttachFragment(Fragment fragment) {
-        }
-
-        @Nullable
-        @Override
-        public View onFindViewById(int id) {
-            return FragmentTestCase.this.findViewById(id);
-        }
-
-        @Override
-        public boolean onHasView() {
-            return true;
-        }
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysUIRunner.java b/packages/SystemUI/tests/src/com/android/systemui/SysUIRunner.java
deleted file mode 100644
index fd99d1d..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/SysUIRunner.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2017 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;
-
-import android.support.test.internal.runner.junit4.statement.RunAfters;
-import android.support.test.internal.runner.junit4.statement.RunBefores;
-import android.support.test.internal.runner.junit4.statement.UiThreadStatement;
-
-import com.android.systemui.utils.TestableLooper.LooperStatement;
-import com.android.systemui.utils.TestableLooper.RunWithLooper;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.internal.runners.statements.FailOnTimeout;
-import org.junit.runners.BlockJUnit4ClassRunner;
-import org.junit.runners.model.FrameworkMethod;
-import org.junit.runners.model.InitializationError;
-import org.junit.runners.model.Statement;
-
-import java.util.List;
-
-public class SysUIRunner extends BlockJUnit4ClassRunner {
-
-    private final long mTimeout;
-    private final Class<?> mKlass;
-
-    public SysUIRunner(Class<?> klass) throws InitializationError {
-        super(klass);
-        mKlass = klass;
-        // Can't seem to get reference to timeout parameter from here, so set default to 10 mins.
-        mTimeout = 10 * 60 * 1000;
-    }
-
-    @Override
-    protected Statement methodInvoker(FrameworkMethod method, Object test) {
-        return shouldRunOnUiThread(method) ? new UiThreadStatement(
-                methodInvokerInt(method, test), true) : methodInvokerInt(method, test);
-    }
-
-    protected Statement methodInvokerInt(FrameworkMethod method, Object test) {
-        RunWithLooper annotation = method.getAnnotation(RunWithLooper.class);
-        if (annotation == null) annotation = mKlass.getAnnotation(RunWithLooper.class);
-        if (annotation != null) {
-            return new LooperStatement(super.methodInvoker(method, test),
-                    annotation.setAsMainLooper(), test);
-        }
-        return super.methodInvoker(method, test);
-    }
-
-    protected Statement withBefores(FrameworkMethod method, Object target, Statement statement) {
-        List befores = this.getTestClass().getAnnotatedMethods(Before.class);
-        return befores.isEmpty() ? statement : new RunBefores(method, statement,
-                befores, target);
-    }
-
-    protected Statement withAfters(FrameworkMethod method, Object target, Statement statement) {
-        List afters = this.getTestClass().getAnnotatedMethods(After.class);
-        return afters.isEmpty() ? statement : new RunAfters(method, statement, afters,
-                target);
-    }
-
-    protected Statement withPotentialTimeout(FrameworkMethod method, Object test, Statement next) {
-        long timeout = this.getTimeout(method.getAnnotation(Test.class));
-        if (timeout <= 0L && mTimeout > 0L) {
-            timeout = mTimeout;
-        }
-
-        return timeout <= 0L ? next : new FailOnTimeout(next, timeout);
-    }
-
-    private long getTimeout(Test annotation) {
-        return annotation == null ? 0L : annotation.timeout();
-    }
-
-    public boolean shouldRunOnUiThread(FrameworkMethod method) {
-        if (mKlass.getAnnotation(UiThreadTest.class) != null) {
-            return true;
-        } else {
-            return UiThreadStatement.shouldRunOnUiThread(method);
-        }
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
new file mode 100644
index 0000000..15cebc7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 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;
+
+import android.app.Fragment;
+import android.support.test.InstrumentationRegistry;
+import android.testing.BaseFragmentTest;
+
+import com.android.systemui.utils.leaks.LeakCheckedTest;
+import com.android.systemui.utils.leaks.LeakCheckedTest.SysuiLeakCheck;
+
+import org.junit.Before;
+import org.junit.Rule;
+
+public abstract class SysuiBaseFragmentTest extends BaseFragmentTest {
+
+    public static final Class<?>[] ALL_SUPPORTED_CLASSES = LeakCheckedTest.ALL_SUPPORTED_CLASSES;
+
+    @Rule
+    public final SysuiLeakCheck mLeakCheck = new SysuiLeakCheck();
+
+    protected final TestableDependency mDependency = new TestableDependency(mContext);
+    protected SysuiTestableContext mSysuiContext;
+
+    public SysuiBaseFragmentTest(Class<? extends Fragment> cls) {
+        super(cls);
+    }
+
+    @Before
+    public void SysuiSetup() {
+        System.setProperty("dexmaker.share_classloader", "true");
+        SystemUIFactory.createFromConfig(mContext);
+        // TODO: Figure out another way to give reference to a SysuiTestableContext.
+        mSysuiContext = (SysuiTestableContext) mContext;
+    }
+
+    @Override
+    protected SysuiTestableContext getContext() {
+        return new SysuiTestableContext(InstrumentationRegistry.getContext(), mLeakCheck);
+    }
+
+    public void injectLeakCheckedDependencies(Class<?>... cls) {
+        for (Class<?> c : cls) {
+            injectLeakCheckedDependency(c);
+        }
+    }
+
+    public <T> void injectLeakCheckedDependency(Class<T> c) {
+        mDependency.injectTestDependency(c, mLeakCheck.getLeakChecker(c));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index c0e7e80..aadae0f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -15,49 +15,38 @@
  */
 package com.android.systemui;
 
-import static org.mockito.Mockito.mock;
-
 import android.content.Context;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.MessageQueue;
 import android.support.test.InstrumentationRegistry;
-import android.util.ArrayMap;
+import android.testing.LeakCheck;
 
-import com.android.systemui.Dependency.DependencyKey;
-import com.android.systemui.utils.TestableContext;
-import com.android.systemui.utils.leaks.Tracker;
-
-import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 
 /**
  * Base class that does System UI specific setup.
  */
 public abstract class SysuiTestCase {
 
-    private Throwable mException;
     private Handler mHandler;
-    protected TestableContext mContext;
-    protected TestDependency mDependency;
+    @Rule
+    public SysuiTestableContext mContext = new SysuiTestableContext(
+            InstrumentationRegistry.getContext(), getLeakCheck());
+    public TestableDependency mDependency = new TestableDependency(mContext);
 
     @Before
     public void SysuiSetup() throws Exception {
-        mException = null;
         System.setProperty("dexmaker.share_classloader", "true");
-        mContext = new TestableContext(InstrumentationRegistry.getTargetContext(), this);
         SystemUIFactory.createFromConfig(mContext);
-        mDependency = new TestDependency();
-        mDependency.mContext = mContext;
-        mDependency.start();
     }
 
-    @After
-    public void cleanup() throws Exception {
-        mContext.getSettingsProvider().clearOverrides(this);
+    protected LeakCheck getLeakCheck() {
+        return null;
     }
 
-    protected Context getContext() {
+    public Context getContext() {
         return mContext;
     }
 
@@ -84,48 +73,11 @@
         }
     }
 
-    // Used for leak tracking, returns null to indicate no leak tracking by default.
-    public Tracker getTracker(String tag) {
-        return null;
-    }
-
-    public <T> T injectMockDependency(Class<T> cls) {
-        final T mock = mock(cls);
-        mDependency.injectTestDependency(cls, mock);
-        return mock;
-    }
-
-    public <T> void injectTestDependency(Class<T> cls, T obj) {
-        mDependency.injectTestDependency(cls, obj);
-    }
-
-    public <T> void injectTestDependency(DependencyKey<T> key, T obj) {
-        mDependency.injectTestDependency(key, obj);
-    }
-
     public static final class EmptyRunnable implements Runnable {
         public void run() {
         }
     }
 
-    public static class TestDependency extends Dependency {
-        private final ArrayMap<Object, Object> mObjs = new ArrayMap<>();
-
-        private <T> void injectTestDependency(DependencyKey<T> key, T obj) {
-            mObjs.put(key, obj);
-        }
-
-        private <T> void injectTestDependency(Class<T> key, T obj) {
-            mObjs.put(key, obj);
-        }
-
-        @Override
-        protected <T> T createDependency(Object key) {
-            if (mObjs.containsKey(key)) return (T) mObjs.get(key);
-            return super.createDependency(key);
-        }
-    }
-
     public static final class Idler implements MessageQueue.IdleHandler {
         private final Runnable mCallback;
         private boolean mIdle;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
new file mode 100644
index 0000000..b94a2fb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 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;
+
+import android.content.Context;
+import android.testing.LeakCheck;
+import android.testing.TestableContext;
+import android.util.ArrayMap;
+
+public class SysuiTestableContext extends TestableContext implements SysUiServiceProvider {
+
+    private ArrayMap<Class<?>, Object> mComponents;
+
+    public SysuiTestableContext(Context base) {
+        super(base);
+    }
+
+    public SysuiTestableContext(Context base, LeakCheck check) {
+        super(base, check);
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T> T getComponent(Class<T> interfaceType) {
+        return (T) (mComponents != null ? mComponents.get(interfaceType) : null);
+    }
+
+    public <T, C extends T> void putComponent(Class<T> interfaceType, C component) {
+        if (mComponents == null) mComponents = new ArrayMap<>();
+        mComponents.put(interfaceType, component);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
new file mode 100644
index 0000000..53a7994
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 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;
+
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.util.ArrayMap;
+
+public class TestableDependency extends Dependency {
+    private final ArrayMap<Object, Object> mObjs = new ArrayMap<>();
+
+    public TestableDependency(Context context) {
+        mContext = context;
+        if (SystemUIFactory.getInstance() == null) {
+            SystemUIFactory.createFromConfig(context);
+        }
+        start();
+    }
+
+    public <T> T injectMockDependency(Class<T> cls) {
+        final T mock = mock(cls);
+        injectTestDependency(cls, mock);
+        return mock;
+    }
+
+    public <T> void injectTestDependency(DependencyKey<T> key, T obj) {
+        mObjs.put(key, obj);
+    }
+
+    public <T> void injectTestDependency(Class<T> key, T obj) {
+        mObjs.put(key, obj);
+    }
+
+    @Override
+    protected <T> T createDependency(Object key) {
+        if (mObjs.containsKey(key)) return (T) mObjs.get(key);
+        return super.createDependency(key);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/UiThreadTest.java b/packages/SystemUI/tests/src/com/android/systemui/UiThreadTest.java
deleted file mode 100644
index 58369b1..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/UiThreadTest.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2017 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;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * When applied to a class, all tests, befores, and afters will behave as if
- * they have @UiThreadTest applied to them.
- */
-@Target({ElementType.METHOD, ElementType.TYPE})
-@Retention(RetentionPolicy.RUNTIME)
-public @interface UiThreadTest {
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java
index 5477afa8..048936b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java
@@ -47,7 +47,7 @@
             return;
         }
 
-        mContext.getSettingsProvider().acquireOverridesBuilder(this)
+        mContext.getSettingsProvider().acquireOverridesBuilder()
                 .addSetting("secure", Settings.Secure.DOZE_ALWAYS_ON, null)
                 .build();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index ba39671..cdbde5e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -39,19 +39,20 @@
 import static org.mockito.Mockito.when;
 
 import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
 import android.view.Display;
 
-import com.android.systemui.SysUIRunner;
-import com.android.systemui.UiThreadTest;
 import com.android.internal.hardware.AmbientDisplayConfiguration;
 import com.android.systemui.util.wakelock.WakeLockFake;
 
+import android.testing.UiThreadTest;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @SmallTest
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
 @UiThreadTest
 public class DozeMachineTest {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notification/PropertyAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/notification/PropertyAnimatorTest.java
index 53053fa..8484bed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notification/PropertyAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/notification/PropertyAnimatorTest.java
@@ -14,22 +14,25 @@
 
 package com.android.systemui.notification;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
-import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-
+import android.testing.AndroidTestingRunner;
+import android.testing.UiThreadTest;
 import android.util.FloatProperty;
 import android.util.Property;
 import android.view.View;
 import android.view.animation.Interpolator;
 
-import com.android.systemui.SysUIRunner;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.UiThreadTest;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
 import com.android.systemui.statusbar.stack.AnimationFilter;
 import com.android.systemui.statusbar.stack.AnimationProperties;
@@ -39,18 +42,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
 @SmallTest
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
 @UiThreadTest
 public class PropertyAnimatorTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java
index 658966c..4f0815d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java
@@ -72,7 +72,7 @@
     private PackageManager mMockPm;
     private PluginListener mMockListener;
     private PluginInstanceManager mPluginInstanceManager;
-    private PluginManager mMockManager;
+    private PluginManagerImpl mMockManager;
     private VersionInfo mMockVersionInfo;
 
     @Before
@@ -82,7 +82,7 @@
         mContextWrapper = new MyContextWrapper(getContext());
         mMockPm = mock(PackageManager.class);
         mMockListener = mock(PluginListener.class);
-        mMockManager = mock(PluginManager.class);
+        mMockManager = mock(PluginManagerImpl.class);
         when(mMockManager.getClassLoader(any(), any()))
                 .thenReturn(getClass().getClassLoader());
         mMockVersionInfo = mock(VersionInfo.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
index 053e5cf2..a3d5d5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
@@ -34,7 +34,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.annotations.ProvidesInterface;
 import com.android.systemui.plugins.PluginInstanceManager.PluginInfo;
-import com.android.systemui.plugins.PluginManager.PluginInstanceManagerFactory;
+import com.android.systemui.plugins.PluginManagerImpl.PluginInstanceManagerFactory;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -50,7 +50,7 @@
 
     private PluginInstanceManagerFactory mMockFactory;
     private PluginInstanceManager mMockPluginInstance;
-    private PluginManager mPluginManager;
+    private PluginManagerImpl mPluginManager;
     private PluginListener mMockListener;
 
     private UncaughtExceptionHandler mRealExceptionHandler;
@@ -66,7 +66,8 @@
         when(mMockFactory.createPluginInstanceManager(Mockito.any(), Mockito.any(), Mockito.any(),
                 Mockito.anyBoolean(), Mockito.any(), Mockito.any(), Mockito.any()))
                 .thenReturn(mMockPluginInstance);
-        mPluginManager = new PluginManager(getContext(), mMockFactory, true, mMockExceptionHandler);
+        mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, true,
+                mMockExceptionHandler);
         resetExceptionHandler();
         mMockListener = mock(PluginListener.class);
     }
@@ -98,7 +99,7 @@
 
     @Test
     public void testNonDebuggable() {
-        mPluginManager = new PluginManager(getContext(), mMockFactory, false,
+        mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, false,
                 mMockExceptionHandler);
         resetExceptionHandler();
 
@@ -148,7 +149,7 @@
 
         ComponentName testComponent = new ComponentName(getContext().getPackageName(),
                 PluginManagerTest.class.getName());
-        Intent intent = new Intent(PluginManager.DISABLE_PLUGIN);
+        Intent intent = new Intent(PluginManagerImpl.DISABLE_PLUGIN);
         intent.setData(Uri.parse("package://" + testComponent.flattenToString()));
         mPluginManager.onReceive(mContext, intent);
         verify(nm).cancel(eq(testComponent.getClassName()), eq(SystemMessage.NOTE_PLUGIN));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 7153340..deb31da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -21,14 +21,16 @@
 
 import com.android.keyguard.CarrierText;
 import com.android.systemui.Dependency;
-import com.android.systemui.FragmentTestCase;
 import com.android.systemui.R;
-import com.android.systemui.SysUIRunner;
+
+import android.testing.AndroidTestingRunner;
+
+import com.android.systemui.SysuiBaseFragmentTest;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
-import com.android.systemui.util.LayoutInflaterBuilder;
-import com.android.systemui.utils.TestableLooper;
-import com.android.systemui.utils.TestableLooper.RunWithLooper;
+import android.testing.LayoutInflaterBuilder;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -38,9 +40,9 @@
 import android.view.View;
 import android.widget.FrameLayout;
 
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
-public class QSFragmentTest extends FragmentTestCase {
+public class QSFragmentTest extends SysuiBaseFragmentTest {
 
     public QSFragmentTest() {
         super(QSFragment.class);
@@ -56,8 +58,9 @@
                         .replace(CarrierText.class, View.class)
                         .build());
 
-        injectTestDependency(Dependency.BG_LOOPER, TestableLooper.get(this).getLooper());
-        injectMockDependency(UserSwitcherController.class);
+        mDependency.injectTestDependency(Dependency.BG_LOOPER,
+                TestableLooper.get(this).getLooper());
+        mDependency.injectMockDependency(UserSwitcherController.class);
         injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
index e38c30f..1ff373c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
@@ -33,8 +33,8 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.policy.SecurityController;
-import com.android.systemui.util.LayoutInflaterBuilder;
-import com.android.systemui.utils.TestableImageView;
+import android.testing.LayoutInflaterBuilder;
+import android.testing.TestableImageView;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -57,8 +57,8 @@
 
     @Before
     public void setUp() {
-        injectTestDependency(SecurityController.class, mSecurityController);
-        injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper());
+        mDependency.injectTestDependency(SecurityController.class, mSecurityController);
+        mDependency.injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper());
         mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE,
                 new LayoutInflaterBuilder(mContext)
                         .replace("ImageView", TestableImageView.class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index d1e17f4..5ae107a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -21,13 +21,13 @@
 import static org.mockito.Mockito.when;
 
 import com.android.systemui.Dependency;
-import com.android.systemui.SysUIRunner;
+import android.testing.AndroidTestingRunner;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.qs.QSTile.State;
 import com.android.systemui.qs.QSTileHost;
-import com.android.systemui.utils.TestableLooper;
-import com.android.systemui.utils.TestableLooper.RunWithLooper;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -37,7 +37,7 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 @SmallTest
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
 @RunWithLooper
 public class TileQueryHelperTest extends SysuiTestCase {
     private TestableLooper mBGLooper;
@@ -46,7 +46,7 @@
     @Before
     public void setup() {
         mBGLooper = TestableLooper.get(this);
-        injectTestDependency(Dependency.BG_LOOPER, mBGLooper.getLooper());
+        mDependency.injectTestDependency(Dependency.BG_LOOPER, mBGLooper.getLooper());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index 6d7b50f..ddd6615 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -25,12 +25,12 @@
 import android.service.quicksettings.Tile;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import com.android.systemui.SysUIRunner;
+import android.testing.AndroidTestingRunner;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
-import com.android.systemui.utils.TestableLooper;
-import com.android.systemui.utils.TestableLooper.RunWithLooper;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
 
 import org.junit.After;
 import org.junit.Before;
@@ -42,7 +42,7 @@
 import java.util.ArrayList;
 
 @SmallTest
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
 public class TileServicesTest extends SysuiTestCase {
     private static int NUM_FAKES = TileServices.DEFAULT_MAX_BOUND * 2;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
index 8520bdb..5b9270d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
@@ -33,41 +33,35 @@
 import static org.mockito.Mockito.when;
 
 import android.app.INotificationManager;
-import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
 import android.app.NotificationManager;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.ParceledListSlice;
 import android.graphics.drawable.Drawable;
 import android.service.notification.StatusBarNotification;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
+import android.testing.AndroidTestingRunner;
+import android.testing.UiThreadTest;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.Switch;
 import android.widget.TextView;
-import com.android.internal.util.CharSequences;
+
 import com.android.systemui.R;
-import com.android.systemui.SysUIRunner;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.UiThreadTest;
 
 import org.junit.Before;
-import org.junit.runner.RunWith;
 import org.junit.Test;
-import org.mockito.Mockito;
-import java.util.Arrays;
+import org.junit.runner.RunWith;
+
 import java.util.Collections;
 import java.util.concurrent.CountDownLatch;
 
 @SmallTest
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
 @UiThreadTest
 public class NotificationInfoTest extends SysuiTestCase {
     private static final String TEST_PACKAGE_NAME = "test_package";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java
index b8be4fa..c2c6336 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java
@@ -14,17 +14,17 @@
 
 package com.android.systemui.statusbar;
 
-import com.android.systemui.SysUIRunner;
-import com.android.systemui.utils.TestableLooper;
-import com.android.systemui.utils.TestableLooper.RunWithLooper;
-import com.android.systemui.utils.ViewUtils;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+import android.testing.ViewUtils;
 import com.android.systemui.utils.leaks.LeakCheckedTest;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
 public class NotificationMenuRowTest extends LeakCheckedTest {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index 3ccb160..e41a827 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -22,7 +22,7 @@
 import com.android.internal.app.NightDisplayController;
 import com.android.systemui.Prefs;
 import com.android.systemui.Prefs.Key;
-import com.android.systemui.SysUIRunner;
+import android.testing.AndroidTestingRunner;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.qs.QSTileHost;
 
@@ -32,7 +32,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mockito;
 
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
 public class AutoTileManagerTest extends SysuiTestCase {
 
     private QSTileHost mQsTileHost;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index f55115e..a9acda3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -23,24 +23,22 @@
 import android.view.View;
 import android.view.ViewPropertyAnimator;
 
-import com.android.systemui.FragmentTestCase;
 import com.android.systemui.R;
-import com.android.systemui.SysUIRunner;
+import android.testing.AndroidTestingRunner;
+
+import com.android.systemui.SysuiBaseFragmentTest;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.SecurityController;
 import com.android.systemui.tuner.TunerService;
-import com.android.systemui.utils.TestableLooper.RunWithLooper;
+import android.testing.TestableLooper.RunWithLooper;
 
 import org.junit.Before;
 import org.junit.runner.RunWith;
 import org.junit.Test;
 import org.mockito.Mockito;
 
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
-public class CollapsedStatusBarFragmentTest extends FragmentTestCase {
+public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
 
     private NotificationIconAreaController mMockNotificiationAreaController;
     private View mNotificationAreaInner;
@@ -51,9 +49,9 @@
 
     @Before
     public void setup() {
-        mContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
-        mContext.putComponent(StatusBar.class, mock(StatusBar.class));
-        mContext.putComponent(TunerService.class, mock(TunerService.class));
+        mSysuiContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
+        mSysuiContext.putComponent(StatusBar.class, mock(StatusBar.class));
+        mSysuiContext.putComponent(TunerService.class, mock(TunerService.class));
         injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
         mMockNotificiationAreaController = mock(NotificationIconAreaController.class);
         mNotificationAreaInner = mock(View.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
index 1fa9846..e7cdcb5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
@@ -19,24 +19,25 @@
 
 import android.content.Context;
 import android.os.Looper;
+import android.testing.AndroidTestingRunner;
 import android.view.Display;
 import android.view.WindowManager;
 
 import com.android.systemui.Dependency;
-import com.android.systemui.FragmentTestCase;
-import com.android.systemui.SysUIRunner;
+
+import com.android.systemui.SysuiBaseFragmentTest;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.utils.TestableLooper.RunWithLooper;
+import android.testing.TestableLooper.RunWithLooper;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
-public class NavigationBarFragmentTest extends FragmentTestCase {
+public class NavigationBarFragmentTest extends SysuiBaseFragmentTest {
 
     public NavigationBarFragmentTest() {
         super(NavigationBarFragment.class);
@@ -44,11 +45,11 @@
 
     @Before
     public void setup() {
-        injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper());
-        mContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
-        mContext.putComponent(StatusBar.class, mock(StatusBar.class));
-        mContext.putComponent(Recents.class, mock(Recents.class));
-        mContext.putComponent(Divider.class, mock(Divider.class));
+        mDependency.injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper());
+        mSysuiContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
+        mSysuiContext.putComponent(StatusBar.class, mock(StatusBar.class));
+        mSysuiContext.putComponent(Recents.class, mock(Recents.class));
+        mSysuiContext.putComponent(Divider.class, mock(Divider.class));
         injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
         WindowManager windowManager = mock(WindowManager.class);
         Display defaultDisplay = mContext.getSystemService(WindowManager.class).getDefaultDisplay();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerTest.java
index e3a5ef0..3e79a04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerTest.java
@@ -42,8 +42,8 @@
 
     @Before
     public void setup() {
-        mPluginManager = injectMockDependency(PluginManager.class);
-        mTunerService = injectMockDependency(TunerService.class);
+        mPluginManager = mDependency.injectMockDependency(PluginManager.class);
+        mTunerService = mDependency.injectMockDependency(TunerService.class);
         mExtensionController = Dependency.get(ExtensionController.class);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index 8cbf95b..efa232b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -18,7 +18,7 @@
 
 import com.android.settingslib.Utils;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.utils.FakeSettingsProvider.SettingOverrider;
+import android.testing.TestableSettings.SettingOverrider;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -92,9 +92,9 @@
                         attr);
 
         // Must set the Settings value before instantiating the NetworkControllerImpl due to bugs in
-        // FakeSettingsProvider.
+        // TestableSettings.
         SettingOverrider settingsOverrider =
-                mContext.getSettingsProvider().acquireOverridesBuilder(this)
+                mContext.getSettingsProvider().acquireOverridesBuilder()
                         .addSetting("global", Settings.Global.NETWORK_SCORING_UI_ENABLED, "1")
                         .build();
         super.setUp(); // re-instantiate NetworkControllImpl now that setting has been updated
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/FakeContentResolver.java b/packages/SystemUI/tests/src/com/android/systemui/utils/FakeContentResolver.java
deleted file mode 100644
index 34f2e01..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/FakeContentResolver.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * 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.utils;
-
-import android.content.ContentProvider;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.IContentProvider;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.util.ArraySet;
-
-import com.google.android.collect.Maps;
-
-import java.util.Map;
-
-/**
- * Alternative to a MockContentResolver that falls back to real providers.
- */
-public class FakeContentResolver extends ContentResolver {
-
-    private final Map<String, ContentProvider> mProviders = Maps.newHashMap();
-    private final ContentResolver mParent;
-    private final ArraySet<ContentProvider> mInUse = new ArraySet<>();
-    private boolean mFallbackToExisting;
-
-    public FakeContentResolver(Context context) {
-        super(context);
-        mParent = context.getContentResolver();
-        mFallbackToExisting = true;
-    }
-
-    /**
-     * Sets whether existing providers should be returned when a mock does not exist.
-     * The default is true.
-     */
-    public void setFallbackToExisting(boolean fallbackToExisting) {
-        mFallbackToExisting = fallbackToExisting;
-    }
-
-    /**
-     * Adds access to a provider based on its authority
-     *
-     * @param name The authority name associated with the provider.
-     * @param provider An instance of {@link android.content.ContentProvider} or one of its
-     * subclasses, or null.
-     */
-    public void addProvider(String name, ContentProvider provider) {
-        mProviders.put(name, provider);
-    }
-
-    @Override
-    protected IContentProvider acquireProvider(Context context, String name) {
-        final ContentProvider provider = mProviders.get(name);
-        if (provider != null) {
-            return provider.getIContentProvider();
-        } else {
-            return mFallbackToExisting ? mParent.acquireProvider(name) : null;
-        }
-    }
-
-    @Override
-    protected IContentProvider acquireExistingProvider(Context context, String name) {
-        final ContentProvider provider = mProviders.get(name);
-        if (provider != null) {
-            return provider.getIContentProvider();
-        } else {
-            return mFallbackToExisting ? mParent.acquireExistingProvider(
-                    new Uri.Builder().authority(name).build()) : null;
-        }
-    }
-
-    @Override
-    public boolean releaseProvider(IContentProvider provider) {
-        if (!mFallbackToExisting) return true;
-        if (mInUse.contains(provider)) {
-            mInUse.remove(provider);
-            return true;
-        }
-        return mParent.releaseProvider(provider);
-    }
-
-    @Override
-    protected IContentProvider acquireUnstableProvider(Context c, String name) {
-        final ContentProvider provider = mProviders.get(name);
-        if (provider != null) {
-            return provider.getIContentProvider();
-        } else {
-            return mFallbackToExisting ? mParent.acquireUnstableProvider(name) : null;
-        }
-    }
-
-    @Override
-    public boolean releaseUnstableProvider(IContentProvider icp) {
-        if (!mFallbackToExisting) return true;
-        if (mInUse.contains(icp)) {
-            mInUse.remove(icp);
-            return true;
-        }
-        return mParent.releaseUnstableProvider(icp);
-    }
-
-    @Override
-    public void unstableProviderDied(IContentProvider icp) {
-        if (!mFallbackToExisting) return;
-        if (mInUse.contains(icp)) {
-            return;
-        }
-        mParent.unstableProviderDied(icp);
-    }
-
-    @Override
-    public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
-        if (!mFallbackToExisting) return;
-        if (!mProviders.containsKey(uri.getAuthority())) {
-            super.notifyChange(uri, observer, syncToNetwork);
-        }
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProvider.java b/packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProvider.java
deleted file mode 100644
index f40fe4c..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProvider.java
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * 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.utils;
-
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.provider.Settings;
-import android.support.annotation.VisibleForTesting;
-import android.test.mock.MockContentProvider;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Log;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.utils.FakeSettingsProvider.SettingOverrider.Builder;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Allows calls to android.provider.Settings to be tested easier.  A SettingOverride
- * can be acquired and a set of specific settings can be set to a value (and not changed
- * in the system when set), so that they can be tested without breaking the test device.
- * <p>
- * To use, in the before method acquire the override add all settings that will affect if
- * your test passes or not.
- *
- * <pre class="prettyprint">
- * {@literal
- * mSettingOverride = mTestableContext.getSettingsProvider().acquireOverridesBuilder()
- *         .addSetting("secure", Secure.USER_SETUP_COMPLETE, "0")
- *         .build();
- * }
- * </pre>
- *
- * Then in the after free up the settings.
- *
- * <pre class="prettyprint">
- * {@literal
- * mSettingOverride.release();
- * }
- * </pre>
- */
-public class FakeSettingsProvider extends MockContentProvider {
-
-    private static final String TAG = "FakeSettingsProvider";
-    private static final boolean DEBUG = false;
-
-    // Number of times to try to acquire a setting if in use.
-    private static final int MAX_TRIES = 10;
-    // Time to wait for each setting.  WAIT_TIMEOUT * MAX_TRIES will be the maximum wait time
-    // for a setting.
-    private static final long WAIT_TIMEOUT = 1000;
-
-    private final Map<String, SettingOverrider> mOverrideMap = new ArrayMap<>();
-    private final Map<SysuiTestCase, List<SettingOverrider>> mOwners = new ArrayMap<>();
-
-    private static FakeSettingsProvider sInstance;
-    private final ContentProviderClient mSettings;
-    private final ContentResolver mResolver;
-
-    private FakeSettingsProvider(ContentProviderClient settings, ContentResolver resolver) {
-        mSettings = settings;
-        mResolver = resolver;
-    }
-
-    public Builder acquireOverridesBuilder(SysuiTestCase test) {
-        return new Builder(this, test);
-    }
-
-    public void clearOverrides(SysuiTestCase test) {
-        List<SettingOverrider> overrides = mOwners.remove(test);
-        if (overrides != null) {
-            overrides.forEach(override -> override.ensureReleased());
-        }
-    }
-
-    public Bundle call(String method, String arg, Bundle extras) {
-        // Methods are "GET_system", "GET_global", "PUT_secure", etc.
-        final String[] commands = method.split("_", 2);
-        final String op = commands[0];
-        final String table = commands[1];
-
-        synchronized (mOverrideMap) {
-            SettingOverrider overrider = mOverrideMap.get(key(table, arg));
-            if (overrider == null) {
-                // Fall through to real settings.
-                try {
-                    if (DEBUG) Log.d(TAG, "Falling through to real settings " + method);
-                    // TODO: Add our own version of caching to handle this.
-                    Bundle call = mSettings.call(method, arg, extras);
-                    call.remove(Settings.CALL_METHOD_TRACK_GENERATION_KEY);
-                    return call;
-                } catch (RemoteException e) {
-                    throw new RuntimeException(e);
-                }
-            }
-            String value;
-            Bundle out = new Bundle();
-            switch (op) {
-                case "GET":
-                    value = overrider.get(table, arg);
-                    if (value != null) {
-                        out.putString(Settings.NameValueTable.VALUE, value);
-                    }
-                    break;
-                case "PUT":
-                    value = extras.getString(Settings.NameValueTable.VALUE, null);
-                    if (value != null) {
-                        overrider.put(table, arg, value);
-                    } else {
-                        overrider.remove(table, arg);
-                    }
-                    break;
-                default:
-                    throw new UnsupportedOperationException("Unknown command " + method);
-            }
-            return out;
-        }
-    }
-
-    private void acquireSettings(SettingOverrider overridder, Set<String> keys,
-            SysuiTestCase owner) throws AcquireTimeoutException {
-        synchronized (mOwners) {
-            List<SettingOverrider> list = mOwners.get(owner);
-            if (list == null) {
-                list = new ArrayList<>();
-                mOwners.put(owner, list);
-            }
-            list.add(overridder);
-        }
-        synchronized (mOverrideMap) {
-            for (int i = 0; i < MAX_TRIES; i++) {
-                if (checkKeys(keys, false)) break;
-                try {
-                    if (DEBUG) Log.d(TAG, "Waiting for contention to finish");
-                    mOverrideMap.wait(WAIT_TIMEOUT);
-                } catch (InterruptedException e) {
-                }
-            }
-            checkKeys(keys, true);
-            for (String key : keys) {
-                if (DEBUG) Log.d(TAG, "Acquiring " + key);
-                mOverrideMap.put(key, overridder);
-            }
-        }
-    }
-
-    private void releaseSettings(Set<String> keys) {
-        synchronized (mOverrideMap) {
-            for (String key : keys) {
-                if (DEBUG) Log.d(TAG, "Releasing " + key);
-                mOverrideMap.remove(key);
-            }
-            if (DEBUG) Log.d(TAG, "Notifying");
-            mOverrideMap.notify();
-        }
-    }
-
-    @VisibleForTesting
-    public Object getLock() {
-        return mOverrideMap;
-    }
-
-    private boolean checkKeys(Set<String> keys, boolean shouldThrow)
-            throws AcquireTimeoutException {
-        for (String key : keys) {
-            if (mOverrideMap.containsKey(key)) {
-                if (shouldThrow) {
-                    throw new AcquireTimeoutException("Could not acquire " + key);
-                }
-                return false;
-            }
-        }
-        return true;
-    }
-
-    public static class SettingOverrider {
-        private final Set<String> mValidKeys;
-        private final Map<String, String> mValueMap = new ArrayMap<>();
-        private final FakeSettingsProvider mProvider;
-        private boolean mReleased;
-
-        private SettingOverrider(Set<String> keys, FakeSettingsProvider provider) {
-            mValidKeys = new ArraySet<>(keys);
-            mProvider = provider;
-        }
-
-        private void ensureReleased() {
-            if (!mReleased) {
-                release();
-            }
-        }
-
-        public void release() {
-            mProvider.releaseSettings(mValidKeys);
-            mReleased = true;
-        }
-
-        private void putDirect(String key, String value) {
-            mValueMap.put(key, value);
-        }
-
-        public void put(String table, String key, String value) {
-            if (!mValidKeys.contains(key(table, key))) {
-                throw new IllegalArgumentException("Key " + table + " " + key
-                        + " not acquired for this overrider");
-            }
-            mValueMap.put(key(table, key), value);
-        }
-
-        public void remove(String table, String key) {
-            if (!mValidKeys.contains(key(table, key))) {
-                throw new IllegalArgumentException("Key " + table + " " + key
-                        + " not acquired for this overrider");
-            }
-            mValueMap.remove(key(table, key));
-        }
-
-        public String get(String table, String key) {
-            if (!mValidKeys.contains(key(table, key))) {
-                throw new IllegalArgumentException("Key " + table + " " + key
-                        + " not acquired for this overrider");
-            }
-            Log.d(TAG, "Get " + table + " " + key + " " + mValueMap.get(key(table, key)));
-            return mValueMap.get(key(table, key));
-        }
-
-        public static class Builder {
-            private final FakeSettingsProvider mProvider;
-            private final SysuiTestCase mOwner;
-            private Set<String> mKeys = new ArraySet<>();
-            private Map<String, String> mValues = new ArrayMap<>();
-
-            private Builder(FakeSettingsProvider provider, SysuiTestCase test) {
-                mProvider = provider;
-                mOwner = test;
-            }
-
-            public Builder addSetting(String table, String key) {
-                mKeys.add(key(table, key));
-                return this;
-            }
-
-            public Builder addSetting(String table, String key, String value) {
-                addSetting(table, key);
-                mValues.put(key(table, key), value);
-                return this;
-            }
-
-            public SettingOverrider build() throws AcquireTimeoutException {
-                SettingOverrider overrider = new SettingOverrider(mKeys, mProvider);
-                mProvider.acquireSettings(overrider, mKeys, mOwner);
-                mValues.forEach((key, value) -> overrider.putDirect(key, value));
-                return overrider;
-            }
-        }
-    }
-
-    public static class AcquireTimeoutException extends Exception {
-        public AcquireTimeoutException(String str) {
-            super(str);
-        }
-    }
-
-    private static String key(String table, String key) {
-        return table + "_" + key;
-    }
-
-    /**
-     * Since the settings provider is cached inside android.provider.Settings, this must
-     * be gotten statically to ensure there is only one instance referenced.
-     * @param settings
-     */
-    public static FakeSettingsProvider getFakeSettingsProvider(ContentProviderClient settings,
-            ContentResolver resolver) {
-        if (sInstance == null) {
-            sInstance = new FakeSettingsProvider(settings, resolver);
-        }
-        return sInstance;
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProviderTest.java
deleted file mode 100644
index 63bb5e7..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProviderTest.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * 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.utils;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import android.content.ContentResolver;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.provider.Settings;
-import android.provider.Settings.Global;
-import android.provider.Settings.Secure;
-import android.support.test.runner.AndroidJUnit4;
-import android.util.Log;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.utils.FakeSettingsProvider.AcquireTimeoutException;
-import com.android.systemui.utils.FakeSettingsProvider.SettingOverrider;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class FakeSettingsProviderTest extends SysuiTestCase {
-
-    public static final String NONEXISTENT_SETTING = "nonexistent_setting";
-    private static final String TAG = "FakeSettingsProviderTest";
-    private SettingOverrider mOverrider;
-    private ContentResolver mContentResolver;
-
-    @Before
-    public void setup() throws AcquireTimeoutException {
-        mOverrider = mContext.getSettingsProvider().acquireOverridesBuilder(this)
-                .addSetting("secure", NONEXISTENT_SETTING)
-                .addSetting("global", NONEXISTENT_SETTING, "initial value")
-                .addSetting("global", Global.DEVICE_PROVISIONED)
-                .build();
-        mContentResolver = mContext.getContentResolver();
-    }
-
-    @After
-    public void teardown() {
-        if (mOverrider != null) {
-            mOverrider.release();
-        }
-    }
-
-    @Test
-    public void testInitialValueSecure() {
-        String value = Secure.getString(mContentResolver, NONEXISTENT_SETTING);
-        assertNull(value);
-    }
-
-    @Test
-    public void testInitialValueGlobal() {
-        String value = Global.getString(mContentResolver, NONEXISTENT_SETTING);
-        assertEquals("initial value", value);
-    }
-
-    @Test
-    public void testSeparateTables() {
-        Secure.putString(mContentResolver, NONEXISTENT_SETTING, "something");
-        Global.putString(mContentResolver, NONEXISTENT_SETTING, "else");
-        assertEquals("something", Secure.getString(mContentResolver, NONEXISTENT_SETTING));
-        assertEquals("something", mOverrider.get("secure", NONEXISTENT_SETTING));
-        assertEquals("else", Global.getString(mContentResolver, NONEXISTENT_SETTING));
-        assertEquals("else", mOverrider.get("global", NONEXISTENT_SETTING));
-    }
-
-    @Test
-    public void testPassThrough() {
-        // Grab the value of a setting that is not overridden.
-        assertTrue(Secure.getInt(mContentResolver, Secure.USER_SETUP_COMPLETE, 0) != 0);
-    }
-
-    @Test
-    public void testOverrideExisting() {
-        // Grab the value of a setting that is overridden and will be different than the actual
-        // value.
-        assertNull(Global.getString(mContentResolver, Global.DEVICE_PROVISIONED));
-    }
-
-    @Test
-    public void testRelease() {
-        // Verify different value.
-        assertNull(Global.getString(mContentResolver, Global.DEVICE_PROVISIONED));
-        mOverrider.release();
-        mOverrider = null;
-        // Verify actual value after release.
-        assertEquals("1", Global.getString(mContentResolver, Global.DEVICE_PROVISIONED));
-    }
-
-    @Test
-    public void testAutoRelease() throws Exception {
-        super.cleanup();
-        mContext.getSettingsProvider().acquireOverridesBuilder(this)
-                .addSetting("global", Global.DEVICE_PROVISIONED)
-                .build();
-    }
-
-    @Test
-    public void testContention() throws AcquireTimeoutException, InterruptedException {
-        SettingOverrider[] overriders = new SettingOverrider[2];
-        Object lock = new Object();
-        String secure = "secure";
-        String key = "something shared";
-        String[] result = new String[1];
-        overriders[0] = mContext.getSettingsProvider().acquireOverridesBuilder(this)
-                .addSetting(secure, key, "Some craziness")
-                .build();
-        synchronized (lock) {
-            HandlerThread t = runOnHandler(() -> {
-                try {
-                    // Grab the lock that will be used for the settings ownership to ensure
-                    // we have some contention going on.
-                    synchronized (mContext.getSettingsProvider().getLock()) {
-                        synchronized (lock) {
-                            // Let the other thread know to release the settings, but it won't
-                            // be able to until this thread waits in the build() method.
-                            lock.notify();
-                        }
-                        overriders[1] = mContext.getSettingsProvider()
-                                .acquireOverridesBuilder(FakeSettingsProviderTest.this)
-                                .addSetting(secure, key, "default value")
-                                .build();
-                        // Ensure that the default is the one we set, and not left over from
-                        // the other setting override.
-                        result[0] = Settings.Secure.getString(mContentResolver, key);
-                        synchronized (lock) {
-                            // Let the main thread know we are done.
-                            lock.notify();
-                        }
-                    }
-                } catch (AcquireTimeoutException e) {
-                    Log.e(TAG, "Couldn't acquire setting", e);
-                }
-            });
-            // Wait for the thread to hold the acquire lock, then release the settings.
-            lock.wait();
-            overriders[0].release();
-            // Wait for the thread to be done getting the value.
-            lock.wait();
-            // Quit and cleanup.
-            t.quitSafely();
-            assertNotNull(overriders[1]);
-            overriders[1].release();
-        }
-        // Verify the value was the expected one from the thread's SettingOverride.
-        assertEquals("default value", result[0]);
-    }
-
-    private HandlerThread runOnHandler(Runnable r) {
-        HandlerThread t = new HandlerThread("Test Thread");
-        t.start();
-        new Handler(t.getLooper()).post(r);
-        return t;
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java b/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java
deleted file mode 100644
index 1429390..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * 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.utils;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentCallbacks;
-import android.content.ComponentName;
-import android.content.ContentProviderClient;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.ArrayMap;
-import android.view.LayoutInflater;
-
-import com.android.systemui.SysUiServiceProvider;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.utils.leaks.Tracker;
-
-public class TestableContext extends ContextWrapper implements SysUiServiceProvider {
-
-    private final FakeContentResolver mFakeContentResolver;
-    private final FakeSettingsProvider mSettingsProvider;
-
-    private ArrayMap<String, Object> mMockSystemServices;
-    private ArrayMap<ComponentName, IBinder> mMockServices;
-    private ArrayMap<ServiceConnection, ComponentName> mActiveServices;
-    private ArrayMap<Class<?>, Object> mComponents;
-
-    private PackageManager mMockPackageManager;
-    private Tracker mReceiver;
-    private Tracker mService;
-    private Tracker mComponent;
-
-    public TestableContext(Context base, SysuiTestCase test) {
-        super(base);
-        mFakeContentResolver = new FakeContentResolver(base);
-        ContentProviderClient settings = base.getContentResolver()
-                .acquireContentProviderClient(Settings.AUTHORITY);
-        mSettingsProvider = FakeSettingsProvider.getFakeSettingsProvider(settings,
-                mFakeContentResolver);
-        mFakeContentResolver.addProvider(Settings.AUTHORITY, mSettingsProvider);
-        mReceiver = test.getTracker("receiver");
-        mService = test.getTracker("service");
-        mComponent = test.getTracker("component");
-    }
-
-    public void setMockPackageManager(PackageManager mock) {
-        mMockPackageManager = mock;
-    }
-
-    @Override
-    public PackageManager getPackageManager() {
-        if (mMockPackageManager != null) {
-            return mMockPackageManager;
-        }
-        return super.getPackageManager();
-    }
-
-    @Override
-    public Resources getResources() {
-        return super.getResources();
-    }
-
-    public <T> void addMockSystemService(Class<T> service, T mock) {
-        addMockSystemService(getSystemServiceName(service), mock);
-    }
-
-    public void addMockSystemService(String name, Object service) {
-        mMockSystemServices = lazyInit(mMockSystemServices);
-        mMockSystemServices.put(name, service);
-    }
-
-    public void addMockService(ComponentName component, IBinder service) {
-        mMockServices = lazyInit(mMockServices);
-        mMockServices.put(component, service);
-    }
-
-    private <T, V> ArrayMap<T, V> lazyInit(ArrayMap<T, V> services) {
-        return services != null ? services : new ArrayMap<T, V>();
-    }
-
-    @Override
-    public Object getSystemService(String name) {
-        if (mMockSystemServices != null && mMockSystemServices.containsKey(name)) {
-            return mMockSystemServices.get(name);
-        }
-        if (name.equals(LAYOUT_INFLATER_SERVICE)) {
-            return getBaseContext().getSystemService(LayoutInflater.class).cloneInContext(this);
-        }
-        return super.getSystemService(name);
-    }
-
-    public FakeSettingsProvider getSettingsProvider() {
-        return mSettingsProvider;
-    }
-
-    @Override
-    public FakeContentResolver getContentResolver() {
-        return mFakeContentResolver;
-    }
-
-    @Override
-    public Context getApplicationContext() {
-        // Return this so its always a TestableContext.
-        return this;
-    }
-
-    @Override
-    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
-        if (mReceiver != null) mReceiver.getLeakInfo(receiver).addAllocation(new Throwable());
-        return super.registerReceiver(receiver, filter);
-    }
-
-    @Override
-    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
-            String broadcastPermission, Handler scheduler) {
-        if (mReceiver != null) mReceiver.getLeakInfo(receiver).addAllocation(new Throwable());
-        return super.registerReceiver(receiver, filter, broadcastPermission, scheduler);
-    }
-
-    @Override
-    public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
-            IntentFilter filter, String broadcastPermission, Handler scheduler) {
-        if (mReceiver != null) mReceiver.getLeakInfo(receiver).addAllocation(new Throwable());
-        return super.registerReceiverAsUser(receiver, user, filter, broadcastPermission,
-                scheduler);
-    }
-
-    @Override
-    public void unregisterReceiver(BroadcastReceiver receiver) {
-        if (mReceiver != null) mReceiver.getLeakInfo(receiver).clearAllocations();
-        super.unregisterReceiver(receiver);
-    }
-
-    @Override
-    public boolean bindService(Intent service, ServiceConnection conn, int flags) {
-        if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
-        if (checkMocks(service.getComponent(), conn)) return true;
-        return super.bindService(service, conn, flags);
-    }
-
-    @Override
-    public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
-            Handler handler, UserHandle user) {
-        if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
-        if (checkMocks(service.getComponent(), conn)) return true;
-        return super.bindServiceAsUser(service, conn, flags, handler, user);
-    }
-
-    @Override
-    public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
-            UserHandle user) {
-        if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
-        if (checkMocks(service.getComponent(), conn)) return true;
-        return super.bindServiceAsUser(service, conn, flags, user);
-    }
-
-    private boolean checkMocks(ComponentName component, ServiceConnection conn) {
-        if (mMockServices != null && component != null && mMockServices.containsKey(component)) {
-            mActiveServices = lazyInit(mActiveServices);
-            mActiveServices.put(conn, component);
-            conn.onServiceConnected(component, mMockServices.get(component));
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public void unbindService(ServiceConnection conn) {
-        if (mService != null) mService.getLeakInfo(conn).clearAllocations();
-        if (mActiveServices != null && mActiveServices.containsKey(conn)) {
-            conn.onServiceDisconnected(mActiveServices.get(conn));
-            mActiveServices.remove(conn);
-            return;
-        }
-        super.unbindService(conn);
-    }
-
-    public boolean isBound(ComponentName component) {
-        return mActiveServices != null && mActiveServices.containsValue(component);
-    }
-
-    @Override
-    public void registerComponentCallbacks(ComponentCallbacks callback) {
-        if (mComponent != null) mComponent.getLeakInfo(callback).addAllocation(new Throwable());
-        super.registerComponentCallbacks(callback);
-    }
-
-    @Override
-    public void unregisterComponentCallbacks(ComponentCallbacks callback) {
-        if (mComponent != null) mComponent.getLeakInfo(callback).clearAllocations();
-        super.unregisterComponentCallbacks(callback);
-    }
-
-    @SuppressWarnings("unchecked")
-    public <T> T getComponent(Class<T> interfaceType) {
-        return (T) (mComponents != null ? mComponents.get(interfaceType) : null);
-    }
-
-    public <T, C extends T> void putComponent(Class<T> interfaceType, C component) {
-        mComponents = lazyInit(mComponents);
-        mComponents.put(interfaceType, component);
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableImageView.java b/packages/SystemUI/tests/src/com/android/systemui/utils/TestableImageView.java
deleted file mode 100644
index b131460..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableImageView.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.utils;
-
-import android.annotation.DrawableRes;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.ImageView;
-
-public class TestableImageView extends ImageView {
-
-    private int mLastResId = -1;
-
-    public TestableImageView(Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    public void setImageResource(@DrawableRes int resId) {
-        mLastResId = resId;
-        super.setImageResource(resId);
-    }
-
-    public int getLastImageResource() {
-        return mLastResId;
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableLooper.java b/packages/SystemUI/tests/src/com/android/systemui/utils/TestableLooper.java
deleted file mode 100644
index 8902e0c..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableLooper.java
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Copyright (C) 2017 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.utils;
-
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.MessageQueue;
-import android.util.ArrayMap;
-
-import org.junit.runners.model.Statement;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.util.Map;
-
-/**
- * Creates a looper on the current thread with control over if/when messages are
- * executed. Warning: This class works through some reflection and may break/need
- * to be updated from time to time.
- */
-public class TestableLooper {
-
-    private final Method mNext;
-    private final Method mRecycleUnchecked;
-
-    private Looper mLooper;
-    private MessageQueue mQueue;
-    private boolean mMain;
-    private Object mOriginalMain;
-    private MessageHandler mMessageHandler;
-
-    private int mParsedCount;
-    private Handler mHandler;
-    private Message mEmptyMessage;
-
-    public TestableLooper() throws Exception {
-        this(true);
-    }
-
-    public TestableLooper(boolean setMyLooper) throws Exception {
-        setupQueue(setMyLooper);
-        mNext = mQueue.getClass().getDeclaredMethod("next");
-        mNext.setAccessible(true);
-        mRecycleUnchecked = Message.class.getDeclaredMethod("recycleUnchecked");
-        mRecycleUnchecked.setAccessible(true);
-    }
-
-    public Looper getLooper() {
-        return mLooper;
-    }
-
-    private void clearLooper() throws NoSuchFieldException, IllegalAccessException {
-        Field field = Looper.class.getDeclaredField("sThreadLocal");
-        field.setAccessible(true);
-        ThreadLocal<Looper> sThreadLocal = (ThreadLocal<Looper>) field.get(null);
-        sThreadLocal.set(null);
-    }
-
-    private boolean setForCurrentThread() throws NoSuchFieldException, IllegalAccessException {
-        if (Looper.myLooper() != mLooper) {
-            Field field = Looper.class.getDeclaredField("sThreadLocal");
-            field.setAccessible(true);
-            ThreadLocal<Looper> sThreadLocal = (ThreadLocal<Looper>) field.get(null);
-            sThreadLocal.set(mLooper);
-            return true;
-        }
-        return false;
-    }
-
-    private void setupQueue(boolean setMyLooper) throws Exception {
-        if (setMyLooper) {
-            clearLooper();
-            Looper.prepare();
-            mLooper = Looper.myLooper();
-        } else {
-            Constructor<Looper> constructor = Looper.class.getDeclaredConstructor(
-                    boolean.class);
-            constructor.setAccessible(true);
-            mLooper = constructor.newInstance(true);
-        }
-
-        mQueue = mLooper.getQueue();
-        mHandler = new Handler(mLooper);
-    }
-
-    public void setAsMainLooper() throws NoSuchFieldException, IllegalAccessException {
-        mMain = true;
-        setAsMainInt();
-    }
-
-    private void setAsMainInt() throws NoSuchFieldException, IllegalAccessException {
-        Field field = mLooper.getClass().getDeclaredField("sMainLooper");
-        field.setAccessible(true);
-        if (mOriginalMain == null) {
-            mOriginalMain = field.get(null);
-        }
-        field.set(null, mLooper);
-    }
-
-    /**
-     * Must be called if setAsMainLooper is called to restore the main looper when the
-     * test is complete, otherwise the main looper will not be available for any subsequent
-     * tests.
-     */
-    public void destroy() throws NoSuchFieldException, IllegalAccessException {
-        if (Looper.myLooper() == mLooper) {
-            clearLooper();
-        }
-        if (mMain && mOriginalMain != null) {
-            Field field = mLooper.getClass().getDeclaredField("sMainLooper");
-            field.setAccessible(true);
-            field.set(null, mOriginalMain);
-            mOriginalMain = null;
-        }
-    }
-
-    public void setMessageHandler(MessageHandler handler) {
-        mMessageHandler = handler;
-    }
-
-    /**
-     * Parse num messages from the message queue.
-     *
-     * @param num Number of messages to parse
-     */
-    public int processMessages(int num) {
-        for (int i = 0; i < num; i++) {
-            if (!parseMessageInt()) {
-                return i + 1;
-            }
-        }
-        return num;
-    }
-
-    public void processAllMessages() {
-        while (processQueuedMessages() != 0) ;
-    }
-
-    private int processQueuedMessages() {
-        int count = 0;
-        mEmptyMessage = mHandler.obtainMessage(1);
-        mHandler.sendMessageDelayed(mEmptyMessage, 1);
-        while (parseMessageInt()) count++;
-        return count;
-    }
-
-    private boolean parseMessageInt() {
-        try {
-            Message result = (Message) mNext.invoke(mQueue);
-            if (result != null) {
-                // This is a break message.
-                if (result == mEmptyMessage) {
-                    mRecycleUnchecked.invoke(result);
-                    return false;
-                }
-
-                if (mMessageHandler != null) {
-                    if (mMessageHandler.onMessageHandled(result)) {
-                        result.getTarget().dispatchMessage(result);
-                        mRecycleUnchecked.invoke(result);
-                    } else {
-                        mRecycleUnchecked.invoke(result);
-                        // Message handler indicated it doesn't want us to continue.
-                        return false;
-                    }
-                } else {
-                    result.getTarget().dispatchMessage(result);
-                    mRecycleUnchecked.invoke(result);
-                }
-            } else {
-                // No messages, don't continue parsing
-                return false;
-            }
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
-        return true;
-    }
-
-    /**
-     * Runs an executable with myLooper set and processes all messages added.
-     */
-    public void runWithLooper(RunnableWithException runnable) throws Exception {
-        boolean set = setForCurrentThread();
-        runnable.run();
-        processAllMessages();
-        if (set) clearLooper();
-    }
-
-    public interface RunnableWithException {
-        void run() throws Exception;
-    }
-
-    @Retention(RetentionPolicy.RUNTIME)
-    @Target({ElementType.METHOD, ElementType.TYPE})
-    public @interface RunWithLooper {
-        boolean setAsMainLooper() default false;
-    }
-
-    private static final Map<Object, TestableLooper> sLoopers = new ArrayMap<>();
-
-    public static TestableLooper get(Object test) {
-        return sLoopers.get(test);
-    }
-
-    public static class LooperStatement extends Statement {
-        private final boolean mSetAsMain;
-        private final Statement mBase;
-        private final TestableLooper mLooper;
-
-        public LooperStatement(Statement base, boolean setAsMain, Object test) {
-            mBase = base;
-            try {
-                mLooper = new TestableLooper(false);
-                sLoopers.put(test, mLooper);
-                mSetAsMain = setAsMain;
-            } catch (Exception e) {
-                throw new RuntimeException(e);
-            }
-        }
-
-        @Override
-        public void evaluate() throws Throwable {
-            mLooper.setForCurrentThread();
-            if (mSetAsMain) {
-                mLooper.setAsMainLooper();
-            }
-
-            try {
-                mBase.evaluate();
-            } finally {
-                mLooper.destroy();
-            }
-        }
-    }
-
-    public interface MessageHandler {
-        /**
-         * Return true to have the message executed and delivered to target.
-         * Return false to not execute the message and stop executing messages.
-         */
-        boolean onMessageHandled(Message m);
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableLooperTest.java b/packages/SystemUI/tests/src/com/android/systemui/utils/TestableLooperTest.java
deleted file mode 100644
index 2416e1d..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableLooperTest.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2017 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.utils;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-
-import com.android.systemui.SysUIRunner;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.utils.TestableLooper.MessageHandler;
-import com.android.systemui.utils.TestableLooper.RunWithLooper;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(SysUIRunner.class)
-@RunWithLooper
-public class TestableLooperTest extends SysuiTestCase {
-
-    private TestableLooper mTestableLooper;
-
-    @Before
-    public void setup() throws Exception {
-        mTestableLooper = TestableLooper.get(this);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        mTestableLooper.destroy();
-    }
-
-    @Test
-    public void testMessageExecuted() throws Exception {
-        Handler h = new Handler();
-        Runnable r = mock(Runnable.class);
-        h.post(r);
-        verify(r, never()).run();
-        mTestableLooper.processAllMessages();
-        verify(r).run();
-    }
-
-    @Test
-    public void testMessageCallback() throws Exception {
-        Handler h = new Handler();
-        Message m = h.obtainMessage(3);
-        Runnable r = mock(Runnable.class);
-        MessageHandler messageHandler = mock(MessageHandler.class);
-        when(messageHandler.onMessageHandled(any())).thenReturn(false);
-        mTestableLooper.setMessageHandler(messageHandler);
-
-        m.sendToTarget();
-        h.post(r);
-
-        mTestableLooper.processAllMessages();
-
-        verify(messageHandler).onMessageHandled(eq(m));
-        // This should never be run becaus the mock returns false on the first message, and
-        // the second will get skipped.
-        verify(r, never()).run();
-    }
-
-    @Test
-    public void testProcessNumberOfMessages() throws Exception {
-        Handler h = new Handler();
-        Runnable r = mock(Runnable.class);
-        h.post(r);
-        h.post(r);
-        h.post(r);
-
-        mTestableLooper.processMessages(2);
-
-        verify(r, times(2)).run();
-    }
-
-    @Test
-    public void testProcessAllMessages() throws Exception {
-        Handler h = new Handler();
-        Runnable r = mock(Runnable.class);
-        Runnable poster = () -> h.post(r);
-        h.post(poster);
-
-        mTestableLooper.processAllMessages();
-        verify(r).run();
-    }
-
-    @Test
-    public void test3Chain() throws Exception {
-        Handler h = new Handler();
-        Runnable r = mock(Runnable.class);
-        Runnable poster = () -> h.post(r);
-        Runnable poster2 = () -> h.post(poster);
-        h.post(poster2);
-
-        mTestableLooper.processAllMessages();
-        verify(r).run();
-    }
-
-    @Test
-    public void testProcessAllMessages_2Messages() throws Exception {
-        Handler h = new Handler();
-        Runnable r = mock(Runnable.class);
-        Runnable r2 = mock(Runnable.class);
-        h.post(r);
-        h.post(r2);
-
-        mTestableLooper.processAllMessages();
-        verify(r).run();
-        verify(r2).run();
-    }
-
-    @Test
-    public void testMainLooper() throws Exception {
-        assertNotEquals(Looper.myLooper(), Looper.getMainLooper());
-
-        Looper originalMain = Looper.getMainLooper();
-        mTestableLooper.setAsMainLooper();
-        assertEquals(Looper.myLooper(), Looper.getMainLooper());
-        Runnable r = mock(Runnable.class);
-
-        new Handler(Looper.getMainLooper()).post(r);
-        mTestableLooper.processAllMessages();
-
-        verify(r).run();
-        mTestableLooper.destroy();
-
-        assertEquals(originalMain, Looper.getMainLooper());
-    }
-
-    @Test
-    public void testNotMyLooper() throws Exception {
-        TestableLooper looper = new TestableLooper(false);
-
-        assertEquals(Looper.myLooper(), mTestableLooper.getLooper());
-        assertNotEquals(Looper.myLooper(), looper.getLooper());
-
-        Runnable r = mock(Runnable.class);
-        Runnable r2 = mock(Runnable.class);
-        new Handler().post(r);
-        new Handler(looper.getLooper()).post(r2);
-
-        looper.processAllMessages();
-        verify(r2).run();
-        verify(r, never()).run();
-
-        mTestableLooper.processAllMessages();
-        verify(r).run();
-    }
-
-    @Test
-    public void testNonMainLooperAnnotation() {
-        assertNotEquals(Looper.myLooper(), Looper.getMainLooper());
-    }
-
-    @Test
-    @RunWithLooper(setAsMainLooper = true)
-    public void testMainLooperAnnotation() {
-        assertEquals(Looper.myLooper(), Looper.getMainLooper());
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/ViewUtils.java b/packages/SystemUI/tests/src/com/android/systemui/utils/ViewUtils.java
deleted file mode 100644
index 678b9f4..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/ViewUtils.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2017 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.utils;
-
-import android.content.pm.ApplicationInfo;
-import android.graphics.PixelFormat;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.Log;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
-import android.support.test.InstrumentationRegistry;
-
-import com.android.systemui.SysuiTestCase;
-
-public class ViewUtils {
-
-    public static void attachView(View view) {
-        // Make sure hardware acceleration isn't turned on.
-        view.getContext().getApplicationInfo().flags &=
-                ~(ApplicationInfo.FLAG_HARDWARE_ACCELERATED);
-        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
-                LayoutParams.TYPE_APPLICATION_OVERLAY,
-                0, PixelFormat.TRANSLUCENT);
-        InstrumentationRegistry.getContext()
-                .getSystemService(WindowManager.class).addView(view, lp);
-    }
-
-    public static void detachView(View view) {
-        InstrumentationRegistry.getContext()
-                .getSystemService(WindowManager.class).removeViewImmediate(view);
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/BaseLeakChecker.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/BaseLeakChecker.java
index b118fdc..d94ecc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/BaseLeakChecker.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/BaseLeakChecker.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 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
@@ -14,6 +14,9 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.testing.LeakCheck;
+import android.testing.LeakCheck.Tracker;
+
 import com.android.systemui.Dumpable;
 import com.android.systemui.statusbar.policy.CallbackController;
 
@@ -24,7 +27,7 @@
 
     private final Tracker mTracker;
 
-    public BaseLeakChecker(LeakCheckedTest test, String tag) {
+    public BaseLeakChecker(LeakCheck test, String tag) {
         mTracker = test.getTracker(tag);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
index fa07d33..a843cca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
@@ -15,6 +15,7 @@
 package com.android.systemui.utils.leaks;
 
 import android.os.Bundle;
+import android.testing.LeakCheck;
 
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -24,7 +25,7 @@
 
 public class FakeBatteryController extends BaseLeakChecker<BatteryStateChangeCallback>
         implements BatteryController {
-    public FakeBatteryController(LeakCheckedTest test) {
+    public FakeBatteryController(LeakCheck test) {
         super(test, "battery");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
index 6074a01..0ba0319 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
@@ -14,6 +14,8 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.testing.LeakCheck;
+
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.systemui.statusbar.policy.BluetoothController;
 import com.android.systemui.statusbar.policy.BluetoothController.Callback;
@@ -23,7 +25,7 @@
 public class FakeBluetoothController extends BaseLeakChecker<Callback> implements
         BluetoothController {
 
-    public FakeBluetoothController(LeakCheckedTest test) {
+    public FakeBluetoothController(LeakCheck test) {
         super(test, "bluetooth");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeCastController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeCastController.java
index 08211f8..51149ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeCastController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeCastController.java
@@ -14,13 +14,15 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.testing.LeakCheck;
+
 import com.android.systemui.statusbar.policy.CastController;
 import com.android.systemui.statusbar.policy.CastController.Callback;
 
 import java.util.Set;
 
 public class FakeCastController extends BaseLeakChecker<Callback> implements CastController {
-    public FakeCastController(LeakCheckedTest test) {
+    public FakeCastController(LeakCheck test) {
         super(test, "cast");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeDataSaverController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeDataSaverController.java
index 857a785..886722e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeDataSaverController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeDataSaverController.java
@@ -14,12 +14,14 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.testing.LeakCheck;
+
 import com.android.systemui.statusbar.policy.DataSaverController;
 import com.android.systemui.statusbar.policy.DataSaverController.Listener;
 
 public class FakeDataSaverController extends BaseLeakChecker<Listener> implements DataSaverController {
 
-    public FakeDataSaverController(LeakCheckedTest test) {
+    public FakeDataSaverController(LeakCheck test) {
         super(test, "datasaver");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeExtensionController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeExtensionController.java
index c0f5783..b9d188a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeExtensionController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeExtensionController.java
@@ -14,7 +14,8 @@
 
 package com.android.systemui.utils.leaks;
 
-import static org.mockito.Mockito.mock;
+import android.testing.LeakCheck;
+import android.testing.LeakCheck.Tracker;
 
 import com.android.systemui.statusbar.policy.ExtensionController;
 
@@ -25,7 +26,7 @@
 
     private final Tracker mTracker;
 
-    public FakeExtensionController(LeakCheckedTest test) {
+    public FakeExtensionController(LeakCheck test) {
         mTracker = test.getTracker("extension");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeFlashlightController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeFlashlightController.java
index 630abd7..f6fd2cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeFlashlightController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeFlashlightController.java
@@ -14,12 +14,14 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.testing.LeakCheck;
+
 import com.android.systemui.statusbar.policy.FlashlightController;
 import com.android.systemui.statusbar.policy.FlashlightController.FlashlightListener;
 
 public class FakeFlashlightController extends BaseLeakChecker<FlashlightListener>
         implements FlashlightController {
-    public FakeFlashlightController(LeakCheckedTest test) {
+    public FakeFlashlightController(LeakCheck test) {
         super(test, "flashlight");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java
index 781960d..69e2361 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java
@@ -14,12 +14,14 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.testing.LeakCheck;
+
 import com.android.systemui.statusbar.policy.HotspotController;
 import com.android.systemui.statusbar.policy.HotspotController.Callback;
 
 public class FakeHotspotController extends BaseLeakChecker<Callback> implements HotspotController {
 
-    public FakeHotspotController(LeakCheckedTest test) {
+    public FakeHotspotController(LeakCheck test) {
         super(test, "hotspot");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java
index 21871fc..51e35cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java
@@ -14,13 +14,15 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.testing.LeakCheck;
+
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 
 public class FakeKeyguardMonitor implements KeyguardMonitor {
 
     private final BaseLeakChecker<Callback> mCallbackController;
 
-    public FakeKeyguardMonitor(LeakCheckedTest test) {
+    public FakeKeyguardMonitor(LeakCheck test) {
         mCallbackController = new BaseLeakChecker<Callback>(test, "keyguard");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeLocationController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeLocationController.java
index eab436c..29d7f1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeLocationController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeLocationController.java
@@ -14,12 +14,14 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.testing.LeakCheck;
+
 import com.android.systemui.statusbar.policy.LocationController;
 import com.android.systemui.statusbar.policy.LocationController.LocationSettingsChangeCallback;
 
 public class FakeLocationController extends BaseLeakChecker<LocationSettingsChangeCallback>
         implements LocationController {
-    public FakeLocationController(LeakCheckedTest test) {
+    public FakeLocationController(LeakCheck test) {
         super(test, "location");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeManagedProfileController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeManagedProfileController.java
index 0ec0d77..18b07cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeManagedProfileController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeManagedProfileController.java
@@ -14,12 +14,14 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.testing.LeakCheck;
+
 import com.android.systemui.statusbar.phone.ManagedProfileController;
 import com.android.systemui.statusbar.phone.ManagedProfileController.Callback;
 
 public class FakeManagedProfileController extends BaseLeakChecker<Callback> implements
         ManagedProfileController {
-    public FakeManagedProfileController(LeakCheckedTest test) {
+    public FakeManagedProfileController(LeakCheck test) {
         super(test, "profile");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
index 47ed5ca..64fe8dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
@@ -15,25 +15,23 @@
 package com.android.systemui.utils.leaks;
 
 import android.os.Bundle;
+import android.testing.LeakCheck;
 
 import com.android.settingslib.net.DataUsageController;
 import com.android.systemui.statusbar.policy.DataSaverController;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
 public class FakeNetworkController extends BaseLeakChecker<SignalCallback>
         implements NetworkController {
 
     private final FakeDataSaverController mDataSaverController;
     private final BaseLeakChecker<EmergencyListener> mEmergencyChecker;
 
-    public FakeNetworkController(LeakCheckedTest test) {
+    public FakeNetworkController(LeakCheck test) {
         super(test, "network");
         mDataSaverController = new FakeDataSaverController(test);
-        mEmergencyChecker = new BaseLeakChecker<EmergencyListener>(test, "emergency");
+        mEmergencyChecker = new BaseLeakChecker<>(test, "emergency");
     }
 
     @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNextAlarmController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNextAlarmController.java
index 707fc4b..5ae8e22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNextAlarmController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNextAlarmController.java
@@ -14,13 +14,15 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.testing.LeakCheck;
+
 import com.android.systemui.statusbar.policy.NextAlarmController;
 import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
 
 public class FakeNextAlarmController extends BaseLeakChecker<NextAlarmChangeCallback>
         implements NextAlarmController {
 
-    public FakeNextAlarmController(LeakCheckedTest test) {
+    public FakeNextAlarmController(LeakCheck test) {
         super(test, "alarm");
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
index 59a9361..0a83a89 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
@@ -15,17 +15,17 @@
 package com.android.systemui.utils.leaks;
 
 import android.content.Context;
+import android.testing.LeakCheck;
 
 import com.android.systemui.plugins.Plugin;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.PluginManager;
 
-public class FakePluginManager extends PluginManager {
+public class FakePluginManager implements PluginManager {
 
     private final BaseLeakChecker<PluginListener> mLeakChecker;
 
-    public FakePluginManager(Context context, LeakCheckedTest test) {
-        super(context);
+    public FakePluginManager(LeakCheck test) {
         mLeakChecker = new BaseLeakChecker<>(test, "Plugin");
     }
 
@@ -36,11 +36,38 @@
     }
 
     @Override
+    public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls) {
+        mLeakChecker.addCallback(listener);
+    }
+
+    @Override
+    public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls,
+            boolean allowMultiple) {
+        mLeakChecker.addCallback(listener);
+    }
+
+    @Override
+    public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
+            Class<?> cls) {
+        mLeakChecker.addCallback(listener);
+    }
+
+    @Override
     public void removePluginListener(PluginListener<?> listener) {
         mLeakChecker.removeCallback(listener);
     }
 
     @Override
+    public <T> boolean dependsOn(Plugin p, Class<T> cls) {
+        return false;
+    }
+
+    @Override
+    public <T extends Plugin> T getOneShotPlugin(Class<T> cls) {
+        return null;
+    }
+
+    @Override
     public <T extends Plugin> T getOneShotPlugin(String action, Class<?> cls) {
         return null;
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
index 00e2404..d60fe78 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
@@ -14,12 +14,14 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.testing.LeakCheck;
+
 import com.android.systemui.statusbar.policy.RotationLockController;
 import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback;
 
 public class FakeRotationLockController extends BaseLeakChecker<RotationLockControllerCallback>
         implements RotationLockController {
-    public FakeRotationLockController(LeakCheckedTest test) {
+    public FakeRotationLockController(LeakCheck test) {
         super(test, "rotation");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java
index 2d53c77..157b8a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java
@@ -14,12 +14,14 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.testing.LeakCheck;
+
 import com.android.systemui.statusbar.policy.SecurityController;
 import com.android.systemui.statusbar.policy.SecurityController.SecurityControllerCallback;
 
 public class FakeSecurityController extends BaseLeakChecker<SecurityControllerCallback>
         implements SecurityController {
-    public FakeSecurityController(LeakCheckedTest test) {
+    public FakeSecurityController(LeakCheck test) {
         super(test, "security");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
index b13535f..6b501af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
@@ -14,6 +14,8 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.testing.LeakCheck;
+
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager;
@@ -21,7 +23,7 @@
 public class FakeStatusBarIconController extends BaseLeakChecker<IconManager>
         implements StatusBarIconController {
 
-    public FakeStatusBarIconController(LeakCheckedTest test) {
+    public FakeStatusBarIconController(LeakCheck test) {
         super(test, "StatusBarGroup");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java
index b841ce9..8db82e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java
@@ -15,6 +15,7 @@
 package com.android.systemui.utils.leaks;
 
 import android.content.Context;
+import android.testing.LeakCheck;
 
 import com.android.systemui.tuner.TunerService;
 
@@ -22,10 +23,8 @@
 
     private final BaseLeakChecker<Tunable> mBaseLeakChecker;
 
-    public FakeTunerService(Context context, LeakCheckedTest test) {
-        super(context);
+    public FakeTunerService(LeakCheck test) {
         mBaseLeakChecker = new BaseLeakChecker<>(test, "tunable");
-        destroy();
     }
 
     @Override
@@ -40,4 +39,39 @@
     public void removeTunable(Tunable tunable) {
         mBaseLeakChecker.removeCallback(tunable);
     }
+
+    @Override
+    public void clearAll() {
+
+    }
+
+    @Override
+    public void destroy() {
+
+    }
+
+    @Override
+    public String getValue(String setting) {
+        return null;
+    }
+
+    @Override
+    public int getValue(String setting, int def) {
+        return def;
+    }
+
+    @Override
+    public String getValue(String setting, String def) {
+        return def;
+    }
+
+    @Override
+    public void setValue(String setting, String value) {
+
+    }
+
+    @Override
+    public void setValue(String setting, int value) {
+
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeUserInfoController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeUserInfoController.java
index 578b310..f7ef653a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeUserInfoController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeUserInfoController.java
@@ -14,12 +14,14 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.testing.LeakCheck;
+
 import com.android.systemui.statusbar.policy.UserInfoController;
 import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
 
 public class FakeUserInfoController extends BaseLeakChecker<OnUserInfoChangedListener>
         implements UserInfoController {
-    public FakeUserInfoController(LeakCheckedTest test) {
+    public FakeUserInfoController(LeakCheck test) {
         super(test, "user_info");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeZenModeController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeZenModeController.java
index 7581363..fb9bf7a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeZenModeController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeZenModeController.java
@@ -18,12 +18,13 @@
 import android.net.Uri;
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenModeConfig.ZenRule;
+import android.testing.LeakCheck;
 
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.statusbar.policy.ZenModeController.Callback;
 
 public class FakeZenModeController extends BaseLeakChecker<Callback> implements ZenModeController {
-    public FakeZenModeController(LeakCheckedTest test) {
+    public FakeZenModeController(LeakCheck test) {
         super(test, "zen");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
index 6c51524..94af7733 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
@@ -15,8 +15,8 @@
 package com.android.systemui.utils.leaks;
 
 import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doAnswer;
 
+import android.testing.LeakCheck;
 import android.util.ArrayMap;
 
 import com.android.systemui.SysuiTestCase;
@@ -25,7 +25,6 @@
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BluetoothController;
-import com.android.systemui.statusbar.policy.CallbackController;
 import com.android.systemui.statusbar.policy.CastController;
 import com.android.systemui.statusbar.policy.FlashlightController;
 import com.android.systemui.statusbar.policy.HotspotController;
@@ -41,12 +40,7 @@
 
 import org.junit.Assert;
 import org.junit.Rule;
-import org.junit.rules.TestWatcher;
-import org.junit.runner.Description;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
 
-import java.util.HashMap;
 import java.util.Map;
 
 /**
@@ -56,10 +50,7 @@
 public abstract class LeakCheckedTest extends SysuiTestCase {
     private static final String TAG = "LeakCheckedTest";
 
-    private final Map<String, Tracker> mTrackers = new HashMap<>();
-    private final Map<Class, Object> mLeakCheckers = new ArrayMap<>();
-
-    public static final Class<?>[] ALL_SUPPORTED_CLASSES = new Class[] {
+    public static final Class<?>[] ALL_SUPPORTED_CLASSES = new Class[]{
             BluetoothController.class,
             LocationController.class,
             RotationLockController.class,
@@ -80,71 +71,11 @@
     };
 
     @Rule
-    public TestWatcher successWatcher = new TestWatcher() {
-        @Override
-        protected void succeeded(Description description) {
-            verify();
-        }
-    };
-
-    public <T> T getLeakChecker(Class<T> cls) {
-        Object obj = mLeakCheckers.get(cls);
-        if (obj == null) {
-            // Lazy create checkers so we only have the ones we need.
-            if (cls == BluetoothController.class) {
-                obj = new FakeBluetoothController(this);
-            } else if (cls == LocationController.class) {
-                obj = new FakeLocationController(this);
-            } else if (cls == RotationLockController.class) {
-                obj = new FakeRotationLockController(this);
-            } else if (cls == ZenModeController.class) {
-                obj = new FakeZenModeController(this);
-            } else if (cls == CastController.class) {
-                obj = new FakeCastController(this);
-            } else if (cls == HotspotController.class) {
-                obj = new FakeHotspotController(this);
-            } else if (cls == FlashlightController.class) {
-                obj = new FakeFlashlightController(this);
-            } else if (cls == UserInfoController.class) {
-                obj = new FakeUserInfoController(this);
-            } else if (cls == KeyguardMonitor.class) {
-                obj = new FakeKeyguardMonitor(this);
-            } else if (cls == BatteryController.class) {
-                obj = new FakeBatteryController(this);
-            } else if (cls == SecurityController.class) {
-                obj = new FakeSecurityController(this);
-            } else if (cls == ManagedProfileController.class) {
-                obj = new FakeManagedProfileController(this);
-            } else if (cls == NextAlarmController.class) {
-                obj = new FakeNextAlarmController(this);
-            } else if (cls == NetworkController.class) {
-                obj = new FakeNetworkController(this);
-            } else if (cls == PluginManager.class) {
-                obj = new FakePluginManager(mContext, this);
-            } else if (cls == TunerService.class) {
-                obj = new FakeTunerService(mContext, this);
-            } else if (cls == StatusBarIconController.class) {
-                obj = new FakeStatusBarIconController(this);
-            } else {
-                Assert.fail(cls.getName() + " is not supported by LeakCheckedTest yet");
-            }
-            mLeakCheckers.put(cls, obj);
-        }
-        return (T) obj;
-    }
+    public SysuiLeakCheck mLeakCheck = new SysuiLeakCheck();
 
     @Override
-    public Tracker getTracker(String tag) {
-        Tracker t = mTrackers.get(tag);
-        if (t == null) {
-            t = new Tracker();
-            mTrackers.put(tag, t);
-        }
-        return t;
-    }
-
-    public void verify() {
-        mTrackers.values().forEach(Tracker::verify);
+    public LeakCheck getLeakCheck() {
+        return mLeakCheck;
     }
 
     public void injectLeakCheckedDependencies(Class<?>... cls) {
@@ -154,26 +85,61 @@
     }
 
     public <T> void injectLeakCheckedDependency(Class<T> c) {
-        injectTestDependency(c, getLeakChecker(c));
+        mDependency.injectTestDependency(c, mLeakCheck.getLeakChecker(c));
     }
 
-    public <T extends CallbackController> T addListening(T mock, Class<T> cls, String tag) {
-        doAnswer(new Answer<Void>() {
-            @Override
-            public Void answer(InvocationOnMock invocation) throws Throwable {
-                getTracker(tag).getLeakInfo(invocation.getArguments()[0])
-                        .addAllocation(new Throwable());
-                return null;
+    public static class SysuiLeakCheck extends LeakCheck {
+
+        private final Map<Class, Object> mLeakCheckers = new ArrayMap<>();
+
+        public SysuiLeakCheck() {
+            super();
+        }
+
+        public <T> T getLeakChecker(Class<T> cls) {
+            Object obj = mLeakCheckers.get(cls);
+            if (obj == null) {
+                // Lazy create checkers so we only have the ones we need.
+                if (cls == BluetoothController.class) {
+                    obj = new FakeBluetoothController(this);
+                } else if (cls == LocationController.class) {
+                    obj = new FakeLocationController(this);
+                } else if (cls == RotationLockController.class) {
+                    obj = new FakeRotationLockController(this);
+                } else if (cls == ZenModeController.class) {
+                    obj = new FakeZenModeController(this);
+                } else if (cls == CastController.class) {
+                    obj = new FakeCastController(this);
+                } else if (cls == HotspotController.class) {
+                    obj = new FakeHotspotController(this);
+                } else if (cls == FlashlightController.class) {
+                    obj = new FakeFlashlightController(this);
+                } else if (cls == UserInfoController.class) {
+                    obj = new FakeUserInfoController(this);
+                } else if (cls == KeyguardMonitor.class) {
+                    obj = new FakeKeyguardMonitor(this);
+                } else if (cls == BatteryController.class) {
+                    obj = new FakeBatteryController(this);
+                } else if (cls == SecurityController.class) {
+                    obj = new FakeSecurityController(this);
+                } else if (cls == ManagedProfileController.class) {
+                    obj = new FakeManagedProfileController(this);
+                } else if (cls == NextAlarmController.class) {
+                    obj = new FakeNextAlarmController(this);
+                } else if (cls == NetworkController.class) {
+                    obj = new FakeNetworkController(this);
+                } else if (cls == PluginManager.class) {
+                    obj = new FakePluginManager(this);
+                } else if (cls == TunerService.class) {
+                    obj = new FakeTunerService(this);
+                } else if (cls == StatusBarIconController.class) {
+                    obj = new FakeStatusBarIconController(this);
+                } else {
+                    Assert.fail(cls.getName() + " is not supported by LeakCheckedTest yet");
+                }
+                mLeakCheckers.put(cls, obj);
             }
-        }).when(mock).addCallback(any());
-        doAnswer(new Answer<Void>() {
-            @Override
-            public Void answer(InvocationOnMock invocation) throws Throwable {
-                getTracker(tag).getLeakInfo(invocation.getArguments()[0]).clearAllocations();
-                return null;
-            }
-        }).when(mock).removeCallback(any());
-        mLeakCheckers.put(cls, mock);
-        return mock;
+            return (T) obj;
+        }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakInfo.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakInfo.java
deleted file mode 100644
index 1d016fb..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakInfo.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.utils.leaks;
-
-import android.util.Log;
-
-import org.junit.Assert;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-public class LeakInfo {
-    private static final String TAG = "LeakInfo";
-    private List<Throwable> mThrowables = new ArrayList<>();
-
-    LeakInfo() {
-    }
-
-    public void addAllocation(Throwable t) {
-        // TODO: Drop off the first element in the stack trace here to have a cleaner stack.
-        mThrowables.add(t);
-    }
-
-    public void clearAllocations() {
-        mThrowables.clear();
-    }
-
-    void verify() {
-        if (mThrowables.size() == 0) return;
-        Log.e(TAG, "Listener or binding not properly released");
-        for (Throwable t : mThrowables) {
-            Log.e(TAG, "Allocation found", t);
-        }
-        StringWriter writer = new StringWriter();
-        mThrowables.get(0).printStackTrace(new PrintWriter(writer));
-        Assert.fail("Listener or binding not properly released\n"
-                + writer.toString());
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/Tracker.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/Tracker.java
deleted file mode 100644
index 26ffd10..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/Tracker.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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.utils.leaks;
-
-import android.util.ArrayMap;
-
-import com.android.systemui.utils.leaks.LeakInfo;
-
-import java.util.Map;
-
-public class Tracker {
-    private Map<Object, LeakInfo> mObjects = new ArrayMap<>();
-
-    public LeakInfo getLeakInfo(Object object) {
-        LeakInfo leakInfo = mObjects.get(object);
-        if (leakInfo == null) {
-            leakInfo = new LeakInfo();
-            mObjects.put(object, leakInfo);
-        }
-        return leakInfo;
-    }
-
-    void verify() {
-        mObjects.values().forEach(LeakInfo::verify);
-    }
-}