Add Slices for Enhanced 4G LTE

Add Slices for Enhanced 4G LTE
Enhanced 4G LTE Slice Provider:
Create slice to display appropriate message with further instructions
Enhanced 4G LTE Slice Broadcast Reciver:
1. Change the setting via ImsManager
2. Ask to requery the slice in one second to display updated settings if 1 is valid or display appropriate message

Bug: 79270171
Test: Robotests
Test: Use support-slices-demos-debug.apk to test on device
Change-Id: I48d412de94d5d9f1ad42a299691ec5cf8001c6a1
diff --git a/src/com/android/settings/mobilenetwork/Enhanced4gLteSliceHelper.java b/src/com/android/settings/mobilenetwork/Enhanced4gLteSliceHelper.java
new file mode 100644
index 0000000..de542ac
--- /dev/null
+++ b/src/com/android/settings/mobilenetwork/Enhanced4gLteSliceHelper.java
@@ -0,0 +1,295 @@
+/*
+ * 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.mobilenetwork;
+
+import static android.app.slice.Slice.EXTRA_TOGGLE_STATE;
+
+import android.app.PendingIntent;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.PersistableBundle;
+import androidx.core.graphics.drawable.IconCompat;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.util.Log;
+
+import androidx.slice.Slice;
+import androidx.slice.builders.ListBuilder;
+import androidx.slice.builders.SliceAction;
+
+import com.android.ims.ImsManager;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.slices.SettingsSliceProvider;
+import com.android.settings.slices.SliceBroadcastReceiver;
+import com.android.settings.slices.SliceBuilderUtils;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Helper class to control slices for enhanced 4g LTE settings.
+ */
+public class Enhanced4gLteSliceHelper {
+
+    private static final String TAG = "Enhanced4gLteSliceHelper";
+
+    /**
+     * Settings slice path to enhanced 4g LTE setting.
+     */
+    public static final String PATH_ENHANCED_4G_LTE = "enhanced_4g_lte";
+
+    /**
+     * Action passed for changes to enhanced 4g LTE slice (toggle).
+     */
+    public static final String ACTION_ENHANCED_4G_LTE_CHANGED =
+            "com.android.settings.mobilenetwork.action.ENHANCED_4G_LTE_CHANGED";
+
+    /**
+     * Slice Uri for Enhanced 4G slice
+     */
+    public static final Uri SLICE_URI = new Uri.Builder()
+            .scheme(ContentResolver.SCHEME_CONTENT)
+            .authority(SettingsSliceProvider.SLICE_AUTHORITY)
+            .appendPath(PATH_ENHANCED_4G_LTE)
+            .build();
+    /**
+     * Action for mobile network settings activity which
+     * allows setting configuration for Enhanced 4G LTE
+     * related settings
+     */
+    public static final String ACTION_MOBILE_NETWORK_SETTINGS_ACTIVITY =
+            "android.settings.NETWORK_OPERATOR_SETTINGS";
+
+    /**
+     * Timeout for querying enhanced 4g lte setting from ims manager.
+     */
+    private static final int TIMEOUT_MILLIS = 2000;
+
+    private final Context mContext;
+
+    /**
+     * Phone package name
+     */
+    private static final String PACKAGE_PHONE = "com.android.phone";
+
+    /**
+     * String resource type
+     */
+    private static final String RESOURCE_TYPE_STRING = "string";
+
+    /**
+     * Enhanced 4g lte mode title variant resource name
+     */
+    private static final String RESOURCE_ENHANCED_4G_LTE_MODE_TITLE_VARIANT =
+          "enhanced_4g_lte_mode_title_variant";
+
+    @VisibleForTesting
+    public Enhanced4gLteSliceHelper(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Returns Slice object for enhanced_4g_lte settings.
+     *
+     * If enhanced 4g LTE is not supported for the current carrier, this method will return slice
+     * with not supported message.
+     *
+     * If enhanced 4g LTE is not editable for the current carrier, this method will return slice
+     * with not editable message.
+     *
+     * If enhanced 4g LTE setting can be changed, this method will return the slice to toggle
+     * enhanced 4g LTE option with ACTION_ENHANCED_4G_LTE_CHANGED as endItem.
+     */
+    public Slice createEnhanced4gLteSlice(Uri sliceUri) {
+        final int subId = getDefaultVoiceSubId();
+
+        if (subId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            Log.d(TAG, "Invalid subscription Id");
+            return null;
+        }
+
+        final ImsManager imsManager = getImsManager(subId);
+
+        if (!imsManager.isVolteEnabledByPlatform()
+                || !imsManager.isVolteProvisionedOnDevice()) {
+            Log.d(TAG, "Setting is either not provisioned or not enabled by Platform");
+            return null;
+        }
+
+        if (isCarrierConfigManagerKeyEnabled(
+                CarrierConfigManager.KEY_HIDE_ENHANCED_4G_LTE_BOOL, subId, false)
+                || !isCarrierConfigManagerKeyEnabled(
+                CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL, subId,
+                true)) {
+            Log.d(TAG, "Setting is either hidden or not editable");
+            return null;
+        }
+
+        try {
+            return getEnhanced4gLteSlice(sliceUri,
+                    isEnhanced4gLteModeEnabled(imsManager), subId);
+        } catch (InterruptedException | TimeoutException | ExecutionException e) {
+            Log.e(TAG, "Unable to read the current Enhanced 4g LTE status", e);
+            return null;
+        }
+    }
+
+    private boolean isEnhanced4gLteModeEnabled(ImsManager imsManager)
+            throws InterruptedException, ExecutionException, TimeoutException {
+        final FutureTask<Boolean> isEnhanced4gLteOnTask = new FutureTask<>(new Callable<Boolean>() {
+            @Override
+            public Boolean call() {
+                return imsManager.isEnhanced4gLteModeSettingEnabledByUser();
+            }
+        });
+        final ExecutorService executor = Executors.newSingleThreadExecutor();
+        executor.execute(isEnhanced4gLteOnTask);
+
+        return isEnhanced4gLteOnTask.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+    }
+
+    /**
+     * Builds a toggle slice where the intent takes you to the Enhanced 4G LTE page and the toggle
+     * enables/disables Enhanced 4G LTE mode setting.
+     */
+    private Slice getEnhanced4gLteSlice(Uri sliceUri, boolean isEnhanced4gLteEnabled, int subId) {
+        final IconCompat icon = IconCompat.createWithResource(mContext,
+                R.drawable.ic_launcher_settings);
+
+        return new ListBuilder(mContext, sliceUri, ListBuilder.INFINITY)
+                .setAccentColor(Utils.getColorAccentDefaultColor(mContext))
+                .addRow(b -> b
+                        .setTitle(getEnhanced4glteModeTitle(subId))
+                        .addEndItem(
+                                new SliceAction(
+                                        getBroadcastIntent(ACTION_ENHANCED_4G_LTE_CHANGED),
+                                        null /* actionTitle */, isEnhanced4gLteEnabled))
+                        .setPrimaryAction(new SliceAction(
+                                getActivityIntent(ACTION_MOBILE_NETWORK_SETTINGS_ACTIVITY),
+                                icon,
+                                getEnhanced4glteModeTitle(subId))))
+                .build();
+    }
+
+    protected ImsManager getImsManager(int subId) {
+        return ImsManager.getInstance(mContext, SubscriptionManager.getPhoneId(subId));
+    }
+
+    /**
+     * Handles Enhanced 4G LTE mode setting change from Enhanced 4G LTE slice and posts
+     * notification. Should be called when intent action is ACTION_ENHANCED_4G_LTE_CHANGED
+     *
+     * @param intent action performed
+     */
+    public void handleEnhanced4gLteChanged(Intent intent) {
+        final int subId = getDefaultVoiceSubId();
+
+        if (subId > SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            final ImsManager imsManager = getImsManager(subId);
+            if (imsManager.isVolteEnabledByPlatform()
+                    && imsManager.isVolteProvisionedOnDevice()) {
+                final boolean currentValue = imsManager.isEnhanced4gLteModeSettingEnabledByUser()
+                        && imsManager.isNonTtyOrTtyOnVolteEnabled();
+                final boolean newValue = intent.getBooleanExtra(EXTRA_TOGGLE_STATE,
+                        currentValue);
+                if (newValue != currentValue) {
+                    imsManager.setEnhanced4gLteModeSetting(newValue);
+                }
+            }
+        }
+        // notify change in slice in any case to get re-queried. This would result in displaying
+        // appropriate message with the updated setting.
+        final Uri uri = SliceBuilderUtils.getUri(PATH_ENHANCED_4G_LTE, false /*isPlatformSlice*/);
+        mContext.getContentResolver().notifyChange(uri, null);
+    }
+
+    private CharSequence getEnhanced4glteModeTitle(int subId) {
+        CharSequence ret = mContext.getText(R.string.enhanced_4g_lte_mode_title);
+        try {
+            if (isCarrierConfigManagerKeyEnabled(
+                    CarrierConfigManager.KEY_ENHANCED_4G_LTE_TITLE_VARIANT_BOOL,
+                    subId,
+                    false)) {
+                final PackageManager manager = mContext.getPackageManager();
+                final Resources resources = manager.getResourcesForApplication(
+                        PACKAGE_PHONE);
+                final int resId = resources.getIdentifier(
+                        RESOURCE_ENHANCED_4G_LTE_MODE_TITLE_VARIANT,
+                        RESOURCE_TYPE_STRING, PACKAGE_PHONE);
+                ret = resources.getText(resId);
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "package name not found");
+        }
+        return ret;
+    }
+
+    /**
+     * Returns {@code true} when the key is enabled for the carrier, and {@code false} otherwise.
+     */
+    private boolean isCarrierConfigManagerKeyEnabled(String key,
+            int subId, boolean defaultValue) {
+        final CarrierConfigManager configManager = getCarrierConfigManager();
+        boolean ret = defaultValue;
+        if (configManager != null) {
+            final PersistableBundle bundle = configManager.getConfigForSubId(subId);
+            if (bundle != null) {
+                ret = bundle.getBoolean(key, defaultValue);
+            }
+        }
+        return ret;
+    }
+
+    protected CarrierConfigManager getCarrierConfigManager() {
+        return mContext.getSystemService(CarrierConfigManager.class);
+    }
+
+    private PendingIntent getBroadcastIntent(String action) {
+        final Intent intent = new Intent(action);
+        intent.setClass(mContext, SliceBroadcastReceiver.class);
+        return PendingIntent.getBroadcast(mContext, 0 /* requestCode */, intent,
+                PendingIntent.FLAG_CANCEL_CURRENT);
+    }
+
+    /**
+     * Returns the current default voice subId obtained from SubscriptionManager
+     */
+    protected int getDefaultVoiceSubId() {
+        return SubscriptionManager.getDefaultVoiceSubscriptionId();
+    }
+
+    /**
+     * Returns PendingIntent to start activity specified by action
+     */
+    private PendingIntent getActivityIntent(String action) {
+        final Intent intent = new Intent(action);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        return PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, 0 /* flags */);
+    }
+}
+
diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java
index f4dfd6e..0fd5c42 100644
--- a/src/com/android/settings/slices/SettingsSliceProvider.java
+++ b/src/com/android/settings/slices/SettingsSliceProvider.java
@@ -36,6 +36,7 @@
 import com.android.settings.core.BasePreferenceController;
 import com.android.settings.location.LocationSliceBuilder;
 import com.android.settings.notification.ZenModeSliceBuilder;
+import com.android.settings.mobilenetwork.Enhanced4gLteSliceHelper;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.wifi.WifiSliceBuilder;
 import com.android.settings.wifi.calling.WifiCallingSliceHelper;
@@ -190,23 +191,27 @@
                 Log.e(TAG, "Requested blocked slice with Uri: " + sliceUri);
                 return null;
             }
-
-            // If adding a new Slice, do not directly match Slice URIs.
-            // Use {@link SlicesDatabaseAccessor}.
-            if (WifiCallingSliceHelper.WIFI_CALLING_URI.equals(sliceUri)) {
-                return FeatureFactory.getFactory(getContext())
-                        .getSlicesFeatureProvider()
-                        .getNewWifiCallingSliceHelper(getContext())
-                        .createWifiCallingSlice(sliceUri);
-            } else if (WifiSliceBuilder.WIFI_URI.equals(sliceUri)) {
-                return WifiSliceBuilder.getSlice(getContext());
-            } else if (ZenModeSliceBuilder.ZEN_MODE_URI.equals(sliceUri)) {
-                return ZenModeSliceBuilder.getSlice(getContext());
-            } else if (BluetoothSliceBuilder.BLUETOOTH_URI.equals(sliceUri)) {
-                return BluetoothSliceBuilder.getSlice(getContext());
-            } else if (LocationSliceBuilder.LOCATION_URI.equals(sliceUri)) {
-                return LocationSliceBuilder.getSlice(getContext());
-            }
+        // If adding a new Slice, do not directly match Slice URIs.
+        // Use {@link SlicesDatabaseAccessor}.
+        if (WifiCallingSliceHelper.WIFI_CALLING_URI.equals(sliceUri)) {
+            return FeatureFactory.getFactory(getContext())
+                    .getSlicesFeatureProvider()
+                    .getNewWifiCallingSliceHelper(getContext())
+                    .createWifiCallingSlice(sliceUri);
+        } else if (WifiSliceBuilder.WIFI_URI.equals(sliceUri)) {
+            return WifiSliceBuilder.getSlice(getContext());
+        } else if (ZenModeSliceBuilder.ZEN_MODE_URI.equals(sliceUri)) {
+            return ZenModeSliceBuilder.getSlice(getContext());
+        } else if (BluetoothSliceBuilder.BLUETOOTH_URI.equals(sliceUri)) {
+            return BluetoothSliceBuilder.getSlice(getContext());
+        } else if (LocationSliceBuilder.LOCATION_URI.equals(sliceUri)) {
+            return LocationSliceBuilder.getSlice(getContext());
+        } else if (Enhanced4gLteSliceHelper.SLICE_URI.equals(sliceUri)) {
+            return FeatureFactory.getFactory(getContext())
+                    .getSlicesFeatureProvider()
+                    .getNewEnhanced4gLteSliceHelper(getContext())
+                    .createEnhanced4gLteSlice(sliceUri);
+        }
 
             SliceData cachedSliceData = mSliceWeakDataCache.get(sliceUri);
             if (cachedSliceData == null) {
diff --git a/src/com/android/settings/slices/SliceBroadcastReceiver.java b/src/com/android/settings/slices/SliceBroadcastReceiver.java
index d81734a..231298c 100644
--- a/src/com/android/settings/slices/SliceBroadcastReceiver.java
+++ b/src/com/android/settings/slices/SliceBroadcastReceiver.java
@@ -24,6 +24,7 @@
 import static com.android.settings.slices.SettingsSliceProvider.EXTRA_SLICE_PLATFORM_DEFINED;
 import static com.android.settings.wifi.calling.WifiCallingSliceHelper.ACTION_WIFI_CALLING_CHANGED;
 import static com.android.settings.wifi.WifiSliceBuilder.ACTION_WIFI_SLICE_CHANGED;
+import static com.android.settings.mobilenetwork.Enhanced4gLteSliceHelper.ACTION_ENHANCED_4G_LTE_CHANGED;
 
 import android.app.slice.Slice;
 import android.content.BroadcastReceiver;
@@ -84,6 +85,12 @@
             case ACTION_ZEN_MODE_SLICE_CHANGED:
                 ZenModeSliceBuilder.handleUriChange(context, intent);
                 break;
+            case ACTION_ENHANCED_4G_LTE_CHANGED:
+                FeatureFactory.getFactory(context)
+                        .getSlicesFeatureProvider()
+                        .getNewEnhanced4gLteSliceHelper(context)
+                        .handleEnhanced4gLteChanged(intent);
+                break;
             default:
                 final String uriString = intent.getStringExtra(SliceBroadcastRelay.EXTRA_URI);
                 if (!TextUtils.isEmpty(uriString)) {
diff --git a/src/com/android/settings/slices/SlicesFeatureProvider.java b/src/com/android/settings/slices/SlicesFeatureProvider.java
index 8dd6547..8395a58 100644
--- a/src/com/android/settings/slices/SlicesFeatureProvider.java
+++ b/src/com/android/settings/slices/SlicesFeatureProvider.java
@@ -2,6 +2,7 @@
 
 import android.content.Context;
 
+import com.android.settings.mobilenetwork.Enhanced4gLteSliceHelper;
 import com.android.settings.wifi.calling.WifiCallingSliceHelper;
 
 /**
@@ -31,4 +32,10 @@
      * Gets new WifiCallingSliceHelper object
      */
     WifiCallingSliceHelper getNewWifiCallingSliceHelper(Context context);
+
+    /**
+     * Gets new Enhanced4gLteSliceHelper object
+     */
+    Enhanced4gLteSliceHelper getNewEnhanced4gLteSliceHelper(Context context);
 }
+
diff --git a/src/com/android/settings/slices/SlicesFeatureProviderImpl.java b/src/com/android/settings/slices/SlicesFeatureProviderImpl.java
index 16684bf..988bcfe 100644
--- a/src/com/android/settings/slices/SlicesFeatureProviderImpl.java
+++ b/src/com/android/settings/slices/SlicesFeatureProviderImpl.java
@@ -2,6 +2,7 @@
 
 import android.content.Context;
 
+import com.android.settings.mobilenetwork.Enhanced4gLteSliceHelper;
 import com.android.settings.wifi.calling.WifiCallingSliceHelper;
 import com.android.settingslib.utils.ThreadUtils;
 
@@ -45,4 +46,9 @@
     public WifiCallingSliceHelper getNewWifiCallingSliceHelper(Context context) {
         return new WifiCallingSliceHelper(context);
     }
+
+    @Override
+    public Enhanced4gLteSliceHelper getNewEnhanced4gLteSliceHelper(Context context) {
+        return new Enhanced4gLteSliceHelper(context);
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/mobilenetwork/Enhanced4gLteSliceHelperTest.java b/tests/robotests/src/com/android/settings/mobilenetwork/Enhanced4gLteSliceHelperTest.java
new file mode 100644
index 0000000..625a68b
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/mobilenetwork/Enhanced4gLteSliceHelperTest.java
@@ -0,0 +1,281 @@
+/*
+ * 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.mobilenetwork;
+
+import static android.app.slice.Slice.EXTRA_TOGGLE_STATE;
+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.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.telephony.CarrierConfigManager;
+
+import androidx.slice.Slice;
+import androidx.slice.SliceItem;
+import androidx.slice.SliceMetadata;
+import androidx.slice.SliceProvider;
+import androidx.slice.core.SliceAction;
+import androidx.slice.core.SliceQuery;
+import androidx.slice.widget.SliceLiveData;
+
+import com.android.ims.ImsManager;
+import com.android.settings.R;
+import com.android.settings.slices.SettingsSliceProvider;
+import com.android.settings.slices.SliceBroadcastReceiver;
+import com.android.settings.slices.SlicesFeatureProvider;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.List;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class Enhanced4gLteSliceHelperTest {
+
+    private Context mContext;
+    @Mock
+    private CarrierConfigManager mMockCarrierConfigManager;
+
+    @Mock
+    private ImsManager mMockImsManager;
+
+    private FakeEnhanced4gLteSliceHelper mEnhanced4gLteSliceHelper;
+    private SettingsSliceProvider mProvider;
+    private SliceBroadcastReceiver mReceiver;
+    private FakeFeatureFactory mFeatureFactory;
+    private SlicesFeatureProvider mSlicesFeatureProvider;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+
+        //setup for SettingsSliceProvider tests
+        mProvider = spy(new SettingsSliceProvider());
+        doReturn(mContext).when(mProvider).getContext();
+
+        //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());
+        doReturn(resources).when(mContext).getResources();
+
+        mEnhanced4gLteSliceHelper = new FakeEnhanced4gLteSliceHelper(mContext);
+
+        // Set-up specs for SliceMetadata.
+        SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
+    }
+
+    @Test
+    public void test_CreateEnhanced4gLteSlice_invalidSubId() {
+        mEnhanced4gLteSliceHelper.setDefaultVoiceSubId(-1);
+
+        final Slice slice = mEnhanced4gLteSliceHelper.createEnhanced4gLteSlice(
+                Enhanced4gLteSliceHelper.SLICE_URI);
+
+        assertThat(slice).isNull();
+    }
+
+    @Test
+    public void test_CreateEnhanced4gLteSlice_enhanced4gLteNotSupported() {
+        when(mMockImsManager.isVolteEnabledByPlatform()).thenReturn(false);
+
+        final Slice slice = mEnhanced4gLteSliceHelper.createEnhanced4gLteSlice(
+                Enhanced4gLteSliceHelper.SLICE_URI);
+
+        assertThat(mEnhanced4gLteSliceHelper.getDefaultVoiceSubId()).isEqualTo(1);
+        assertThat(slice).isNull();
+    }
+
+    @Test
+    public void test_CreateEnhanced4gLteSlice_success() {
+        when(mMockImsManager.isVolteEnabledByPlatform()).thenReturn(true);
+        when(mMockImsManager.isVolteProvisionedOnDevice()).thenReturn(true);
+        when(mMockImsManager.isEnhanced4gLteModeSettingEnabledByUser()).thenReturn(true);
+        when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true);
+        when(mMockCarrierConfigManager.getConfigForSubId(1)).thenReturn(null);
+
+        final Slice slice = mEnhanced4gLteSliceHelper.createEnhanced4gLteSlice(
+                Enhanced4gLteSliceHelper.SLICE_URI);
+
+        assertThat(mEnhanced4gLteSliceHelper.getDefaultVoiceSubId()).isEqualTo(1);
+        testEnhanced4gLteSettingsToggleSlice(slice);
+    }
+
+    @Test
+    public void test_SettingSliceProvider_getsRightSliceEnhanced4gLte() {
+        when(mMockImsManager.isVolteEnabledByPlatform()).thenReturn(true);
+        when(mMockImsManager.isVolteProvisionedOnDevice()).thenReturn(true);
+        when(mMockImsManager.isEnhanced4gLteModeSettingEnabledByUser()).thenReturn(true);
+        when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true);
+        when(mMockCarrierConfigManager.getConfigForSubId(1)).thenReturn(null);
+        when(mSlicesFeatureProvider.getNewEnhanced4gLteSliceHelper(mContext))
+                .thenReturn(mEnhanced4gLteSliceHelper);
+
+        final Slice slice = mProvider.onBindSlice(Enhanced4gLteSliceHelper.SLICE_URI);
+
+        assertThat(mEnhanced4gLteSliceHelper.getDefaultVoiceSubId()).isEqualTo(1);
+        testEnhanced4gLteSettingsToggleSlice(slice);
+    }
+
+    @Test
+    public void test_SliceBroadcastReceiver_toggleOffEnhanced4gLte() {
+        when(mMockImsManager.isVolteEnabledByPlatform()).thenReturn(true);
+        when(mMockImsManager.isVolteProvisionedOnDevice()).thenReturn(true);
+        when(mMockImsManager.isEnhanced4gLteModeSettingEnabledByUser()).thenReturn(false);
+        when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true);
+        when(mSlicesFeatureProvider.getNewEnhanced4gLteSliceHelper(mContext))
+                .thenReturn(mEnhanced4gLteSliceHelper);
+
+        ArgumentCaptor<Boolean> mEnhanced4gLteSettingCaptor = ArgumentCaptor.forClass(
+                Boolean.class);
+
+        // turn on Enhanced4gLte setting
+        Intent intent = new Intent(Enhanced4gLteSliceHelper.ACTION_ENHANCED_4G_LTE_CHANGED);
+        intent.putExtra(EXTRA_TOGGLE_STATE, true);
+
+        // change the setting
+        mReceiver.onReceive(mContext, intent);
+
+        verify((mMockImsManager)).setEnhanced4gLteModeSetting(
+                mEnhanced4gLteSettingCaptor.capture());
+
+        // assert the change
+        assertThat(mEnhanced4gLteSettingCaptor.getValue()).isTrue();
+    }
+
+    private void testEnhanced4gLteSettingsUnavailableSlice(Slice slice,
+            PendingIntent expectedPrimaryAction) {
+        final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
+
+        //Check there is no toggle action
+        final List<SliceAction> toggles = metadata.getToggles();
+        assertThat(toggles).isEmpty();
+
+        // Check whether the primary action is to open Enhanced4gLte settings activity
+        final PendingIntent primaryPendingIntent =
+                metadata.getPrimaryAction().getAction();
+        assertThat(primaryPendingIntent).isEqualTo(expectedPrimaryAction);
+
+        // Check the title
+        final List<SliceItem> sliceItems = slice.getItems();
+        assertTitle(sliceItems, mContext.getString(R.string.enhanced_4g_lte_mode_title));
+    }
+
+    private void testEnhanced4gLteSettingsToggleSlice(Slice slice) {
+        final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
+
+        final List<SliceAction> toggles = metadata.getToggles();
+        assertThat(toggles).hasSize(1);
+
+        final SliceAction mainToggleAction = toggles.get(0);
+
+        // Check intent in Toggle Action
+        final PendingIntent togglePendingIntent = mainToggleAction.getAction();
+        final PendingIntent expectedToggleIntent = getBroadcastIntent(
+                Enhanced4gLteSliceHelper.ACTION_ENHANCED_4G_LTE_CHANGED);
+        assertThat(togglePendingIntent).isEqualTo(expectedToggleIntent);
+
+        // Check primary intent
+        final PendingIntent primaryPendingIntent = metadata.getPrimaryAction().getAction();
+        final PendingIntent expectedPendingIntent =
+                getActivityIntent(Enhanced4gLteSliceHelper.ACTION_MOBILE_NETWORK_SETTINGS_ACTIVITY);
+        assertThat(primaryPendingIntent).isEqualTo(expectedPendingIntent);
+
+        // Check the title
+        final List<SliceItem> sliceItems = slice.getItems();
+        assertTitle(sliceItems, mContext.getString(R.string.enhanced_4g_lte_mode_title));
+    }
+
+    private PendingIntent getBroadcastIntent(String action) {
+        final Intent intent = new Intent(action);
+        intent.setClass(mContext, SliceBroadcastReceiver.class);
+        return PendingIntent.getBroadcast(mContext, 0 /* requestCode */, intent,
+                PendingIntent.FLAG_CANCEL_CURRENT);
+    }
+
+    private PendingIntent getActivityIntent(String action) {
+        final Intent intent = new Intent(action);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        return PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, 0 /* flags */);
+    }
+
+    private void assertTitle(List<SliceItem> sliceItems, String title) {
+        boolean hasTitle = false;
+        for (SliceItem item : sliceItems) {
+            List<SliceItem> titleItems = SliceQuery.findAll(item, FORMAT_TEXT, HINT_TITLE,
+                    null /* non-hints */);
+            if (titleItems == null) {
+                continue;
+            }
+
+            hasTitle = true;
+            for (SliceItem subTitleItem : titleItems) {
+                assertThat(subTitleItem.getText()).isEqualTo(title);
+            }
+        }
+        assertThat(hasTitle).isTrue();
+    }
+
+    private class FakeEnhanced4gLteSliceHelper extends Enhanced4gLteSliceHelper {
+        int mSubId = 1;
+
+        FakeEnhanced4gLteSliceHelper(Context context) {
+            super(context);
+        }
+
+        @Override
+        protected CarrierConfigManager getCarrierConfigManager() {
+            return mMockCarrierConfigManager;
+        }
+
+        @Override
+        protected ImsManager getImsManager(int subId) {
+            return mMockImsManager;
+        }
+
+        protected int getDefaultVoiceSubId() {
+            return mSubId;
+        }
+
+        protected void setDefaultVoiceSubId(int id) {
+            mSubId = id;
+        }
+    }
+}