Merge "Fix crash in SliceView" into pi-preview1-androidx-dev
diff --git a/compat/src/androidTest/java/androidx/core/app/NotificationCompatTest.java b/compat/src/androidTest/java/androidx/core/app/NotificationCompatTest.java
index b4117e3..2c28cc8 100644
--- a/compat/src/androidTest/java/androidx/core/app/NotificationCompatTest.java
+++ b/compat/src/androidTest/java/androidx/core/app/NotificationCompatTest.java
@@ -526,71 +526,87 @@
     }
 
     @Test
-    public void messagingStyle_isGroupConversation() {
+    public void testMessagingStyle_isGroupConversation() {
         mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.P;
         NotificationCompat.MessagingStyle messagingStyle =
                 new NotificationCompat.MessagingStyle("self name")
                         .setGroupConversation(true)
                         .setConversationTitle("test conversation title");
-        new NotificationCompat.Builder(mContext, "test id")
+        Notification notification = new NotificationCompat.Builder(mContext, "test id")
                 .setSmallIcon(1)
                 .setContentTitle("test title")
                 .setStyle(messagingStyle)
                 .build();
 
-        assertTrue(messagingStyle.isGroupConversation());
+        NotificationCompat.MessagingStyle result =
+                NotificationCompat.MessagingStyle
+                        .extractMessagingStyleFromNotification(notification);
+
+        assertTrue(result.isGroupConversation());
     }
 
     @Test
-    public void messagingStyle_isGroupConversation_noConversationTitle() {
+    public void testMessagingStyle_isGroupConversation_noConversationTitle() {
         mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.P;
         NotificationCompat.MessagingStyle messagingStyle =
                 new NotificationCompat.MessagingStyle("self name")
                         .setGroupConversation(true)
                         .setConversationTitle(null);
-        new NotificationCompat.Builder(mContext, "test id")
+        Notification notification = new NotificationCompat.Builder(mContext, "test id")
                 .setSmallIcon(1)
                 .setContentTitle("test title")
                 .setStyle(messagingStyle)
                 .build();
 
-        assertTrue(messagingStyle.isGroupConversation());
+        NotificationCompat.MessagingStyle result =
+                NotificationCompat.MessagingStyle
+                        .extractMessagingStyleFromNotification(notification);
+
+        assertTrue(result.isGroupConversation());
     }
 
     @Test
-    public void messagingStyle_isGroupConversation_withConversationTitle_legacy() {
+    public void testMessagingStyle_isGroupConversation_withConversationTitle_legacy() {
         // In legacy (version < P), isGroupConversation is controlled by conversationTitle.
         mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
         NotificationCompat.MessagingStyle messagingStyle =
                 new NotificationCompat.MessagingStyle("self name")
                         .setConversationTitle("test conversation title");
-        new NotificationCompat.Builder(mContext, "test id")
+        Notification notification = new NotificationCompat.Builder(mContext, "test id")
                 .setSmallIcon(1)
                 .setContentTitle("test title")
                 .setStyle(messagingStyle)
                 .build();
 
-        assertTrue(messagingStyle.isGroupConversation());
+        NotificationCompat.MessagingStyle result =
+                NotificationCompat.MessagingStyle
+                        .extractMessagingStyleFromNotification(notification);
+
+        assertTrue(result.isGroupConversation());
     }
 
     @Test
-    public void messagingStyle_isGroupConversation_withoutConversationTitle_legacy() {
+    public void testMessagingStyle_isGroupConversation_withoutConversationTitle_legacy() {
         // In legacy (version < P), isGroupConversation is controlled by conversationTitle.
         mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
         NotificationCompat.MessagingStyle messagingStyle =
                 new NotificationCompat.MessagingStyle("self name")
                         .setConversationTitle(null);
-        new NotificationCompat.Builder(mContext, "test id")
+        Notification notification = new NotificationCompat.Builder(mContext, "test id")
                 .setSmallIcon(1)
                 .setContentTitle("test title")
                 .setStyle(messagingStyle)
                 .build();
 
-        assertFalse(messagingStyle.isGroupConversation());
+        NotificationCompat.MessagingStyle result =
+                NotificationCompat.MessagingStyle
+                        .extractMessagingStyleFromNotification(notification);
+
+        assertFalse(result.isGroupConversation());
     }
 
     @Test
-    public void messagingStyle_isGroupConversation_withConversationTitle_legacyWithOverride() {
+    public void testMessagingStyle_isGroupConversation_withConversationTitle_legacyWithOverride() {
         // #setGroupConversation should always take precedence over legacy behavior, so a non-null
         // title shouldn't affect #isGroupConversation.
         mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
@@ -598,17 +614,21 @@
                 new NotificationCompat.MessagingStyle("self name")
                         .setGroupConversation(false)
                         .setConversationTitle("test conversation title");
-        new NotificationCompat.Builder(mContext, "test id")
+        Notification notification = new NotificationCompat.Builder(mContext, "test id")
                 .setSmallIcon(1)
                 .setContentTitle("test title")
                 .setStyle(messagingStyle)
                 .build();
 
-        assertFalse(messagingStyle.isGroupConversation());
+        NotificationCompat.MessagingStyle result =
+                NotificationCompat.MessagingStyle
+                        .extractMessagingStyleFromNotification(notification);
+
+        assertFalse(result.isGroupConversation());
     }
 
     @Test
-    public void messagingStyle_isGroupConversation_withoutConversationTitle_legacyWithOverride() {
+    public void testMessagingStyle_isGroupConversation_withoutTitle_legacyWithOverride() {
         // #setGroupConversation should always take precedence over legacy behavior, so a null
         // title shouldn't affect #isGroupConversation.
         mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
@@ -616,13 +636,17 @@
                 new NotificationCompat.MessagingStyle("self name")
                         .setGroupConversation(true)
                         .setConversationTitle(null);
-        new NotificationCompat.Builder(mContext, "test id")
+        Notification notification = new NotificationCompat.Builder(mContext, "test id")
                 .setSmallIcon(1)
                 .setContentTitle("test title")
                 .setStyle(messagingStyle)
                 .build();
 
-        assertTrue(messagingStyle.isGroupConversation());
+        NotificationCompat.MessagingStyle result =
+                NotificationCompat.MessagingStyle
+                        .extractMessagingStyleFromNotification(notification);
+
+        assertTrue(result.isGroupConversation());
     }
 
     @Test
diff --git a/compat/src/main/java/androidx/core/app/NotificationCompat.java b/compat/src/main/java/androidx/core/app/NotificationCompat.java
index b82885f..b0da59b 100644
--- a/compat/src/main/java/androidx/core/app/NotificationCompat.java
+++ b/compat/src/main/java/androidx/core/app/NotificationCompat.java
@@ -2287,6 +2287,12 @@
         @RestrictTo(LIBRARY_GROUP)
         @Override
         public void apply(NotificationBuilderWithBuilderAccessor builder) {
+            // This is called because we need to apply legacy logic before writing MessagingInfo
+            // data into the bundle. This does nothing in >= P, but in < P this will apply the
+            // correct group conversation status to new fields which will then be decoded properly
+            // by #extractMessagingStyleFromNotification.
+            setGroupConversation(isGroupConversation());
+
             if (Build.VERSION.SDK_INT >= 24) {
                 Notification.MessagingStyle style =
                         new Notification.MessagingStyle(mUserDisplayName)
diff --git a/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/Log.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/Log.kt
index 23a1afa..fa558c5 100644
--- a/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/Log.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/Log.kt
@@ -18,16 +18,17 @@
 
 object Log {
 
-    var currentLevel: LogLevel = LogLevel.INFO
+    var currentLevel: LogLevel = LogLevel.ERROR
 
     var logConsumer: LogConsumer = StdOutLogConsumer()
 
     fun setLevel(level: String?) {
         currentLevel = when (level) {
+            "info" -> LogLevel.INFO
             "error" -> LogLevel.ERROR
             "debug" -> LogLevel.DEBUG
             "verbose" -> LogLevel.VERBOSE
-            else -> LogLevel.INFO
+            else -> LogLevel.ERROR
         }
     }
 
diff --git a/slices/builders/src/main/java/androidx/slice/builders/GridBuilder.java b/slices/builders/src/main/java/androidx/slice/builders/GridBuilder.java
index 5e0e521..7c672d7 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/GridBuilder.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/GridBuilder.java
@@ -24,6 +24,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.core.util.Consumer;
@@ -296,6 +297,7 @@
          */
         @NonNull
         @Deprecated
+        @RequiresApi(23)
         public CellBuilder addLargeImage(@NonNull Icon image) {
             return addImage(image, ListBuilder.LARGE_IMAGE, false /* isLoading */);
         }
@@ -312,6 +314,7 @@
          */
         @NonNull
         @Deprecated
+        @RequiresApi(23)
         public CellBuilder addLargeImage(@Nullable Icon image, boolean isLoading) {
             return addImage(image, ListBuilder.LARGE_IMAGE, isLoading);
         }
@@ -324,6 +327,7 @@
          */
         @NonNull
         @Deprecated
+        @RequiresApi(23)
         public CellBuilder addImage(@NonNull Icon image) {
             return addImage(image, ListBuilder.SMALL_IMAGE, false /* isLoading */);
         }
@@ -333,6 +337,7 @@
          */
         @NonNull
         @Deprecated
+        @RequiresApi(23)
         public CellBuilder addImage(@Nullable Icon image, boolean isLoading) {
             return addImage(image, ListBuilder.SMALL_IMAGE, isLoading);
         }
@@ -342,6 +347,7 @@
          */
         @NonNull
         @Deprecated
+        @RequiresApi(23)
         public CellBuilder addImage(@NonNull Icon image, @ListBuilder.ImageMode int imageMode) {
             return addImage(image, imageMode, false /* isLoading */);
         }
@@ -351,6 +357,7 @@
          */
         @NonNull
         @Deprecated
+        @RequiresApi(23)
         public CellBuilder addImage(@Nullable Icon image, @ListBuilder.ImageMode int imageMode,
                 boolean isLoading) {
             mImpl.addImage(IconCompat.createFromIcon(image), imageMode, isLoading);
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 612a4d7..c0b5bc3 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/ListBuilder.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/ListBuilder.java
@@ -28,6 +28,7 @@
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.core.util.Consumer;
@@ -712,6 +713,7 @@
          * @deprecated TO BE REMOVED
          */
         @NonNull
+        @RequiresApi(23)
         public InputRangeBuilder setThumb(@NonNull Icon thumb) {
             return setThumb(IconCompat.createFromIcon(thumb));
         }
@@ -846,6 +848,7 @@
          */
         @Deprecated
         @NonNull
+        @RequiresApi(23)
         public RowBuilder setTitleItem(@NonNull Icon icon) {
             return setTitleItem(icon, ICON_IMAGE);
         }
@@ -855,6 +858,7 @@
          */
         @Deprecated
         @NonNull
+        @RequiresApi(23)
         public RowBuilder setTitleItem(@Nullable Icon icon, boolean isLoading) {
             return setTitleItem(icon, ICON_IMAGE, isLoading);
         }
@@ -863,6 +867,7 @@
          * @deprecated TO BE REMOVED.
          */
         @Deprecated
+        @RequiresApi(23)
         public RowBuilder setTitleItem(@NonNull Icon icon, @ImageMode int imageMode) {
             mImpl.setTitleItem(IconCompat.createFromIcon(icon), imageMode, false /* isLoading */);
             return this;
@@ -873,6 +878,7 @@
          */
         @Deprecated
         @NonNull
+        @RequiresApi(23)
         public RowBuilder setTitleItem(@Nullable Icon icon, @ImageMode int imageMode,
                 boolean isLoading) {
             mImpl.setTitleItem(IconCompat.createFromIcon(icon), imageMode,
@@ -1053,6 +1059,7 @@
          */
         @Deprecated
         @NonNull
+        @RequiresApi(23)
         public RowBuilder addEndItem(@NonNull Icon icon) {
             return addEndItem(icon, ICON_IMAGE, false /* isLoading */);
         }
@@ -1062,6 +1069,7 @@
          */
         @Deprecated
         @NonNull
+        @RequiresApi(23)
         public RowBuilder addEndItem(@NonNull Icon icon, boolean isLoading) {
             return addEndItem(icon, ICON_IMAGE, isLoading);
         }
@@ -1071,6 +1079,7 @@
          */
         @Deprecated
         @NonNull
+        @RequiresApi(23)
         public RowBuilder addEndItem(@NonNull Icon icon, @ImageMode int imageMode) {
             return addEndItem(icon, imageMode, false /* isLoading */);
         }
@@ -1080,6 +1089,7 @@
          */
         @Deprecated
         @NonNull
+        @RequiresApi(23)
         public RowBuilder addEndItem(@Nullable Icon icon, @ImageMode int imageMode,
                 boolean isLoading) {
             if (mHasEndActionOrToggle) {
diff --git a/slices/builders/src/main/java/androidx/slice/builders/MessagingSliceBuilder.java b/slices/builders/src/main/java/androidx/slice/builders/MessagingSliceBuilder.java
index 7dafb35..6fb22ef 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/MessagingSliceBuilder.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/MessagingSliceBuilder.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
+import android.os.Build;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
@@ -120,7 +121,9 @@
          * Add the icon used to display contact in the messaging experience
          */
         public MessageBuilder addSource(IconCompat source) {
-            mImpl.addSource(source.toIcon());
+            if (Build.VERSION.SDK_INT >= 23) {
+                mImpl.addSource(source.toIcon());
+            }
             return this;
         }
 
diff --git a/slices/builders/src/main/java/androidx/slice/builders/SliceAction.java b/slices/builders/src/main/java/androidx/slice/builders/SliceAction.java
index e4e28f5..b98c723 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/SliceAction.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/SliceAction.java
@@ -25,6 +25,7 @@
 import androidx.annotation.IntRange;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.slice.Slice;
@@ -41,6 +42,7 @@
      * @deprecated TO BE REMOVED
      */
     @Deprecated
+    @RequiresApi(23)
     public SliceAction(@NonNull PendingIntent action, @NonNull Icon actionIcon,
             @NonNull CharSequence actionTitle) {
         this(action, actionIcon, ICON_IMAGE, actionTitle);
@@ -50,6 +52,7 @@
      * @deprecated TO BE REMOVED
      */
     @Deprecated
+    @RequiresApi(23)
     public SliceAction(@NonNull PendingIntent action, @NonNull Icon actionIcon,
             @ListBuilder.ImageMode int imageMode, @NonNull CharSequence actionTitle) {
         this(action, IconCompat.createFromIcon(actionIcon), imageMode, actionTitle);
@@ -59,6 +62,7 @@
      * @deprecated TO BE REMOVED
      */
     @Deprecated
+    @RequiresApi(23)
     public SliceAction(@NonNull PendingIntent action, @NonNull Icon actionIcon,
             @NonNull CharSequence actionTitle, boolean isChecked) {
         this(action, IconCompat.createFromIcon(actionIcon), actionTitle, isChecked);
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBasicImpl.java b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBasicImpl.java
index c999594..f53b9f5 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBasicImpl.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBasicImpl.java
@@ -19,7 +19,9 @@
 import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 
 import android.graphics.drawable.Icon;
+import android.os.Build;
 
+import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.slice.Slice;
@@ -44,8 +46,10 @@
     @Override
     public void apply(Slice.Builder builder) {
         if (mLastMessage != null) {
-            if (mLastMessage.mIcon != null) {
-                builder.addIcon(IconCompat.createFromIcon(mLastMessage.mIcon), null);
+            if (Build.VERSION.SDK_INT >= 23) {
+                if (mLastMessage.mIcon != null) {
+                    builder.addIcon(IconCompat.createFromIcon(mLastMessage.mIcon), null);
+                }
             }
             if (mLastMessage.mText != null) {
                 builder.addText(mLastMessage.mText, null);
@@ -75,6 +79,7 @@
     public static final class MessageBuilder extends TemplateBuilderImpl
             implements MessagingBuilder.MessageBuilder {
 
+        @RequiresApi(23)
         private Icon mIcon;
         private CharSequence mText;
         private long mTimestamp;
@@ -94,6 +99,7 @@
         /**
          */
         @Override
+        @RequiresApi(23)
         public void addSource(Icon source) {
             mIcon = source;
         }
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBuilder.java b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBuilder.java
index dd66616..a9fb0cc 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBuilder.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBuilder.java
@@ -20,6 +20,7 @@
 
 import android.graphics.drawable.Icon;
 
+import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 
 /**
@@ -44,6 +45,7 @@
         /**
          * Add the icon used to display contact in the messaging experience
          */
+        @RequiresApi(23)
         void addSource(Icon source);
 
         /**
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingListV1Impl.java b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingListV1Impl.java
index e47871a..99f0f34 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingListV1Impl.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingListV1Impl.java
@@ -22,6 +22,7 @@
 
 import android.graphics.drawable.Icon;
 
+import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.slice.Slice;
@@ -85,6 +86,7 @@
         /**
          */
         @Override
+        @RequiresApi(23)
         public void addSource(Icon source) {
             mListBuilder.setTitleItem(IconCompat.createFromIcon(source), SMALL_IMAGE);
         }
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingV1Impl.java b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingV1Impl.java
index e601491..63214da 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingV1Impl.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingV1Impl.java
@@ -20,6 +20,7 @@
 
 import android.graphics.drawable.Icon;
 
+import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.slice.Slice;
@@ -71,6 +72,7 @@
         /**
          */
         @Override
+        @RequiresApi(23)
         public void addSource(Icon source) {
             getBuilder().addIcon(IconCompat.createFromIcon(source),
                     android.app.slice.Slice.SUBTYPE_SOURCE);
diff --git a/slices/core/src/main/java/androidx/slice/Slice.java b/slices/core/src/main/java/androidx/slice/Slice.java
index ac571f7..c6ced6d 100644
--- a/slices/core/src/main/java/androidx/slice/Slice.java
+++ b/slices/core/src/main/java/androidx/slice/Slice.java
@@ -39,6 +39,7 @@
 import static androidx.slice.SliceConvert.unwrap;
 import static androidx.slice.core.SliceHints.HINT_KEYWORDS;
 import static androidx.slice.core.SliceHints.HINT_LAST_UPDATED;
+import static androidx.slice.core.SliceHints.HINT_PERMISSION_REQUEST;
 import static androidx.slice.core.SliceHints.HINT_TTL;
 
 import android.app.PendingIntent;
@@ -84,9 +85,24 @@
      * @hide
      */
     @RestrictTo(Scope.LIBRARY)
-    @StringDef({HINT_TITLE, HINT_LIST, HINT_LIST_ITEM, HINT_LARGE, HINT_ACTIONS, HINT_SELECTED,
-            HINT_HORIZONTAL, HINT_NO_TINT, HINT_PARTIAL, HINT_SUMMARY, HINT_SEE_MORE,
-            HINT_SHORTCUT, HINT_KEYWORDS, HINT_TTL, HINT_LAST_UPDATED})
+    @StringDef({
+            HINT_TITLE,
+            HINT_LIST,
+            HINT_LIST_ITEM,
+            HINT_LARGE,
+            HINT_ACTIONS,
+            HINT_SELECTED,
+            HINT_HORIZONTAL,
+            HINT_NO_TINT,
+            HINT_PARTIAL,
+            HINT_SUMMARY,
+            HINT_SEE_MORE,
+            HINT_SHORTCUT,
+            HINT_KEYWORDS,
+            HINT_TTL,
+            HINT_LAST_UPDATED,
+            HINT_PERMISSION_REQUEST,
+    })
     public @interface SliceHint{ }
 
     private final SliceItem[] mItems;
diff --git a/slices/core/src/main/java/androidx/slice/compat/SliceProviderCompat.java b/slices/core/src/main/java/androidx/slice/compat/SliceProviderCompat.java
index 5e8eed0..182b3c2 100644
--- a/slices/core/src/main/java/androidx/slice/compat/SliceProviderCompat.java
+++ b/slices/core/src/main/java/androidx/slice/compat/SliceProviderCompat.java
@@ -15,9 +15,12 @@
  */
 package androidx.slice.compat;
 
-import static android.app.slice.Slice.HINT_LIST_ITEM;
+import static android.app.slice.Slice.HINT_SHORTCUT;
+import static android.app.slice.Slice.HINT_TITLE;
 import static android.app.slice.SliceProvider.SLICE_TYPE;
 
+import static androidx.slice.core.SliceHints.HINT_PERMISSION_REQUEST;
+
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.ContentProvider;
@@ -175,7 +178,7 @@
                 b.putParcelable(EXTRA_SLICE, null);
             }
             return b;
-        } else if (method.equals(METHOD_MAP_INTENT)) {
+        } else if (method.equals(METHOD_MAP_ONLY_INTENT)) {
             Intent intent = extras.getParcelable(EXTRA_INTENT);
             Uri uri = mSliceProvider.onMapIntentToUri(intent);
             Bundle b = new Bundle();
@@ -249,13 +252,19 @@
      */
     public static Slice createPermissionSlice(Context context, Uri sliceUri,
             String callingPackage) {
-        return new Slice.Builder(sliceUri)
+        Slice.Builder parent = new Slice.Builder(sliceUri);
+
+        Slice.Builder action = new Slice.Builder(parent)
+                .addHints(HINT_TITLE, HINT_SHORTCUT)
                 .addAction(createPermissionIntent(context, sliceUri, callingPackage),
-                        new Slice.Builder(sliceUri.buildUpon().appendPath("permission").build())
-                                .addText(getPermissionString(context, callingPackage), null)
-                                .build(), null)
-                .addHints(HINT_LIST_ITEM)
-                .build();
+                        new Slice.Builder(parent).build(), null);
+
+        parent.addSubSlice(new Slice.Builder(sliceUri.buildUpon().appendPath("permission").build())
+                .addText(getPermissionString(context, callingPackage), null)
+                .addSubSlice(action.build())
+                .build());
+
+        return parent.addHints(HINT_PERMISSION_REQUEST).build();
     }
 
     /**
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 1c13f94..3fb9ada 100644
--- a/slices/core/src/main/java/androidx/slice/core/SliceHints.java
+++ b/slices/core/src/main/java/androidx/slice/core/SliceHints.java
@@ -78,6 +78,8 @@
      */
     public static final String SUBTYPE_MILLIS = "millis";
 
+    public static final String HINT_PERMISSION_REQUEST = "permission_request";
+
     @IntDef({
             LARGE_IMAGE, SMALL_IMAGE, ICON_IMAGE, UNKNOWN_IMAGE
     })
diff --git a/slices/view/api/current.txt b/slices/view/api/current.txt
index caa2692..0e1c759 100644
--- a/slices/view/api/current.txt
+++ b/slices/view/api/current.txt
@@ -28,6 +28,7 @@
     method public java.util.List<java.lang.String> getSliceKeywords();
     method public java.util.List<androidx.slice.core.SliceAction> getToggles();
     method public boolean hasLargeMode();
+    method public boolean isPermissionSlice();
     field public static final int LOADED_ALL = 2; // 0x2
     field public static final int LOADED_NONE = 0; // 0x0
     field public static final int LOADED_PARTIAL = 1; // 0x1
diff --git a/slices/view/src/androidTest/AndroidManifest.xml b/slices/view/src/androidTest/AndroidManifest.xml
index 3bb50d5..78f3ad8 100644
--- a/slices/view/src/androidTest/AndroidManifest.xml
+++ b/slices/view/src/androidTest/AndroidManifest.xml
@@ -20,10 +20,15 @@
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
-    <application>
+    <application
+        android:label="Slice Render Test">
         <provider android:name="androidx.slice.SliceManagerTest$TestSliceProvider"
                   android:authorities="androidx.slice.view.test"
-                  android:exported="true"/>
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="androidx.slice.action.TEST" />
+            </intent-filter>
+        </provider>
 
         <activity android:name="androidx.slice.render.SliceRenderActivity"
             android:theme="@style/AppTheme.NoActionBar">
diff --git a/slices/view/src/androidTest/java/androidx/slice/SliceManagerTest.java b/slices/view/src/androidTest/java/androidx/slice/SliceManagerTest.java
index 52e6ef6..2c6731d 100644
--- a/slices/view/src/androidTest/java/androidx/slice/SliceManagerTest.java
+++ b/slices/view/src/androidTest/java/androidx/slice/SliceManagerTest.java
@@ -30,11 +30,14 @@
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
-import androidx.annotation.NonNull;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+
+import androidx.annotation.NonNull;
 import androidx.core.os.BuildCompat;
+import androidx.slice.render.SliceRenderActivity;
+import androidx.slice.widget.SliceLiveData;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -42,8 +45,6 @@
 
 import java.util.concurrent.Executor;
 
-import androidx.slice.widget.SliceLiveData;
-
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class SliceManagerTest {
@@ -124,14 +125,27 @@
         assertEquals(SliceLiveData.SUPPORTED_SPECS, mManager.getPinnedSpecs(uri));
     }
 
-    //@Test
-    //public void testMapIntentToUri() {
-    //    Uri expected = Uri.parse("content://androidx.slice.view.test/render");
-    //    Slice s = new Slice.Builder(expected).build();
-    //    when(mSliceProvider.onBindSlice(eq(expected))).thenReturn(s);
-    //    Uri uri = mManager.mapIntentToUri(new Intent(mContext, SliceRenderActivity.class));
-    //    assertEquals(expected, uri);
-    //}
+    @Test
+    public void testMapIntentToUriStatic() {
+        Uri expected = Uri.parse("content://androidx.slice.view.test/render");
+
+        Uri uri = mManager.mapIntentToUri(new Intent(mContext, SliceRenderActivity.class));
+
+        assertEquals(expected, uri);
+    }
+
+    @Test
+    public void testMapIntentToUri() {
+        Uri expected = Uri.parse("content://androidx.slice.view.test/render");
+        Intent intent = new Intent("androidx.slice.action.TEST")
+                .setPackage(mContext.getPackageName());
+
+        when(mSliceProvider.onMapIntentToUri(eq(intent))).thenReturn(expected);
+        Uri uri = mManager.mapIntentToUri(intent);
+
+        assertEquals(expected, uri);
+        verify(mSliceProvider).onMapIntentToUri(eq(intent));
+    }
 
     public static class TestSliceProvider extends SliceProvider {
 
diff --git a/slices/view/src/androidTest/java/androidx/slice/SliceMetadataTest.java b/slices/view/src/androidTest/java/androidx/slice/SliceMetadataTest.java
index 32a01db..d378e2e 100644
--- a/slices/view/src/androidTest/java/androidx/slice/SliceMetadataTest.java
+++ b/slices/view/src/androidTest/java/androidx/slice/SliceMetadataTest.java
@@ -47,6 +47,7 @@
 import androidx.slice.builders.GridRowBuilder;
 import androidx.slice.builders.ListBuilder;
 import androidx.slice.builders.SliceAction;
+import androidx.slice.compat.SliceProviderCompat;
 import androidx.slice.core.SliceActionImpl;
 import androidx.slice.core.SliceHints;
 import androidx.slice.render.SliceRenderActivity;
@@ -588,6 +589,16 @@
         assertEquals(0, retrievedLastUpdated2);
     }
 
+    @Test
+    public void testIsPermissionSlice() {
+        Uri uri = Uri.parse("content://pkg/slice");
+        Slice permissionSlice =
+                SliceProviderCompat.createPermissionSlice(mContext, uri, mContext.getPackageName());
+
+        SliceMetadata metadata = SliceMetadata.from(mContext, permissionSlice);
+        assertEquals(true, metadata.isPermissionSlice());
+    }
+
     private PendingIntent getIntent(String action) {
         Intent intent = new Intent(action);
         intent.setClassName(mContext.getPackageName(), SliceRenderActivity.class.getName());
diff --git a/slices/view/src/androidTest/java/androidx/slice/render/SliceCreator.java b/slices/view/src/androidTest/java/androidx/slice/render/SliceCreator.java
index 4873382..e0bccfd 100644
--- a/slices/view/src/androidTest/java/androidx/slice/render/SliceCreator.java
+++ b/slices/view/src/androidTest/java/androidx/slice/render/SliceCreator.java
@@ -40,6 +40,7 @@
 import androidx.slice.builders.ListBuilder;
 import androidx.slice.builders.MessagingSliceBuilder;
 import androidx.slice.builders.SliceAction;
+import androidx.slice.compat.SliceProviderCompat;
 import androidx.slice.view.test.R;
 
 import java.util.Arrays;
@@ -58,7 +59,7 @@
 
     public static final String[] URI_PATHS = {"message", "wifi", "wifi2", "note", "ride",
             "ride-ttl", "toggle", "toggle2", "contact", "gallery", "subscription", "subscription2",
-            "weather", "reservation", "inputrange", "range"};
+            "weather", "reservation", "inputrange", "range", "permission"};
 
     private final Context mContext;
 
@@ -116,6 +117,8 @@
                 return createStarRatingInputRange(sliceUri);
             case "/range":
                 return createDownloadProgressRange(sliceUri);
+            case "/permission":
+                return createPermissionSlice(sliceUri);
         }
         throw new IllegalArgumentException("Unknown uri " + sliceUri);
     }
@@ -472,11 +475,11 @@
                 .build();
     }
 
-
     private Slice createStarRatingInputRange(Uri sliceUri) {
         IconCompat icon = IconCompat.createWithResource(getContext(), R.drawable.ic_star_on);
         SliceAction primaryAction =
-                new SliceAction(getBroadcastIntent(ACTION_TOAST, "open star rating"), icon, "Rate");
+                new SliceAction(getBroadcastIntent(ACTION_TOAST, "open star rating"), icon,
+                        "Rate");
         ListBuilder lb = new ListBuilder(getContext(), sliceUri, INFINITY);
         return lb.setColor(0xffff4081)
                 .addInputRange(new ListBuilder.InputRangeBuilder(lb)
@@ -495,7 +498,8 @@
         IconCompat icon = IconCompat.createWithResource(getContext(), R.drawable.ic_star_on);
         SliceAction primaryAction =
                 new SliceAction(
-                        getBroadcastIntent(ACTION_TOAST, "open download"), icon, "Download");
+                        getBroadcastIntent(ACTION_TOAST, "open download"), icon,
+                        "Download");
         ListBuilder lb = new ListBuilder(getContext(), sliceUri, INFINITY);
         return lb.setColor(0xffff4081)
                 .addRange(new ListBuilder.RangeBuilder(lb)
@@ -507,6 +511,11 @@
                 .build();
     }
 
+    private Slice createPermissionSlice(Uri uri) {
+        return SliceProviderCompat.createPermissionSlice(getContext(), uri,
+                getContext().getPackageName());
+    }
+
     private PendingIntent getIntent(String action) {
         Intent intent = new Intent(action);
         intent.setClassName(getContext().getPackageName(), SliceRenderActivity.class.getName());
diff --git a/slices/view/src/main/java/androidx/slice/SliceMetadata.java b/slices/view/src/main/java/androidx/slice/SliceMetadata.java
index db5f7ae..9a4648f 100644
--- a/slices/view/src/main/java/androidx/slice/SliceMetadata.java
+++ b/slices/view/src/main/java/androidx/slice/SliceMetadata.java
@@ -27,6 +27,7 @@
 
 import static androidx.slice.core.SliceHints.HINT_KEYWORDS;
 import static androidx.slice.core.SliceHints.HINT_LAST_UPDATED;
+import static androidx.slice.core.SliceHints.HINT_PERMISSION_REQUEST;
 import static androidx.slice.core.SliceHints.HINT_TTL;
 import static androidx.slice.core.SliceHints.SUBTYPE_MAX;
 import static androidx.slice.core.SliceHints.SUBTYPE_VALUE;
@@ -279,6 +280,18 @@
     }
 
     /**
+     * To present a slice from another app, the app must grant uri permissions for the slice. If
+     * these permissions have not been granted and the app slice is requested then
+     * a permission request slice will be returned instead, allowing the user to grant permission.
+     * This method can be used to identify if a slice is a permission request.
+     *
+     * @return whether this slice represents a permission request.
+     */
+    public boolean isPermissionSlice() {
+        return mSlice.hasHint(HINT_PERMISSION_REQUEST);
+    }
+
+    /**
      * @return the group of actions associated with the provided slice, if they exist.
      * @hide
      */
diff --git a/slices/view/src/main/java/androidx/slice/widget/SliceView.java b/slices/view/src/main/java/androidx/slice/widget/SliceView.java
index b151437..48f7c81 100644
--- a/slices/view/src/main/java/androidx/slice/widget/SliceView.java
+++ b/slices/view/src/main/java/androidx/slice/widget/SliceView.java
@@ -532,7 +532,7 @@
         long expiry = sliceMetadata.getExpiry();
         long now = System.currentTimeMillis();
         mCurrentView.setLastUpdated(lastUpdated);
-        boolean expired = expiry != SliceHints.INFINITY && now > expiry;
+        boolean expired = expiry != 0 && expiry != SliceHints.INFINITY && now > expiry;
         mCurrentView.setShowLastUpdated(mShowLastUpdated && expired);
 
         // Set the slice