Merge "Add getDescendants to Settings Slice Provider" into pi-dev
diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java
index 1a73ea7..4b4b9c2 100644
--- a/src/com/android/settings/slices/SettingsSliceProvider.java
+++ b/src/com/android/settings/slices/SettingsSliceProvider.java
@@ -18,19 +18,26 @@
 
 import android.app.PendingIntent;
 import android.app.slice.SliceManager;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.net.wifi.WifiManager;
+import android.provider.SettingsSlicesContract;
 import android.support.annotation.VisibleForTesting;
 import android.support.v4.graphics.drawable.IconCompat;
+import android.text.TextUtils;
 import android.util.Log;
+import android.util.Pair;
 
 import com.android.settings.R;
 import com.android.settingslib.utils.ThreadUtils;
 
 import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 import java.util.Map;
 import java.util.WeakHashMap;
 
@@ -144,6 +151,85 @@
         return SliceBuilderUtils.buildSlice(getContext(), cachedSliceData);
     }
 
+    /**
+     * Get a list of all valid Uris based on the keys indexed in the Slices database.
+     * <p>
+     * This will return a list of {@link Uri uris} depending on {@param uri}, following:
+     * 1. Authority & Full Path -> Only {@param uri}. It is only a prefix for itself.
+     * 2. Authority & No path -> A list of authority/action/$KEY$, where
+     * {@code $KEY$} is a list of all Slice-enabled keys for the authority.
+     * 3. Authority & action path -> A list of authority/action/$KEY$, where
+     * {@code $KEY$} is a list of all Slice-enabled keys for the authority.
+     * 4. Empty authority & path -> A list of Uris with all keys for both supported authorities.
+     * 5. Else -> Empty list.
+     * <p>
+     * Note that the authority will stay consistent with {@param uri}, and the list of valid Slice
+     * keys depends on if the authority is {@link SettingsSlicesContract#AUTHORITY} or
+     * {@link #SLICE_AUTHORITY}.
+     *
+     * @param uri The uri to look for descendants under.
+     * @returns all valid Settings uris for which {@param uri} is a prefix.
+     */
+    @Override
+    public Collection<Uri> onGetSliceDescendants(Uri uri) {
+        final List<Uri> descendants = new ArrayList<>();
+        final Pair<Boolean, String> pathData = SliceBuilderUtils.getPathData(uri);
+
+        if (pathData != null) {
+            // Uri has a full path and will not have any descendants.
+            descendants.add(uri);
+            return descendants;
+        }
+
+        final String authority = uri.getAuthority();
+        final String pathPrefix = uri.getPath();
+        final boolean isPathEmpty = pathPrefix.isEmpty();
+
+        // No path nor authority. Return all possible Uris.
+        if (isPathEmpty && TextUtils.isEmpty(authority)) {
+            final List<String> platformKeys = mSlicesDatabaseAccessor.getSliceKeys(
+                    true /* isPlatformSlice */);
+            final List<String> oemKeys = mSlicesDatabaseAccessor.getSliceKeys(
+                    false /* isPlatformSlice */);
+            final List<Uri> allUris = buildUrisFromKeys(platformKeys,
+                    SettingsSlicesContract.AUTHORITY);
+            allUris.addAll(buildUrisFromKeys(oemKeys, SettingsSliceProvider.SLICE_AUTHORITY));
+
+            return allUris;
+        }
+
+        // Path is anything but empty, "action", or "intent". Return empty list.
+        if (!isPathEmpty
+                && !TextUtils.equals(pathPrefix, "/" + SettingsSlicesContract.PATH_SETTING_ACTION)
+                && !TextUtils.equals(pathPrefix,
+                "/" + SettingsSlicesContract.PATH_SETTING_INTENT)) {
+            // Invalid path prefix, there are no valid Uri descendants.
+            return descendants;
+        }
+
+        // Can assume authority belongs to the provider. Return all Uris for the authority.
+        final boolean isPlatformUri = TextUtils.equals(authority, SettingsSlicesContract.AUTHORITY);
+        final List<String> keys = mSlicesDatabaseAccessor.getSliceKeys(isPlatformUri);
+        return buildUrisFromKeys(keys, authority);
+    }
+
+    private List<Uri> buildUrisFromKeys(List<String> keys, String authority) {
+        final List<Uri> descendants = new ArrayList<>();
+
+        final Uri.Builder builder = new Uri.Builder()
+                .scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(authority)
+                .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION);
+
+        final String newUriPathPrefix = SettingsSlicesContract.PATH_SETTING_ACTION + "/";
+        for (String key : keys) {
+            builder.path(newUriPathPrefix + key);
+            descendants.add(builder.build());
+        }
+
+        return descendants;
+    }
+
     @VisibleForTesting
     void loadSlice(Uri uri) {
         long startBuildTime = System.currentTimeMillis();
diff --git a/src/com/android/settings/slices/SliceBuilderUtils.java b/src/com/android/settings/slices/SliceBuilderUtils.java
index a2479a2..e6ba4a8 100644
--- a/src/com/android/settings/slices/SliceBuilderUtils.java
+++ b/src/com/android/settings/slices/SliceBuilderUtils.java
@@ -130,7 +130,7 @@
         // Example: "/action/wifi" -> [{}, "action", "wifi"]
         //          "/action/longer/path" -> [{}, "action", "longer/path"]
         if (split.length != 3) {
-            throw new IllegalArgumentException("Uri (" + uri + ") has incomplete path: " + path);
+            return null;
         }
 
         final boolean isInline = TextUtils.equals(SettingsSlicesContract.PATH_SETTING_ACTION,
diff --git a/src/com/android/settings/slices/SlicesDatabaseAccessor.java b/src/com/android/settings/slices/SlicesDatabaseAccessor.java
index 82b3506..0509f9c 100644
--- a/src/com/android/settings/slices/SlicesDatabaseAccessor.java
+++ b/src/com/android/settings/slices/SlicesDatabaseAccessor.java
@@ -29,6 +29,9 @@
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.slices.SlicesDatabaseHelper.IndexColumns;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import androidx.slice.Slice;
 
 /**
@@ -51,10 +54,12 @@
     // Cursor value for boolean true
     private final int TRUE = 1;
 
-    Context mContext;
+    private final Context mContext;
+    private final SlicesDatabaseHelper mHelper;
 
     public SlicesDatabaseAccessor(Context context) {
         mContext = context;
+        mHelper = SlicesDatabaseHelper.getInstance(mContext);
     }
 
     /**
@@ -79,16 +84,44 @@
         return buildSliceData(cursor, null /* uri */, false /* isInlineOnly */);
     }
 
+    /**
+     * @return a list of keys in the Slices database matching on {@param isPlatformSlice}.
+     */
+    public List<String> getSliceKeys(boolean isPlatformSlice) {
+        final String whereClause;
+
+        if (isPlatformSlice) {
+            whereClause = IndexColumns.PLATFORM_SLICE + " = 1";
+        } else {
+            whereClause = IndexColumns.PLATFORM_SLICE + " = 0";
+        }
+
+        final SQLiteDatabase database = mHelper.getReadableDatabase();
+        final String[] columns = new String[]{IndexColumns.KEY};
+        final List<String> keys = new ArrayList<>();
+
+        try (final Cursor resultCursor = database.query(TABLE_SLICES_INDEX, columns, whereClause,
+                null /* selection */, null /* groupBy */, null /* having */, null /* orderBy */)) {
+            if (!resultCursor.moveToFirst()) {
+                return keys;
+            }
+
+            do {
+                keys.add(resultCursor.getString(0 /* key index */));
+            } while (resultCursor.moveToNext());
+        }
+
+        return keys;
+    }
+
     private Cursor getIndexedSliceData(String path) {
         verifyIndexing();
 
         final String whereClause = buildKeyMatchWhereClause();
-        final SlicesDatabaseHelper helper = SlicesDatabaseHelper.getInstance(mContext);
-        final SQLiteDatabase database = helper.getReadableDatabase();
+        final SQLiteDatabase database = mHelper.getReadableDatabase();
         final String[] selection = new String[]{path};
-
-        Cursor resultCursor = database.query(TABLE_SLICES_INDEX, SELECT_COLUMNS_ALL, whereClause,
-                selection, null /* groupBy */, null /* having */, null /* orderBy */);
+        final Cursor resultCursor = database.query(TABLE_SLICES_INDEX, SELECT_COLUMNS_ALL,
+                whereClause, selection, null /* groupBy */, null /* having */, null /* orderBy */);
 
         int numResults = resultCursor.getCount();
 
diff --git a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
index d54d16f..b5399ea 100644
--- a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
@@ -17,6 +17,8 @@
 
 package com.android.settings.slices;
 
+import static android.content.ContentResolver.SCHEME_CONTENT;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.spy;
@@ -41,6 +43,7 @@
 
 import androidx.slice.Slice;
 
+import java.util.Collection;
 import java.util.HashMap;
 
 /**
@@ -114,7 +117,190 @@
         assertThat(cachedData).isNull();
     }
 
+    @Test
+    public void getDescendantUris_fullActionUri_returnsSelf() {
+        final Uri uri = SliceBuilderUtils.getUri(
+                SettingsSlicesContract.PATH_SETTING_ACTION + "/key", true);
+
+        final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
+
+        assertThat(descendants).containsExactly(uri);
+    }
+
+    @Test
+    public void getDescendantUris_fullIntentUri_returnsSelf() {
+        final Uri uri = SliceBuilderUtils.getUri(
+                SettingsSlicesContract.PATH_SETTING_ACTION + "/key", true);
+
+        final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
+
+        assertThat(descendants).containsExactly(uri);
+    }
+
+    @Test
+    public void getDescendantUris_wrongPath_returnsEmpty() {
+        final Uri uri = SliceBuilderUtils.getUri("invalid_path", true);
+
+        final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
+
+        assertThat(descendants).isEmpty();
+    }
+
+    @Test
+    public void getDescendantUris_invalidPath_returnsEmpty() {
+        final String key = "platform_key";
+        insertSpecialCase(key, true /* isPlatformSlice */);
+        final Uri uri = new Uri.Builder()
+                .scheme(SCHEME_CONTENT)
+                .authority(SettingsSlicesContract.AUTHORITY)
+                .appendPath("invalid")
+                .build();
+
+        final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
+
+        assertThat(descendants).isEmpty();
+    }
+
+    @Test
+    public void getDescendantUris_platformSlice_doesNotReturnOEMSlice() {
+        insertSpecialCase("oem_key", false /* isPlatformSlice */);
+        final Uri uri = new Uri.Builder()
+                .scheme(SCHEME_CONTENT)
+                .authority(SettingsSlicesContract.AUTHORITY)
+                .build();
+
+        final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
+
+        assertThat(descendants).isEmpty();
+    }
+
+    @Test
+    public void getDescendantUris_oemSlice_doesNotReturnPlatformSlice() {
+        insertSpecialCase("platform_key", true /* isPlatformSlice */);
+        final Uri uri = new Uri.Builder()
+                .scheme(SCHEME_CONTENT)
+                .authority(SettingsSliceProvider.SLICE_AUTHORITY)
+                .build();
+
+        final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
+
+        assertThat(descendants).isEmpty();
+    }
+
+    @Test
+    public void getDescendantUris_oemSlice_returnsOEMUriDescendant() {
+        final String key = "oem_key";
+        insertSpecialCase(key, false /* isPlatformSlice */);
+        final Uri uri = new Uri.Builder()
+                .scheme(SCHEME_CONTENT)
+                .authority(SettingsSliceProvider.SLICE_AUTHORITY)
+                .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
+                .build();
+        final Uri expectedUri = new Uri.Builder()
+                .scheme(SCHEME_CONTENT)
+                .authority(SettingsSliceProvider.SLICE_AUTHORITY)
+                .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
+                .appendPath(key)
+                .build();
+
+        final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
+
+        assertThat(descendants).containsExactly(expectedUri);
+    }
+
+    @Test
+    public void getDescendantUris_oemSliceNoPath_returnsOEMUriDescendant() {
+        final String key = "oem_key";
+        insertSpecialCase(key, false /* isPlatformSlice */);
+        final Uri uri = new Uri.Builder()
+                .scheme(SCHEME_CONTENT)
+                .authority(SettingsSliceProvider.SLICE_AUTHORITY)
+                .build();
+        final Uri expectedUri = new Uri.Builder()
+                .scheme(SCHEME_CONTENT)
+                .authority(SettingsSliceProvider.SLICE_AUTHORITY)
+                .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
+                .appendPath(key)
+                .build();
+
+        final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
+
+        assertThat(descendants).containsExactly(expectedUri);
+    }
+
+    @Test
+    public void getDescendantUris_platformSlice_returnsPlatformUriDescendant() {
+        final String key = "platform_key";
+        insertSpecialCase(key, true /* isPlatformSlice */);
+        final Uri uri = new Uri.Builder()
+                .scheme(SCHEME_CONTENT)
+                .authority(SettingsSlicesContract.AUTHORITY)
+                .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
+                .build();
+        final Uri expectedUri = new Uri.Builder()
+                .scheme(SCHEME_CONTENT)
+                .authority(SettingsSlicesContract.AUTHORITY)
+                .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
+                .appendPath(key)
+                .build();
+
+        final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
+
+        assertThat(descendants).containsExactly(expectedUri);
+    }
+
+    @Test
+    public void getDescendantUris_platformSliceNoPath_returnsPlatformUriDescendant() {
+        final String key = "platform_key";
+        insertSpecialCase(key, true /* isPlatformSlice */);
+        final Uri uri = new Uri.Builder()
+                .scheme(SCHEME_CONTENT)
+                .authority(SettingsSlicesContract.AUTHORITY)
+                .build();
+        final Uri expectedUri = new Uri.Builder()
+                .scheme(SCHEME_CONTENT)
+                .authority(SettingsSlicesContract.AUTHORITY)
+                .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
+                .appendPath(key)
+                .build();
+
+        final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
+
+        assertThat(descendants).containsExactly(expectedUri);
+    }
+
+    @Test
+    public void getDescendantUris_noAuthorityNorPath_returnsAllUris() {
+        final String platformKey = "platform_key";
+        final String oemKey = "oemKey";
+        insertSpecialCase(platformKey, true /* isPlatformSlice */);
+        insertSpecialCase(oemKey, false /* isPlatformSlice */);
+        final Uri uri = new Uri.Builder()
+                .scheme(SCHEME_CONTENT)
+                .build();
+        final Uri expectedPlatformUri = new Uri.Builder()
+                .scheme(SCHEME_CONTENT)
+                .authority(SettingsSlicesContract.AUTHORITY)
+                .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
+                .appendPath(platformKey)
+                .build();
+        final Uri expectedOemUri = new Uri.Builder()
+                .scheme(SCHEME_CONTENT)
+                .authority(SettingsSliceProvider.SLICE_AUTHORITY)
+                .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
+                .appendPath(oemKey)
+                .build();
+
+        final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
+
+        assertThat(descendants).containsExactly(expectedPlatformUri, expectedOemUri);
+    }
+
     private void insertSpecialCase(String key) {
+        insertSpecialCase(key, true);
+    }
+
+    private void insertSpecialCase(String key, boolean isPlatformSlice) {
         ContentValues values = new ContentValues();
         values.put(SlicesDatabaseHelper.IndexColumns.KEY, key);
         values.put(SlicesDatabaseHelper.IndexColumns.TITLE, TITLE);
@@ -123,6 +309,8 @@
         values.put(SlicesDatabaseHelper.IndexColumns.ICON_RESOURCE, 1234);
         values.put(SlicesDatabaseHelper.IndexColumns.FRAGMENT, "test");
         values.put(SlicesDatabaseHelper.IndexColumns.CONTROLLER, "test");
+        values.put(SlicesDatabaseHelper.IndexColumns.PLATFORM_SLICE, isPlatformSlice);
+        values.put(SlicesDatabaseHelper.IndexColumns.SLICE_TYPE, SliceData.SliceType.INTENT);
 
         mDb.replaceOrThrow(SlicesDatabaseHelper.Tables.TABLE_SLICES_INDEX, null, values);
     }
diff --git a/tests/robotests/src/com/android/settings/slices/SliceBuilderUtilsTest.java b/tests/robotests/src/com/android/settings/slices/SliceBuilderUtilsTest.java
index 0672c86..8b99c4a 100644
--- a/tests/robotests/src/com/android/settings/slices/SliceBuilderUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SliceBuilderUtilsTest.java
@@ -295,14 +295,16 @@
         assertThat(pathPair.second).isEqualTo(KEY);
     }
 
-    @Test(expected = IllegalArgumentException.class)
+    @Test
     public void getPathData_noKey_returnsNull() {
         final Uri uri = new Uri.Builder()
                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
                 .build();
 
-        SliceBuilderUtils.getPathData(uri);
+        final Pair<Boolean, String> pathPair = SliceBuilderUtils.getPathData(uri);
+
+        assertThat(pathPair).isNull();
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/slices/SlicesDatabaseAccessorTest.java b/tests/robotests/src/com/android/settings/slices/SlicesDatabaseAccessorTest.java
index 77c9891..45ec064 100644
--- a/tests/robotests/src/com/android/settings/slices/SlicesDatabaseAccessorTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SlicesDatabaseAccessorTest.java
@@ -18,6 +18,7 @@
 package com.android.settings.slices;
 
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Mockito.spy;
 
 import android.content.ContentValues;
@@ -35,15 +36,17 @@
 import org.junit.runner.RunWith;
 import org.robolectric.RuntimeEnvironment;
 
+import java.util.List;
+
 @RunWith(SettingsRobolectricTestRunner.class)
 public class SlicesDatabaseAccessorTest {
 
-    private final String fakeTitle = "title";
-    private final String fakeSummary = "summary";
-    private final String fakeScreenTitle = "screen_title";
-    private final int fakeIcon = 1234;
-    private final String fakeFragmentClassName = FakeIndexProvider.class.getName();
-    private final String fakeControllerName = FakePreferenceController.class.getName();
+    private final String FAKE_TITLE = "title";
+    private final String FAKE_SUMMARY = "summary";
+    private final String FAKE_SCREEN_TITLE = "screen_title";
+    private final int FAKE_ICON = 1234;
+    private final String FAKE_FRAGMENT_NAME = FakeIndexProvider.class.getName();
+    private final String FAKE_CONTROLLER_NAME = FakePreferenceController.class.getName();
 
     private Context mContext;
     private SQLiteDatabase mDb;
@@ -70,13 +73,13 @@
         SliceData data = mAccessor.getSliceDataFromKey(key);
 
         assertThat(data.getKey()).isEqualTo(key);
-        assertThat(data.getTitle()).isEqualTo(fakeTitle);
-        assertThat(data.getSummary()).isEqualTo(fakeSummary);
-        assertThat(data.getScreenTitle()).isEqualTo(fakeScreenTitle);
-        assertThat(data.getIconResource()).isEqualTo(fakeIcon);
-        assertThat(data.getFragmentClassName()).isEqualTo(fakeFragmentClassName);
+        assertThat(data.getTitle()).isEqualTo(FAKE_TITLE);
+        assertThat(data.getSummary()).isEqualTo(FAKE_SUMMARY);
+        assertThat(data.getScreenTitle()).isEqualTo(FAKE_SCREEN_TITLE);
+        assertThat(data.getIconResource()).isEqualTo(FAKE_ICON);
+        assertThat(data.getFragmentClassName()).isEqualTo(FAKE_FRAGMENT_NAME);
         assertThat(data.getUri()).isNull();
-        assertThat(data.getPreferenceController()).isEqualTo(fakeControllerName);
+        assertThat(data.getPreferenceController()).isEqualTo(FAKE_CONTROLLER_NAME);
     }
 
     @Test(expected = IllegalStateException.class)
@@ -96,13 +99,13 @@
         SliceData data = mAccessor.getSliceDataFromUri(uri);
 
         assertThat(data.getKey()).isEqualTo(key);
-        assertThat(data.getTitle()).isEqualTo(fakeTitle);
-        assertThat(data.getSummary()).isEqualTo(fakeSummary);
-        assertThat(data.getScreenTitle()).isEqualTo(fakeScreenTitle);
-        assertThat(data.getIconResource()).isEqualTo(fakeIcon);
-        assertThat(data.getFragmentClassName()).isEqualTo(fakeFragmentClassName);
+        assertThat(data.getTitle()).isEqualTo(FAKE_TITLE);
+        assertThat(data.getSummary()).isEqualTo(FAKE_SUMMARY);
+        assertThat(data.getScreenTitle()).isEqualTo(FAKE_SCREEN_TITLE);
+        assertThat(data.getIconResource()).isEqualTo(FAKE_ICON);
+        assertThat(data.getFragmentClassName()).isEqualTo(FAKE_FRAGMENT_NAME);
         assertThat(data.getUri()).isEqualTo(uri);
-        assertThat(data.getPreferenceController()).isEqualTo(fakeControllerName);
+        assertThat(data.getPreferenceController()).isEqualTo(FAKE_CONTROLLER_NAME);
     }
 
     @Test(expected = IllegalStateException.class)
@@ -111,15 +114,62 @@
         mAccessor.getSliceDataFromUri(uri);
     }
 
+    @Test
+    public void getDescendantUris_platformSlice_doesNotReturnOEMSlice() {
+        final String key = "oem_key";
+        final boolean isPlatformSlice = false;
+        insertSpecialCase(key, isPlatformSlice);
+        final List<String> keys = mAccessor.getSliceKeys(!isPlatformSlice);
+
+        assertThat(keys).isEmpty();
+    }
+
+    @Test
+    public void getDescendantUris_oemSlice_doesNotReturnPlatformSlice() {
+        final String key = "platform_key";
+        final boolean isPlatformSlice = true;
+        insertSpecialCase(key, isPlatformSlice);
+        final List<String> keys = mAccessor.getSliceKeys(!isPlatformSlice);
+
+        assertThat(keys).isEmpty();
+    }
+
+    @Test
+    public void getDescendantUris_oemSlice_returnsOEMUriDescendant() {
+        final String key = "oem_key";
+        final boolean isPlatformSlice = false;
+        insertSpecialCase(key, isPlatformSlice);
+        final List<String> keys = mAccessor.getSliceKeys(isPlatformSlice);
+
+        assertThat(keys).containsExactly(key);
+    }
+
+    @Test
+    public void getDescendantUris_platformSlice_returnsPlatformUriDescendant() {
+        final String key = "platform_key";
+        final boolean isPlatformSlice = true;
+        insertSpecialCase(key, isPlatformSlice);
+        final List<String> keys = mAccessor.getSliceKeys(isPlatformSlice);
+
+        assertThat(keys).containsExactly(key);
+    }
+
     private void insertSpecialCase(String key) {
+        insertSpecialCase(key, true);
+    }
+
+    private void insertSpecialCase(String key, boolean isPlatformSlice) {
         ContentValues values = new ContentValues();
         values.put(SlicesDatabaseHelper.IndexColumns.KEY, key);
-        values.put(SlicesDatabaseHelper.IndexColumns.TITLE, fakeTitle);
-        values.put(SlicesDatabaseHelper.IndexColumns.SUMMARY, fakeSummary);
-        values.put(SlicesDatabaseHelper.IndexColumns.SCREENTITLE, fakeScreenTitle);
-        values.put(SlicesDatabaseHelper.IndexColumns.ICON_RESOURCE, fakeIcon);
-        values.put(SlicesDatabaseHelper.IndexColumns.FRAGMENT, fakeFragmentClassName);
-        values.put(SlicesDatabaseHelper.IndexColumns.CONTROLLER, fakeControllerName);
+        values.put(SlicesDatabaseHelper.IndexColumns.TITLE, FAKE_TITLE);
+        values.put(SlicesDatabaseHelper.IndexColumns.SUMMARY, FAKE_SUMMARY);
+        values.put(SlicesDatabaseHelper.IndexColumns.SCREENTITLE, FAKE_SCREEN_TITLE);
+        values.put(SlicesDatabaseHelper.IndexColumns.ICON_RESOURCE, FAKE_ICON);
+        values.put(SlicesDatabaseHelper.IndexColumns.FRAGMENT, FAKE_FRAGMENT_NAME);
+        values.put(SlicesDatabaseHelper.IndexColumns.CONTROLLER, FAKE_CONTROLLER_NAME);
+        values.put(SlicesDatabaseHelper.IndexColumns.PLATFORM_SLICE, isPlatformSlice);
+        values.put(SlicesDatabaseHelper.IndexColumns.SLICE_TYPE, SliceData.SliceType.INTENT);
+
 
         mDb.replaceOrThrow(SlicesDatabaseHelper.Tables.TABLE_SLICES_INDEX, null, values);
     }