Merge "Update airplane mode pref key so it's sliceable."
diff --git a/src/com/android/settings/notification/ZenRulePreference.java b/src/com/android/settings/notification/ZenRulePreference.java
index 60e4bde..1be5857 100644
--- a/src/com/android/settings/notification/ZenRulePreference.java
+++ b/src/com/android/settings/notification/ZenRulePreference.java
@@ -113,7 +113,7 @@
 
     protected void setAttributes(AutomaticZenRule rule) {
         final boolean isSchedule = ZenModeConfig.isValidScheduleConditionId(
-                rule.getConditionId());
+                rule.getConditionId(), true);
         final boolean isEvent = ZenModeConfig.isValidEventConditionId(rule.getConditionId());
         final boolean isSystemRule = isSchedule || isEvent;
 
diff --git a/src/com/android/settings/slices/CustomSliceManager.java b/src/com/android/settings/slices/CustomSliceManager.java
new file mode 100644
index 0000000..f3df3c1
--- /dev/null
+++ b/src/com/android/settings/slices/CustomSliceManager.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.slices;
+
+import android.content.Context;
+import android.net.Uri;
+import android.util.ArrayMap;
+
+import java.util.Map;
+
+/**
+ * Manages custom {@link androidx.slice.Slice Slices}, which are all Slices not backed by
+ * preferences.
+ * <p>
+ *     By default, all Slices in Settings should be built by a
+ * </p>
+ */
+public class CustomSliceManager {
+
+    protected final Map<Uri, Class<? extends CustomSliceable>> mUriMap;
+
+    private final Context mContext;
+
+    public CustomSliceManager(Context context) {
+        mContext = context;
+        mUriMap = new ArrayMap<>();
+        addSlices();
+    }
+
+    /**
+     * Return a {@link CustomSliceable} associated to the Uri.
+     * <p>
+     * Do not change this method signature to accommodate for a special-case slicable - a context is
+     * the only thing that should be needed to create the object.
+     */
+    public CustomSliceable getSliceableFromUri(Uri uri) {
+        final Class clazz = mUriMap.get(uri);
+
+        if (clazz == null) {
+            throw new IllegalArgumentException("No Slice found for uri: " + uri);
+        }
+
+        return CustomSliceable.createInstance(mContext, clazz);
+    }
+
+    /**
+     * Return a {@link CustomSliceable} associated to the Action.
+     * <p>
+     * Do not change this method signature to accommodate for a special-case sliceable - a context
+     * is the only thing that should be needed to create the object.
+     */
+    public CustomSliceable getSliceableFromIntentAction(String action) {
+        return getSliceableFromUri(Uri.parse(action));
+    }
+
+    /**
+     * Returns {@code true} if {@param uri} is a valid Slice Uri handled by
+     * {@link CustomSliceManager}.
+     */
+    public boolean isValidUri(Uri uri) {
+        return mUriMap.containsKey(uri);
+    }
+
+    /**
+     * Returns {@code true} if {@param action} is a valid intent action handled by
+     * {@link CustomSliceManager}.
+     */
+    public boolean isValidAction(String action) {
+        return isValidUri(Uri.parse(action));
+    }
+
+    private void addSlices() {
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/slices/CustomSliceable.java b/src/com/android/settings/slices/CustomSliceable.java
new file mode 100644
index 0000000..afe7170
--- /dev/null
+++ b/src/com/android/settings/slices/CustomSliceable.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.slices;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+import androidx.slice.Slice;
+
+
+/**
+ * Common functions for custom Slices.
+ * <p>
+ *     A template for all Settings slices which are not represented by a Preference. By
+ *     standardizing the methods used by the Slice helpers, we can use generically take actions
+ *     rather than maintaining a list of all of the custom slices every time we reference Slices in
+ *     Settings.
+ * <p>
+ *     By default, all Slices in Settings should be built through Preference Controllers extending
+ *     {@link com.android.settings.core.BasePreferenceController}, which are automatically piped
+ *     into Settings-Slices infrastructure. Cases where you should implement this interface are:
+ *     <ul>
+ *         <li>Multi-line slices</li>
+ *         <li>Slices that don't exist in the UI</li>
+ *         <li>Preferences that use a supported component, like a Switch Bar</li>
+ *     </ul>
+ * <p>
+ *      Note that if your UI is supported because the Preference is not backed by a
+ *      {@link com.android.settings.dashboard.DashboardFragment}, then you should first convert the
+ *      existing fragment into a dashboard fragment, and then extend
+ *      {@link com.android.settings.core.BasePreferenceController}.
+ * <p>
+ *     If you implement this interface, you should add your Slice to {@link CustomSliceManager}.
+ */
+public interface CustomSliceable {
+
+    /**
+     * @return an complete instance of the {@link Slice}.
+     */
+    Slice getSlice(Context context);
+
+    /**
+     * @return a {@link android.content.ContentResolver#SCHEME_CONTENT content} {@link Uri} which
+     * backs the {@link Slice} returned by {@link #getSlice(Context)}.
+     */
+    Uri getUri();
+
+    /**
+     * Handles the actions sent by the {@link Intent intents} bound to the {@link Slice} returned by
+     * {@link #getSlice(Context)}.
+     *
+     * @param intent which has the action taken on a {@link Slice}.
+     */
+    void onNotifyChange(Intent intent);
+
+    /**
+     * Settings Slices which can represent components that are updatable by the framework should
+     * listen to changes matched to the {@link IntentFilter} returned here.
+     *
+     * @return an {@link IntentFilter} for updates related to the {@link Slice} returned by
+     * {@link #getSlice(Context)}.
+     */
+    default IntentFilter getIntentFilter() {
+        return null;
+    }
+
+    /**
+     * Build an instance of a {@link CustomSliceable} which has a {@link Context}-only constructor.
+     */
+    static CustomSliceable createInstance(Context context, Class<CustomSliceable> sliceableClass) {
+        try {
+            //final Class<CustomSliceable> clazz = Class.forName(sliceableClassName);
+            final Constructor<CustomSliceable> sliceable =
+                    sliceableClass.getConstructor(Context.class);
+            final Object[] params = new Object[]{context};
+            return sliceable.newInstance(params);
+        } catch (NoSuchMethodException | InstantiationException |
+                IllegalArgumentException | InvocationTargetException | IllegalAccessException e) {
+            throw new IllegalStateException(
+                    "Invalid sliceable class: " + sliceableClass, e);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java
index 9cde91a..da705c9 100644
--- a/src/com/android/settings/slices/SettingsSliceProvider.java
+++ b/src/com/android/settings/slices/SettingsSliceProvider.java
@@ -32,15 +32,15 @@
 import android.util.Log;
 import android.util.Pair;
 
-import com.android.settings.bluetooth.BluetoothSliceBuilder;
-import com.android.settings.core.BasePreferenceController;
 import com.android.settings.flashlight.FlashlightSliceBuilder;
 import com.android.settings.location.LocationSliceBuilder;
 import com.android.settings.mobilenetwork.Enhanced4gLteSliceHelper;
 import com.android.settings.notification.ZenModeSliceBuilder;
 import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.core.BasePreferenceController;
 import com.android.settings.wifi.WifiSliceBuilder;
 import com.android.settings.wifi.calling.WifiCallingSliceHelper;
+import com.android.settings.bluetooth.BluetoothSliceBuilder;
 import com.android.settingslib.SliceBroadcastRelay;
 import com.android.settingslib.utils.ThreadUtils;
 
@@ -114,10 +114,14 @@
             "com.android.settings.slice.extra.platform";
 
     @VisibleForTesting
+    CustomSliceManager mCustomSliceManager;
+
+    @VisibleForTesting
     SlicesDatabaseAccessor mSlicesDatabaseAccessor;
 
     @VisibleForTesting
     Map<Uri, SliceData> mSliceWeakDataCache;
+
     @VisibleForTesting
     Map<Uri, SliceData> mSliceDataCache;
 
@@ -135,6 +139,8 @@
         mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(getContext());
         mSliceDataCache = new ConcurrentHashMap<>();
         mSliceWeakDataCache = new WeakHashMap<>();
+        mCustomSliceManager = FeatureFactory.getFactory(
+                getContext()).getSlicesFeatureProvider().getCustomSliceManager(getContext());
         return true;
     }
 
@@ -151,6 +157,15 @@
 
     @Override
     public void onSlicePinned(Uri sliceUri) {
+        if (mCustomSliceManager.isValidUri(sliceUri)) {
+            final CustomSliceable sliceable = mCustomSliceManager.getSliceableFromUri(sliceUri);
+            final IntentFilter filter = sliceable.getIntentFilter();
+            if (filter != null) {
+                registerIntentToUri(filter, sliceUri);
+            }
+            return;
+        }
+
         if (WifiSliceBuilder.WIFI_URI.equals(sliceUri)) {
             registerIntentToUri(WifiSliceBuilder.INTENT_FILTER, sliceUri);
             mRegisteredUris.add(sliceUri);
@@ -162,7 +177,7 @@
             registerIntentToUri(BluetoothSliceBuilder.INTENT_FILTER, sliceUri);
             return;
         } else if (FlashlightSliceBuilder.FLASHLIGHT_URI.equals(sliceUri)) {
-            registerIntentToUri(FlashlightSliceBuilder.INTENT_FILTER , sliceUri);
+            registerIntentToUri(FlashlightSliceBuilder.INTENT_FILTER, sliceUri);
             mRegisteredUris.add(sliceUri);
             return;
         }
@@ -197,8 +212,14 @@
                 return null;
             }
 
-            // If adding a new Slice, do not directly match Slice URIs.
-            // Use {@link SlicesDatabaseAccessor}.
+            // Before adding a slice to {@link CustomSliceManager}, please get approval
+            // from the Settings team.
+            if (mCustomSliceManager.isValidUri(sliceUri)) {
+                final CustomSliceable sliceable = mCustomSliceManager.getSliceableFromUri(
+                        sliceUri);
+                return sliceable.getSlice(getContext());
+            }
+
             if (WifiCallingSliceHelper.WIFI_CALLING_URI.equals(sliceUri)) {
                 return FeatureFactory.getFactory(getContext())
                         .getSlicesFeatureProvider()
@@ -433,4 +454,4 @@
         }
         return new String[0];
     }
-}
+}
\ No newline at end of file
diff --git a/src/com/android/settings/slices/SliceBroadcastReceiver.java b/src/com/android/settings/slices/SliceBroadcastReceiver.java
index 7d50410..a44a2cd 100644
--- a/src/com/android/settings/slices/SliceBroadcastReceiver.java
+++ b/src/com/android/settings/slices/SliceBroadcastReceiver.java
@@ -71,6 +71,15 @@
         final boolean isPlatformSlice = intent.getBooleanExtra(EXTRA_SLICE_PLATFORM_DEFINED,
                 false /* default */);
 
+        final CustomSliceManager mCustomSliceManager = FeatureFactory.getFactory(
+                context).getSlicesFeatureProvider().getCustomSliceManager(context);
+        if (mCustomSliceManager.isValidAction(action)) {
+            final CustomSliceable sliceable =
+                    mCustomSliceManager.getSliceableFromIntentAction(action);
+            sliceable.onNotifyChange(intent);
+            return;
+        }
+
         switch (action) {
             case ACTION_TOGGLE_CHANGED:
                 final boolean isChecked = intent.getBooleanExtra(Slice.EXTRA_TOGGLE_STATE, false);
diff --git a/src/com/android/settings/slices/SlicesFeatureProvider.java b/src/com/android/settings/slices/SlicesFeatureProvider.java
index 8395a58..5940aa4 100644
--- a/src/com/android/settings/slices/SlicesFeatureProvider.java
+++ b/src/com/android/settings/slices/SlicesFeatureProvider.java
@@ -28,6 +28,8 @@
      */
     void indexSliceData(Context context);
 
+    CustomSliceManager getCustomSliceManager(Context context);
+
     /**
      * Gets new WifiCallingSliceHelper object
      */
diff --git a/src/com/android/settings/slices/SlicesFeatureProviderImpl.java b/src/com/android/settings/slices/SlicesFeatureProviderImpl.java
index 988bcfe..fc2298c 100644
--- a/src/com/android/settings/slices/SlicesFeatureProviderImpl.java
+++ b/src/com/android/settings/slices/SlicesFeatureProviderImpl.java
@@ -13,24 +13,33 @@
 
     private SlicesIndexer mSlicesIndexer;
     private SliceDataConverter mSliceDataConverter;
+    private CustomSliceManager mCustomSliceManager;
 
     @Override
     public SlicesIndexer getSliceIndexer(Context context) {
         if (mSlicesIndexer == null) {
-            mSlicesIndexer = new SlicesIndexer(context);
+            mSlicesIndexer = new SlicesIndexer(context.getApplicationContext());
         }
         return mSlicesIndexer;
     }
 
     @Override
     public SliceDataConverter getSliceDataConverter(Context context) {
-        if(mSliceDataConverter == null) {
+        if (mSliceDataConverter == null) {
             mSliceDataConverter = new SliceDataConverter(context.getApplicationContext());
         }
         return mSliceDataConverter;
     }
 
     @Override
+    public CustomSliceManager getCustomSliceManager(Context context) {
+        if (mCustomSliceManager == null) {
+            mCustomSliceManager = new CustomSliceManager(context.getApplicationContext());
+        }
+        return mCustomSliceManager;
+    }
+
+    @Override
     public void indexSliceDataAsync(Context context) {
         SlicesIndexer indexer = getSliceIndexer(context);
         ThreadUtils.postOnBackgroundThread(indexer);
diff --git a/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java
index d8e447d..b6eb603 100644
--- a/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java
@@ -354,7 +354,7 @@
                 mActivity, null, null, null);
 
         final SubscriptionInfo subInfo = new SubscriptionInfo(0, "123456", 0, "name", "carrier",
-                0, 0, "number", 0, null, 123, 456, "ZX");
+                0, 0, "number", 0, null, "123", "456", "ZX");
         when(mSubscriptionManager.getDefaultDataSubscriptionInfo()).thenReturn(subInfo);
         assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
     }
diff --git a/tests/robotests/src/com/android/settings/mobilenetwork/Enhanced4gLteSliceHelperTest.java b/tests/robotests/src/com/android/settings/mobilenetwork/Enhanced4gLteSliceHelperTest.java
index 9c719cd..434a89d 100644
--- a/tests/robotests/src/com/android/settings/mobilenetwork/Enhanced4gLteSliceHelperTest.java
+++ b/tests/robotests/src/com/android/settings/mobilenetwork/Enhanced4gLteSliceHelperTest.java
@@ -20,6 +20,7 @@
 import static android.app.slice.Slice.HINT_TITLE;
 import static android.app.slice.SliceItem.FORMAT_TEXT;
 import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
@@ -37,6 +38,7 @@
 import com.android.settings.slices.SettingsSliceProvider;
 import com.android.settings.slices.SliceBroadcastReceiver;
 import com.android.settings.slices.SlicesFeatureProvider;
+import com.android.settings.slices.CustomSliceManager;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
@@ -61,13 +63,13 @@
 @RunWith(SettingsRobolectricTestRunner.class)
 public class Enhanced4gLteSliceHelperTest {
 
-    private Context mContext;
     @Mock
     private CarrierConfigManager mMockCarrierConfigManager;
 
     @Mock
     private ImsManager mMockImsManager;
 
+    private Context mContext;
     private FakeEnhanced4gLteSliceHelper mEnhanced4gLteSliceHelper;
     private SettingsSliceProvider mProvider;
     private SliceBroadcastReceiver mReceiver;
@@ -79,16 +81,20 @@
         MockitoAnnotations.initMocks(this);
         mContext = spy(RuntimeEnvironment.application);
 
+        mFeatureFactory = FakeFeatureFactory.setupForTest();
+        mSlicesFeatureProvider = mFeatureFactory.getSlicesFeatureProvider();
+
+        when(mSlicesFeatureProvider.getCustomSliceManager(any(Context.class)))
+                .thenReturn(new CustomSliceManager(mContext));
+
         //setup for SettingsSliceProvider tests
         mProvider = spy(new SettingsSliceProvider());
         doReturn(mContext).when(mProvider).getContext();
+        mProvider.onCreateSliceProvider();
 
         //setup for SliceBroadcastReceiver test
         mReceiver = spy(new SliceBroadcastReceiver());
 
-        mFeatureFactory = FakeFeatureFactory.setupForTest();
-        mSlicesFeatureProvider = mFeatureFactory.getSlicesFeatureProvider();
-
         // Prevent crash in SliceMetadata.
         Resources resources = spy(mContext.getResources());
         doReturn(60).when(resources).getDimensionPixelSize(anyInt());
diff --git a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
index c72e9f6..ea2f2ca 100644
--- a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
@@ -109,6 +109,7 @@
         mProvider.mSliceWeakDataCache = new HashMap<>();
         mProvider.mSliceDataCache = new HashMap<>();
         mProvider.mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(mContext);
+        mProvider.mCustomSliceManager = new CustomSliceManager(mContext);
         when(mProvider.getContext()).thenReturn(mContext);
 
         mDb = SlicesDatabaseHelper.getInstance(mContext).getWritableDatabase();
diff --git a/tests/robotests/src/com/android/settings/slices/SliceBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/slices/SliceBroadcastReceiverTest.java
index d578348..955cdf1 100644
--- a/tests/robotests/src/com/android/settings/slices/SliceBroadcastReceiverTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SliceBroadcastReceiverTest.java
@@ -18,12 +18,15 @@
 package com.android.settings.slices;
 
 import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.app.slice.Slice;
 import android.content.ContentResolver;
@@ -82,6 +85,8 @@
         mSearchFeatureProvider = new SearchFeatureProviderImpl();
         mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
         mFakeFeatureFactory.searchFeatureProvider = mSearchFeatureProvider;
+        when(mFakeFeatureFactory.slicesFeatureProvider.getCustomSliceManager(any()))
+                .thenReturn(new CustomSliceManager(mContext));
         mLoggingNameArgumentCatpor = ArgumentCaptor.forClass(Pair.class);
         mLoggingValueArgumentCatpor = ArgumentCaptor.forClass(Pair.class);
     }
diff --git a/tests/robotests/src/com/android/settings/slices/SpecialCaseSliceManagerTest.java b/tests/robotests/src/com/android/settings/slices/SpecialCaseSliceManagerTest.java
new file mode 100644
index 0000000..ab226f9
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/slices/SpecialCaseSliceManagerTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.settings.slices;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.provider.SettingsSlicesContract;
+
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+
+import androidx.slice.Slice;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class SpecialCaseSliceManagerTest {
+
+    private Context mContext;
+
+    private CustomSliceManager mCustomSliceManager;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+        mCustomSliceManager = spy(new CustomSliceManager(mContext));
+        mCustomSliceManager.mUriMap.clear();
+        mCustomSliceManager.mUriMap.put(FakeSliceable.URI, FakeSliceable.class);
+    }
+
+    @Test
+    public void getSliceableFromUri_returnsCorrectObject() {
+        final CustomSliceable sliceable = mCustomSliceManager.getSliceableFromUri(
+                FakeSliceable.URI);
+
+        assertThat(sliceable).isInstanceOf(FakeSliceable.class);
+    }
+
+    @Test
+    public void getSliceableFromIntentAction_returnsCorrectObject() {
+        final CustomSliceable sliceable =
+                mCustomSliceManager.getSliceableFromIntentAction(FakeSliceable.URI.toString());
+
+        assertThat(sliceable).isInstanceOf(FakeSliceable.class);
+    }
+
+    @Test
+    public void isValidUri_validUri_returnsTrue() {
+        final boolean isValidUri = mCustomSliceManager.isValidUri(FakeSliceable.URI);
+
+        assertThat(isValidUri).isTrue();
+    }
+
+    @Test
+    public void isValidUri_invalidUri_returnsFalse() {
+        final boolean isValidUri = mCustomSliceManager.isValidUri(null);
+
+        assertThat(isValidUri).isFalse();
+    }
+
+    @Test
+    public void isValidAction_validActions_returnsTrue() {
+        final boolean isValidAction =
+                mCustomSliceManager.isValidAction(FakeSliceable.URI.toString());
+
+        assertThat(isValidAction).isTrue();
+    }
+
+    @Test
+    public void isValidAction_invalidAction_returnsFalse() {
+        final boolean isValidAction = mCustomSliceManager.isValidAction("action");
+
+        assertThat(isValidAction).isFalse();
+    }
+
+    static class FakeSliceable implements CustomSliceable {
+
+        static final String KEY = "magic key of khazad dum";
+
+        static final Uri URI = new Uri.Builder()
+                .scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(SettingsSliceProvider.SLICE_AUTHORITY)
+                .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
+                .appendPath(KEY)
+                .build();
+
+        static final Slice SLICE = new Slice.Builder(URI).build();
+
+        static boolean backingData = false;
+
+        public FakeSliceable(Context context) {}
+
+        @Override
+        public Slice getSlice(Context context) {
+            return SLICE;
+        }
+
+        @Override
+        public Uri getUri() {
+            return URI;
+        }
+
+        @Override
+        public void onNotifyChange(Intent intent) {
+            backingData = !backingData;
+        }
+
+        @Override
+        public IntentFilter getIntentFilter() {
+            return new IntentFilter();
+        }
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java
index cdc8ecb..c9c9abb 100644
--- a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java
@@ -20,6 +20,8 @@
 import static android.app.slice.Slice.HINT_TITLE;
 import static android.app.slice.SliceItem.FORMAT_TEXT;
 import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
@@ -40,6 +42,7 @@
 import com.android.settings.slices.SliceBroadcastReceiver;
 import com.android.settings.slices.SliceData;
 import com.android.settings.slices.SlicesFeatureProvider;
+import com.android.settings.slices.CustomSliceManager;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
@@ -88,12 +91,15 @@
         //setup for SettingsSliceProvider tests
         mProvider = spy(new SettingsSliceProvider());
         doReturn(mContext).when(mProvider).getContext();
+        mProvider.onCreateSliceProvider();
 
         //setup for SliceBroadcastReceiver test
         mReceiver = spy(new SliceBroadcastReceiver());
 
         mFeatureFactory = FakeFeatureFactory.setupForTest();
         mSlicesFeatureProvider = mFeatureFactory.getSlicesFeatureProvider();
+        when(mSlicesFeatureProvider.getCustomSliceManager(any(Context.class)))
+                .thenReturn(new CustomSliceManager(mContext));
 
         // Prevent crash in SliceMetadata.
         Resources resources = spy(mContext.getResources());