Merge "[Settings] Use 1030 opcode, deprecate 1024."
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a842d36..1369867 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -10084,12 +10084,6 @@
     <!-- Title for Connected device shortcut [CHAR LIMIT=30] -->
     <string name="devices_title">Devices</string>
 
-    <!-- UI debug setting: Enable High Refresh Rate virtual panel [CHAR LIMIT=25] -->
-    <string name="high_frequency_display_device_title">High Frequency Panel</string>
-
-    <!-- UI debug setting: Enable High Refresh Rate virtual panel [CHAR LIMIT=50] -->
-    <string name="high_frequency_display_device_summary">Enable Virtual High Frequency Panel</string>
-
     <!-- Homepage bottom menu. Title for display all Settings [CHAR LIMIT=30] -->
     <string name="homepage_all_settings">All Settings</string>
 
diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml
index 5a36463..3d08cd8 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -364,11 +364,6 @@
             android:entries="@array/overlay_display_devices_entries"
             android:entryValues="@array/overlay_display_devices_values" />
 
-        <SwitchPreference
-            android:key="high_frequency_display_device"
-            android:title="@string/high_frequency_display_device_title"
-            android:summary="@string/high_frequency_display_device_summary" />
-
         <com.android.settings.display.DensityPreference
             android:key="density"
             android:title="@string/developer_smallest_width" />
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index cc8bd2e..5f42e89 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -440,7 +440,6 @@
         controllers.add(new TransitionAnimationScalePreferenceController(context));
         controllers.add(new AnimatorDurationScalePreferenceController(context));
         controllers.add(new SecondaryDisplayPreferenceController(context));
-        controllers.add(new HighFrequencyDisplayPreferenceController(context));
         controllers.add(new GpuViewUpdatesPreferenceController(context));
         controllers.add(new HardwareLayersUpdatesPreferenceController(context));
         controllers.add(new DebugGpuOverdrawPreferenceController(context));
diff --git a/src/com/android/settings/development/HighFrequencyDisplayPreferenceController.java b/src/com/android/settings/development/HighFrequencyDisplayPreferenceController.java
deleted file mode 100644
index ad8e9f8..0000000
--- a/src/com/android/settings/development/HighFrequencyDisplayPreferenceController.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * 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.development;
-
-import android.content.Context;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-
-import androidx.annotation.VisibleForTesting;
-import androidx.preference.Preference;
-import androidx.preference.SwitchPreference;
-
-import com.android.settings.core.PreferenceControllerMixin;
-import com.android.settingslib.development.DeveloperOptionsPreferenceController;
-
-public class HighFrequencyDisplayPreferenceController extends DeveloperOptionsPreferenceController
-        implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
-
-    private static final String HIGH_FREQUENCY_DISPLAY_KEY = "high_frequency_display_device";
-
-    private static final String SURFACE_FLINGER_SERVICE_KEY = "SurfaceFlinger";
-    private static final String SURFACE_COMPOSER_INTERFACE_KEY = "android.ui.ISurfaceComposer";
-    private static final int SURFACE_FLINGER_HIGH_FREQUENCY_DISPLAY_CODE = 1029;
-
-    private final IBinder mSurfaceFlingerBinder;
-
-    public HighFrequencyDisplayPreferenceController(Context context) {
-        super(context);
-        mSurfaceFlingerBinder = ServiceManager.getService(SURFACE_FLINGER_SERVICE_KEY);
-    }
-
-    @Override
-    public String getPreferenceKey() {
-        return HIGH_FREQUENCY_DISPLAY_KEY;
-    }
-
-    @Override
-    public boolean onPreferenceChange(Preference preference, Object newValue) {
-        Boolean isEnabled = (Boolean) newValue;
-        writeHighFrequencyDisplaySetting(isEnabled);
-        ((SwitchPreference) preference).setChecked(isEnabled);
-        return true;
-    }
-
-    @Override
-    public void updateState(Preference preference) {
-        boolean enableHighFrequencyPanel = readHighFrequencyDisplaySetting();
-        ((SwitchPreference) preference).setChecked(enableHighFrequencyPanel);
-    }
-
-    @Override
-    protected void onDeveloperOptionsSwitchDisabled() {
-        super.onDeveloperOptionsSwitchDisabled();
-        writeHighFrequencyDisplaySetting(false);
-        ((SwitchPreference) mPreference).setChecked(false);
-    }
-
-    @VisibleForTesting
-    boolean readHighFrequencyDisplaySetting() {
-        boolean isEnabled = false;
-        try {
-            if (mSurfaceFlingerBinder != null) {
-                final Parcel data = Parcel.obtain();
-                final Parcel result = Parcel.obtain();
-                data.writeInterfaceToken(SURFACE_COMPOSER_INTERFACE_KEY);
-                data.writeInt(0);
-                data.writeInt(0);
-                mSurfaceFlingerBinder.transact(
-                        SURFACE_FLINGER_HIGH_FREQUENCY_DISPLAY_CODE,
-                        data, result, 0);
-
-                if (result.readInt() != 1 || result.readInt() != 1) {
-                    isEnabled = true;
-                }
-            }
-        } catch (RemoteException ex) {
-            // intentional no-op
-        }
-        return isEnabled;
-    }
-
-    @VisibleForTesting
-    void writeHighFrequencyDisplaySetting(boolean isEnabled) {
-        int multiplier;
-        int divisor;
-
-        if (isEnabled) {
-            // 60Hz * 3/2 = 90Hz
-            multiplier = 2;
-            divisor = 3;
-        } else {
-            multiplier = 1;
-            divisor = 1;
-        }
-
-        try {
-            if (mSurfaceFlingerBinder != null) {
-                final Parcel data = Parcel.obtain();
-                data.writeInterfaceToken(SURFACE_COMPOSER_INTERFACE_KEY);
-                data.writeInt(multiplier);
-                data.writeInt(divisor);
-                mSurfaceFlingerBinder.transact(
-                        SURFACE_FLINGER_HIGH_FREQUENCY_DISPLAY_CODE,
-                        data, null, 0);
-            }
-        } catch (RemoteException ex) {
-            // intentional no-op
-        }
-    }
-}
diff --git a/src/com/android/settings/fuelgauge/BatteryInfo.java b/src/com/android/settings/fuelgauge/BatteryInfo.java
index 06cdad6..1f11f5a 100644
--- a/src/com/android/settings/fuelgauge/BatteryInfo.java
+++ b/src/com/android/settings/fuelgauge/BatteryInfo.java
@@ -148,49 +148,7 @@
         new AsyncTask<Void, Void, BatteryInfo>() {
             @Override
             protected BatteryInfo doInBackground(Void... params) {
-                final BatteryStats stats;
-                final long batteryStatsTime = System.currentTimeMillis();
-                if (statsHelper == null) {
-                    final BatteryStatsHelper localStatsHelper = new BatteryStatsHelper(context,
-                            true);
-                    localStatsHelper.create((Bundle) null);
-                    stats = localStatsHelper.getStats();
-                } else {
-                    stats = statsHelper.getStats();
-                }
-                BatteryUtils.logRuntime(LOG_TAG, "time for getStats", batteryStatsTime);
-
-                final long startTime = System.currentTimeMillis();
-                PowerUsageFeatureProvider provider =
-                        FeatureFactory.getFactory(context).getPowerUsageFeatureProvider(context);
-                final long elapsedRealtimeUs =
-                        PowerUtil.convertMsToUs(SystemClock.elapsedRealtime());
-
-                Intent batteryBroadcast = context.registerReceiver(null,
-                        new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
-                // 0 means we are discharging, anything else means charging
-                boolean discharging =
-                        batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) == 0;
-
-                if (discharging && provider != null
-                        && provider.isEnhancedBatteryPredictionEnabled(context)) {
-                    Estimate estimate = provider.getEnhancedBatteryPrediction(context);
-                    if (estimate != null) {
-                        BatteryUtils
-                                .logRuntime(LOG_TAG, "time for enhanced BatteryInfo", startTime);
-                        return BatteryInfo.getBatteryInfo(context, batteryBroadcast, stats,
-                                estimate, elapsedRealtimeUs, shortString);
-                    }
-                }
-                long prediction = discharging
-                        ? stats.computeBatteryTimeRemaining(elapsedRealtimeUs) : 0;
-                Estimate estimate = new Estimate(
-                        PowerUtil.convertUsToMs(prediction),
-                        false, /* isBasedOnUsage */
-                        Estimate.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN);
-                BatteryUtils.logRuntime(LOG_TAG, "time for regular BatteryInfo", startTime);
-                return BatteryInfo.getBatteryInfo(context, batteryBroadcast, stats,
-                        estimate, elapsedRealtimeUs, shortString);
+                return getBatteryInfo(context, statsHelper, shortString);
             }
 
             @Override
@@ -202,6 +160,53 @@
         }.execute();
     }
 
+    public static BatteryInfo getBatteryInfo(final Context context,
+            final BatteryStatsHelper statsHelper, boolean shortString) {
+        final BatteryStats stats;
+        final long batteryStatsTime = System.currentTimeMillis();
+        if (statsHelper == null) {
+            final BatteryStatsHelper localStatsHelper = new BatteryStatsHelper(context,
+                    true);
+            localStatsHelper.create((Bundle) null);
+            stats = localStatsHelper.getStats();
+        } else {
+            stats = statsHelper.getStats();
+        }
+        BatteryUtils.logRuntime(LOG_TAG, "time for getStats", batteryStatsTime);
+
+        final long startTime = System.currentTimeMillis();
+        PowerUsageFeatureProvider provider =
+                FeatureFactory.getFactory(context).getPowerUsageFeatureProvider(context);
+        final long elapsedRealtimeUs =
+                PowerUtil.convertMsToUs(SystemClock.elapsedRealtime());
+
+        final Intent batteryBroadcast = context.registerReceiver(null,
+                new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+        // 0 means we are discharging, anything else means charging
+        final boolean discharging =
+                batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) == 0;
+
+        if (discharging && provider != null
+                && provider.isEnhancedBatteryPredictionEnabled(context)) {
+            Estimate estimate = provider.getEnhancedBatteryPrediction(context);
+            if (estimate != null) {
+                BatteryUtils
+                        .logRuntime(LOG_TAG, "time for enhanced BatteryInfo", startTime);
+                return BatteryInfo.getBatteryInfo(context, batteryBroadcast, stats,
+                        estimate, elapsedRealtimeUs, shortString);
+            }
+        }
+        final long prediction = discharging
+                ? stats.computeBatteryTimeRemaining(elapsedRealtimeUs) : 0;
+        final Estimate estimate = new Estimate(
+                PowerUtil.convertUsToMs(prediction),
+                false, /* isBasedOnUsage */
+                Estimate.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN);
+        BatteryUtils.logRuntime(LOG_TAG, "time for regular BatteryInfo", startTime);
+        return BatteryInfo.getBatteryInfo(context, batteryBroadcast, stats,
+                estimate, elapsedRealtimeUs, shortString);
+    }
+
     @WorkerThread
     public static BatteryInfo getBatteryInfoOld(Context context, Intent batteryBroadcast,
             BatteryStats stats, long elapsedRealtimeUs, boolean shortString) {
diff --git a/src/com/android/settings/homepage/CardContentLoader.java b/src/com/android/settings/homepage/CardContentLoader.java
index 22cde86..7168b67 100644
--- a/src/com/android/settings/homepage/CardContentLoader.java
+++ b/src/com/android/settings/homepage/CardContentLoader.java
@@ -32,6 +32,7 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.slice.Slice;
 
+import com.android.settings.homepage.deviceinfo.BatterySlice;
 import com.android.settings.homepage.deviceinfo.DataUsageSlice;
 import com.android.settings.homepage.deviceinfo.DeviceInfoSlice;
 import com.android.settingslib.utils.AsyncLoaderCompat;
@@ -101,17 +102,15 @@
                     .setCardType(ContextualCard.CardType.SLICE)
                     .setIsHalfWidth(false)
                     .build());
-            //TODO(b/115971399): Will change following values of SliceUri and Name
-            // after landing these slice cards.
-//            add(new ContextualCard.Builder()
-//                    .setSliceUri("content://com.android.settings.slices/battery_card")
-//                    .setName(packageName + "/" + "battery_card")
-//                    .setPackageName(packageName)
-//                    .setRankingScore(rankingScore)
-//                    .setAppVersion(appVersionCode)
-//                    .setCardType(ContextualCard.CardType.SLICE)
-//                    .setIsHalfWidth(true)
-//                    .build());
+            add(new ContextualCard.Builder()
+                    .setSliceUri(BatterySlice.BATTERY_CARD_URI)
+                    .setName(BatterySlice.PATH_BATTERY_INFO)
+                    .setPackageName(packageName)
+                    .setRankingScore(rankingScore)
+                    .setAppVersion(appVersionCode)
+                    .setCardType(ContextualCard.CardType.SLICE)
+                    .setIsHalfWidth(false)
+                    .build());
             add(new ContextualCard.Builder()
                     .setSliceUri(DeviceInfoSlice.DEVICE_INFO_CARD_URI)
                     .setName(DeviceInfoSlice.PATH_DEVICE_INFO)
diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java
index c6040f6..da7e7f2 100644
--- a/src/com/android/settings/homepage/SettingsHomepageActivity.java
+++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java
@@ -65,7 +65,13 @@
     private void showFragment(Fragment fragment, int id, String tag) {
         final FragmentManager fragmentManager = getSupportFragmentManager();
         final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
-        fragmentTransaction.add(id, fragment, tag);
+        final Fragment showFragment = fragmentManager.findFragmentById(id);
+
+        if (showFragment == null) {
+            fragmentTransaction.add(id, fragment, tag);
+        } else {
+            fragmentTransaction.show(showFragment);
+        }
         fragmentTransaction.commit();
     }
 }
\ No newline at end of file
diff --git a/src/com/android/settings/homepage/contextualcards/SettingsContextualCardProvider.java b/src/com/android/settings/homepage/contextualcards/SettingsContextualCardProvider.java
index 26f86b9..0d72ceb 100644
--- a/src/com/android/settings/homepage/contextualcards/SettingsContextualCardProvider.java
+++ b/src/com/android/settings/homepage/contextualcards/SettingsContextualCardProvider.java
@@ -20,6 +20,7 @@
 
 import android.annotation.Nullable;
 
+import com.android.settings.homepage.deviceinfo.BatterySlice;
 import com.android.settings.homepage.deviceinfo.DataUsageSlice;
 import com.android.settings.homepage.deviceinfo.DeviceInfoSlice;
 import com.android.settings.homepage.deviceinfo.StorageSlice;
@@ -63,12 +64,18 @@
                         .setSliceUri(EmergencyInfoSlice.EMERGENCY_INFO_CARD_URI.toString())
                         .setCardName(EmergencyInfoSlice.PATH_EMERGENCY_INFO_CARD)
                         .build();
+        final ContextualCard batteryInfoCard =
+                ContextualCard.newBuilder()
+                        .setSliceUri(BatterySlice.BATTERY_CARD_URI.toSafeString())
+                        .setCardName(BatterySlice.PATH_BATTERY_INFO)
+                        .build();
         final ContextualCardList cards = ContextualCardList.newBuilder()
                 .addCard(wifiCard)
                 .addCard(dataUsageCard)
                 .addCard(deviceInfoCard)
                 .addCard(storageInfoCard)
                 .addCard(emergencyInfoCard)
+                .addCard(batteryInfoCard)
                 .build();
 
         return cards;
diff --git a/src/com/android/settings/homepage/deviceinfo/BatterySlice.java b/src/com/android/settings/homepage/deviceinfo/BatterySlice.java
new file mode 100644
index 0000000..1090b40
--- /dev/null
+++ b/src/com/android/settings/homepage/deviceinfo/BatterySlice.java
@@ -0,0 +1,156 @@
+/*
+ * 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.homepage.deviceinfo;
+
+import android.app.PendingIntent;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.PowerManager;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.core.graphics.drawable.IconCompat;
+import androidx.slice.Slice;
+import androidx.slice.builders.ListBuilder;
+import androidx.slice.builders.SliceAction;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+import com.android.settings.SubSettings;
+import com.android.settings.Utils;
+import com.android.settings.fuelgauge.BatteryInfo;
+import com.android.settings.fuelgauge.PowerUsageSummary;
+import com.android.settings.slices.CustomSliceable;
+import com.android.settings.slices.SettingsSliceProvider;
+import com.android.settings.slices.SliceBuilderUtils;
+
+/**
+ * Utility class to build a Battery Slice, and handle all associated actions.
+ */
+public class BatterySlice implements CustomSliceable {
+    private static final String TAG = "BatterySlice";
+
+    /**
+     * The path denotes the unique name of battery slice.
+     */
+    public static final String PATH_BATTERY_INFO = "battery_card";
+
+    /**
+     * Backing Uri for the Battery Slice.
+     */
+    public static final Uri BATTERY_CARD_URI = new Uri.Builder()
+            .scheme(ContentResolver.SCHEME_CONTENT)
+            .authority(SettingsSliceProvider.SLICE_AUTHORITY)
+            .appendPath(PATH_BATTERY_INFO)
+            .build();
+
+    private final Context mContext;
+
+    private BatteryInfo mBatteryInfo;
+    private boolean mIsBatteryInfoLoading;
+
+    public BatterySlice(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Return a {@link BatterySlice} bound to {@link #BATTERY_CARD_URI}
+     */
+    @Override
+    public Slice getSlice() {
+        if (mBatteryInfo == null) {
+            mIsBatteryInfoLoading = true;
+            loadBatteryInfo();
+        }
+        final IconCompat icon = IconCompat.createWithResource(mContext,
+                R.drawable.ic_settings_battery);
+        final CharSequence title = mContext.getText(R.string.power_usage_summary_title);
+        final SliceAction primarySliceAction = new SliceAction(getPrimaryAction(), icon, title);
+        final Slice slice = new ListBuilder(mContext, BATTERY_CARD_URI, ListBuilder.INFINITY)
+                .setAccentColor(Utils.getColorAccentDefaultColor(mContext))
+                .setHeader(new ListBuilder.HeaderBuilder().setTitle(title))
+                .addRow(new ListBuilder.RowBuilder()
+                        .setTitle(getBatteryPercentString(), mIsBatteryInfoLoading)
+                        .setSubtitle(getSummary(), mIsBatteryInfoLoading)
+                        .setPrimaryAction(primarySliceAction))
+                .build();
+        mBatteryInfo = null;
+        mIsBatteryInfoLoading = false;
+        return slice;
+    }
+
+    @Override
+    public Uri getUri() {
+        return BATTERY_CARD_URI;
+    }
+
+    @Override
+    public void onNotifyChange(Intent intent) {
+
+    }
+
+    @Override
+    public Intent getIntent() {
+        final String screenTitle = mContext.getText(R.string.power_usage_summary_title).toString();
+        final Uri contentUri = new Uri.Builder().appendPath(PATH_BATTERY_INFO).build();
+        return SliceBuilderUtils.buildSearchResultPageIntent(mContext,
+                PowerUsageSummary.class.getName(), PATH_BATTERY_INFO, screenTitle,
+                MetricsProto.MetricsEvent.SLICE)
+                .setClassName(mContext.getPackageName(), SubSettings.class.getName())
+                .setData(contentUri);
+    }
+
+    @Override
+    public IntentFilter getIntentFilter() {
+        final IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
+        intentFilter.addAction(Intent.ACTION_POWER_CONNECTED);
+        intentFilter.addAction(Intent.ACTION_POWER_DISCONNECTED);
+        intentFilter.addAction(Intent.ACTION_BATTERY_LEVEL_CHANGED);
+        return intentFilter;
+    }
+
+    @VisibleForTesting
+    void loadBatteryInfo() {
+        BatteryInfo.getBatteryInfo(mContext, info -> {
+            mBatteryInfo = info;
+            mContext.getContentResolver().notifyChange(getUri(), null);
+        }, true);
+    }
+
+    @VisibleForTesting
+    CharSequence getBatteryPercentString() {
+        return mBatteryInfo == null ? null : mBatteryInfo.batteryPercentString;
+    }
+
+    @VisibleForTesting
+    CharSequence getSummary() {
+        if (mBatteryInfo == null) {
+            return null;
+        }
+        return mBatteryInfo.remainingLabel == null ? mBatteryInfo.statusLabel
+                : mBatteryInfo.remainingLabel;
+    }
+
+    private PendingIntent getPrimaryAction() {
+        final Intent intent = getIntent();
+        return PendingIntent.getActivity(mContext, 0 /* requestCode */,
+                intent, 0 /* flags */);
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/slices/CustomSliceManager.java b/src/com/android/settings/slices/CustomSliceManager.java
index 8fa2fb6..a207fcc 100644
--- a/src/com/android/settings/slices/CustomSliceManager.java
+++ b/src/com/android/settings/slices/CustomSliceManager.java
@@ -20,12 +20,14 @@
 import android.net.Uri;
 import android.util.ArrayMap;
 
+import com.android.settings.homepage.deviceinfo.BatterySlice;
 import com.android.settings.homepage.deviceinfo.DataUsageSlice;
 import com.android.settings.homepage.deviceinfo.DeviceInfoSlice;
 import com.android.settings.homepage.deviceinfo.StorageSlice;
 import com.android.settings.wifi.WifiSlice;
 
 import java.util.Map;
+import java.util.WeakHashMap;
 
 /**
  * Manages custom {@link androidx.slice.Slice Slices}, which are all Slices not backed by
@@ -39,10 +41,12 @@
     protected final Map<Uri, Class<? extends CustomSliceable>> mUriMap;
 
     private final Context mContext;
+    private final Map<Uri, CustomSliceable> mSliceableCache;
 
     public CustomSliceManager(Context context) {
         mContext = context.getApplicationContext();
         mUriMap = new ArrayMap<>();
+        mSliceableCache = new WeakHashMap<>();
         addSlices();
     }
 
@@ -53,13 +57,18 @@
      * the only thing that should be needed to create the object.
      */
     public CustomSliceable getSliceableFromUri(Uri uri) {
-        final Class clazz = mUriMap.get(uri);
+        if (mSliceableCache.containsKey(uri)) {
+            return mSliceableCache.get(uri);
+        }
 
+        final Class clazz = mUriMap.get(uri);
         if (clazz == null) {
             throw new IllegalArgumentException("No Slice found for uri: " + uri);
         }
 
-        return CustomSliceable.createInstance(mContext, clazz);
+        final CustomSliceable sliceable = CustomSliceable.createInstance(mContext, clazz);
+        mSliceableCache.put(uri, sliceable);
+        return sliceable;
     }
 
     /**
@@ -93,5 +102,6 @@
         mUriMap.put(DataUsageSlice.DATA_USAGE_CARD_URI, DataUsageSlice.class);
         mUriMap.put(DeviceInfoSlice.DEVICE_INFO_CARD_URI, DeviceInfoSlice.class);
         mUriMap.put(StorageSlice.STORAGE_CARD_URI, StorageSlice.class);
+        mUriMap.put(BatterySlice.BATTERY_CARD_URI, BatterySlice.class);
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/settings/wifi/WifiSlice.java b/src/com/android/settings/wifi/WifiSlice.java
index b055bcf..4a78ded 100644
--- a/src/com/android/settings/wifi/WifiSlice.java
+++ b/src/com/android/settings/wifi/WifiSlice.java
@@ -74,7 +74,7 @@
             .build();
 
     @VisibleForTesting
-    static final int DEFAULT_EXPANDED_ROW_COUNT = 3;
+    static final int DEFAULT_EXPANDED_ROW_COUNT = 4;
 
     private final Context mContext;
 
diff --git a/tests/robotests/src/com/android/settings/development/HighFrequencyPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/HighFrequencyPreferenceControllerTest.java
deleted file mode 100644
index 700f544..0000000
--- a/tests/robotests/src/com/android/settings/development/HighFrequencyPreferenceControllerTest.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * 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.development;
-
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.os.IBinder;
-import android.os.RemoteException;
-
-import androidx.preference.PreferenceScreen;
-import androidx.preference.SwitchPreference;
-
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.util.ReflectionHelpers;
-
-@RunWith(SettingsRobolectricTestRunner.class)
-public class HighFrequencyPreferenceControllerTest {
-
-    private Context mContext;
-    private SwitchPreference mPreference;
-
-    @Mock
-    private PreferenceScreen mScreen;
-    @Mock
-    private IBinder mSurfaceFlingerBinder;
-
-    private HighFrequencyDisplayPreferenceController mController;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mContext = RuntimeEnvironment.application;
-        mPreference = new SwitchPreference(mContext);
-        mController = spy(new HighFrequencyDisplayPreferenceController(mContext));
-        ReflectionHelpers.setField(mController, "mSurfaceFlingerBinder", mSurfaceFlingerBinder);
-        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
-        mController.displayPreference(mScreen);
-    }
-
-    @Test
-    public void onPreferenceChange_settingToggledOn_shouldWriteTrueToHighFrequencySetting() {
-        mController.onPreferenceChange(mPreference, true /* new value */);
-
-        verify(mController).writeHighFrequencyDisplaySetting(true);
-    }
-
-    @Test
-    public void onPreferenceChange_settingToggledOff_shouldWriteFalseToHighFrequencySetting() {
-        mController.onPreferenceChange(mPreference, false /* new value */);
-
-        verify(mController).writeHighFrequencyDisplaySetting(false);
-    }
-
-    @Test
-    public void updateState_settingEnabled_shouldCheckPreference() throws RemoteException {
-        mController.writeHighFrequencyDisplaySetting(true);
-        mController.updateState(mPreference);
-
-        verify(mController).readHighFrequencyDisplaySetting();
-    }
-
-    @Test
-    public void updateState_settingDisabled_shouldUnCheckPreference() throws RemoteException {
-        mController.writeHighFrequencyDisplaySetting(true);
-        mController.updateState(mPreference);
-
-        verify(mController).readHighFrequencyDisplaySetting();
-    }
-
-    @Test
-    public void onDeveloperOptionsSwitchDisabled_preferenceChecked_shouldTurnOffPreference() {
-        mController.onDeveloperOptionsSwitchDisabled();
-
-        verify(mController).writeHighFrequencyDisplaySetting(false);
-    }
-
-    @Test
-    public void onDeveloperOptionsSwitchDisabled_preferenceUnchecked_shouldNotTurnOffPreference() {
-        mController.onDeveloperOptionsSwitchDisabled();
-
-        verify(mController).writeHighFrequencyDisplaySetting(false);
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/homepage/CardContentLoaderTest.java b/tests/robotests/src/com/android/settings/homepage/CardContentLoaderTest.java
index a7527f3..853cf20 100644
--- a/tests/robotests/src/com/android/settings/homepage/CardContentLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/CardContentLoaderTest.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.net.Uri;
 
+import com.android.settings.homepage.deviceinfo.BatterySlice;
 import com.android.settings.homepage.deviceinfo.DataUsageSlice;
 import com.android.settings.homepage.deviceinfo.DeviceInfoSlice;
 import com.android.settings.homepage.deviceinfo.StorageSlice;
@@ -54,17 +55,18 @@
     }
 
     @Test
-    public void createStaticCards_shouldReturnTwoCards() {
+    public void createStaticCards_shouldReturnFourCards() {
         final List<ContextualCard> defaultData = mCardContentLoader.createStaticCards();
 
-        assertThat(defaultData).hasSize(2);
+        assertThat(defaultData).hasSize(3);
     }
 
     @Test
-    public void createStaticCards_shouldContainDataUsageAndDeviceInfo() {
+    public void createStaticCards_shouldContainCorrectCards() {
         final Uri dataUsage = DataUsageSlice.DATA_USAGE_CARD_URI;
         final Uri deviceInfo = DeviceInfoSlice.DEVICE_INFO_CARD_URI;
-        final List<Uri> expectedUris = Arrays.asList(dataUsage, deviceInfo);
+        final Uri batteryInfo = BatterySlice.BATTERY_CARD_URI;
+        final List<Uri> expectedUris = Arrays.asList(dataUsage, deviceInfo, batteryInfo);
 
         final List<Uri> actualCardUris = mCardContentLoader.createStaticCards().stream().map(
                 ContextualCard::getSliceUri).collect(Collectors.toList());
diff --git a/tests/robotests/src/com/android/settings/homepage/deviceinfo/BatterySliceTest.java b/tests/robotests/src/com/android/settings/homepage/deviceinfo/BatterySliceTest.java
new file mode 100644
index 0000000..8baaab4
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/homepage/deviceinfo/BatterySliceTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.homepage.deviceinfo;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+
+import androidx.core.graphics.drawable.IconCompat;
+import androidx.slice.Slice;
+import androidx.slice.SliceItem;
+import androidx.slice.SliceMetadata;
+import androidx.slice.SliceProvider;
+import androidx.slice.core.SliceAction;
+import androidx.slice.widget.SliceLiveData;
+
+import com.android.settings.R;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.SliceTester;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.List;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class BatterySliceTest {
+
+    private Context mContext;
+    private BatterySlice mBatterySlice;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+
+        // Set-up specs for SliceMetadata.
+        SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
+
+        mBatterySlice = spy(new BatterySlice(mContext));
+    }
+
+    @Test
+    public void getSlice_shouldBeCorrectSliceContent() {
+        doNothing().when(mBatterySlice).loadBatteryInfo();
+        doReturn("10%").when(mBatterySlice).getBatteryPercentString();
+        doReturn("test").when(mBatterySlice).getSummary();
+        final Slice slice = mBatterySlice.getSlice();
+        final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
+        final SliceAction primaryAction = metadata.getPrimaryAction();
+        final IconCompat expectedIcon = IconCompat.createWithResource(mContext,
+                R.drawable.ic_settings_battery);
+        assertThat(primaryAction.getIcon().toString()).isEqualTo(expectedIcon.toString());
+
+        final List<SliceItem> sliceItems = slice.getItems();
+        SliceTester.assertTitle(sliceItems, mContext.getString(R.string.power_usage_summary_title));
+    }
+}