Add dumpable to QSTile. Dump QSTileHost, QSPanel, QuickQSPanel

Dumps state of all the tiles in QSTileHost. The dump of
QSTileHost should match that of QSPanel. The dump of
QuickQSPanel should match the first N items of
the QSPanel dump. If there's any discrepancy, it's cause for
investigating.

It also dumps state of QSTileView.

An example of the dump can be found in:
https://paste.googleplex.com/4811551678660608

Test: manual, dumpsys
Fixes: 129544734
Change-Id: I3249ffcac1bc5ab3d7666bc8884bda4b3a4eb4e2
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index fcaf981..89aa96d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -17,6 +17,7 @@
 package com.android.systemui.qs;
 
 import static com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState;
+import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
 
 import android.annotation.Nullable;
 import android.content.ComponentName;
@@ -37,6 +38,8 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
+import com.android.systemui.DumpController;
+import com.android.systemui.Dumpable;
 import com.android.systemui.R;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSTile;
@@ -51,11 +54,17 @@
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collection;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+
 /** View that represents the quick settings tile panel (when expanded/pulled down). **/
-public class QSPanel extends LinearLayout implements Tunable, Callback, BrightnessMirrorListener {
+public class QSPanel extends LinearLayout implements Tunable, Callback, BrightnessMirrorListener,
+        Dumpable {
 
     public static final String QS_SHOW_BRIGHTNESS = "qs_show_brightness";
     public static final String QS_SHOW_HEADER = "qs_show_header";
@@ -74,6 +83,7 @@
 
     private QSDetail.Callback mCallback;
     private BrightnessController mBrightnessController;
+    private DumpController mDumpController;
     protected QSTileHost mHost;
 
     protected QSSecurityFooter mFooter;
@@ -93,6 +103,12 @@
     }
 
     public QSPanel(Context context, AttributeSet attrs) {
+        this(context, attrs, null);
+    }
+
+    @Inject
+    public QSPanel(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
+            DumpController dumpController) {
         super(context, attrs);
         mContext = context;
 
@@ -119,6 +135,7 @@
 
         mBrightnessController = new BrightnessController(getContext(),
                 findViewById(R.id.brightness_slider));
+        mDumpController = dumpController;
     }
 
     protected void addDivider() {
@@ -170,6 +187,7 @@
         if (mBrightnessMirrorController != null) {
             mBrightnessMirrorController.addCallback(this);
         }
+        if (mDumpController != null) mDumpController.addListener(this);
     }
 
     @Override
@@ -184,6 +202,7 @@
         if (mBrightnessMirrorController != null) {
             mBrightnessMirrorController.removeCallback(this);
         }
+        if (mDumpController != null) mDumpController.removeListener(this);
         super.onDetachedFromWindow();
     }
 
@@ -649,6 +668,18 @@
         }
     }
 
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println(getClass().getSimpleName() + ":");
+        pw.println("  Tile records:");
+        for (TileRecord record : mRecords) {
+            if (record.tile instanceof Dumpable) {
+                pw.print("    "); ((Dumpable) record.tile).dump(fd, pw, args);
+                pw.print("    "); pw.println(record.tileView.toString());
+            }
+        }
+    }
+
     protected static class Record {
         DetailAdapter detailAdapter;
         int x;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index dfc3e66..3c4898c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -30,6 +30,8 @@
 import android.util.Log;
 
 import com.android.systemui.Dependency;
+import com.android.systemui.DumpController;
+import com.android.systemui.Dumpable;
 import com.android.systemui.R;
 import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.plugins.PluginListener;
@@ -47,6 +49,8 @@
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -61,7 +65,7 @@
 
 /** Platform implementation of the quick settings tile host **/
 @Singleton
-public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory> {
+public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, Dumpable {
     private static final String TAG = "QSTileHost";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
@@ -73,6 +77,7 @@
     private final TileServices mServices;
     private final TunerService mTunerService;
     private final PluginManager mPluginManager;
+    private final DumpController mDumpController;
 
     private final List<Callback> mCallbacks = new ArrayList<>();
     private AutoTileManager mAutoTiles;
@@ -89,17 +94,20 @@
             @Named(Dependency.BG_LOOPER_NAME) Looper bgLooper,
             PluginManager pluginManager,
             TunerService tunerService,
-            Provider<AutoTileManager> autoTiles) {
+            Provider<AutoTileManager> autoTiles,
+            DumpController dumpController) {
         mIconController = iconController;
         mContext = context;
         mTunerService = tunerService;
         mPluginManager = pluginManager;
+        mDumpController = dumpController;
 
         mServices = new TileServices(this, bgLooper);
 
         defaultFactory.setHost(this);
         mQsFactories.add(defaultFactory);
         pluginManager.addPluginListener(this, QSFactory.class, true);
+        mDumpController.addListener(this);
 
         mainHandler.post(() -> {
             // This is technically a hack to avoid circular dependency of
@@ -121,6 +129,7 @@
         mTunerService.removeTunable(this);
         mServices.destroy();
         mPluginManager.removePluginListener(this);
+        mDumpController.removeListener(this);
     }
 
     @Override
@@ -363,4 +372,11 @@
         }
         return tiles;
     }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("QSTileHost:");
+        mTiles.values().stream().filter(obj -> obj instanceof Dumpable)
+                .forEach(o -> ((Dumpable) o).dump(fd, pw, args));
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 995dc66..93b55e8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.qs;
 
+import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
+
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -25,6 +27,7 @@
 import android.widget.LinearLayout;
 
 import com.android.systemui.Dependency;
+import com.android.systemui.DumpController;
 import com.android.systemui.R;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.qs.QSTile.SignalState;
@@ -36,6 +39,9 @@
 import java.util.ArrayList;
 import java.util.Collection;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+
 /**
  * Version of QSPanel that only shows N Quick Tiles in the QS Header.
  */
@@ -49,8 +55,10 @@
     private int mMaxTiles;
     protected QSPanel mFullPanel;
 
-    public QuickQSPanel(Context context, AttributeSet attrs) {
-        super(context, attrs);
+    @Inject
+    public QuickQSPanel(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
+            DumpController dumpController) {
+        super(context, attrs, dumpController);
         if (mFooter != null) {
             removeView(mFooter.getView());
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index 3c59f69..31526bf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -47,6 +47,7 @@
     private boolean mAnimationEnabled = true;
     private int mState = -1;
     private int mTint;
+    private QSTile.Icon mLastIcon;
 
     public QSIconViewImpl(Context context) {
         super(context);
@@ -75,6 +76,16 @@
     }
 
     @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append('[');
+        sb.append("state=" + mState);
+        sb.append(", tint=" + mTint);
+        if (mLastIcon != null) sb.append(", lastIcon=" + mLastIcon.toString());
+        sb.append("]");
+        return sb.toString();
+    }
+
+    @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         final int w = getMeasuredWidth();
         int top = 0;
@@ -91,6 +102,7 @@
         if (!Objects.equals(icon, iv.getTag(R.id.qs_icon_tag))
                 || !Objects.equals(state.slash, iv.getTag(R.id.qs_slash_tag))) {
             boolean shouldAnimate = allowAnimations && shouldAnimate(iv);
+            mLastIcon = icon;
             Drawable d = icon != null
                     ? shouldAnimate ? icon.getDrawable(mContext)
                     : icon.getInvisibleDrawable(mContext) : null;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index a732a25..c186e59 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -318,6 +318,16 @@
         }
     }
 
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append('[');
+        sb.append("locInScreen=(" + mLocInScreen[0] + ", " + mLocInScreen[1] + ")");
+        sb.append(", iconView=" + mIcon.toString());
+        sb.append(", tileState=" + mTileState);
+        sb.append("]");
+        return sb.toString();
+    }
+
     private class H extends Handler {
         private static final int STATE_CHANGED = 1;
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index e275690..1f857ff 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -52,6 +52,7 @@
 import com.android.settingslib.RestrictedLockUtilsInternal;
 import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
+import com.android.systemui.Dumpable;
 import com.android.systemui.Prefs;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.qs.DetailAdapter;
@@ -63,6 +64,8 @@
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.QuickStatusBarHeader;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 
 /**
@@ -74,7 +77,7 @@
  *
  * @param <TState> see above
  */
-public abstract class QSTileImpl<TState extends State> implements QSTile, LifecycleOwner {
+public abstract class QSTileImpl<TState extends State> implements QSTile, LifecycleOwner, Dumpable {
     protected final String TAG = "Tile." + getClass().getSimpleName();
     protected static final boolean DEBUG = Log.isLoggable("Tile", Log.DEBUG);
 
@@ -592,4 +595,10 @@
             return context.getDrawable(mAnimatedResId).getConstantState().newDrawable();
         }
     }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println(this.getClass().getSimpleName() + ":");
+        pw.print("    "); pw.println(getState().toString());
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
index 1e486c0..d521e55 100644
--- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
@@ -29,6 +29,8 @@
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.qs.QSCarrierGroup;
 import com.android.systemui.qs.QSFooterImpl;
+import com.android.systemui.qs.QSPanel;
+import com.android.systemui.qs.QuickQSPanel;
 import com.android.systemui.qs.QuickStatusBarHeader;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.phone.LockIcon;
@@ -154,6 +156,16 @@
          * Creates the keyguard LockIcon.
          */
         LockIcon createLockIcon();
+
+        /**
+         * Creates the QSPanel.
+         */
+        QSPanel createQSPanel();
+
+        /**
+         * Creates the QuickQSPanel.
+         */
+        QuickQSPanel createQuickQSPanel();
     }
 
     /**