Added support for hostExtra to ListBuilder

Test: manual on phone using /tts slice in SliceBrowser
Fixes: b/162543846
Relnote: Added new API ListBuilder#setHostExtra and SliceMetadata#getHostExtras to save and extract additional information for host from slice.
Change-Id: Ib07683a36ee66e722af0bc873837fdc373c5905f
diff --git a/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBrowser.java b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBrowser.java
index 690140f..37b2fde 100644
--- a/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBrowser.java
+++ b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBrowser.java
@@ -35,6 +35,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.BaseColumns;
+import android.speech.tts.TextToSpeech;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.TypedValue;
@@ -67,6 +68,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Locale;
 
 /**
  * Example use of SliceView. Uses a search bar to select/auto-complete a slice uri which is
@@ -88,6 +90,7 @@
     private LiveData<Slice> mSliceLiveData;
     private SliceView mSliceView;
     private SharedPreferences mSharedPreferences;
+    private TextToSpeech mTextToSpeech;
 
     // Mode menu
     private int mSelectedMode;
@@ -145,6 +148,8 @@
                 return false;
             }
         });
+        mTextToSpeech = new TextToSpeech(getApplicationContext(), status -> {
+        });
 
         mSharedPreferences = getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE);
 
@@ -357,6 +362,11 @@
             }
             mShowingSerialized = false;
             mSliceView.setSlice(slice);
+            Bundle hostExtras = SliceMetadata.from(this, slice).getHostExtras();
+            if (hostExtras.getString("tts") != null) {
+                mTextToSpeech.setLanguage(Locale.ENGLISH);
+                mTextToSpeech.speak(hostExtras.getString("tts"), TextToSpeech.QUEUE_FLUSH, null);
+            }
         });
     }
 
diff --git a/slices/builders/api/current.txt b/slices/builders/api/current.txt
index 2274a73..c4633a5 100644
--- a/slices/builders/api/current.txt
+++ b/slices/builders/api/current.txt
@@ -38,6 +38,7 @@
     method public androidx.slice.builders.ListBuilder addSelection(androidx.slice.builders.SelectionBuilder);
     method public androidx.slice.builders.ListBuilder setAccentColor(@ColorInt int);
     method public androidx.slice.builders.ListBuilder setHeader(androidx.slice.builders.ListBuilder.HeaderBuilder);
+    method public androidx.slice.builders.ListBuilder setHostExtra(String, String);
     method public androidx.slice.builders.ListBuilder setIsError(boolean);
     method public androidx.slice.builders.ListBuilder setKeywords(java.util.Set<java.lang.String!>!);
     method public androidx.slice.builders.ListBuilder setLayoutDirection(int);
diff --git a/slices/builders/api/public_plus_experimental_current.txt b/slices/builders/api/public_plus_experimental_current.txt
index 2274a73..c4633a5 100644
--- a/slices/builders/api/public_plus_experimental_current.txt
+++ b/slices/builders/api/public_plus_experimental_current.txt
@@ -38,6 +38,7 @@
     method public androidx.slice.builders.ListBuilder addSelection(androidx.slice.builders.SelectionBuilder);
     method public androidx.slice.builders.ListBuilder setAccentColor(@ColorInt int);
     method public androidx.slice.builders.ListBuilder setHeader(androidx.slice.builders.ListBuilder.HeaderBuilder);
+    method public androidx.slice.builders.ListBuilder setHostExtra(String, String);
     method public androidx.slice.builders.ListBuilder setIsError(boolean);
     method public androidx.slice.builders.ListBuilder setKeywords(java.util.Set<java.lang.String!>!);
     method public androidx.slice.builders.ListBuilder setLayoutDirection(int);
diff --git a/slices/builders/api/restricted_current.txt b/slices/builders/api/restricted_current.txt
index 0d562805..ecd0dc5 100644
--- a/slices/builders/api/restricted_current.txt
+++ b/slices/builders/api/restricted_current.txt
@@ -38,6 +38,7 @@
     method public androidx.slice.builders.ListBuilder addSelection(androidx.slice.builders.SelectionBuilder);
     method public androidx.slice.builders.ListBuilder setAccentColor(@ColorInt int);
     method public androidx.slice.builders.ListBuilder setHeader(androidx.slice.builders.ListBuilder.HeaderBuilder);
+    method public androidx.slice.builders.ListBuilder setHostExtra(String, String);
     method public androidx.slice.builders.ListBuilder setIsError(boolean);
     method public androidx.slice.builders.ListBuilder setKeywords(java.util.Set<java.lang.String!>!);
     method public androidx.slice.builders.ListBuilder setLayoutDirection(int);
diff --git a/slices/builders/src/main/java/androidx/slice/builders/ListBuilder.java b/slices/builders/src/main/java/androidx/slice/builders/ListBuilder.java
index 03b74fc..fe62480 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/ListBuilder.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/ListBuilder.java
@@ -381,6 +381,19 @@
         return this;
     }
 
+
+    /**
+     * Sets additional information to be passed to the host of the slice.
+     *
+     * @param key The name of the extra data
+     * @param value The String data value
+     */
+    @NonNull
+    public ListBuilder setHostExtra(@NonNull String key, @NonNull String value) {
+        mImpl.setHostExtra(key, value);
+        return this;
+    }
+
     /**
      * If all content in a slice cannot be shown, the row added here may be displayed where the
      * content is cut off. This row should have an affordance to take the user to an activity to
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilder.java b/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilder.java
index 62fcc98..0aff93b 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilder.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilder.java
@@ -21,6 +21,7 @@
 import android.app.PendingIntent;
 
 import androidx.annotation.ColorInt;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
@@ -140,5 +141,13 @@
      * Sets the desired layout direction for the content in this slice.
      */
     void setLayoutDirection(int layoutDirection);
+
+    /**
+     * Sets additional information to be passed to the host of the slice.
+     *
+     * @param key The name of the extra data
+     * @param value The String data value
+     */
+    void setHostExtra(@NonNull String key, @NonNull String value);
 }
 
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilderBasicImpl.java b/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilderBasicImpl.java
index 4fad6d9..fb63466 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilderBasicImpl.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilderBasicImpl.java
@@ -22,15 +22,19 @@
 import static android.app.slice.Slice.HINT_TTL;
 import static android.app.slice.Slice.SUBTYPE_COLOR;
 import static android.app.slice.Slice.SUBTYPE_LAYOUT_DIRECTION;
+import static android.app.slice.SliceItem.FORMAT_BUNDLE;
 import static android.app.slice.SliceItem.FORMAT_TEXT;
 
 import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 import static androidx.slice.builders.ListBuilder.INFINITY;
+import static androidx.slice.core.SliceHints.SUBTYPE_HOST_EXTRAS;
 import static androidx.slice.core.SliceHints.SUBTYPE_MILLIS;
 
 import android.app.PendingIntent;
+import android.os.Bundle;
 
 import androidx.annotation.ColorInt;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
@@ -63,6 +67,7 @@
     private CharSequence mSubtitle;
     private SliceAction mSliceAction;
     private IconCompat mIconCompat;
+    private Bundle mHostExtras;
 
     /**
      */
@@ -237,6 +242,14 @@
         getBuilder().addInt(layoutDirection, SUBTYPE_LAYOUT_DIRECTION);
     }
 
+    @Override
+    public void setHostExtra(@NonNull String key, @NonNull String value) {
+        if (mHostExtras == null) {
+            mHostExtras = new Bundle();
+        }
+        mHostExtras.putString(key, value);
+    }
+
     /**
      */
     @Override
@@ -271,6 +284,11 @@
         if (mIconCompat != null) {
             builder.addIcon(mIconCompat, null, new String[] { HINT_TITLE });
         }
+
+        if (mHostExtras != null) {
+            slice.addItem(
+                    new SliceItem(mHostExtras, FORMAT_BUNDLE, SUBTYPE_HOST_EXTRAS, new String[0]));
+        }
         builder.addSubSlice(slice.build());
     }
 }
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilderImpl.java b/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilderImpl.java
index 83f1662..a857830 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilderImpl.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilderImpl.java
@@ -34,6 +34,7 @@
 import static android.app.slice.Slice.SUBTYPE_RANGE;
 import static android.app.slice.Slice.SUBTYPE_VALUE;
 import static android.app.slice.SliceItem.FORMAT_ACTION;
+import static android.app.slice.SliceItem.FORMAT_BUNDLE;
 import static android.app.slice.SliceItem.FORMAT_SLICE;
 import static android.app.slice.SliceItem.FORMAT_TEXT;
 
@@ -42,12 +43,14 @@
 import static androidx.slice.builders.ListBuilder.INFINITY;
 import static androidx.slice.builders.ListBuilder.RANGE_MODE_DETERMINATE;
 import static androidx.slice.core.SliceHints.HINT_END_OF_SECTION;
+import static androidx.slice.core.SliceHints.SUBTYPE_HOST_EXTRAS;
 import static androidx.slice.core.SliceHints.SUBTYPE_MILLIS;
 import static androidx.slice.core.SliceHints.SUBTYPE_MIN;
 import static androidx.slice.core.SliceHints.SUBTYPE_SELECTION;
 
 import android.app.PendingIntent;
 import android.net.Uri;
+import android.os.Bundle;
 
 import androidx.annotation.ColorInt;
 import androidx.annotation.NonNull;
@@ -89,6 +92,7 @@
     private boolean mFirstRowChecked;
     private boolean mIsFirstRowTypeValid;
     private boolean mFirstRowHasText;
+    private Bundle mHostExtras;
 
     public ListBuilderImpl(Slice.Builder b, SliceSpec spec) {
         this(b, spec, new SystemClock());
@@ -125,6 +129,10 @@
             }
             getBuilder().addSubSlice(sb.addHints(HINT_KEYWORDS).build());
         }
+        if (mHostExtras != null) {
+            builder.addItem(new SliceItem(mHostExtras, FORMAT_BUNDLE, SUBTYPE_HOST_EXTRAS,
+                    new String[0]));
+        }
     }
 
     /**
@@ -295,6 +303,15 @@
         getBuilder().addInt(layoutDirection, SUBTYPE_LAYOUT_DIRECTION);
     }
 
+    @Override
+    public void setHostExtra(@NonNull String key, @NonNull String value) {
+        if (mHostExtras == null) {
+            mHostExtras = new Bundle();
+        }
+        mHostExtras.putString(key, value);
+    }
+
+
     /**
      * There are some requirements that first row of a list is not a grid row and has some text.
      * This method helps check whether first row fulfils these requirements.
diff --git a/slices/core/api/restricted_current.txt b/slices/core/api/restricted_current.txt
index 82062d6..a2f829a 100644
--- a/slices/core/api/restricted_current.txt
+++ b/slices/core/api/restricted_current.txt
@@ -241,6 +241,7 @@
     field public static final int RAW_IMAGE_SMALL = 3; // 0x3
     field public static final String SLICE_METADATA_KEY = "android.metadata.SLICE_URI";
     field public static final int SMALL_IMAGE = 1; // 0x1
+    field public static final String SUBTYPE_HOST_EXTRAS = "host_extras";
     field public static final String SUBTYPE_MILLIS = "millis";
     field public static final String SUBTYPE_MIN = "min";
     field public static final String SUBTYPE_SELECTION = "selection";
diff --git a/slices/core/src/main/java/androidx/slice/SliceConvert.java b/slices/core/src/main/java/androidx/slice/SliceConvert.java
index 22a01e6..da18bfa 100644
--- a/slices/core/src/main/java/androidx/slice/SliceConvert.java
+++ b/slices/core/src/main/java/androidx/slice/SliceConvert.java
@@ -17,6 +17,7 @@
 
 
 import static android.app.slice.SliceItem.FORMAT_ACTION;
+import static android.app.slice.SliceItem.FORMAT_BUNDLE;
 import static android.app.slice.SliceItem.FORMAT_IMAGE;
 import static android.app.slice.SliceItem.FORMAT_INT;
 import static android.app.slice.SliceItem.FORMAT_LONG;
@@ -26,6 +27,7 @@
 
 import android.content.Context;
 import android.content.res.Resources;
+import android.os.Bundle;
 import android.util.Log;
 
 import androidx.annotation.RequiresApi;
@@ -77,6 +79,9 @@
                 case FORMAT_LONG:
                     builder.addLong(item.getLong(), item.getSubType(), item.getHints());
                     break;
+                case FORMAT_BUNDLE:
+                    builder.addBundle((Bundle) item.mObj, item.getSubType(), item.getHints());
+                    break;
             }
         }
         return builder.build();
@@ -140,6 +145,10 @@
                 case FORMAT_LONG:
                     builder.addLong(item.getLong(), item.getSubType(), item.getHints());
                     break;
+                case FORMAT_BUNDLE:
+                    builder.addItem(new SliceItem(item.getBundle(), item.getFormat(),
+                            item.getSubType(), item.getHints()));
+                    break;
             }
         }
         return builder.build();
diff --git a/slices/core/src/main/java/androidx/slice/SliceItem.java b/slices/core/src/main/java/androidx/slice/SliceItem.java
index 67e9292..10c87f0 100644
--- a/slices/core/src/main/java/androidx/slice/SliceItem.java
+++ b/slices/core/src/main/java/androidx/slice/SliceItem.java
@@ -17,6 +17,7 @@
 package androidx.slice;
 
 import static android.app.slice.SliceItem.FORMAT_ACTION;
+import static android.app.slice.SliceItem.FORMAT_BUNDLE;
 import static android.app.slice.SliceItem.FORMAT_IMAGE;
 import static android.app.slice.SliceItem.FORMAT_INT;
 import static android.app.slice.SliceItem.FORMAT_LONG;
@@ -99,7 +100,7 @@
      */
     @RestrictTo(Scope.LIBRARY)
     @StringDef({FORMAT_SLICE, FORMAT_TEXT, FORMAT_IMAGE, FORMAT_ACTION, FORMAT_INT,
-            FORMAT_LONG, FORMAT_REMOTE_INPUT, FORMAT_LONG})
+            FORMAT_LONG, FORMAT_REMOTE_INPUT, FORMAT_LONG, FORMAT_BUNDLE})
     @Retention(RetentionPolicy.SOURCE)
     public @interface SliceType {
     }
@@ -424,6 +425,8 @@
             case FORMAT_LONG:
                 dest.putLong(OBJ, (Long) mObj);
                 break;
+            case FORMAT_BUNDLE:
+                dest.putBundle(OBJ, (Bundle) mObj);
         }
     }
 
@@ -445,6 +448,8 @@
                 return in.getInt(OBJ);
             case FORMAT_LONG:
                 return in.getLong(OBJ);
+            case FORMAT_BUNDLE:
+                return in.getBundle(OBJ);
         }
         throw new RuntimeException("Unsupported type " + type);
     }
diff --git a/slices/core/src/main/java/androidx/slice/SliceItemHolder.java b/slices/core/src/main/java/androidx/slice/SliceItemHolder.java
index 504e9a7..9e4a7ac 100644
--- a/slices/core/src/main/java/androidx/slice/SliceItemHolder.java
+++ b/slices/core/src/main/java/androidx/slice/SliceItemHolder.java
@@ -17,6 +17,7 @@
 package androidx.slice;
 
 import static android.app.slice.SliceItem.FORMAT_ACTION;
+import static android.app.slice.SliceItem.FORMAT_BUNDLE;
 import static android.app.slice.SliceItem.FORMAT_IMAGE;
 import static android.app.slice.SliceItem.FORMAT_INT;
 import static android.app.slice.SliceItem.FORMAT_LONG;
@@ -25,6 +26,7 @@
 import static android.app.slice.SliceItem.FORMAT_TEXT;
 
 import android.app.PendingIntent;
+import android.os.Bundle;
 import android.os.Parcelable;
 import android.text.Spanned;
 
@@ -64,6 +66,8 @@
     int mInt = 0;
     @ParcelField(value = 5, defaultValue = "0")
     long mLong = 0;
+    @ParcelField(value = 6, defaultValue = "null")
+    Bundle mBundle = null;
 
     @NonParcelField
     private SliceItemPool mPool;
@@ -109,6 +113,9 @@
             case FORMAT_LONG:
                 mLong = (Long) mObj;
                 break;
+            case FORMAT_BUNDLE:
+                mBundle = (Bundle) mObj;
+
         }
         if (SliceItemHolder.sHandler != null) {
             SliceItemHolder.sHandler.handle(this, format);
@@ -141,6 +148,8 @@
                 return mInt;
             case FORMAT_LONG:
                 return mLong;
+            case FORMAT_BUNDLE:
+                return mBundle;
             default:
                 throw new IllegalArgumentException("Unrecognized format " + format);
         }
diff --git a/slices/core/src/main/java/androidx/slice/core/SliceHints.java b/slices/core/src/main/java/androidx/slice/core/SliceHints.java
index c85391a..460ec07 100644
--- a/slices/core/src/main/java/androidx/slice/core/SliceHints.java
+++ b/slices/core/src/main/java/androidx/slice/core/SliceHints.java
@@ -119,6 +119,8 @@
      */
     public static final String SUBTYPE_SELECTION_OPTION_VALUE = "selection_option_value";
 
+    public static final String SUBTYPE_HOST_EXTRAS = "host_extras";
+
     @IntDef({
             LARGE_IMAGE, SMALL_IMAGE, ICON_IMAGE, RAW_IMAGE_SMALL, RAW_IMAGE_LARGE, UNKNOWN_IMAGE
     })
diff --git a/slices/test/src/main/java/androidx/slice/test/SampleSliceProvider.java b/slices/test/src/main/java/androidx/slice/test/SampleSliceProvider.java
index 18ccdc9..4e52c6b 100644
--- a/slices/test/src/main/java/androidx/slice/test/SampleSliceProvider.java
+++ b/slices/test/src/main/java/androidx/slice/test/SampleSliceProvider.java
@@ -81,6 +81,7 @@
     public static final String EXTRA_TOAST_MESSAGE = "com.example.androidx.extra.TOAST_MESSAGE";
     public static final String ACTION_TOAST_RANGE_VALUE =
             "com.example.androidx.slice.action.TOAST_RANGE_VALUE";
+    public static final String ACTION_PLAY_TTS = "com.example.androidx.slice.action.PLAY_TTS";
 
     public static final String[] URI_PATHS = {
             "message",
@@ -118,7 +119,8 @@
             "longtext",
             "loading",
             "selection",
-            "notification"
+            "notification",
+            "tts"
     };
 
     /**
@@ -228,6 +230,8 @@
                 return createSelectionSlice(sliceUri);
             case "/notification":
                 return createNotificationSlice(sliceUri);
+            case "/tts":
+                return createTtsSlice(sliceUri);
         }
         Log.w(TAG, String.format("Unknown uri: %s", sliceUri));
         return null;
@@ -1392,6 +1396,24 @@
                 .build();
     }
 
+    private Slice createTtsSlice(Uri sliceUri) {
+        Slice slice = new ListBuilder(getContext(), sliceUri, INFINITY)
+                // Attach additional information for host. Depending on the host apps, this
+                // information might or might not be used.
+                // In this case, SliceBrowser is customized to play TTS when binding the slice.
+                .setHostExtra("tts", "hello world")
+                .addRow(
+                        new RowBuilder().setPrimaryAction(
+                                SliceAction.create(
+                                        getBroadcastIntent(ACTION_PLAY_TTS, null),
+                                        IconCompat.createWithResource(getContext(),
+                                                R.drawable.message),
+                                        ICON_IMAGE, "TTS"
+                                )
+                        ).setTitle("Text to speech").setSubtitle("Play")).build();
+        return slice;
+    }
+
     private PendingIntent getIntent(String action) {
         Intent intent = new Intent(action);
         PendingIntent pi = PendingIntent.getActivity(getContext(), 0, intent, 0);
diff --git a/slices/test/src/main/java/androidx/slice/test/SliceBroadcastReceiver.java b/slices/test/src/main/java/androidx/slice/test/SliceBroadcastReceiver.java
index 78f0ffd..524f868 100644
--- a/slices/test/src/main/java/androidx/slice/test/SliceBroadcastReceiver.java
+++ b/slices/test/src/main/java/androidx/slice/test/SliceBroadcastReceiver.java
@@ -38,7 +38,6 @@
  */
 @RequiresApi(19)
 public class SliceBroadcastReceiver extends BroadcastReceiver {
-
     @SuppressWarnings("deprecation")
     @Override
     public void onReceive(final Context context, Intent i) {
@@ -76,6 +75,9 @@
                 context.getContentResolver().notifyChange(getUri("inputrange", context), null);
                 context.getContentResolver().notifyChange(getUri("richinputrange", context), null);
                 break;
+            case SampleSliceProvider.ACTION_PLAY_TTS:
+                context.getContentResolver().notifyChange(getUri("tts", context), null);
+                break;
         }
     }
 }
diff --git a/slices/view/api/current.txt b/slices/view/api/current.txt
index 8ed32c3..43d96ee 100644
--- a/slices/view/api/current.txt
+++ b/slices/view/api/current.txt
@@ -5,6 +5,7 @@
     method public static androidx.slice.SliceMetadata! from(android.content.Context, androidx.slice.Slice);
     method public long getExpiry();
     method public int getHeaderType();
+    method public android.os.Bundle getHostExtras();
     method public android.app.PendingIntent? getInputRangeAction();
     method public long getLastUpdatedTime();
     method public int getLoadingState();
diff --git a/slices/view/api/public_plus_experimental_current.txt b/slices/view/api/public_plus_experimental_current.txt
index 8ed32c3..43d96ee 100644
--- a/slices/view/api/public_plus_experimental_current.txt
+++ b/slices/view/api/public_plus_experimental_current.txt
@@ -5,6 +5,7 @@
     method public static androidx.slice.SliceMetadata! from(android.content.Context, androidx.slice.Slice);
     method public long getExpiry();
     method public int getHeaderType();
+    method public android.os.Bundle getHostExtras();
     method public android.app.PendingIntent? getInputRangeAction();
     method public long getLastUpdatedTime();
     method public int getLoadingState();
diff --git a/slices/view/api/restricted_current.txt b/slices/view/api/restricted_current.txt
index 42d75d4..e21df42e 100644
--- a/slices/view/api/restricted_current.txt
+++ b/slices/view/api/restricted_current.txt
@@ -5,6 +5,7 @@
     method public static androidx.slice.SliceMetadata! from(android.content.Context, androidx.slice.Slice);
     method public long getExpiry();
     method public int getHeaderType();
+    method public android.os.Bundle getHostExtras();
     method public android.app.PendingIntent? getInputRangeAction();
     method public long getLastUpdatedTime();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.slice.widget.ListContent! getListContent();
diff --git a/slices/view/src/main/java/androidx/slice/SliceMetadata.java b/slices/view/src/main/java/androidx/slice/SliceMetadata.java
index 257963d..de6f132 100644
--- a/slices/view/src/main/java/androidx/slice/SliceMetadata.java
+++ b/slices/view/src/main/java/androidx/slice/SliceMetadata.java
@@ -30,12 +30,14 @@
 import static android.app.slice.Slice.SUBTYPE_MAX;
 import static android.app.slice.Slice.SUBTYPE_VALUE;
 import static android.app.slice.SliceItem.FORMAT_ACTION;
+import static android.app.slice.SliceItem.FORMAT_BUNDLE;
 import static android.app.slice.SliceItem.FORMAT_INT;
 import static android.app.slice.SliceItem.FORMAT_LONG;
 import static android.app.slice.SliceItem.FORMAT_SLICE;
 import static android.app.slice.SliceItem.FORMAT_TEXT;
 
 import static androidx.slice.core.SliceHints.HINT_CACHED;
+import static androidx.slice.core.SliceHints.SUBTYPE_HOST_EXTRAS;
 import static androidx.slice.core.SliceHints.SUBTYPE_MIN;
 import static androidx.slice.widget.EventInfo.ROW_TYPE_PROGRESS;
 import static androidx.slice.widget.EventInfo.ROW_TYPE_SLIDER;
@@ -43,6 +45,7 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
+import android.os.Bundle;
 import android.text.TextUtils;
 
 import androidx.annotation.IntDef;
@@ -104,6 +107,7 @@
     private SliceAction mPrimaryAction;
     private List<SliceAction> mSliceActions;
     private @EventInfo.SliceRowType int mTemplateType;
+    private final Bundle mHostExtras;
 
     /**
      * Create a SliceMetadata object to provide access to some information around the slice and
@@ -136,6 +140,13 @@
         if (updatedItem != null) {
             mLastUpdated = updatedItem.getLong();
         }
+        SliceItem hostExtrasItem = SliceQuery.findSubtype(slice, FORMAT_BUNDLE,
+                SUBTYPE_HOST_EXTRAS);
+        if (hostExtrasItem != null && hostExtrasItem.mObj instanceof Bundle) {
+            mHostExtras = (Bundle) hostExtrasItem.mObj;
+        } else {
+            mHostExtras = Bundle.EMPTY;
+        }
         mListContent = new ListContent(slice);
         mHeaderContent = mListContent.getHeader();
         mTemplateType = mListContent.getHeaderTemplateType();
@@ -247,6 +258,11 @@
         return toggles;
     }
 
+    @NonNull
+    public Bundle getHostExtras() {
+        return mHostExtras;
+    }
+
     /**
      * Sends the intent to adjust the state of the provided toggle action.
      *
diff --git a/slices/view/src/main/java/androidx/slice/SliceXml.java b/slices/view/src/main/java/androidx/slice/SliceXml.java
index a952669..55043fe 100644
--- a/slices/view/src/main/java/androidx/slice/SliceXml.java
+++ b/slices/view/src/main/java/androidx/slice/SliceXml.java
@@ -195,6 +195,9 @@
                         v = parser.getText();
                         b.addLong(Long.parseLong(v), subtype, hints);
                         break;
+                    case android.app.slice.SliceItem.FORMAT_BUNDLE:
+                        // Nothing for now
+                        break;
                     default:
                         throw new IllegalArgumentException("Unrecognized format " + format);
                 }
@@ -327,6 +330,9 @@
             case android.app.slice.SliceItem.FORMAT_LONG:
                 serializer.text(String.valueOf(item.getLong()));
                 break;
+            case android.app.slice.SliceItem.FORMAT_BUNDLE:
+                // Nothing for now
+                break;
             default:
                 throw new IllegalArgumentException("Unrecognized format " + format);
         }