Finish annotating Notification.extras.

This covers all useful data from the basic Builder as well
as each of the Styles that is not otherwise captured on the
Notification object itself.

Extras are now prettyprinted in dump() output.

Bug: 8270485
Change-Id: I47fc50860dab6409793f57e904cc60296310d5cf
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index a7543a8..8d994c4 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -427,22 +427,48 @@
     public String[] kind;
 
     /**
-     * Extra key for people values (type TBD).
-     *
+     * Additional semantic data to be carried around with this Notification.
      * @hide
      */
-    public static final String EXTRA_PEOPLE = "android.people";
+    public Bundle extras = new Bundle();
+
+    // extras keys for Builder inputs
     /** @hide */
     public static final String EXTRA_TITLE = "android.title";
     /** @hide */
+    public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big";
+    /** @hide */
     public static final String EXTRA_TEXT = "android.text";
     /** @hide */
-    public static final String EXTRA_SUBTEXT = "android.subtext";
+    public static final String EXTRA_SUB_TEXT = "android.subText";
+    /** @hide */
+    public static final String EXTRA_INFO_TEXT = "android.infoText";
+    /** @hide */
+    public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
     /** @hide */
     public static final String EXTRA_SMALL_ICON = "android.icon";
-
     /** @hide */
-    public Bundle extras = new Bundle();
+    public static final String EXTRA_LARGE_ICON = "android.largeIcon";
+    /** @hide */
+    public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big";
+    /** @hide */
+    public static final String EXTRA_PROGRESS = "android.progress";
+    /** @hide */
+    public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
+    /** @hide */
+    public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
+    /** @hide */
+    public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
+    /** @hide */
+    public static final String EXTRA_SHOW_WHEN = "android.showWhen";
+    /** @hide from BigPictureStyle */
+    public static final String EXTRA_PICTURE = "android.picture";
+    /** @hide from InboxStyle */
+    public static final String EXTRA_TEXT_LINES = "android.textLines";
+
+    // extras keys for other interesting pieces of information
+    /** @hide */
+    public static final String EXTRA_PEOPLE = "android.people";
 
     /**
      * Structure to encapsulate an "action", including title and icon, that can be attached to a Notification.
@@ -1621,19 +1647,29 @@
                 mActions.toArray(n.actions);
             }
 
-            n.extras = mExtras != null ? new Bundle(mExtras) : new Bundle();
-
-            // Store original information used in the construction of this object
-            n.extras.putCharSequence(EXTRA_TITLE, mContentTitle);
-            n.extras.putCharSequence(EXTRA_TEXT, mContentText);
-            n.extras.putCharSequence(EXTRA_SUBTEXT, mSubText);
-            n.extras.putInt(EXTRA_SMALL_ICON, mSmallIcon);
-            //n.extras.putByteArray(EXTRA_LARGE_ICON, ...
-
             return n;
         }
 
         /**
+         * Capture, in the provided bundle, semantic information used in the construction of
+         * this Notification object.
+         * @hide
+         */
+        public void addExtras(Bundle extras) {
+            // Store original information used in the construction of this object
+            extras.putCharSequence(EXTRA_TITLE, mContentTitle);
+            extras.putCharSequence(EXTRA_TEXT, mContentText);
+            extras.putCharSequence(EXTRA_SUB_TEXT, mSubText);
+            extras.putCharSequence(EXTRA_INFO_TEXT, mContentInfo);
+            extras.putInt(EXTRA_SMALL_ICON, mSmallIcon);
+            extras.putInt(EXTRA_PROGRESS, mProgress);
+            extras.putInt(EXTRA_PROGRESS_MAX, mProgressMax);
+            extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, mProgressIndeterminate);
+            extras.putBoolean(EXTRA_SHOW_CHRONOMETER, mUseChronometer);
+            extras.putBoolean(EXTRA_SHOW_WHEN, mShowWhen);
+        }
+
+        /**
          * @deprecated Use {@link #build()} instead.
          */
         @Deprecated
@@ -1646,11 +1682,22 @@
          * object.
          */
         public Notification build() {
+            final Notification n;
+
             if (mStyle != null) {
-                return mStyle.build();
+                n = mStyle.build();
             } else {
-                return buildUnstyled();
+                n = buildUnstyled();
             }
+
+            n.extras = mExtras != null ? new Bundle(mExtras) : new Bundle();
+
+            addExtras(n.extras);
+            if (mStyle != null) {
+                mStyle.addExtras(n.extras);
+            }
+
+            return n;
         }
 
         /**
@@ -1664,7 +1711,6 @@
         }
     }
 
-
     /**
      * An object that can apply a rich notification style to a {@link Notification.Builder}
      * object.
@@ -1739,6 +1785,18 @@
             return contentView;
         }
 
+        /**
+         * @hide
+         */
+        public void addExtras(Bundle extras) {
+            if (mSummaryTextSet) {
+                extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
+            }
+            if (mBigContentTitle != null) {
+                extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
+            }
+        }
+
         public abstract Notification build();
     }
 
@@ -1813,6 +1871,18 @@
             return contentView;
         }
 
+        /**
+         * @hide
+         */
+        public void addExtras(Bundle extras) {
+            super.addExtras(extras);
+
+            if (mBigLargeIconSet) {
+                extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
+            }
+            extras.putParcelable(EXTRA_PICTURE, mPicture);
+        }
+
         @Override
         public Notification build() {
             checkBuilder();
@@ -1878,6 +1948,15 @@
             return this;
         }
 
+        /**
+         * @hide
+         */
+        public void addExtras(Bundle extras) {
+            super.addExtras(extras);
+
+            extras.putCharSequence(EXTRA_TEXT, mBigText);
+        }
+
         private RemoteViews makeBigContentView() {
             // Remove the content text so line3 only shows if you have a summary
             final boolean hadThreeLines = (mBuilder.mContentText != null && mBuilder.mSubText != null);
@@ -1964,6 +2043,15 @@
             return this;
         }
 
+        /**
+         * @hide
+         */
+        public void addExtras(Bundle extras) {
+            super.addExtras(extras);
+            CharSequence[] a = new CharSequence[mTexts.size()];
+            extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
+        }
+
         private RemoteViews makeBigContentView() {
             // Remove the content text so line3 disappears unless you have a summary
             mBuilder.mContentText = null;
@@ -2005,13 +2093,6 @@
             Notification wip = mBuilder.buildUnstyled();
             wip.bigContentView = makeBigContentView();
 
-            StringBuilder builder = new StringBuilder();
-            for (CharSequence str : mTexts) {
-                builder.append(str);
-                builder.append("\n");
-            }
-            wip.extras.putCharSequence(EXTRA_TEXT, builder);
-
             return wip;
         }
     }
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index cfb892f..97120bf 100644
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -43,6 +43,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.database.ContentObserver;
+import android.graphics.Bitmap;
 import android.media.AudioManager;
 import android.media.IAudioService;
 import android.media.IRingtonePlayer;
@@ -81,6 +82,7 @@
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.lang.reflect.Array;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -815,22 +817,61 @@
         void dump(PrintWriter pw, String prefix, Context baseContext) {
             final Notification notification = sbn.notification;
             pw.println(prefix + this);
+            pw.println(prefix + "  uid=" + sbn.uid + " userId=" + sbn.getUserId());
             pw.println(prefix + "  icon=0x" + Integer.toHexString(notification.icon)
-                    + " / " + idDebugString(baseContext, this.sbn.pkg, notification.icon));
-            pw.println(prefix + "  pri=" + notification.priority);
-            pw.println(prefix + "  score=" + this.sbn.score);
+                    + " / " + idDebugString(baseContext, sbn.pkg, notification.icon));
+            pw.println(prefix + "  pri=" + notification.priority + " score=" + sbn.score);
             pw.println(prefix + "  contentIntent=" + notification.contentIntent);
             pw.println(prefix + "  deleteIntent=" + notification.deleteIntent);
             pw.println(prefix + "  tickerText=" + notification.tickerText);
             pw.println(prefix + "  contentView=" + notification.contentView);
-            pw.println(prefix + "  uid=" + this.sbn.uid + " userId=" + this.sbn.getUserId());
-            pw.println(prefix + "  defaults=0x" + Integer.toHexString(notification.defaults));
-            pw.println(prefix + "  flags=0x" + Integer.toHexString(notification.flags));
+            pw.println(prefix + String.format("  defaults=0x%08x flags=0x%08x",
+                    notification.defaults, notification.flags));
             pw.println(prefix + "  sound=" + notification.sound);
             pw.println(prefix + "  vibrate=" + Arrays.toString(notification.vibrate));
-            pw.println(prefix + "  ledARGB=0x" + Integer.toHexString(notification.ledARGB)
-                    + " ledOnMS=" + notification.ledOnMS
-                    + " ledOffMS=" + notification.ledOffMS);
+            pw.println(prefix + String.format("  led=0x%08x onMs=%d offMs=%d",
+                    notification.ledARGB, notification.ledOnMS, notification.ledOffMS));
+            if (notification.actions != null && notification.actions.length > 0) {
+                pw.println(prefix + "  actions={");
+                final int N = notification.actions.length;
+                for (int i=0; i<N; i++) {
+                    final Notification.Action action = notification.actions[i];
+                    pw.println(String.format("%s    [%d] \"%s\" -> %s",
+                            prefix,
+                            i,
+                            action.title,
+                            action.actionIntent.toString()
+                            ));
+                }
+                pw.println(prefix + "  }");
+            }
+            if (notification.extras != null && notification.extras.size() > 0) {
+                pw.println(prefix + "  extras={");
+                for (String key : notification.extras.keySet()) {
+                    pw.print(prefix + "    " + key + "=");
+                    Object val = notification.extras.get(key);
+                    if (val == null) {
+                        pw.println("null");
+                    } else {
+                        pw.print(val.toString());
+                        if (val instanceof Bitmap) {
+                            pw.print(String.format(" (%dx%d)",
+                                    ((Bitmap) val).getWidth(),
+                                    ((Bitmap) val).getHeight()));
+                        } else if (val.getClass().isArray()) {
+                            pw.println(" {");
+                            final int N = Array.getLength(val);
+                            for (int i=0; i<N; i++) {
+                                if (i > 0) pw.println(",");
+                                pw.print(prefix + "      " + Array.get(val, i));
+                            }
+                            pw.print("\n" + prefix + "    }");
+                        }
+                        pw.println();
+                    }
+                }
+                pw.println(prefix + "  }");
+            }
         }
 
         @Override
@@ -2081,7 +2122,7 @@
             if (N > 0) {
                 pw.println("  Lights List:");
                 for (int i=0; i<N; i++) {
-                    mLights.get(i).dump(pw, "    ", mContext);
+                    pw.println("    " + mLights.get(i));
                 }
                 pw.println("  ");
             }