Merge "Move search resources to settingslib-search"
diff --git a/res/drawable-nodpi/gesture_ambient_reach b/res/drawable-nodpi/gesture_ambient_wake_lock_screen
similarity index 100%
rename from res/drawable-nodpi/gesture_ambient_reach
rename to res/drawable-nodpi/gesture_ambient_wake_lock_screen
diff --git a/res/raw/gesture_ambient_reach.mp4 b/res/raw/gesture_ambient_wake_lock_screen.mp4
similarity index 100%
rename from res/raw/gesture_ambient_reach.mp4
rename to res/raw/gesture_ambient_wake_lock_screen.mp4
diff --git a/res/values/strings.xml b/res/values/strings.xml
index db08953..3f4cd8d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -9583,10 +9583,10 @@
     <!-- Summary text for ambient display (device) [CHAR LIMIT=NONE]-->
     <string name="ambient_display_pickup_summary" product="device">To check time, notifications, and other info, pick up your device.</string>
 
-    <!-- Preference and settings suggestion title text for reach gesture [CHAR LIMIT=60]-->
-    <string name="ambient_display_reach_title">Reach gesture</string>
+    <!-- Preference and settings suggestion title text for gesture that shows the lock screen [CHAR LIMIT=60]-->
+    <string name="ambient_display_wake_lock_screen_title">Wake lock screen gesture</string>
     <!-- Summary text for ambient display [CHAR LIMIT=NONE]-->
-    <string name="ambient_display_reach_summary" product="default"></string>
+    <string name="ambient_display_wake_lock_screen_summary" product="default"></string>
 
     <!-- Title text for swiping downwards on fingerprint sensor for notifications [CHAR LIMIT=80]-->
     <string name="fingerprint_swipe_for_notifications_title">Swipe fingerprint for notifications</string>
@@ -9939,6 +9939,13 @@
     <!-- UI debug setting: preference summary - describes the behavior of showing a dialog every time an app crashes [CHAR LIMIT=NONE] -->
     <string name="show_first_crash_dialog_summary">Show dialog every time an app crashes</string>
 
+    <!-- UI debug setting: select current app to use ANGLE [CHAR LIMIT=100] -->
+    <string name="angle_enabled_app">Select ANGLE enabled app</string>
+    <!-- UI debug setting: no ANGLE enabled app has been set [CHAR LIMIT=100] -->
+    <string name="angle_enabled_app_not_set">No ANGLE enabled application set</string>
+    <!-- UI debug setting: ANGLE enabled app has been set [CHAR LIMIT=NONE] -->
+    <string name="angle_enabled_app_set">ANGLE enabled application: <xliff:g id="app_name" example="com.company.app">%1$s</xliff:g></string>
+
     <!-- Title for Directory Access settings -->
     <string name="directory_access">Directory access</string>
     <!-- Keywords for Directory Access settings -->
diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml
index 5b6d215..5a36463 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -424,6 +424,9 @@
             android:summary="%s"
             android:title="@string/simulate_color_space" />
 
+        <Preference android:key="angle_enabled_app"
+            android:title="@string/angle_enabled_app" />
+
     </PreferenceCategory>
 
     <PreferenceCategory
diff --git a/res/xml/gestures.xml b/res/xml/gestures.xml
index aa99446..211157c 100644
--- a/res/xml/gestures.xml
+++ b/res/xml/gestures.xml
@@ -28,10 +28,10 @@
         settings:controller="com.android.settings.gestures.AssistGestureSettingsPreferenceController" />
 
     <Preference
-        android:key="gesture_reach_summary"
-        android:title="@string/ambient_display_reach_title"
-        android:fragment="com.android.settings.gestures.ReachGestureSettings"
-        settings:controller="com.android.settings.gestures.ReachGesturePreferenceController" />
+        android:key="gesture_wake_lock_screen_summary"
+        android:title="@string/ambient_display_wake_lock_screen_title"
+        android:fragment="com.android.settings.gestures.WakeLockScreenGestureSettings"
+        settings:controller="com.android.settings.gestures.WakeLockScreenGesturePreferenceController" />
 
     <Preference
         android:key="gesture_swipe_down_fingerprint_input_summary"
diff --git a/res/xml/reach_gesture_settings.xml b/res/xml/wake_lock_screen_gesture_settings.xml
similarity index 62%
rename from res/xml/reach_gesture_settings.xml
rename to res/xml/wake_lock_screen_gesture_settings.xml
index e933e15..5491bb1 100644
--- a/res/xml/reach_gesture_settings.xml
+++ b/res/xml/wake_lock_screen_gesture_settings.xml
@@ -18,20 +18,20 @@
 <PreferenceScreen
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:key="gesture_reach_screen"
-    android:title="@string/ambient_display_reach_title">
+    android:key="gesture_wake_lock_screen_screen"
+    android:title="@string/ambient_display_wake_lock_screen_title">
 
     <com.android.settings.widget.VideoPreference
-        android:key="gesture_reach_video"
-        app:animation="@raw/gesture_ambient_reach"
-        app:preview="@drawable/gesture_ambient_reach" />
+        android:key="gesture_wake_lock_screen_video"
+        app:animation="@raw/gesture_ambient_wake_lock_screen"
+        app:preview="@drawable/gesture_ambient_wake_lock_screen" />
 
     <SwitchPreference
-        android:key="gesture_reach"
-        android:title="@string/ambient_display_reach_title"
-        android:summary="@string/ambient_display_reach_summary"
+        android:key="gesture_wake_lock_screen"
+        android:title="@string/ambient_display_wake_lock_screen_title"
+        android:summary="@string/ambient_display_wake_lock_screen_summary"
         app:keywords="@string/keywords_gesture"
-        app:controller="com.android.settings.gestures.ReachGesturePreferenceController"
+        app:controller="com.android.settings.gestures.WakeLockScreenGesturePreferenceController"
         app:allowDividerAbove="true" />
 
 </PreferenceScreen>
\ No newline at end of file
diff --git a/src/com/android/settings/datausage/AppDataUsageV2.java b/src/com/android/settings/datausage/AppDataUsageV2.java
new file mode 100644
index 0000000..6d6089e
--- /dev/null
+++ b/src/com/android/settings/datausage/AppDataUsageV2.java
@@ -0,0 +1,451 @@
+/*
+ * Copyright (C) 2016 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.datausage;
+
+import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.net.INetworkStatsSession;
+import android.net.NetworkPolicy;
+import android.net.NetworkStatsHistory;
+import android.net.NetworkTemplate;
+import android.net.TrafficStats;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.IconDrawableFactory;
+import android.util.Log;
+import android.view.View;
+import android.widget.AdapterView;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.loader.app.LoaderManager;
+import androidx.loader.content.Loader;
+import androidx.preference.Preference;
+import androidx.preference.Preference.OnPreferenceChangeListener;
+import androidx.preference.PreferenceCategory;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+import com.android.settings.applications.AppInfoBase;
+import com.android.settings.widget.EntityHeaderController;
+import com.android.settingslib.AppItem;
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+import com.android.settingslib.RestrictedLockUtilsInternal;
+import com.android.settingslib.RestrictedSwitchPreference;
+import com.android.settingslib.net.ChartData;
+import com.android.settingslib.net.ChartDataLoaderCompat;
+import com.android.settingslib.net.UidDetail;
+import com.android.settingslib.net.UidDetailProvider;
+
+public class AppDataUsageV2 extends DataUsageBaseFragment implements OnPreferenceChangeListener,
+        DataSaverBackend.Listener {
+
+    private static final String TAG = "AppDataUsageV2";
+
+    public static final String ARG_APP_ITEM = "app_item";
+    public static final String ARG_NETWORK_TEMPLATE = "network_template";
+
+    private static final String KEY_TOTAL_USAGE = "total_usage";
+    private static final String KEY_FOREGROUND_USAGE = "foreground_usage";
+    private static final String KEY_BACKGROUND_USAGE = "background_usage";
+    private static final String KEY_APP_SETTINGS = "app_settings";
+    private static final String KEY_RESTRICT_BACKGROUND = "restrict_background";
+    private static final String KEY_APP_LIST = "app_list";
+    private static final String KEY_CYCLE = "cycle";
+    private static final String KEY_UNRESTRICTED_DATA = "unrestricted_data_saver";
+
+    private static final int LOADER_CHART_DATA = 2;
+    private static final int LOADER_APP_PREF = 3;
+
+    private PackageManager mPackageManager;
+    private final ArraySet<String> mPackages = new ArraySet<>();
+    private Preference mTotalUsage;
+    private Preference mForegroundUsage;
+    private Preference mBackgroundUsage;
+    private Preference mAppSettings;
+    private RestrictedSwitchPreference mRestrictBackground;
+    private PreferenceCategory mAppList;
+
+    private Drawable mIcon;
+    private CharSequence mLabel;
+    private String mPackageName;
+    private INetworkStatsSession mStatsSession;
+    private CycleAdapter mCycleAdapter;
+
+    private long mStart;
+    private long mEnd;
+    private ChartData mChartData;
+    private NetworkTemplate mTemplate;
+    private NetworkPolicy mPolicy;
+    private AppItem mAppItem;
+    private Intent mAppSettingsIntent;
+    private SpinnerPreference mCycle;
+    private RestrictedSwitchPreference mUnrestrictedData;
+    private DataSaverBackend mDataSaverBackend;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        mPackageManager = getPackageManager();
+        final Bundle args = getArguments();
+
+        try {
+            mStatsSession = services.mStatsService.openSession();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+
+        mAppItem = (args != null) ? (AppItem) args.getParcelable(ARG_APP_ITEM) : null;
+        mTemplate = (args != null) ? (NetworkTemplate) args.getParcelable(ARG_NETWORK_TEMPLATE)
+                : null;
+        if (mTemplate == null) {
+            Context context = getContext();
+            mTemplate = DataUsageUtils.getDefaultTemplate(context,
+                    DataUsageUtils.getDefaultSubscriptionId(context));
+        }
+        if (mAppItem == null) {
+            int uid = (args != null) ? args.getInt(AppInfoBase.ARG_PACKAGE_UID, -1)
+                    : getActivity().getIntent().getIntExtra(AppInfoBase.ARG_PACKAGE_UID, -1);
+            if (uid == -1) {
+                // TODO: Log error.
+                getActivity().finish();
+            } else {
+                addUid(uid);
+                mAppItem = new AppItem(uid);
+                mAppItem.addUid(uid);
+            }
+        } else {
+            for (int i = 0; i < mAppItem.uids.size(); i++) {
+                addUid(mAppItem.uids.keyAt(i));
+            }
+        }
+
+        mTotalUsage = findPreference(KEY_TOTAL_USAGE);
+        mForegroundUsage = findPreference(KEY_FOREGROUND_USAGE);
+        mBackgroundUsage = findPreference(KEY_BACKGROUND_USAGE);
+
+        mCycle = (SpinnerPreference) findPreference(KEY_CYCLE);
+        mCycleAdapter = new CycleAdapter(getContext(), mCycle, mCycleListener, false);
+
+        if (mAppItem.key > 0) {
+            if (mPackages.size() != 0) {
+                try {
+                    ApplicationInfo info = mPackageManager.getApplicationInfoAsUser(
+                            mPackages.valueAt(0), 0, UserHandle.getUserId(mAppItem.key));
+                    mIcon = IconDrawableFactory.newInstance(getActivity()).getBadgedIcon(info);
+                    mLabel = info.loadLabel(mPackageManager);
+                    mPackageName = info.packageName;
+                } catch (PackageManager.NameNotFoundException e) {
+                }
+            }
+            if (!UserHandle.isApp(mAppItem.key)) {
+                removePreference(KEY_UNRESTRICTED_DATA);
+                removePreference(KEY_RESTRICT_BACKGROUND);
+            } else {
+                mRestrictBackground = (RestrictedSwitchPreference) findPreference(
+                        KEY_RESTRICT_BACKGROUND);
+                mRestrictBackground.setOnPreferenceChangeListener(this);
+                mUnrestrictedData = (RestrictedSwitchPreference) findPreference(
+                        KEY_UNRESTRICTED_DATA);
+                mUnrestrictedData.setOnPreferenceChangeListener(this);
+            }
+            mDataSaverBackend = new DataSaverBackend(getContext());
+            mAppSettings = findPreference(KEY_APP_SETTINGS);
+
+            mAppSettingsIntent = new Intent(Intent.ACTION_MANAGE_NETWORK_USAGE);
+            mAppSettingsIntent.addCategory(Intent.CATEGORY_DEFAULT);
+
+            PackageManager pm = getPackageManager();
+            boolean matchFound = false;
+            for (String packageName : mPackages) {
+                mAppSettingsIntent.setPackage(packageName);
+                if (pm.resolveActivity(mAppSettingsIntent, 0) != null) {
+                    matchFound = true;
+                    break;
+                }
+            }
+            if (!matchFound) {
+                removePreference(KEY_APP_SETTINGS);
+                mAppSettings = null;
+            }
+
+            if (mPackages.size() > 1) {
+                mAppList = (PreferenceCategory) findPreference(KEY_APP_LIST);
+                getLoaderManager().initLoader(LOADER_APP_PREF, Bundle.EMPTY, mAppPrefCallbacks);
+            } else {
+                removePreference(KEY_APP_LIST);
+            }
+        } else {
+            final Context context = getActivity();
+            UidDetail uidDetail = new UidDetailProvider(context).getUidDetail(mAppItem.key, true);
+            mIcon = uidDetail.icon;
+            mLabel = uidDetail.label;
+            mPackageName = context.getPackageName();
+
+            removePreference(KEY_UNRESTRICTED_DATA);
+            removePreference(KEY_APP_SETTINGS);
+            removePreference(KEY_RESTRICT_BACKGROUND);
+            removePreference(KEY_APP_LIST);
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        TrafficStats.closeQuietly(mStatsSession);
+        super.onDestroy();
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        if (mDataSaverBackend != null) {
+            mDataSaverBackend.addListener(this);
+        }
+        mPolicy = services.mPolicyEditor.getPolicy(mTemplate);
+        getLoaderManager().restartLoader(LOADER_CHART_DATA,
+                ChartDataLoaderCompat.buildArgs(mTemplate, mAppItem), mChartDataCallbacks);
+        updatePrefs();
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        if (mDataSaverBackend != null) {
+            mDataSaverBackend.remListener(this);
+        }
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        if (preference == mRestrictBackground) {
+            mDataSaverBackend.setIsBlacklisted(mAppItem.key, mPackageName, !(Boolean) newValue);
+            updatePrefs();
+            return true;
+        } else if (preference == mUnrestrictedData) {
+            mDataSaverBackend.setIsWhitelisted(mAppItem.key, mPackageName, (Boolean) newValue);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean onPreferenceTreeClick(Preference preference) {
+        if (preference == mAppSettings) {
+            // TODO: target towards entire UID instead of just first package
+            getActivity().startActivityAsUser(mAppSettingsIntent, new UserHandle(
+                    UserHandle.getUserId(mAppItem.key)));
+            return true;
+        }
+        return super.onPreferenceTreeClick(preference);
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.app_data_usage;
+    }
+
+    @Override
+    protected String getLogTag() {
+        return TAG;
+    }
+
+    @VisibleForTesting
+    void updatePrefs() {
+        updatePrefs(getAppRestrictBackground(), getUnrestrictData());
+    }
+
+    private void updatePrefs(boolean restrictBackground, boolean unrestrictData) {
+        final EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfMeteredDataRestricted(
+                getContext(), mPackageName, UserHandle.getUserId(mAppItem.key));
+        if (mRestrictBackground != null) {
+            mRestrictBackground.setChecked(!restrictBackground);
+            mRestrictBackground.setDisabledByAdmin(admin);
+        }
+        if (mUnrestrictedData != null) {
+            if (restrictBackground) {
+                mUnrestrictedData.setVisible(false);
+            } else {
+                mUnrestrictedData.setVisible(true);
+                mUnrestrictedData.setChecked(unrestrictData);
+                mUnrestrictedData.setDisabledByAdmin(admin);
+            }
+        }
+    }
+
+    private void addUid(int uid) {
+        String[] packages = getPackageManager().getPackagesForUid(uid);
+        if (packages != null) {
+            for (int i = 0; i < packages.length; i++) {
+                mPackages.add(packages[i]);
+            }
+        }
+    }
+
+    private void bindData() {
+        final long backgroundBytes, foregroundBytes;
+        if (mChartData == null || mStart == 0) {
+            backgroundBytes = foregroundBytes = 0;
+            mCycle.setVisible(false);
+        } else {
+            mCycle.setVisible(true);
+            final long now = System.currentTimeMillis();
+            NetworkStatsHistory.Entry entry = null;
+            entry = mChartData.detailDefault.getValues(mStart, mEnd, now, entry);
+            backgroundBytes = entry.rxBytes + entry.txBytes;
+            entry = mChartData.detailForeground.getValues(mStart, mEnd, now, entry);
+            foregroundBytes = entry.rxBytes + entry.txBytes;
+        }
+        final long totalBytes = backgroundBytes + foregroundBytes;
+        final Context context = getContext();
+
+        mTotalUsage.setSummary(DataUsageUtils.formatDataUsage(context, totalBytes));
+        mForegroundUsage.setSummary(DataUsageUtils.formatDataUsage(context, foregroundBytes));
+        mBackgroundUsage.setSummary(DataUsageUtils.formatDataUsage(context, backgroundBytes));
+    }
+
+    private boolean getAppRestrictBackground() {
+        final int uid = mAppItem.key;
+        final int uidPolicy = services.mPolicyManager.getUidPolicy(uid);
+        return (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
+    }
+
+    private boolean getUnrestrictData() {
+        if (mDataSaverBackend != null) {
+            return mDataSaverBackend.isWhitelisted(mAppItem.key);
+        }
+        return false;
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+
+        String pkg = mPackages.size() != 0 ? mPackages.valueAt(0) : null;
+        int uid = 0;
+        if (pkg != null) {
+            try {
+                uid = mPackageManager.getPackageUidAsUser(pkg,
+                        UserHandle.getUserId(mAppItem.key));
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.w(TAG, "Skipping UID because cannot find package " + pkg);
+            }
+        }
+
+        final boolean showInfoButton = mAppItem.key > 0;
+
+        final Activity activity = getActivity();
+        final Preference pref = EntityHeaderController
+                .newInstance(activity, this, null /* header */)
+                .setRecyclerView(getListView(), getSettingsLifecycle())
+                .setUid(uid)
+                .setHasAppInfoLink(showInfoButton)
+                .setButtonActions(EntityHeaderController.ActionType.ACTION_NONE,
+                        EntityHeaderController.ActionType.ACTION_NONE)
+                .setIcon(mIcon)
+                .setLabel(mLabel)
+                .setPackageName(pkg)
+                .done(activity, getPrefContext());
+        getPreferenceScreen().addPreference(pref);
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsEvent.APP_DATA_USAGE;
+    }
+
+    private AdapterView.OnItemSelectedListener mCycleListener =
+            new AdapterView.OnItemSelectedListener() {
+        @Override
+        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+            final CycleAdapter.CycleItem cycle = (CycleAdapter.CycleItem) mCycle.getSelectedItem();
+
+            mStart = cycle.start;
+            mEnd = cycle.end;
+            bindData();
+        }
+
+        @Override
+        public void onNothingSelected(AdapterView<?> parent) {
+            // ignored
+        }
+    };
+
+    private final LoaderManager.LoaderCallbacks<ChartData> mChartDataCallbacks =
+            new LoaderManager.LoaderCallbacks<ChartData>() {
+        @Override
+        public Loader<ChartData> onCreateLoader(int id, Bundle args) {
+            return new ChartDataLoaderCompat(getActivity(), mStatsSession, args);
+        }
+
+        @Override
+        public void onLoadFinished(Loader<ChartData> loader, ChartData data) {
+            mChartData = data;
+            mCycleAdapter.updateCycleList(mPolicy, mChartData);
+            bindData();
+        }
+
+        @Override
+        public void onLoaderReset(Loader<ChartData> loader) {
+        }
+    };
+
+    private final LoaderManager.LoaderCallbacks<ArraySet<Preference>> mAppPrefCallbacks =
+        new LoaderManager.LoaderCallbacks<ArraySet<Preference>>() {
+            @Override
+            public Loader<ArraySet<Preference>> onCreateLoader(int i, Bundle bundle) {
+                return new AppPrefLoader(getPrefContext(), mPackages, getPackageManager());
+            }
+
+            @Override
+            public void onLoadFinished(Loader<ArraySet<Preference>> loader,
+                    ArraySet<Preference> preferences) {
+                if (preferences != null && mAppList != null) {
+                    for (Preference preference : preferences) {
+                        mAppList.addPreference(preference);
+                    }
+                }
+            }
+
+            @Override
+            public void onLoaderReset(Loader<ArraySet<Preference>> loader) {
+            }
+        };
+
+    @Override
+    public void onDataSaverChanged(boolean isDataSaving) {
+
+    }
+
+    @Override
+    public void onWhitelistStatusChanged(int uid, boolean isWhitelisted) {
+        if (mAppItem.uids.get(uid, false)) {
+            updatePrefs(getAppRestrictBackground(), isWhitelisted);
+        }
+    }
+
+    @Override
+    public void onBlacklistStatusChanged(int uid, boolean isBlacklisted) {
+        if (mAppItem.uids.get(uid, false)) {
+            updatePrefs(isBlacklisted, getUnrestrictData());
+        }
+    }
+}
diff --git a/src/com/android/settings/datausage/ChartDataUsagePreference.java b/src/com/android/settings/datausage/ChartDataUsagePreference.java
index 321cf74..a0cef3a 100644
--- a/src/com/android/settings/datausage/ChartDataUsagePreference.java
+++ b/src/com/android/settings/datausage/ChartDataUsagePreference.java
@@ -24,7 +24,6 @@
 import android.text.style.ForegroundColorSpan;
 import android.util.AttributeSet;
 import android.util.FeatureFlagUtils;
-import android.util.Pair;
 import android.util.SparseIntArray;
 
 import androidx.annotation.VisibleForTesting;
@@ -35,6 +34,7 @@
 import com.android.settings.Utils;
 import com.android.settings.core.FeatureFlags;
 import com.android.settings.widget.UsageView;
+import com.android.settingslib.net.NetworkCycleChartData;
 import com.android.settingslib.net.NetworkCycleData;
 
 import java.util.List;
@@ -53,7 +53,7 @@
     private long mEnd;
     @Deprecated
     private NetworkStatsHistory mNetwork;
-    private NetworkCycleData mNetworkCycleData;
+    private NetworkCycleChartData mNetworkCycleChartData;
     private int mSecondaryColor;
     private int mSeriesColor;
 
@@ -70,7 +70,7 @@
         super.onBindViewHolder(holder);
         final UsageView chart = (UsageView) holder.findViewById(R.id.data_usage);
         if (FeatureFlagUtils.isEnabled(getContext(), FeatureFlags.DATA_USAGE_V2)) {
-            if (mNetworkCycleData == null) {
+            if (mNetworkCycleChartData == null) {
                 return;
             }
         } else {
@@ -83,7 +83,7 @@
         chart.clearPaths();
         chart.configureGraph(toInt(mEnd - mStart), top);
         if (FeatureFlagUtils.isEnabled(getContext(), FeatureFlags.DATA_USAGE_V2)) {
-            calcPoints(chart, mNetworkCycleData.usageBuckets);
+            calcPoints(chart, mNetworkCycleChartData.getUsageBuckets());
         } else {
             calcPoints(chart);
         }
@@ -98,7 +98,7 @@
     public int getTop() {
         long totalData = 0;
         if (FeatureFlagUtils.isEnabled(getContext(), FeatureFlags.DATA_USAGE_V2)) {
-            totalData = mNetworkCycleData.totalUsage;
+            totalData = mNetworkCycleChartData.getTotalUsage();
         } else {
             NetworkStatsHistory.Entry entry = null;
             final int start = mNetwork.getIndexBefore(mStart);
@@ -158,14 +158,14 @@
 
         long totalData = 0;
         for (NetworkCycleData data : usageSummary) {
-            final long startTime = data.startTime;
-            final long endTime = data.endTime;
+            final long startTime = data.getStartTime();
+            final long endTime = data.getEndTime();
 
             // increment by current bucket total
-            totalData += data.totalUsage;
+            totalData += data.getTotalUsage();
 
             if (points.size() == 1) {
-                points.put(toInt(data.startTime - mStart) - 1, -1);
+                points.put(toInt(startTime - mStart) - 1, -1);
             }
             points.put(toInt(startTime - mStart + 1), (int) (totalData / RESOLUTION));
             points.put(toInt(endTime - mStart), (int) (totalData / RESOLUTION));
@@ -241,10 +241,10 @@
         notifyChanged();
     }
 
-    public void setNetworkCycleData(NetworkCycleData data) {
-        mNetworkCycleData = data;
-        mStart = data.startTime;
-        mEnd = data.endTime;
+    public void setNetworkCycleData(NetworkCycleChartData data) {
+        mNetworkCycleChartData = data;
+        mStart = data.getStartTime();
+        mEnd = data.getEndTime();
         notifyChanged();
     }
 
diff --git a/src/com/android/settings/datausage/CycleAdapter.java b/src/com/android/settings/datausage/CycleAdapter.java
index bc4f649..9378dea 100644
--- a/src/com/android/settings/datausage/CycleAdapter.java
+++ b/src/com/android/settings/datausage/CycleAdapter.java
@@ -156,7 +156,7 @@
      * Rebuild list based on network data. Always selects the newest item,
      * updating the inspection range on chartData.
      */
-    public boolean updateCycleList(List<NetworkCycleData> cycleData) {
+    public boolean updateCycleList(List<? extends NetworkCycleData> cycleData) {
         // stash away currently selected cycle to try restoring below
         final CycleAdapter.CycleItem previousItem = (CycleAdapter.CycleItem)
             mSpinner.getSelectedItem();
@@ -164,7 +164,7 @@
 
         final Context context = getContext();
         for (NetworkCycleData data : cycleData) {
-            add(new CycleAdapter.CycleItem(context, data.startTime, data.endTime));
+            add(new CycleAdapter.CycleItem(context, data.getStartTime(), data.getEndTime()));
         }
 
         // force pick the current cycle (first item)
diff --git a/src/com/android/settings/datausage/DataUsageListV2.java b/src/com/android/settings/datausage/DataUsageListV2.java
index 301eb44..ea652b3 100644
--- a/src/com/android/settings/datausage/DataUsageListV2.java
+++ b/src/com/android/settings/datausage/DataUsageListV2.java
@@ -61,8 +61,8 @@
 import com.android.settings.widget.LoadingViewController;
 import com.android.settingslib.AppItem;
 import com.android.settingslib.net.ChartDataLoaderCompat;
-import com.android.settingslib.net.NetworkCycleDataLoader;
-import com.android.settingslib.net.NetworkCycleData;
+import com.android.settingslib.net.NetworkCycleChartDataLoader;
+import com.android.settingslib.net.NetworkCycleChartData;
 import com.android.settingslib.net.NetworkStatsSummaryLoader;
 import com.android.settingslib.net.UidDetailProvider;
 
@@ -107,7 +107,7 @@
     int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     @VisibleForTesting
     int mNetworkType;
-    private List<NetworkCycleData> mCycleData;
+    private List<NetworkCycleChartData> mCycleData;
 
     private LoadingViewController mLoadingViewController;
     private UidDetailProvider mUidDetailProvider;
@@ -322,7 +322,7 @@
                 mNetworkStatsDetailCallbacks);
 
         final long totalBytes = mCycleData != null
-            ? mCycleData.get(mCycleSpinner.getSelectedItemPosition()).totalUsage : 0;
+            ? mCycleData.get(mCycleSpinner.getSelectedItemPosition()).getTotalUsage() : 0;
         final CharSequence totalPhrase = DataUsageUtils.formatDataUsage(getActivity(), totalBytes);
         mUsageAmount.setTitle(getString(R.string.data_used_template, totalPhrase));
     }
@@ -485,11 +485,11 @@
         }
     };
 
-    private final LoaderCallbacks<List<NetworkCycleData>> mNetworkCycleDataCallbacks =
-            new LoaderCallbacks<List<NetworkCycleData>>() {
+    private final LoaderCallbacks<List<NetworkCycleChartData>> mNetworkCycleDataCallbacks =
+            new LoaderCallbacks<List<NetworkCycleChartData>>() {
         @Override
-        public Loader<List<NetworkCycleData>> onCreateLoader(int id, Bundle args) {
-            return new NetworkCycleDataLoader.Builder(getContext())
+        public Loader<List<NetworkCycleChartData>> onCreateLoader(int id, Bundle args) {
+            return NetworkCycleChartDataLoader.builder(getContext())
                     .setNetworkPolicy(services.mPolicyEditor.getPolicy(mTemplate))
                     .setNetworkType(mNetworkType)
                     .setNetworkTemplate(mTemplate)
@@ -498,8 +498,8 @@
         }
 
         @Override
-        public void onLoadFinished(Loader<List<NetworkCycleData>> loader,
-                List<NetworkCycleData> data) {
+        public void onLoadFinished(Loader<List<NetworkCycleChartData>> loader,
+                List<NetworkCycleChartData> data) {
             mLoadingViewController.showContent(false /* animate */);
             mCycleData = data;
             // calculate policy cycles based on available data
@@ -507,7 +507,7 @@
         }
 
         @Override
-        public void onLoaderReset(Loader<List<NetworkCycleData>> loader) {
+        public void onLoaderReset(Loader<List<NetworkCycleChartData>> loader) {
             mCycleData = null;
         }
     };
diff --git a/src/com/android/settings/development/AngleEnabledAppPreferenceController.java b/src/com/android/settings/development/AngleEnabledAppPreferenceController.java
new file mode 100644
index 0000000..3a7f6bf
--- /dev/null
+++ b/src/com/android/settings/development/AngleEnabledAppPreferenceController.java
@@ -0,0 +1,119 @@
+/*
+ * 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 com.android.settings.development.DevelopmentOptionsActivityRequestCodes
+        .REQUEST_CODE_ANGLE_ENABLED_APP;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.provider.Settings;
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.development.DeveloperOptionsPreferenceController;
+
+public class AngleEnabledAppPreferenceController extends DeveloperOptionsPreferenceController
+        implements PreferenceControllerMixin, OnActivityResultListener {
+
+    private static final String ANGLE_ENABLED_APP_KEY = "angle_enabled_app";
+
+    private final DevelopmentSettingsDashboardFragment mFragment;
+    private final PackageManager mPackageManager;
+
+    public AngleEnabledAppPreferenceController(Context context,
+            DevelopmentSettingsDashboardFragment fragment) {
+        super(context);
+        mFragment = fragment;
+        mPackageManager = mContext.getPackageManager();
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return ANGLE_ENABLED_APP_KEY;
+    }
+
+    @Override
+    public boolean handlePreferenceTreeClick(Preference preference) {
+        if (ANGLE_ENABLED_APP_KEY.equals(preference.getKey())) {
+            // pass it on to settings
+            final Intent intent = getActivityStartIntent();
+            mFragment.startActivityForResult(intent, REQUEST_CODE_ANGLE_ENABLED_APP);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        updatePreferenceSummary();
+    }
+
+    @Override
+    public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode != REQUEST_CODE_ANGLE_ENABLED_APP || resultCode != Activity.RESULT_OK) {
+            return false;
+        }
+        Settings.Global.putString(mContext.getContentResolver(), Settings.Global.ANGLE_ENABLED_APP,
+                data.getAction());
+        updatePreferenceSummary();
+        return true;
+    }
+
+    @Override
+    protected void onDeveloperOptionsSwitchDisabled() {
+        super.onDeveloperOptionsSwitchDisabled();
+        mPreference.setSummary(mContext.getResources().getString(
+                R.string.angle_enabled_app_not_set));
+    }
+
+    @VisibleForTesting
+    Intent getActivityStartIntent() {
+        Intent intent = new Intent(mContext, AppPicker.class);
+        intent.putExtra(AppPicker.EXTRA_NON_SYSTEM, true /* value */);
+        return intent;
+    }
+
+    private void updatePreferenceSummary() {
+        final String angleEnabledApp = Settings.Global.getString(
+                mContext.getContentResolver(), Settings.Global.ANGLE_ENABLED_APP);
+        if (angleEnabledApp != null && angleEnabledApp.length() > 0) {
+            mPreference.setSummary(mContext.getResources().getString(
+                    R.string.angle_enabled_app_set,
+                    getAppLabel(angleEnabledApp)));
+        } else {
+            mPreference.setSummary(mContext.getResources().getString(
+                    R.string.angle_enabled_app_not_set));
+        }
+    }
+
+    private String getAppLabel(String angleEnabledApp) {
+        try {
+            final ApplicationInfo ai = mPackageManager.getApplicationInfo(angleEnabledApp,
+                    PackageManager.GET_DISABLED_COMPONENTS);
+            final CharSequence lab = mPackageManager.getApplicationLabel(ai);
+            return lab != null ? lab.toString() : angleEnabledApp;
+        } catch (PackageManager.NameNotFoundException e) {
+            return angleEnabledApp;
+        }
+    }
+}
diff --git a/src/com/android/settings/development/AppPicker.java b/src/com/android/settings/development/AppPicker.java
index 433f31a..04f318f 100644
--- a/src/com/android/settings/development/AppPicker.java
+++ b/src/com/android/settings/development/AppPicker.java
@@ -45,9 +45,11 @@
     public static final String EXTRA_REQUESTIING_PERMISSION
             = "com.android.settings.extra.REQUESTIING_PERMISSION";
     public static final String EXTRA_DEBUGGABLE = "com.android.settings.extra.DEBUGGABLE";
+    public static final String EXTRA_NON_SYSTEM = "com.android.settings.extra.NON_SYSTEM";
 
     private String mPermissionName;
     private boolean mDebuggableOnly;
+    private boolean mNonSystemOnly;
 
     @Override
     protected void onCreate(Bundle icicle) {
@@ -55,6 +57,7 @@
 
         mPermissionName = getIntent().getStringExtra(EXTRA_REQUESTIING_PERMISSION);
         mDebuggableOnly = getIntent().getBooleanExtra(EXTRA_DEBUGGABLE, false);
+        mNonSystemOnly = getIntent().getBooleanExtra(EXTRA_NON_SYSTEM, false);
 
         mAdapter = new AppListAdapter(this);
         if (mAdapter.getCount() <= 0) {
@@ -113,6 +116,11 @@
                     }
                 }
 
+                // Filter out apps that are system apps if requested
+                if (mNonSystemOnly && ai.isSystemApp()) {
+                    continue;
+                }
+
                 // Filter out apps that do not request the permission if required.
                 if (mPermissionName != null) {
                     boolean requestsPermission = false;
diff --git a/src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java b/src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java
index b7b2759..6e3ec93 100644
--- a/src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java
+++ b/src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java
@@ -25,4 +25,6 @@
     int REQUEST_CODE_DEBUG_APP = 1;
 
     int REQUEST_MOCK_LOCATION_APP = 2;
+
+    int REQUEST_CODE_ANGLE_ENABLED_APP = 3;
 }
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index 873cec4..cc8bd2e 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -405,6 +405,7 @@
         controllers.add(new SelectDebugAppPreferenceController(context, fragment));
         controllers.add(new WaitForDebuggerPreferenceController(context));
         controllers.add(new EnableGpuDebugLayersPreferenceController(context));
+        controllers.add(new AngleEnabledAppPreferenceController(context, fragment));
         controllers.add(new VerifyAppsOverUsbPreferenceController(context));
         controllers.add(new LogdSizePreferenceController(context));
         controllers.add(new LogPersistPreferenceController(context, fragment, lifecycle));
diff --git a/src/com/android/settings/gestures/ReachGesturePreferenceController.java b/src/com/android/settings/gestures/WakeLockScreenGesturePreferenceController.java
similarity index 69%
rename from src/com/android/settings/gestures/ReachGesturePreferenceController.java
rename to src/com/android/settings/gestures/WakeLockScreenGesturePreferenceController.java
index 6bfe4f3..1f1630e 100644
--- a/src/com/android/settings/gestures/ReachGesturePreferenceController.java
+++ b/src/com/android/settings/gestures/WakeLockScreenGesturePreferenceController.java
@@ -16,7 +16,7 @@
 
 package com.android.settings.gestures;
 
-import static android.provider.Settings.Secure.DOZE_REACH_GESTURE;
+import static android.provider.Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE;
 
 import android.annotation.UserIdInt;
 import android.content.Context;
@@ -26,33 +26,34 @@
 
 import com.android.internal.hardware.AmbientDisplayConfiguration;
 
-public class ReachGesturePreferenceController extends GesturePreferenceController {
+public class WakeLockScreenGesturePreferenceController extends GesturePreferenceController {
 
     private static final int ON = 1;
     private static final int OFF = 0;
 
-    private static final String PREF_KEY_VIDEO = "gesture_reach_video";
-    private final String mReachUpPrefKey;
+    private static final String PREF_KEY_VIDEO = "gesture_wake_lock_screen_video";
+    private final String mWakeLockScreenPrefKey;
 
     private AmbientDisplayConfiguration mAmbientConfig;
     @UserIdInt
     private final int mUserId;
 
-    public ReachGesturePreferenceController(Context context, String key) {
+    public WakeLockScreenGesturePreferenceController(Context context, String key) {
         super(context, key);
         mUserId = UserHandle.myUserId();
-        mReachUpPrefKey = key;
+        mWakeLockScreenPrefKey = key;
     }
 
-    public ReachGesturePreferenceController setConfig(AmbientDisplayConfiguration config) {
+    public WakeLockScreenGesturePreferenceController
+        setConfig(AmbientDisplayConfiguration config) {
         mAmbientConfig = config;
         return this;
     }
 
     @Override
     public int getAvailabilityStatus() {
-        // No hardware support for Reach Gesture
-        if (!getAmbientConfig().reachGestureAvailable()) {
+        // No hardware support for this Gesture
+        if (!getAmbientConfig().wakeLockScreenGestureAvailable()) {
             return UNSUPPORTED_ON_DEVICE;
         }
 
@@ -61,7 +62,7 @@
 
     @Override
     public boolean isSliceable() {
-        return TextUtils.equals(getPreferenceKey(), "gesture_reach");
+        return TextUtils.equals(getPreferenceKey(), "gesture_wake_lock_screen");
     }
 
     @Override
@@ -71,17 +72,17 @@
 
     @Override
     public boolean isChecked() {
-        return getAmbientConfig().reachGestureEnabled(mUserId);
+        return getAmbientConfig().wakeLockScreenGestureEnabled(mUserId);
     }
 
     @Override
     public String getPreferenceKey() {
-        return mReachUpPrefKey;
+        return mWakeLockScreenPrefKey;
     }
 
     @Override
     public boolean setChecked(boolean isChecked) {
-        return Settings.Secure.putInt(mContext.getContentResolver(), DOZE_REACH_GESTURE,
+        return Settings.Secure.putInt(mContext.getContentResolver(), DOZE_WAKE_LOCK_SCREEN_GESTURE,
                 isChecked ? ON : OFF);
     }
 
diff --git a/src/com/android/settings/gestures/ReachGestureSettings.java b/src/com/android/settings/gestures/WakeLockScreenGestureSettings.java
similarity index 84%
rename from src/com/android/settings/gestures/ReachGestureSettings.java
rename to src/com/android/settings/gestures/WakeLockScreenGestureSettings.java
index 3df9fcf..e6b7265 100644
--- a/src/com/android/settings/gestures/ReachGestureSettings.java
+++ b/src/com/android/settings/gestures/WakeLockScreenGestureSettings.java
@@ -33,12 +33,12 @@
 import java.util.List;
 
 @SearchIndexable
-public class ReachGestureSettings extends DashboardFragment {
+public class WakeLockScreenGestureSettings extends DashboardFragment {
 
-    private static final String TAG = "ReachGestureSettings";
+    private static final String TAG = "WakeLockScreenGestureSettings";
 
     public static final String PREF_KEY_SUGGESTION_COMPLETE =
-            "pref_reach_gesture_suggestion_complete";
+            "pref_wake_lock_screen_gesture_suggestion_complete";
 
     @Override
     public void onAttach(Context context) {
@@ -48,13 +48,13 @@
         SharedPreferences prefs = suggestionFeatureProvider.getSharedPrefs(context);
         prefs.edit().putBoolean(PREF_KEY_SUGGESTION_COMPLETE, true).apply();
 
-        use(ReachGesturePreferenceController.class)
+        use(WakeLockScreenGesturePreferenceController.class)
             .setConfig(new AmbientDisplayConfiguration(context));
     }
 
     @Override
     public int getMetricsCategory() {
-        return MetricsProto.MetricsEvent.SETTINGS_GESTURE_REACH;
+        return MetricsProto.MetricsEvent.SETTINGS_GESTURE_WAKE_LOCK_SCREEN;
     }
 
     @Override
@@ -64,7 +64,7 @@
 
     @Override
     protected int getPreferenceScreenResId() {
-        return R.xml.reach_gesture_settings;
+        return R.xml.wake_lock_screen_gesture_settings;
     }
 
     public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
@@ -73,7 +73,7 @@
                 public List<SearchIndexableResource> getXmlResourcesToIndex(
                         Context context, boolean enabled) {
                     final SearchIndexableResource sir = new SearchIndexableResource(context);
-                    sir.xmlResId = R.xml.reach_gesture_settings;
+                    sir.xmlResId = R.xml.wake_lock_screen_gesture_settings;
                     return Arrays.asList(sir);
                 }
             };
diff --git a/src/com/android/settings/homepage/CardContentLoader.java b/src/com/android/settings/homepage/CardContentLoader.java
index 9805ae3..5b81a23 100644
--- a/src/com/android/settings/homepage/CardContentLoader.java
+++ b/src/com/android/settings/homepage/CardContentLoader.java
@@ -17,16 +17,21 @@
 package com.android.settings.homepage;
 
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.database.Cursor;
+import android.util.Log;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
 
+import com.android.settings.homepage.deviceinfo.DataUsageSlice;
 import com.android.settingslib.utils.AsyncLoaderCompat;
 
 import java.util.ArrayList;
 import java.util.List;
 
 public class CardContentLoader extends AsyncLoaderCompat<List<ContextualCard>> {
+    private static final String TAG = "CardContentLoader";
     static final int CARD_CONTENT_LOADER_ID = 1;
 
     private Context mContext;
@@ -49,9 +54,9 @@
     @Override
     public List<ContextualCard> loadInBackground() {
         final List<ContextualCard> result = new ArrayList<>();
-        try (Cursor cursor = CardDatabaseHelper.getInstance(mContext).getContextualCards()) {
+        try (Cursor cursor = getContextualCardsFromProvider()) {
             if (cursor.getCount() == 0) {
-                //TODO(b/113372471): Load Default static cards and return 3 static cards
+                result.addAll(createStaticCards());
                 return result;
             }
             for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
@@ -65,4 +70,58 @@
         }
         return result;
     }
+
+    @VisibleForTesting
+    Cursor getContextualCardsFromProvider() {
+        return CardDatabaseHelper.getInstance(mContext).getContextualCards();
+    }
+
+    @VisibleForTesting
+    List<ContextualCard> createStaticCards() {
+        final long appVersionCode = getAppVersionCode();
+        final String packageName = mContext.getPackageName();
+        final double rankingScore = 0.0;
+        final List<ContextualCard> result = new ArrayList() {{
+            add(new ContextualCard.Builder()
+                    .setSliceUri(DataUsageSlice.DATA_USAGE_CARD_URI.toString())
+                    .setName(packageName + "/" + DataUsageSlice.PATH_DATA_USAGE_CARD)
+                    .setPackageName(packageName)
+                    .setRankingScore(rankingScore)
+                    .setAppVersion(appVersionCode)
+                    .setCardType(ContextualCard.CardType.SLICE)
+                    .setIsHalfWidth(true)
+                    .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/intent/battery_card")
+                    .setName(packageName + "/" + "battery_card")
+                    .setPackageName(packageName)
+                    .setRankingScore(rankingScore)
+                    .setAppVersion(appVersionCode)
+                    .setCardType(ContextualCard.CardType.SLICE)
+                    .setIsHalfWidth(true)
+                    .build());
+            add(new ContextualCard.Builder()
+                    .setSliceUri("content://com.android.settings.slices/intent/device_info_card")
+                    .setName(packageName + "/" + "device_info_card")
+                    .setPackageName(packageName)
+                    .setRankingScore(rankingScore)
+                    .setAppVersion(appVersionCode)
+                    .setCardType(ContextualCard.CardType.SLICE)
+                    .setIsHalfWidth(true)
+                    .build());
+        }};
+        return result;
+    }
+
+    private long getAppVersionCode() {
+        try {
+            return mContext.getPackageManager().getPackageInfo(mContext.getPackageName(),
+                    0 /* flags */).getLongVersionCode();
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "Invalid package name for context", e);
+        }
+        return -1L;
+    }
 }
diff --git a/tests/robotests/assets/grandfather_not_implementing_index_provider b/tests/robotests/assets/grandfather_not_implementing_index_provider
index 1ed50c9..38108f7 100644
--- a/tests/robotests/assets/grandfather_not_implementing_index_provider
+++ b/tests/robotests/assets/grandfather_not_implementing_index_provider
@@ -28,6 +28,7 @@
 com.android.settings.bluetooth.BluetoothPairingDetail
 com.android.settings.bluetooth.DevicePickerFragment
 com.android.settings.datausage.AppDataUsage
+com.android.settings.datausage.AppDataUsageV2
 com.android.settings.datausage.DataUsageList
 com.android.settings.datausage.DataUsageListV2
 com.android.settings.datetime.timezone.TimeZoneSettings
diff --git a/tests/robotests/src/com/android/settings/datausage/AppDataUsageV2Test.java b/tests/robotests/src/com/android/settings/datausage/AppDataUsageV2Test.java
new file mode 100644
index 0000000..d979b68
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/datausage/AppDataUsageV2Test.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2017 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.datausage;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.pm.PackageManager;
+import android.net.NetworkPolicyManager;
+import android.os.Bundle;
+import android.util.ArraySet;
+import android.view.View;
+
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.ShadowEntityHeaderController;
+import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal;
+import com.android.settings.widget.EntityHeaderController;
+import com.android.settingslib.AppItem;
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(shadows = {ShadowEntityHeaderController.class, ShadowRestrictedLockUtilsInternal.class})
+public class AppDataUsageV2Test {
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private EntityHeaderController mHeaderController;
+    @Mock
+    private PackageManager mPackageManager;
+
+    private AppDataUsageV2 mFragment;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        FakeFeatureFactory.setupForTest();
+    }
+
+    @After
+    public void tearDown() {
+        ShadowEntityHeaderController.reset();
+    }
+
+    @Test
+    public void bindAppHeader_allWorkApps_shouldNotShowAppInfoLink() {
+        ShadowEntityHeaderController.setUseMock(mHeaderController);
+        when(mHeaderController.setRecyclerView(any(), any())).thenReturn(mHeaderController);
+        when(mHeaderController.setUid(anyInt())).thenReturn(mHeaderController);
+
+        mFragment = spy(new AppDataUsageV2());
+
+        when(mFragment.getPreferenceManager())
+            .thenReturn(mock(PreferenceManager.class, RETURNS_DEEP_STUBS));
+        doReturn(mock(PreferenceScreen.class)).when(mFragment).getPreferenceScreen();
+        ReflectionHelpers.setField(mFragment, "mAppItem", mock(AppItem.class));
+
+        mFragment.onViewCreated(new View(RuntimeEnvironment.application), new Bundle());
+
+        verify(mHeaderController).setHasAppInfoLink(false);
+    }
+
+    @Test
+    public void bindAppHeader_workApp_shouldSetWorkAppUid() throws
+            PackageManager.NameNotFoundException {
+        final int fakeUserId = 100;
+
+        mFragment = spy(new AppDataUsageV2());
+        final ArraySet<String> packages = new ArraySet<>();
+        packages.add("pkg");
+        final AppItem appItem = new AppItem(123456789);
+
+        ReflectionHelpers.setField(mFragment, "mPackageManager", mPackageManager);
+        ReflectionHelpers.setField(mFragment, "mAppItem", appItem);
+        ReflectionHelpers.setField(mFragment, "mPackages", packages);
+
+        when(mPackageManager.getPackageUidAsUser(anyString(), anyInt()))
+                .thenReturn(fakeUserId);
+
+        ShadowEntityHeaderController.setUseMock(mHeaderController);
+        when(mHeaderController.setRecyclerView(any(), any())).thenReturn(mHeaderController);
+        when(mHeaderController.setUid(fakeUserId)).thenReturn(mHeaderController);
+        when(mHeaderController.setHasAppInfoLink(anyBoolean())).thenReturn(mHeaderController);
+
+        when(mFragment.getPreferenceManager())
+            .thenReturn(mock(PreferenceManager.class, RETURNS_DEEP_STUBS));
+        doReturn(mock(PreferenceScreen.class)).when(mFragment).getPreferenceScreen();
+
+        mFragment.onViewCreated(new View(RuntimeEnvironment.application), new Bundle());
+
+        verify(mHeaderController).setHasAppInfoLink(true);
+        verify(mHeaderController).setUid(fakeUserId);
+    }
+
+    @Test
+    public void changePreference_backgroundData_shouldUpdateUI() {
+        mFragment = spy(new AppDataUsageV2());
+        final AppItem appItem = new AppItem(123456789);
+        final RestrictedSwitchPreference pref = mock(RestrictedSwitchPreference.class);
+        final DataSaverBackend dataSaverBackend = mock(DataSaverBackend.class);
+        ReflectionHelpers.setField(mFragment, "mAppItem", appItem);
+        ReflectionHelpers.setField(mFragment, "mRestrictBackground", pref);
+        ReflectionHelpers.setField(mFragment, "mDataSaverBackend", dataSaverBackend);
+
+        doNothing().when(mFragment).updatePrefs();
+
+        mFragment.onPreferenceChange(pref, true /* value */);
+
+        verify(mFragment).updatePrefs();
+    }
+
+    @Test
+    public void updatePrefs_restrictedByAdmin_shouldDisablePreference() {
+        mFragment = spy(new AppDataUsageV2());
+        final int testUid = 123123;
+        final AppItem appItem = new AppItem(testUid);
+        final RestrictedSwitchPreference restrictBackgroundPref
+                = mock(RestrictedSwitchPreference.class);
+        final RestrictedSwitchPreference unrestrictedDataPref
+                = mock(RestrictedSwitchPreference.class);
+        final DataSaverBackend dataSaverBackend = mock(DataSaverBackend.class);
+        final NetworkPolicyManager networkPolicyManager = mock(NetworkPolicyManager.class);
+        ReflectionHelpers.setField(mFragment, "mAppItem", appItem);
+        ReflectionHelpers.setField(mFragment, "mRestrictBackground", restrictBackgroundPref);
+        ReflectionHelpers.setField(mFragment, "mUnrestrictedData", unrestrictedDataPref);
+        ReflectionHelpers.setField(mFragment, "mDataSaverBackend", dataSaverBackend);
+        ReflectionHelpers.setField(mFragment.services, "mPolicyManager", networkPolicyManager);
+
+        ShadowRestrictedLockUtilsInternal.setRestricted(true);
+        doReturn(NetworkPolicyManager.POLICY_NONE).when(networkPolicyManager)
+                .getUidPolicy(testUid);
+
+        mFragment.updatePrefs();
+
+        verify(restrictBackgroundPref).setDisabledByAdmin(any(EnforcedAdmin.class));
+        verify(unrestrictedDataPref).setDisabledByAdmin(any(EnforcedAdmin.class));
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/development/AngleEnabledAppPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/AngleEnabledAppPreferenceControllerTest.java
new file mode 100644
index 0000000..03837c2
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/AngleEnabledAppPreferenceControllerTest.java
@@ -0,0 +1,138 @@
+/*
+ * 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 com.android.settings.development.DevelopmentOptionsActivityRequestCodes.REQUEST_CODE_ANGLE_ENABLED_APP;
+import static com.google.common.truth.Truth.assertThat;
+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.Activity;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.provider.Settings;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+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 AngleEnabledAppPreferenceControllerTest {
+
+    @Mock
+    private Preference mPreference;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+    @Mock
+    private DevelopmentSettingsDashboardFragment mFragment;
+    @Mock
+    private PackageManager mPackageManager;
+
+    private Context mContext;
+    private AngleEnabledAppPreferenceController mController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mController = spy(new AngleEnabledAppPreferenceController(mContext, mFragment));
+        ReflectionHelpers
+            .setField(mController, "mPackageManager" /* field name */, mPackageManager);
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey()))
+            .thenReturn(mPreference);
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void handlePreferenceTreeClick_preferenceClicked_launchActivity() {
+        final Intent activityStartIntent = new Intent(mContext, AppPicker.class);
+        final String preferenceKey = mController.getPreferenceKey();
+        doReturn(activityStartIntent).when(mController).getActivityStartIntent();
+        when(mPreference.getKey()).thenReturn(preferenceKey);
+        mController.handlePreferenceTreeClick(mPreference);
+
+        verify(mFragment).startActivityForResult(activityStartIntent,
+                REQUEST_CODE_ANGLE_ENABLED_APP);
+    }
+
+    @Test
+    public void updateState_foobarAppSelected_shouldUpdateSummaryWithAngleEnabledAppLabel() {
+        final String angleEnabledApp = "foobar";
+        final ContentResolver contentResolver = mContext.getContentResolver();
+        Settings.Global.putString(contentResolver, Settings.Global.ANGLE_ENABLED_APP,
+                angleEnabledApp);
+        mController.updateState(mPreference);
+
+        verify(mPreference).setSummary(
+                mContext.getString(R.string.angle_enabled_app_set, angleEnabledApp));
+    }
+
+    @Test
+    public void updateState_noAppSelected_shouldUpdateSummaryWithNoAppSelected() {
+        final String angleEnabledApp = null;
+        final ContentResolver contentResolver = mContext.getContentResolver();
+        Settings.Global.putString(contentResolver, Settings.Global.ANGLE_ENABLED_APP,
+                angleEnabledApp);
+        mController.updateState(mPreference);
+
+        verify(mPreference).setSummary(
+            mContext.getString(R.string.angle_enabled_app_not_set));
+    }
+
+    @Test
+    public void onActivityResult_foobarAppSelected_shouldUpdateSummaryWithAngleEnabledLabel() {
+        Intent activityResultIntent = new Intent(mContext, AppPicker.class);
+        final String appLabel = "foobar";
+        activityResultIntent.setAction(appLabel);
+        final boolean result = mController
+            .onActivityResult(REQUEST_CODE_ANGLE_ENABLED_APP, Activity.RESULT_OK,
+                    activityResultIntent);
+
+        assertThat(result).isTrue();
+        verify(mPreference).setSummary(
+                mContext.getString(R.string.angle_enabled_app_set, appLabel));
+    }
+
+    @Test
+    public void onActivityResult_badRequestCode_shouldReturnFalse() {
+        assertThat(mController.onActivityResult(
+                -1 /* requestCode */, -1 /* resultCode */, null /* intent */)).isFalse();
+    }
+
+    @Test
+    public void onDeveloperOptionsSwitchDisabled_shouldDisablePreference() {
+        mController.onDeveloperOptionsSwitchDisabled();
+
+        assertThat(mPreference.isEnabled()).isFalse();
+        verify(mPreference).setSummary(
+                mContext.getString(R.string.angle_enabled_app_not_set));
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/gestures/ReachGesturePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/WakeLockScreenGesturePreferenceControllerTest.java
similarity index 73%
rename from tests/robotests/src/com/android/settings/gestures/ReachGesturePreferenceControllerTest.java
rename to tests/robotests/src/com/android/settings/gestures/WakeLockScreenGesturePreferenceControllerTest.java
index 78e899c..c6249fb 100644
--- a/tests/robotests/src/com/android/settings/gestures/ReachGesturePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/gestures/WakeLockScreenGesturePreferenceControllerTest.java
@@ -37,41 +37,41 @@
 import org.mockito.MockitoAnnotations;
 
 @RunWith(SettingsRobolectricTestRunner.class)
-public class ReachGesturePreferenceControllerTest {
+public class WakeLockScreenGesturePreferenceControllerTest {
 
-    private static final String KEY_REACH = "gesture_reach";
+    private static final String KEY_WAKE_LOCK_SCREEN = "gesture_wake_lock_screen";
 
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private Context mContext;
     @Mock
     private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
 
-    private ReachGesturePreferenceController mController;
+    private WakeLockScreenGesturePreferenceController mController;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mController = new ReachGesturePreferenceController(mContext, KEY_REACH);
+        mController = new WakeLockScreenGesturePreferenceController(mContext, KEY_WAKE_LOCK_SCREEN);
         mController.setConfig(mAmbientDisplayConfiguration);
     }
 
     @Test
     public void testIsChecked_configIsSet_shouldReturnTrue() {
         // Set the setting to be enabled.
-        when(mAmbientDisplayConfiguration.reachGestureEnabled(anyInt())).thenReturn(true);
+        when(mAmbientDisplayConfiguration.wakeLockScreenGestureEnabled(anyInt())).thenReturn(true);
         assertThat(mController.isChecked()).isTrue();
     }
 
     @Test
     public void testIsChecked_configIsNotSet_shouldReturnFalse() {
         // Set the setting to be disabled.
-        when(mAmbientDisplayConfiguration.reachGestureEnabled(anyInt())).thenReturn(false);
+        when(mAmbientDisplayConfiguration.wakeLockScreenGestureEnabled(anyInt())).thenReturn(false);
         assertThat(mController.isChecked()).isFalse();
     }
 
     @Test
     public void getAvailabilityStatus_gestureNotSupported_UNSUPPORTED_ON_DEVICE() {
-        when(mAmbientDisplayConfiguration.reachGestureAvailable()).thenReturn(false);
+        when(mAmbientDisplayConfiguration.wakeLockScreenGestureAvailable()).thenReturn(false);
         final int availabilityStatus = mController.getAvailabilityStatus();
 
         assertThat(availabilityStatus).isEqualTo(UNSUPPORTED_ON_DEVICE);
@@ -79,7 +79,7 @@
 
     @Test
     public void getAvailabilityStatus_gestureSupported_AVAILABLE() {
-        when(mAmbientDisplayConfiguration.reachGestureAvailable()).thenReturn(true);
+        when(mAmbientDisplayConfiguration.wakeLockScreenGestureAvailable()).thenReturn(true);
         final int availabilityStatus = mController.getAvailabilityStatus();
 
         assertThat(availabilityStatus).isEqualTo(AVAILABLE);
@@ -87,15 +87,15 @@
 
     @Test
     public void isSliceableCorrectKey_returnsTrue() {
-        final ReachGesturePreferenceController controller =
-                new ReachGesturePreferenceController(mContext, "gesture_reach");
+        final WakeLockScreenGesturePreferenceController controller =
+                new WakeLockScreenGesturePreferenceController(mContext, KEY_WAKE_LOCK_SCREEN);
         assertThat(controller.isSliceable()).isTrue();
     }
 
     @Test
     public void isSliceableIncorrectKey_returnsFalse() {
-        final ReachGesturePreferenceController controller =
-                new ReachGesturePreferenceController(mContext, "bad_key");
+        final WakeLockScreenGesturePreferenceController controller =
+                new WakeLockScreenGesturePreferenceController(mContext, "bad_key");
         assertThat(controller.isSliceable()).isFalse();
     }
 }
diff --git a/tests/robotests/src/com/android/settings/gestures/ReachGestureSettingsTest.java b/tests/robotests/src/com/android/settings/gestures/WakeLockScreenGestureSettingsTest.java
similarity index 84%
rename from tests/robotests/src/com/android/settings/gestures/ReachGestureSettingsTest.java
rename to tests/robotests/src/com/android/settings/gestures/WakeLockScreenGestureSettingsTest.java
index 9371c71..0cd777d 100644
--- a/tests/robotests/src/com/android/settings/gestures/ReachGestureSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/gestures/WakeLockScreenGestureSettingsTest.java
@@ -30,19 +30,19 @@
 import java.util.List;
 
 @RunWith(SettingsRobolectricTestRunner.class)
-public class ReachGestureSettingsTest {
+public class WakeLockScreenGestureSettingsTest {
 
-    private ReachGestureSettings mSettings;
+    private WakeLockScreenGestureSettings mSettings;
 
     @Before
     public void setUp() {
-        mSettings = new ReachGestureSettings();
+        mSettings = new WakeLockScreenGestureSettings();
     }
 
     @Test
     public void testSearchIndexProvider_shouldIndexResource() {
         final List<SearchIndexableResource> indexRes =
-            ReachGestureSettings.SEARCH_INDEX_DATA_PROVIDER.getXmlResourcesToIndex(
+            WakeLockScreenGestureSettings.SEARCH_INDEX_DATA_PROVIDER.getXmlResourcesToIndex(
                 RuntimeEnvironment.application, true /* enabled */);
 
         assertThat(indexRes).isNotNull();
diff --git a/tests/robotests/src/com/android/settings/homepage/CardContentLoaderTest.java b/tests/robotests/src/com/android/settings/homepage/CardContentLoaderTest.java
new file mode 100644
index 0000000..20ad067
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/homepage/CardContentLoaderTest.java
@@ -0,0 +1,145 @@
+/*
+ * 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+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 CardContentLoaderTest {
+    private static final String[] QUERY_PROJECTION = {
+            CardDatabaseHelper.CardColumns.NAME,
+            CardDatabaseHelper.CardColumns.TYPE,
+            CardDatabaseHelper.CardColumns.SCORE,
+            CardDatabaseHelper.CardColumns.SLICE_URI,
+            CardDatabaseHelper.CardColumns.CATEGORY,
+            CardDatabaseHelper.CardColumns.LOCALIZED_TO_LOCALE,
+            CardDatabaseHelper.CardColumns.PACKAGE_NAME,
+            CardDatabaseHelper.CardColumns.APP_VERSION,
+            CardDatabaseHelper.CardColumns.TITLE_RES_NAME,
+            CardDatabaseHelper.CardColumns.TITLE_TEXT,
+            CardDatabaseHelper.CardColumns.SUMMARY_RES_NAME,
+            CardDatabaseHelper.CardColumns.SUMMARY_TEXT,
+            CardDatabaseHelper.CardColumns.ICON_RES_NAME,
+            CardDatabaseHelper.CardColumns.ICON_RES_ID,
+            CardDatabaseHelper.CardColumns.CARD_ACTION,
+            CardDatabaseHelper.CardColumns.EXPIRE_TIME_MS,
+            CardDatabaseHelper.CardColumns.SUPPORT_HALF_WIDTH
+    };
+
+    private Context mContext;
+    private CardContentLoader mCardContentLoader;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+        mCardContentLoader = spy(new CardContentLoader(mContext));
+    }
+
+    @Test
+    public void loadInBackground_hasDataInDb_shouldReturnData() {
+        final Cursor cursor = generateTwoRowContextualCards();
+        doReturn(cursor).when(mCardContentLoader).getContextualCardsFromProvider();
+
+        final List<ContextualCard> contextualCards = mCardContentLoader.loadInBackground();
+
+        assertThat(contextualCards.size()).isEqualTo(cursor.getCount());
+    }
+
+    @Test
+    public void loadInBackground_hasNoData_shouldReturnThreeDefaultData() {
+        final Cursor cursor = generateEmptyContextualCards();
+        doReturn(cursor).when(mCardContentLoader).getContextualCardsFromProvider();
+
+        final List<ContextualCard> contextualCards = mCardContentLoader.loadInBackground();
+
+        assertThat(contextualCards.size()).isEqualTo(mCardContentLoader.createStaticCards().size());
+    }
+
+    private MatrixCursor generateEmptyContextualCards() {
+        final MatrixCursor result = new MatrixCursor(QUERY_PROJECTION);
+        return result;
+    }
+
+    private MatrixCursor generateTwoRowContextualCards() {
+        final MatrixCursor result = generateEmptyContextualCards();
+        result.addRow(generateFirstFakeData());
+        result.addRow(generateSecondFakeData());
+        return result;
+    }
+
+    private Object[] generateFirstFakeData() {
+        final Object[] ref = new Object[]{
+                "auto_rotate", /* NAME */
+                ContextualCard.CardType.SLICE, /* TYPE */
+                0.5, /* SCORE */
+                "content://com.android.settings.slices/action/auto_rotate", /* SLICE_URI */
+                2, /* CATEGORY */
+                "", /* LOCALIZED_TO_LOCALE */
+                "com.android.settings", /* PACKAGE_NAME */
+                1l, /* APP_VERSION */
+                "", /* TITLE_RES_NAME */
+                "", /* TITLE_TEXT */
+                "", /* SUMMARY_RES_NAME */
+                "", /* SUMMARY_TEXT */
+                "", /* ICON_RES_NAME */
+                0, /* ICON_RES_ID */
+                0, /* CARD_ACTION */
+                -1, /* EXPIRE_TIME_MS */
+                0 /* SUPPORT_HALF_WIDTH */
+        };
+        return ref;
+    }
+
+    private Object[] generateSecondFakeData() {
+        final Object[] ref = new Object[]{
+                "toggle_airplane", /* NAME */
+                ContextualCard.CardType.SLICE, /* TYPE */
+                0.5, /* SCORE */
+                "content://com.android.settings.slices/action/toggle_airplane", /* SLICE_URI */
+                2, /* CATEGORY */
+                "", /* LOCALIZED_TO_LOCALE */
+                "com.android.settings", /* PACKAGE_NAME */
+                1l, /* APP_VERSION */
+                "", /* TITLE_RES_NAME */
+                "", /* TITLE_TEXT */
+                "", /* SUMMARY_RES_NAME */
+                "", /* SUMMARY_TEXT */
+                "", /* ICON_RES_NAME */
+                0, /* ICON_RES_ID */
+                0, /* CARD_ACTION */
+                -1, /* EXPIRE_TIME_MS */
+                0 /* SUPPORT_HALF_WIDTH */
+        };
+        return ref;
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java b/tests/unit/src/com/android/settings/homepage/SettingsHomepageActivityTest.java
similarity index 68%
rename from tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java
rename to tests/unit/src/com/android/settings/homepage/SettingsHomepageActivityTest.java
index 4b237a0..89ec476 100644
--- a/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java
+++ b/tests/unit/src/com/android/settings/homepage/SettingsHomepageActivityTest.java
@@ -16,25 +16,28 @@
 
 package com.android.settings.homepage;
 
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
 import static com.android.settings.homepage.SettingsHomepageActivity.PERSONAL_SETTINGS_TAG;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import android.content.Context;
+import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
 import android.util.FeatureFlagUtils;
 
 import androidx.fragment.app.Fragment;
 
 import com.android.settings.core.FeatureFlags;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.Robolectric;
-import org.robolectric.RuntimeEnvironment;
 
-@RunWith(SettingsRobolectricTestRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class SettingsHomepageActivityTest {
 
     private Context mContext;
@@ -42,16 +45,27 @@
 
     @Before
     public void setUp() {
-        mContext = RuntimeEnvironment.application;
+        mContext = InstrumentationRegistry.getTargetContext();
         FeatureFlagUtils.setEnabled(mContext, FeatureFlags.DYNAMIC_HOMEPAGE, true);
     }
 
+    @After
+    public void tearDown() {
+        FeatureFlagUtils.setEnabled(mContext, FeatureFlags.DYNAMIC_HOMEPAGE, false);
+    }
+
     @Test
     public void launchHomepage_shouldOpenPersonalSettings() {
-        mActivity = Robolectric.setupActivity(SettingsHomepageActivity.class);
+        final Intent intent = new Intent().setClass(mContext, SettingsHomepageActivity.class)
+                .addFlags(FLAG_ACTIVITY_NEW_TASK);
+
+        mActivity = (SettingsHomepageActivity) InstrumentationRegistry.getInstrumentation()
+                .startActivitySync(intent);
+
         final Fragment fragment = mActivity.getSupportFragmentManager()
                 .findFragmentByTag(PERSONAL_SETTINGS_TAG);
 
         assertThat(fragment).isInstanceOf(PersonalSettingsFragment.class);
     }
+
 }