Merge "Added settings UI for camera lift trigger gesture"
diff --git a/res/layout/search_breadcrumb_view.xml b/res/layout/search_breadcrumb_view.xml
index a78b745..08a2651 100644
--- a/res/layout/search_breadcrumb_view.xml
+++ b/res/layout/search_breadcrumb_view.xml
@@ -20,7 +20,7 @@
     android:id="@+id/breadcrumb"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:paddingTop="8dp"
+    android:paddingTop="5dp"
     android:textAppearance="?android:attr/textAppearanceListItemSecondary"
     android:textColor="?android:attr/textColorSecondary"
-    android:ellipsize="marquee"/>
\ No newline at end of file
+    android:ellipsize="marquee"/>
diff --git a/res/layout/search_inline_switch_item.xml b/res/layout/search_inline_switch_item.xml
index 75943c4..526f9fd 100644
--- a/res/layout/search_inline_switch_item.xml
+++ b/res/layout/search_inline_switch_item.xml
@@ -46,9 +46,8 @@
             android:id="@android:id/summary"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:textAppearance="?android:attr/textAppearanceListItemSecondary"
-            android:textColor="?android:attr/textColorSecondary"
-            android:maxLines="10"/>
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceListItemSecondary"/>
 
         <include layout="@layout/search_breadcrumb_view"/>
     </LinearLayout>
diff --git a/res/layout/search_intent_item.xml b/res/layout/search_intent_item.xml
index 31f7f26..98e2a9b 100644
--- a/res/layout/search_intent_item.xml
+++ b/res/layout/search_intent_item.xml
@@ -46,9 +46,8 @@
             android:id="@android:id/summary"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:singleLine="true"
             android:textAppearance="?android:attr/textAppearanceListItemSecondary"
-            android:textColor="?android:attr/textColorSecondary"
-            android:maxLines="3"
             android:ellipsize="marquee"/>
 
         <include layout="@layout/search_breadcrumb_view"/>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 48d4922..cf86956 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4480,10 +4480,10 @@
     <!-- Title for the cellular network in power use UI(i.e. Mobile network scanning: 30% of battery usage) [CHAR_LIMIT=40] -->
     <string name="device_cellular_network">Mobile network scanning</string>
 
-    <!-- Label for time since last full charge in power use UI, i.e. "7 h 20 min ago" [CHAR_LIMIT=60] -->
-    <string name="power_last_full_charge_summary"><xliff:g id="time">%1$s</xliff:g> ago</string>
-    <!-- Label for list of apps using battery in power use UI [CHAR_LIMIT=120] -->
-    <string name="power_usage_list_summary">App usage since full charge (<xliff:g id="time">%1$s</xliff:g> ago)</string>
+    <!-- Label for time since last full charge in power use UI, i.e. "7 h 20 min ago". Note: ^1 should be used in all translations [CHAR_LIMIT=60] -->
+    <string name="power_last_full_charge_summary"><xliff:g id="time">^1</xliff:g> ago</string>
+    <!-- Label for list of apps using battery in power use UI. Note: ^1 should be used in all translations[CHAR_LIMIT=120] -->
+    <string name="power_usage_list_summary">App usage since full charge (<xliff:g id="time">^1</xliff:g> ago)</string>
     <!-- Description for the screen usage item [CHAR_LIMIT=120] -->
     <string name="screen_usage_summary">Amount of time screen has been on since full charge</string>
     <!-- Label for list of different types using battery in power use UI [CHAR_LIMIT=60] -->
@@ -4682,8 +4682,8 @@
     <!-- Representation of a mAh value. [CHAR LIMIT=NONE] -->
     <string name="mah"><xliff:g id="number" example="30">%d</xliff:g> mAh</string>
 
-    <!-- Description for battery usage time for an app, i.e. Used for 30min. [CHAR LIMIT=60] -->
-    <string name="battery_used_for">Used for %1$s</string>
+    <!-- Description for battery usage time for an app, i.e. Used for 30min. Note: ^1 should be used in all translations [CHAR LIMIT=60] -->
+    <string name="battery_used_for">Used for <xliff:g id="time">^1</xliff:g></string>
     <!-- Description for battery usage info for an app, i.e. 60% used by facebook. [CHAR LIMIT=60] -->
     <string name="battery_used_by"><xliff:g id="percent">%1$s</xliff:g> used by <xliff:g id="app">%2$s</xliff:g></string>
     <!-- Description for percentage of battery usage for an app, i.e. Screen: 30% of overall battery. [CHAR LIMIT=60] -->
diff --git a/res/xml/app_list_disclosure_settings.xml b/res/xml/app_list_disclosure_settings.xml
new file mode 100644
index 0000000..2aae95e
--- /dev/null
+++ b/res/xml/app_list_disclosure_settings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+                  xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
+                  android:key="app_list_disclosure_settings">
+    <PreferenceCategory
+            android:key="dashboard_tile_placeholder"/>
+
+    <com.android.settings.widget.FooterPreference
+            android:title="@string/enterprise_privacy_apps_count_estimation_info"
+            android:selectable="false"/>
+</PreferenceScreen>
diff --git a/res/xml/enterprise_privacy_settings.xml b/res/xml/enterprise_privacy_settings.xml
index c2b77b5..dae3b42 100644
--- a/res/xml/enterprise_privacy_settings.xml
+++ b/res/xml/enterprise_privacy_settings.xml
@@ -16,8 +16,8 @@
 
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
                   xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
-        android:key="enterprise_privacy_settings"
-        android:title="@string/enterprise_privacy_settings">
+                  android:key="enterprise_privacy_settings"
+                  android:title="@string/enterprise_privacy_settings">
 
     <!-- Header -->
     <Preference
@@ -46,18 +46,23 @@
 
     <PreferenceCategory android:title="@string/enterprise_privacy_exposure_changes_category">
         <com.android.settings.DividerPreference
+                android:fragment="com.android.settings.enterprise.ApplicationListFragment$EnterpriseInstalledPackages"
                 android:key="number_enterprise_installed_packages"
                 android:title="@string/enterprise_privacy_enterprise_installed_packages"/>
         <com.android.settings.DividerPreference
+                android:fragment="com.android.settings.enterprise.ApplicationListFragment$AdminGrantedPermissionLocation"
                 android:key="enterprise_privacy_number_location_access_packages"
                 android:title="@string/enterprise_privacy_location_access"/>
         <com.android.settings.DividerPreference
+                android:fragment="com.android.settings.enterprise.ApplicationListFragment$AdminGrantedPermissionMicrophone"
                 android:key="enterprise_privacy_number_microphone_access_packages"
                 android:title="@string/enterprise_privacy_microphone_access"/>
         <com.android.settings.DividerPreference
+                android:fragment="com.android.settings.enterprise.ApplicationListFragment$AdminGrantedPermissionCamera"
                 android:key="enterprise_privacy_number_camera_access_packages"
                 android:title="@string/enterprise_privacy_camera_access"/>
         <com.android.settings.DividerPreference
+                android:fragment="com.android.settings.enterprise.EnterpriseSetDefaultAppsListFragment"
                 android:key="number_enterprise_set_default_apps"
                 android:title="@string/enterprise_privacy_enterprise_set_default_apps"/>
         <com.android.settings.DividerPreference
diff --git a/res/xml/enterprise_set_default_apps_settings.xml b/res/xml/enterprise_set_default_apps_settings.xml
new file mode 100644
index 0000000..b006f46
--- /dev/null
+++ b/res/xml/enterprise_set_default_apps_settings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+                  xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
+                  android:key="enterprise_set_default_apps_settings">
+    <PreferenceCategory
+            android:key="dashboard_tile_placeholder"/>
+</PreferenceScreen>
diff --git a/res/xml/suggestion_ordering.xml b/res/xml/suggestion_ordering.xml
index 13a9eaf..f02ac15 100644
--- a/res/xml/suggestion_ordering.xml
+++ b/res/xml/suggestion_ordering.xml
@@ -22,6 +22,7 @@
     <step category="com.android.settings.suggested.category.EMAIL" />
     <step category="com.android.settings.suggested.category.PARTNER_ACCOUNT"
         multiple="true" />
+    <step category="com.android.settings.suggested.category.GESTURE" />
     <step category="com.android.settings.suggested.category.HOTWORD" />
     <step category="com.android.settings.suggested.category.DEFAULT"
         multiple="true" />
diff --git a/src/com/android/settings/ApnEditor.java b/src/com/android/settings/ApnEditor.java
index f97811c..f668957 100644
--- a/src/com/android/settings/ApnEditor.java
+++ b/src/com/android/settings/ApnEditor.java
@@ -788,9 +788,14 @@
      * Check the key fields' validity and save if valid.
      * @param force save even if the fields are not valid, if the app is
      *        being suspended
-     * @return true if the data was saved
+     * @return true if there's no error
      */
     private boolean validateAndSave(boolean force) {
+        // nothing to do if it's a read only APN
+        if (mReadOnlyApn) {
+            return true;
+        }
+
         String name = checkNotSet(mName.getText());
         String apn = checkNotSet(mApn.getText());
         String mcc = checkNotSet(mMcc.getText());
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index d04ae8d..f6f980a 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -81,6 +81,8 @@
 import android.telephony.TelephonyManager;
 import android.text.Spannable;
 import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.text.style.TtsSpan;
@@ -770,8 +772,9 @@
      * @param withSeconds include seconds?
      * @return the formatted elapsed time
      */
-    public static String formatElapsedTime(Context context, double millis, boolean withSeconds) {
-        StringBuilder sb = new StringBuilder();
+    public static CharSequence formatElapsedTime(Context context, double millis,
+            boolean withSeconds) {
+        SpannableStringBuilder sb = new SpannableStringBuilder();
         int seconds = (int) Math.floor(millis / 1000);
         if (!withSeconds) {
             // Round up.
@@ -812,9 +815,15 @@
                         hours, minutes));
             } else {
                 sb.append(context.getString(R.string.battery_history_minutes_no_seconds, minutes));
+
+                // Add ttsSpan if it only have minute value, because it will be read as "meters"
+                TtsSpan ttsSpan = new TtsSpan.MeasureBuilder().setNumber(minutes)
+                        .setUnit("minute").build();
+                sb.setSpan(ttsSpan, 0, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
             }
         }
-        return sb.toString();
+
+        return sb;
     }
 
     /**
@@ -1256,4 +1265,5 @@
         return (volume != null) && (volume.getType() == VolumeInfo.TYPE_PRIVATE)
                 && volume.isMountedReadable();
     }
+
 }
diff --git a/src/com/android/settings/applications/AppLister.java b/src/com/android/settings/applications/AppLister.java
new file mode 100644
index 0000000..425afa0
--- /dev/null
+++ b/src/com/android/settings/applications/AppLister.java
@@ -0,0 +1,69 @@
+/*
+ * 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.applications;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.os.AsyncTask;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Lists apps for current user that fit some criteria specified by includeInCount method
+ * implementation.
+ * This class is similar to {@link AppCounter} class, but but builds actual list of apps instead
+ * of just counting them.
+ */
+public abstract class AppLister extends AsyncTask<Void, Void, List<UserAppInfo>> {
+    protected final PackageManagerWrapper mPm;
+    protected final UserManager mUm;
+
+    public AppLister(PackageManagerWrapper packageManager, UserManager userManager) {
+        mPm = packageManager;
+        mUm = userManager;
+    }
+
+    @Override
+    protected List<UserAppInfo> doInBackground(Void... params) {
+        final List<UserAppInfo> result = new ArrayList<>();
+        for (UserInfo user : mUm.getProfiles(UserHandle.myUserId())) {
+            final List<ApplicationInfo> list =
+                    mPm.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
+                            | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS
+                            | (user.isAdmin() ? PackageManager.MATCH_ANY_USER : 0),
+                            user.id);
+            for (ApplicationInfo info : list) {
+                if (includeInCount(info)) {
+                    result.add(new UserAppInfo(user, info));
+                }
+            }
+        }
+        return result;
+    }
+
+    @Override
+    protected void onPostExecute(List<UserAppInfo> list) {
+        onAppListBuilt(list);
+    }
+
+    protected abstract void onAppListBuilt(List<UserAppInfo> list);
+    protected abstract boolean includeInCount(ApplicationInfo info);
+}
diff --git a/src/com/android/settings/applications/AppStorageSettings.java b/src/com/android/settings/applications/AppStorageSettings.java
index 0676f56..5ad5e48 100644
--- a/src/com/android/settings/applications/AppStorageSettings.java
+++ b/src/com/android/settings/applications/AppStorageSettings.java
@@ -99,6 +99,7 @@
     private static final String KEY_CLEAR_URI = "clear_uri_button";
 
     private static final String KEY_CACHE_CLEARED = "cache_cleared";
+    private static final String KEY_DATA_CLEARED = "data_cleared";
 
     // Views related to cache info
     private Preference mCacheSize;
@@ -115,6 +116,7 @@
 
     private boolean mCanClearData = true;
     private boolean mCacheCleared;
+    private boolean mDataCleared;
 
     private AppStorageSizesController mSizeController;
 
@@ -130,6 +132,8 @@
         super.onCreate(savedInstanceState);
         if (savedInstanceState != null) {
             mCacheCleared = savedInstanceState.getBoolean(KEY_CACHE_CLEARED, false);
+            mDataCleared = savedInstanceState.getBoolean(KEY_DATA_CLEARED, false);
+            mCacheCleared = mCacheCleared || mDataCleared;
         }
 
         addPreferencesFromResource(R.xml.app_storage_settings);
@@ -147,6 +151,7 @@
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
         outState.putBoolean(KEY_CACHE_CLEARED, mCacheCleared);
+        outState.putBoolean(KEY_DATA_CLEARED, mDataCleared);
     }
 
     private void setupViews() {
@@ -527,6 +532,9 @@
         if (mCacheCleared) {
             mSizeController.setCacheCleared(true);
         }
+        if (mDataCleared) {
+            mSizeController.setDataCleared(true);
+        }
 
         mSizeController.updateUi(getContext());
 
@@ -538,7 +546,7 @@
             long dataSize = result.getDataBytes();
             long cacheSize = result.getCacheBytes();
 
-            if (dataSize <= 0 || !mCanClearData) {
+            if (dataSize <= 0 || !mCanClearData || mDataCleared) {
                 mClearDataButton.setEnabled(false);
             } else {
                 mClearDataButton.setEnabled(true);
@@ -564,6 +572,8 @@
             }
             switch (msg.what) {
                 case MSG_CLEAR_USER_DATA:
+                    mDataCleared = true;
+                    mCacheCleared = true;
                     processClearMsg(msg);
                     break;
                 case MSG_CLEAR_CACHE:
diff --git a/src/com/android/settings/applications/AppStorageSizesController.java b/src/com/android/settings/applications/AppStorageSizesController.java
index 23a3eb2..45ece6e 100644
--- a/src/com/android/settings/applications/AppStorageSizesController.java
+++ b/src/com/android/settings/applications/AppStorageSizesController.java
@@ -40,6 +40,7 @@
     private StorageStatsSource.AppStorageStats mLastResult;
     private boolean mLastResultFailed;
     private boolean mCachedCleared;
+    private boolean mDataCleared;
     private long mLastCodeSize = -1;
     private long mLastDataSize = -1;
     private long mLastCacheSize = -1;
@@ -69,7 +70,7 @@
             mTotalSize.setSummary(errorRes);
         } else {
             long codeSize = mLastResult.getCodeBytes();
-            long dataSize = mLastResult.getDataBytes();
+            long dataSize = mDataCleared ? 0 : mLastResult.getDataBytes();
             if (mLastCodeSize != codeSize) {
                 mLastCodeSize = codeSize;
                 mAppSize.setSummary(getSizeStr(context, codeSize));
@@ -78,7 +79,7 @@
                 mLastDataSize = dataSize;
                 mDataSize.setSummary(getSizeStr(context, dataSize));
             }
-            long cacheSize = mCachedCleared ? 0 : mLastResult.getCacheBytes();
+            long cacheSize = (mDataCleared || mCachedCleared) ? 0 : mLastResult.getCacheBytes();
             if (mLastCacheSize != cacheSize) {
                 mLastCacheSize = cacheSize;
                 mCacheSize.setSummary(getSizeStr(context, cacheSize));
@@ -111,6 +112,15 @@
     }
 
     /**
+     * Sets if we have cleared data and should zero the data bytes.
+     * When the data is cleared, the directory are recreated. Directories have some size, but are
+     * empty. We zero this out to best match user expectations.
+     */
+    public void setDataCleared(boolean isCleared) {
+        mDataCleared = isCleared;
+    }
+
+    /**
      * Returns the last result calculated, if it exists. If it does not, returns null.
      */
     public StorageStatsSource.AppStorageStats getLastResult() {
diff --git a/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounter.java b/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounter.java
index 52f1da5..c7d0a62 100644
--- a/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounter.java
+++ b/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounter.java
@@ -31,7 +31,6 @@
 public abstract class AppWithAdminGrantedPermissionsCounter extends AppCounter {
 
     private final String[] mPermissions;
-    private final PackageManagerWrapper mPackageManager;
     private final IPackageManagerWrapper mPackageManagerService;
     private final DevicePolicyManagerWrapper mDevicePolicyManager;
 
@@ -40,18 +39,24 @@
             DevicePolicyManagerWrapper devicePolicyManager) {
         super(context, packageManager);
         mPermissions = permissions;
-        mPackageManager = packageManager;
         mPackageManagerService = packageManagerService;
         mDevicePolicyManager = devicePolicyManager;
     }
 
     @Override
     protected boolean includeInCount(ApplicationInfo info) {
+        return includeInCount(mPermissions, mDevicePolicyManager, mPm, mPackageManagerService,
+                info);
+    }
+
+    public static boolean includeInCount(String[] permissions,
+            DevicePolicyManagerWrapper devicePolicyManager, PackageManagerWrapper packageManager,
+            IPackageManagerWrapper packageManagerService, ApplicationInfo info) {
         if (info.targetSdkVersion >= Build.VERSION_CODES.M) {
             // The app uses run-time permissions. Check whether one or more of the permissions were
             // granted by enterprise policy.
-            for (final String permission : mPermissions) {
-                if (mDevicePolicyManager.getPermissionGrantState(null /* admin */, info.packageName,
+            for (final String permission : permissions) {
+                if (devicePolicyManager.getPermissionGrantState(null /* admin */, info.packageName,
                         permission) == DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED) {
                     return true;
                 }
@@ -61,14 +66,14 @@
 
         // The app uses install-time permissions. Check whether the app requested one or more of the
         // permissions and was installed by enterprise policy, implicitly granting permissions.
-        if (mPackageManager.getInstallReason(info.packageName,
+        if (packageManager.getInstallReason(info.packageName,
                 new UserHandle(UserHandle.getUserId(info.uid)))
                         != PackageManager.INSTALL_REASON_POLICY) {
             return false;
         }
         try {
-            for (final String permission : mPermissions) {
-                if (mPackageManagerService.checkUidPermission(permission, info.uid)
+            for (final String permission : permissions) {
+                if (packageManagerService.checkUidPermission(permission, info.uid)
                         == PackageManager.PERMISSION_GRANTED) {
                     return true;
                 }
diff --git a/src/com/android/settings/applications/AppWithAdminGrantedPermissionsLister.java b/src/com/android/settings/applications/AppWithAdminGrantedPermissionsLister.java
new file mode 100644
index 0000000..b21f31f
--- /dev/null
+++ b/src/com/android/settings/applications/AppWithAdminGrantedPermissionsLister.java
@@ -0,0 +1,46 @@
+/*
+ * 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.applications;
+
+import android.content.pm.ApplicationInfo;
+import android.os.UserManager;
+import com.android.settings.enterprise.DevicePolicyManagerWrapper;
+
+/**
+ * Lists installed apps across all users that have been granted one or more specific permissions by
+ * the admin.
+ */
+public abstract class AppWithAdminGrantedPermissionsLister extends AppLister {
+    private final String[] mPermissions;
+    private final IPackageManagerWrapper mPackageManagerService;
+    private final DevicePolicyManagerWrapper mDevicePolicyManager;
+
+    public AppWithAdminGrantedPermissionsLister(String[] permissions,
+            PackageManagerWrapper packageManager, IPackageManagerWrapper packageManagerService,
+            DevicePolicyManagerWrapper devicePolicyManager, UserManager userManager) {
+        super(packageManager, userManager);
+        mPermissions = permissions;
+        mPackageManagerService = packageManagerService;
+        mDevicePolicyManager = devicePolicyManager;
+    }
+
+    @Override
+    protected boolean includeInCount(ApplicationInfo info) {
+        return AppWithAdminGrantedPermissionsCounter.includeInCount(mPermissions,
+                mDevicePolicyManager, mPm, mPackageManagerService, info);
+    }
+}
diff --git a/src/com/android/settings/applications/ApplicationFeatureProvider.java b/src/com/android/settings/applications/ApplicationFeatureProvider.java
index 5e986db..3266fe0 100644
--- a/src/com/android/settings/applications/ApplicationFeatureProvider.java
+++ b/src/com/android/settings/applications/ApplicationFeatureProvider.java
@@ -16,13 +16,14 @@
 
 package com.android.settings.applications;
 
-import com.android.settings.applications.instantapps.InstantAppButtonsController;
-
+import android.annotation.UserIdInt;
 import android.app.Fragment;
 import android.content.Intent;
 import android.view.View;
 
-import java.util.Set;
+import com.android.settings.applications.instantapps.InstantAppButtonsController;
+
+import java.util.List;
 
 public interface ApplicationFeatureProvider {
 
@@ -49,6 +50,14 @@
     void calculateNumberOfPolicyInstalledApps(boolean async, NumberOfAppsCallback callback);
 
     /**
+     * Asynchronously builds the list of apps installed on the device via policy in the current user
+     * and all its managed profiles.
+     *
+     * @param callback The callback to invoke with the result
+     */
+    void listPolicyInstalledApps(ListOfAppsCallback callback);
+
+    /**
      * Asynchronously calculates the total number of apps installed in the current user and all its
      * managed profiles that have been granted one or more of the given permissions by the admin.
      *
@@ -61,16 +70,28 @@
             NumberOfAppsCallback callback);
 
     /**
-     * Return the persistent preferred activities configured by the admin for the current user and
-     * all its managed profiles. A persistent preferred activity is an activity that the admin
-     * configured to always handle a given intent (e.g. open browser), even if the user has other
-     * apps installed that would also be able to handle the intent.
+     * Asynchronously builds the list of apps installed in the current user and all its
+     * managed profiles that have been granted one or more of the given permissions by the admin.
      *
+     * @param permissions Only consider apps that have been granted one or more of these permissions
+     *        by the admin, either at run-time or install-time
+     * @param callback The callback to invoke with the result
+     */
+    void listAppsWithAdminGrantedPermissions(String[] permissions, ListOfAppsCallback callback);
+
+    /**
+     * Return the persistent preferred activities configured by the admin for the given user.
+     * A persistent preferred activity is an activity that the admin configured to always handle a
+     * given intent (e.g. open browser), even if the user has other apps installed that would also
+     * be able to handle the intent.
+     *
+     * @param userId ID of the user for which to find persistent preferred activities
      * @param intent The intents for which to find persistent preferred activities
      *
-     * @return the persistent preferred activites for the given intent
+     * @return the persistent preferred activites for the given intents, ordered first by user id,
+     * then by package name
      */
-    Set<PersistentPreferredActivityInfo> findPersistentPreferredActivities(Intent[] intents);
+    List<UserAppInfo> findPersistentPreferredActivities(@UserIdInt int userId, Intent[] intents);
 
     /**
      * Callback that receives the number of packages installed on the device.
@@ -79,29 +100,10 @@
         void onNumberOfAppsResult(int num);
     }
 
-    public static class PersistentPreferredActivityInfo {
-        public final String packageName;
-        public final int userId;
-
-        public PersistentPreferredActivityInfo(String packageName, int userId) {
-            this.packageName = packageName;
-            this.userId = userId;
-        }
-
-        @Override
-        public boolean equals(Object other) {
-            if (!(other instanceof PersistentPreferredActivityInfo)) {
-                return false;
-            }
-            final PersistentPreferredActivityInfo otherActivityInfo
-                    = (PersistentPreferredActivityInfo) other;
-            return otherActivityInfo.packageName.equals(packageName)
-                    && otherActivityInfo.userId == userId;
-        }
-
-        @Override
-        public int hashCode() {
-            return packageName.hashCode() ^ userId;
-        }
+    /**
+     * Callback that receives the list of packages installed on the device.
+     */
+    interface ListOfAppsCallback {
+        void onListOfAppsResult(List<UserAppInfo> result);
     }
 }
diff --git a/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java b/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java
index 4171857..a744792 100644
--- a/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java
+++ b/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java
@@ -22,6 +22,7 @@
 import android.content.pm.ComponentInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -31,6 +32,7 @@
 import com.android.settings.applications.instantapps.InstantAppButtonsController;
 import com.android.settings.enterprise.DevicePolicyManagerWrapper;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 
@@ -74,6 +76,13 @@
     }
 
     @Override
+    public void listPolicyInstalledApps(ListOfAppsCallback callback) {
+        final CurrentUserPolicyInstalledAppLister lister =
+                new CurrentUserPolicyInstalledAppLister(mPm, mUm, callback);
+        lister.execute();
+    }
+
+    @Override
     public void calculateNumberOfAppsWithAdminGrantedPermissions(String[] permissions,
             boolean async, NumberOfAppsCallback callback) {
         final CurrentUserAndManagedProfileAppWithAdminGrantedPermissionsCounter counter =
@@ -87,36 +96,43 @@
     }
 
     @Override
-    public Set<PersistentPreferredActivityInfo> findPersistentPreferredActivities(
-            Intent[] intents) {
-        final Set<PersistentPreferredActivityInfo> activities = new ArraySet<>();
-        final List<UserHandle> users = mUm.getUserProfiles();
+    public void listAppsWithAdminGrantedPermissions(String[] permissions,
+            ListOfAppsCallback callback) {
+        final CurrentUserAppWithAdminGrantedPermissionsLister lister =
+                new CurrentUserAppWithAdminGrantedPermissionsLister(permissions, mPm, mPms, mDpm,
+                        mUm, callback);
+        lister.execute();
+    }
+
+    @Override
+    public List<UserAppInfo> findPersistentPreferredActivities(int userId, Intent[] intents) {
+        final List<UserAppInfo> preferredActivities = new ArrayList<>();
+        final Set<UserAppInfo> uniqueApps = new ArraySet<>();
+        final UserInfo userInfo = mUm.getUserInfo(userId);
         for (final Intent intent : intents) {
-            for (final UserHandle user : users) {
-                final int userId = user.getIdentifier();
-                try {
-                    final ResolveInfo resolveInfo = mPms.findPersistentPreferredActivity(intent,
-                            userId);
-                    if (resolveInfo != null) {
-                        ComponentInfo componentInfo = null;
-                        if (resolveInfo.activityInfo != null) {
-                            componentInfo = resolveInfo.activityInfo;
-                        } else if (resolveInfo.serviceInfo != null) {
-                            componentInfo = resolveInfo.serviceInfo;
-                        } else if (resolveInfo.providerInfo != null) {
-                            componentInfo = resolveInfo.providerInfo;
-                        }
-                        if (componentInfo != null) {
-                            activities.add(new PersistentPreferredActivityInfo(
-                                    componentInfo.packageName, userId));
+            try {
+                final ResolveInfo resolveInfo =
+                        mPms.findPersistentPreferredActivity(intent, userId);
+                if (resolveInfo != null) {
+                    ComponentInfo componentInfo = null;
+                    if (resolveInfo.activityInfo != null) {
+                        componentInfo = resolveInfo.activityInfo;
+                    } else if (resolveInfo.serviceInfo != null) {
+                        componentInfo = resolveInfo.serviceInfo;
+                    } else if (resolveInfo.providerInfo != null) {
+                        componentInfo = resolveInfo.providerInfo;
+                    }
+                    if (componentInfo != null) {
+                        UserAppInfo info = new UserAppInfo(userInfo, componentInfo.applicationInfo);
+                        if (uniqueApps.add(info)) {
+                            preferredActivities.add(info);
                         }
                     }
-                } catch (RemoteException exception) {
                 }
+            } catch (RemoteException exception) {
             }
-
         }
-        return activities;
+        return preferredActivities;
     }
 
     private static class CurrentUserAndManagedProfilePolicyInstalledAppCounter
@@ -152,4 +168,39 @@
             mCallback.onNumberOfAppsResult(num);
         }
     }
+
+    private static class CurrentUserPolicyInstalledAppLister extends InstalledAppLister {
+        private ListOfAppsCallback mCallback;
+
+        CurrentUserPolicyInstalledAppLister(PackageManagerWrapper packageManager,
+                UserManager userManager, ListOfAppsCallback callback) {
+            super(packageManager, userManager);
+            mCallback = callback;
+        }
+
+        @Override
+        protected void onAppListBuilt(List<UserAppInfo> list) {
+            mCallback.onListOfAppsResult(list);
+        }
+    }
+
+    private static class CurrentUserAppWithAdminGrantedPermissionsLister extends
+            AppWithAdminGrantedPermissionsLister {
+        private ListOfAppsCallback mCallback;
+
+        CurrentUserAppWithAdminGrantedPermissionsLister(String[] permissions,
+                PackageManagerWrapper packageManager, IPackageManagerWrapper packageManagerService,
+                DevicePolicyManagerWrapper devicePolicyManager, UserManager userManager,
+                ListOfAppsCallback callback) {
+            super(permissions, packageManager, packageManagerService, devicePolicyManager,
+                    userManager);
+            mCallback = callback;
+        }
+
+        @Override
+        protected void onAppListBuilt(List<UserAppInfo> list) {
+            mCallback.onListOfAppsResult(list);
+        }
+    }
+
 }
diff --git a/src/com/android/settings/applications/EnterpriseDefaultApps.java b/src/com/android/settings/applications/EnterpriseDefaultApps.java
new file mode 100644
index 0000000..f48b955
--- /dev/null
+++ b/src/com/android/settings/applications/EnterpriseDefaultApps.java
@@ -0,0 +1,67 @@
+/*
+ * 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.applications;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.MediaStore;
+
+/**
+ * UI grouping of important intents that can be configured by device and profile owners.
+ */
+public enum EnterpriseDefaultApps {
+    BROWSER(new Intent[] {
+            buildIntent(Intent.ACTION_VIEW, Intent.CATEGORY_BROWSABLE, "http:", null)}),
+    CALENDAR(new Intent[] {
+            buildIntent(Intent.ACTION_INSERT, null, null, "vnd.android.cursor.dir/event")}),
+    CAMERA(new Intent[] {
+            new Intent(MediaStore.ACTION_IMAGE_CAPTURE),
+            new Intent(MediaStore.ACTION_VIDEO_CAPTURE)}),
+    CONTACTS(new Intent[] {
+            buildIntent(Intent.ACTION_PICK, null, null, ContactsContract.Contacts.CONTENT_TYPE)}),
+    EMAIL(new Intent[] {
+            new Intent(Intent.ACTION_SENDTO), new Intent(Intent.ACTION_SEND),
+            new Intent(Intent.ACTION_SEND_MULTIPLE)}),
+    MAP(new Intent[] {buildIntent(Intent.ACTION_VIEW, null, "geo:", null)}),
+    PHONE(new Intent[] {new Intent(Intent.ACTION_DIAL), new Intent(Intent.ACTION_CALL)});
+    private final Intent[] mIntents;
+
+    EnterpriseDefaultApps(Intent[] intents) {
+        mIntents = intents;
+    }
+
+    public Intent[] getIntents() {
+        return mIntents;
+    }
+
+    private static Intent buildIntent(String action, String category, String protocol,
+            String type) {
+        final Intent intent = new Intent(action);
+        if (category != null) {
+            intent.addCategory(category);
+        }
+        if (protocol != null) {
+            intent.setData(Uri.parse(protocol));
+        }
+        if (type != null) {
+            intent.setType(type);
+        }
+        return intent;
+    }
+
+}
diff --git a/src/com/android/settings/applications/InstalledAppCounter.java b/src/com/android/settings/applications/InstalledAppCounter.java
index 8065d49..932face 100644
--- a/src/com/android/settings/applications/InstalledAppCounter.java
+++ b/src/com/android/settings/applications/InstalledAppCounter.java
@@ -31,21 +31,24 @@
     public static final int IGNORE_INSTALL_REASON = -1;
 
     private final int mInstallReason;
-    private final PackageManagerWrapper mPackageManager;
 
     public InstalledAppCounter(Context context, int installReason,
             PackageManagerWrapper packageManager) {
         super(context, packageManager);
         mInstallReason = installReason;
-        mPackageManager = packageManager;
     }
 
     @Override
     protected boolean includeInCount(ApplicationInfo info) {
+        return includeInCount(mInstallReason, mPm, info);
+    }
+
+    public static boolean includeInCount(int installReason, PackageManagerWrapper pm,
+            ApplicationInfo info) {
         final int userId = UserHandle.getUserId(info.uid);
-        if (mInstallReason != IGNORE_INSTALL_REASON
-                && mPackageManager.getInstallReason(info.packageName,
-                        new UserHandle(userId)) != mInstallReason) {
+        if (installReason != IGNORE_INSTALL_REASON
+                && pm.getInstallReason(info.packageName,
+                        new UserHandle(userId)) != installReason) {
             return false;
         }
         if ((info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
@@ -57,7 +60,7 @@
         Intent launchIntent = new Intent(Intent.ACTION_MAIN, null)
                 .addCategory(Intent.CATEGORY_LAUNCHER)
                 .setPackage(info.packageName);
-        List<ResolveInfo> intents = mPm.queryIntentActivitiesAsUser(
+        List<ResolveInfo> intents = pm.queryIntentActivitiesAsUser(
                 launchIntent,
                 PackageManager.GET_DISABLED_COMPONENTS
                         | PackageManager.MATCH_DIRECT_BOOT_AWARE
diff --git a/src/com/android/settings/applications/InstalledAppLister.java b/src/com/android/settings/applications/InstalledAppLister.java
new file mode 100644
index 0000000..d8e7c58
--- /dev/null
+++ b/src/com/android/settings/applications/InstalledAppLister.java
@@ -0,0 +1,33 @@
+/*
+ * 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.applications;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.UserManager;
+
+public abstract class InstalledAppLister extends AppLister {
+
+    public InstalledAppLister(PackageManagerWrapper packageManager, UserManager userManager) {
+        super(packageManager, userManager);
+    }
+
+    @Override
+    protected boolean includeInCount(ApplicationInfo info) {
+        return InstalledAppCounter.includeInCount(PackageManager.INSTALL_REASON_POLICY, mPm, info);
+    }
+}
diff --git a/src/com/android/settings/applications/UserAppInfo.java b/src/com/android/settings/applications/UserAppInfo.java
new file mode 100644
index 0000000..04fa713
--- /dev/null
+++ b/src/com/android/settings/applications/UserAppInfo.java
@@ -0,0 +1,58 @@
+/*
+ * 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.applications;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.UserInfo;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * Simple class for bringing together information about application and user for which it was
+ * installed.
+ */
+public class UserAppInfo {
+    public final UserInfo userInfo;
+    public final ApplicationInfo appInfo;
+
+    public UserAppInfo(UserInfo mUserInfo, ApplicationInfo mAppInfo) {
+        this.userInfo = mUserInfo;
+        this.appInfo = mAppInfo;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (other == this) {
+            return true;
+        }
+        if (other == null || getClass() != other.getClass()) {
+            return false;
+        }
+        final UserAppInfo that = (UserAppInfo) other;
+
+        // As UserInfo and AppInfo do not support hashcode/equals contract, assume
+        // equality based on corresponding identity fields.
+        return that.userInfo.id == userInfo.id && TextUtils.equals(that.appInfo.packageName,
+                appInfo.packageName);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(userInfo.id, appInfo.packageName);
+    }
+}
diff --git a/src/com/android/settings/bluetooth/BluetoothSummaryUpdater.java b/src/com/android/settings/bluetooth/BluetoothSummaryUpdater.java
index 282f613..5a2dcb2 100644
--- a/src/com/android/settings/bluetooth/BluetoothSummaryUpdater.java
+++ b/src/com/android/settings/bluetooth/BluetoothSummaryUpdater.java
@@ -17,14 +17,22 @@
 package com.android.settings.bluetooth;
 
 import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
 import android.content.Context;
+import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
+
 import com.android.settings.R;
 import com.android.settings.widget.SummaryUpdater;
 import com.android.settingslib.bluetooth.BluetoothCallback;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
+import java.util.Set;
 
 /**
  * Helper class that listeners to bluetooth callback and notify client when there is update in
@@ -98,7 +106,7 @@
         }
         switch (mConnectionState) {
             case BluetoothAdapter.STATE_CONNECTED:
-                return mContext.getString(R.string.bluetooth_connected_summary);
+                return getConnectedDeviceSummary();
             case BluetoothAdapter.STATE_CONNECTING:
                 return mContext.getString(R.string.bluetooth_connecting);
             case BluetoothAdapter.STATE_DISCONNECTING:
@@ -145,4 +153,27 @@
             : null;
     }
 
+    @VisibleForTesting
+    String getConnectedDeviceSummary() {
+        String deviceName = null;
+        int count = 0;
+        final Set<BluetoothDevice> devices = mBluetoothAdapter.getBondedDevices();
+        if (devices == null || devices.isEmpty()) {
+            return null;
+        }
+
+        for (BluetoothDevice device : devices) {
+            if (device.isConnected()) {
+                deviceName = device.getName();
+                count++;
+                if (count > 1) {
+                    break;
+                }
+            }
+        }
+
+        return count > 1 ? mContext.getString(R.string.bluetooth_connected_multiple_devices_summary)
+                : mContext.getString(R.string.bluetooth_connected_summary, deviceName);
+    }
+
 }
diff --git a/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerBase.java b/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerBase.java
index 0db9e15..f0aca01 100644
--- a/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerBase.java
+++ b/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerBase.java
@@ -16,7 +16,6 @@
 package com.android.settings.enterprise;
 
 import android.content.Context;
-import android.content.Intent;
 import android.support.v7.preference.Preference;
 
 import com.android.settings.R;
@@ -32,6 +31,7 @@
     private final String mPermissionGroup;
     private final ApplicationFeatureProvider mFeatureProvider;
     private final boolean mAsync;
+    private boolean mHasApps;
 
     public AdminGrantedPermissionsPreferenceControllerBase(Context context, Lifecycle lifecycle,
             boolean async, String[] permissions, String permissionGroup) {
@@ -41,6 +41,7 @@
         mFeatureProvider = FeatureFactory.getFactory(context)
                 .getApplicationFeatureProvider(context);
         mAsync = async;
+        mHasApps = false;
     }
 
     @Override
@@ -50,11 +51,13 @@
                 (num) -> {
                     if (num == 0) {
                         preference.setVisible(false);
+                        mHasApps = false;
                     } else {
                         preference.setVisible(true);
                         preference.setSummary(mContext.getResources().getQuantityString(
                                 R.plurals.enterprise_privacy_number_packages_lower_bound,
                                 num, num));
+                        mHasApps = true;
                     }
                 });
     }
@@ -76,7 +79,8 @@
         final Boolean[] haveAppsWithAdminGrantedPermissions = { null };
         mFeatureProvider.calculateNumberOfAppsWithAdminGrantedPermissions(mPermissions,
                 false /* async */, (num) -> haveAppsWithAdminGrantedPermissions[0] = num > 0);
-        return haveAppsWithAdminGrantedPermissions[0];
+        mHasApps = haveAppsWithAdminGrantedPermissions[0];
+        return mHasApps;
     }
 
     @Override
@@ -84,9 +88,9 @@
         if (!getPreferenceKey().equals(preference.getKey())) {
             return false;
         }
-        final Intent intent = new Intent(Intent.ACTION_MANAGE_PERMISSION_APPS)
-                .putExtra(Intent.EXTRA_PERMISSION_NAME, mPermissionGroup);
-        mContext.startActivity(intent);
-        return true;
+        if (!mHasApps) {
+            return false;
+        }
+        return super.handlePreferenceTreeClick(preference);
     }
 }
diff --git a/src/com/android/settings/enterprise/ApplicationListFragment.java b/src/com/android/settings/enterprise/ApplicationListFragment.java
new file mode 100644
index 0000000..ff68a8b
--- /dev/null
+++ b/src/com/android/settings/enterprise/ApplicationListFragment.java
@@ -0,0 +1,115 @@
+/*
+ * 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.enterprise;
+
+import android.Manifest;
+import android.content.Context;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+import com.android.settings.applications.ApplicationFeatureProvider;
+import com.android.settings.core.PreferenceController;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.overlay.FeatureFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Base fragment for displaying a list of applications on a device.
+ * Inner static classes are concrete implementations.
+ */
+public abstract class ApplicationListFragment extends DashboardFragment
+        implements ApplicationListPreferenceController.ApplicationListBuilder {
+
+    static final String TAG = "EnterprisePrivacySettings";
+
+    @Override
+    protected String getLogTag() {
+        return TAG;
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.app_list_disclosure_settings;
+    }
+
+    @Override
+    protected List<PreferenceController> getPreferenceControllers(Context context) {
+        final List controllers = new ArrayList<>();
+        ApplicationListPreferenceController controller = new ApplicationListPreferenceController(
+                context, this, context.getPackageManager(), this);
+        controllers.add(controller);
+        return controllers;
+    }
+
+    private abstract static class AdminGrantedPermission extends ApplicationListFragment {
+        private final String[] mPermissions;
+
+        public AdminGrantedPermission(String[] permissions) {
+            mPermissions = permissions;
+        }
+
+        @Override
+        public void buildApplicationList(Context context,
+                ApplicationFeatureProvider.ListOfAppsCallback callback) {
+            FeatureFactory.getFactory(context).getApplicationFeatureProvider(context)
+                    .listAppsWithAdminGrantedPermissions(mPermissions, callback);
+        }
+
+        @Override
+        public int getMetricsCategory() {
+            return MetricsEvent.ENTERPRISE_PRIVACY_PERMISSIONS;
+        }
+    }
+
+    public static class AdminGrantedPermissionCamera extends AdminGrantedPermission {
+        public AdminGrantedPermissionCamera() {
+            super(new String[] {Manifest.permission.CAMERA});
+        }
+    }
+
+    public static class AdminGrantedPermissionLocation extends AdminGrantedPermission {
+        public AdminGrantedPermissionLocation() {
+            super(new String[] {Manifest.permission.ACCESS_COARSE_LOCATION,
+                    Manifest.permission.ACCESS_FINE_LOCATION});
+        }
+    }
+
+    public static class AdminGrantedPermissionMicrophone extends AdminGrantedPermission {
+        public AdminGrantedPermissionMicrophone() {
+            super(new String[] {Manifest.permission.RECORD_AUDIO});
+        }
+    }
+
+    public static class EnterpriseInstalledPackages extends ApplicationListFragment {
+        public EnterpriseInstalledPackages() {
+        }
+
+        @Override
+        public int getMetricsCategory() {
+            return MetricsEvent.ENTERPRISE_PRIVACY_INSTALLED_APPS;
+        }
+
+        @Override
+        public void buildApplicationList(Context context,
+                ApplicationFeatureProvider.ListOfAppsCallback callback) {
+            FeatureFactory.getFactory(context).getApplicationFeatureProvider(context).
+                    listPolicyInstalledApps(callback);
+        }
+    }
+}
diff --git a/src/com/android/settings/enterprise/ApplicationListPreferenceController.java b/src/com/android/settings/enterprise/ApplicationListPreferenceController.java
new file mode 100644
index 0000000..f9fefa1
--- /dev/null
+++ b/src/com/android/settings/enterprise/ApplicationListPreferenceController.java
@@ -0,0 +1,86 @@
+/*
+ * 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.enterprise;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.applications.ApplicationFeatureProvider;
+import com.android.settings.applications.UserAppInfo;
+import com.android.settings.core.PreferenceController;
+
+import java.util.List;
+
+/**
+ * PreferenceController that builds a dynamic list of applications provided by
+ * {@link ApplicationListBuilder} instance.
+ */
+public class ApplicationListPreferenceController extends PreferenceController
+        implements ApplicationFeatureProvider.ListOfAppsCallback {
+    private final PackageManager mPm;
+    private SettingsPreferenceFragment mParent;
+
+    public ApplicationListPreferenceController(Context context, ApplicationListBuilder builder,
+            PackageManager packageManager, SettingsPreferenceFragment parent) {
+        super(context);
+        mPm = packageManager;
+        mParent = parent;
+        builder.buildApplicationList(context, this);
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return null;
+    }
+
+    @Override
+    public void onListOfAppsResult(List<UserAppInfo> result) {
+        final PreferenceScreen screen = mParent.getPreferenceScreen();
+        if (screen == null) {
+            return;
+        }
+        final Context prefContext = mParent.getPreferenceManager().getContext();
+        for (int position = 0; position < result.size(); position++) {
+            final UserAppInfo item = result.get(position);
+            final Preference preference = new Preference(prefContext);
+            preference.setLayoutResource(R.layout.preference_app);
+            preference.setTitle(item.appInfo.loadLabel(mPm));
+            preference.setIcon(item.appInfo.loadIcon(mPm));
+            preference.setOrder(position);
+            preference.setSelectable(false);
+            screen.addPreference(preference);
+        }
+    }
+
+    /**
+     * Simple interface for building application list within {
+     * @link ApplicationListPreferenceController}
+     */
+    public interface ApplicationListBuilder {
+        void buildApplicationList(Context context,
+                ApplicationFeatureProvider.ListOfAppsCallback callback);
+    }
+}
diff --git a/src/com/android/settings/enterprise/EnterprisePrivacyFeatureProviderImpl.java b/src/com/android/settings/enterprise/EnterprisePrivacyFeatureProviderImpl.java
index 5817cf2..46ecb7e 100644
--- a/src/com/android/settings/enterprise/EnterprisePrivacyFeatureProviderImpl.java
+++ b/src/com/android/settings/enterprise/EnterprisePrivacyFeatureProviderImpl.java
@@ -17,10 +17,8 @@
 package com.android.settings.enterprise;
 
 import android.content.ComponentName;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
diff --git a/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceController.java b/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceController.java
index 254940e..69e0416 100644
--- a/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceController.java
+++ b/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceController.java
@@ -14,7 +14,6 @@
 package com.android.settings.enterprise;
 
 import android.content.Context;
-import android.content.res.Resources;
 import android.support.v7.preference.Preference;
 
 import com.android.settings.R;
diff --git a/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListFragment.java b/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListFragment.java
new file mode 100644
index 0000000..6f173f1
--- /dev/null
+++ b/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListFragment.java
@@ -0,0 +1,59 @@
+/*
+ * 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.enterprise;
+
+import android.content.Context;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+import com.android.settings.core.PreferenceController;
+import com.android.settings.dashboard.DashboardFragment;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Fragment for displaying a list of default applications set by profile or device admin.
+ */
+public class EnterpriseSetDefaultAppsListFragment extends DashboardFragment {
+    static final String TAG = "EnterprisePrivacySettings";
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsEvent.ENTERPRISE_PRIVACY_DEFAULT_APPS;
+    }
+
+    @Override
+    protected String getLogTag() {
+        return TAG;
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.enterprise_set_default_apps_settings;
+    }
+
+    @Override
+    protected List<PreferenceController> getPreferenceControllers(Context context) {
+        final List controllers = new ArrayList<PreferenceController>();
+        final EnterpriseSetDefaultAppsListPreferenceController controller =
+                new EnterpriseSetDefaultAppsListPreferenceController(
+                        context, this, context.getPackageManager());
+        controllers.add(controller);
+        return controllers;
+    }
+}
diff --git a/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListPreferenceController.java b/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListPreferenceController.java
new file mode 100644
index 0000000..51b60b8
--- /dev/null
+++ b/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListPreferenceController.java
@@ -0,0 +1,197 @@
+/*
+ * 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.enterprise;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceCategory;
+import android.support.v7.preference.PreferenceGroup;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.applications.ApplicationFeatureProvider;
+import com.android.settings.applications.EnterpriseDefaultApps;
+import com.android.settings.applications.UserAppInfo;
+import com.android.settings.core.PreferenceController;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.users.UserFeatureProvider;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.List;
+
+
+/**
+ * PreferenceController that builds a dynamic list of default apps set by device or profile owner.
+ */
+public class EnterpriseSetDefaultAppsListPreferenceController extends PreferenceController {
+    private final PackageManager mPm;
+    private final SettingsPreferenceFragment mParent;
+    private final ApplicationFeatureProvider mApplicationFeatureProvider;
+    private final EnterprisePrivacyFeatureProvider mEnterprisePrivacyFeatureProvider;
+    private final UserFeatureProvider mUserFeatureProvider;
+
+    private List<UserInfo> mUsers = Collections.emptyList();
+    private List<EnumMap<EnterpriseDefaultApps, List<ApplicationInfo>>> mApps =
+            Collections.emptyList();
+
+    public EnterpriseSetDefaultAppsListPreferenceController(Context context,
+            SettingsPreferenceFragment parent, PackageManager packageManager) {
+        super(context);
+        mPm = packageManager;
+        mParent = parent;
+        final FeatureFactory factory = FeatureFactory.getFactory(context);
+        mApplicationFeatureProvider = factory.getApplicationFeatureProvider(context);
+        mEnterprisePrivacyFeatureProvider = factory.getEnterprisePrivacyFeatureProvider(context);
+        mUserFeatureProvider = factory.getUserFeatureProvider(context);
+        buildAppList();
+    }
+
+    /**
+     * Builds data for UI. Updates mUsers and mApps so that they contain non-empty list.
+     */
+    private void buildAppList() {
+        mUsers = new ArrayList<>();
+        mApps = new ArrayList<>();
+        for (UserHandle user : mUserFeatureProvider.getUserProfiles()) {
+            boolean hasDefaultsForUser = false;
+            EnumMap<EnterpriseDefaultApps, List<ApplicationInfo>> userMap = null;
+
+            for (EnterpriseDefaultApps typeOfDefault : EnterpriseDefaultApps.values()) {
+                List<UserAppInfo> apps = mApplicationFeatureProvider.
+                        findPersistentPreferredActivities(user.getIdentifier(),
+                                typeOfDefault.getIntents());
+                if (apps.isEmpty()) {
+                    continue;
+                }
+                if (!hasDefaultsForUser) {
+                    hasDefaultsForUser = true;
+                    mUsers.add(apps.get(0).userInfo);
+                    userMap = new EnumMap<>(EnterpriseDefaultApps.class);
+                    mApps.add(userMap);
+                }
+                ArrayList<ApplicationInfo> applicationInfos = new ArrayList<>();
+                for (UserAppInfo userAppInfo : apps) {
+                    applicationInfos.add(userAppInfo.appInfo);
+                }
+                userMap.put(typeOfDefault, applicationInfos);
+            }
+        }
+        new Handler(mContext.getMainLooper()).post(() -> { updateUi(); });
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return null;
+    }
+
+    private void updateUi() {
+        final Context prefContext = mParent.getPreferenceManager().getContext();
+        final PreferenceScreen screen = mParent.getPreferenceScreen();
+        if (screen == null) {
+            return;
+        }
+        if (!mEnterprisePrivacyFeatureProvider.isInCompMode() && mUsers.size() == 1) {
+            createPreferences(prefContext, screen, mApps.get(0));
+        } else {
+            for (int i = 0; i < mUsers.size(); i++) {
+                final UserInfo userInfo = mUsers.get(i);
+                final PreferenceCategory category = new PreferenceCategory(prefContext);
+                screen.addPreference(category);
+                if (userInfo.isManagedProfile()) {
+                    category.setTitle(R.string.managed_device_admin_title);
+                } else {
+                    category.setTitle(R.string.personal_device_admin_title);
+                }
+                category.setOrder(i);
+                createPreferences(prefContext, category, mApps.get(i));
+            }
+        }
+    }
+
+    private void createPreferences(Context prefContext, PreferenceGroup group,
+            EnumMap<EnterpriseDefaultApps, List<ApplicationInfo>> apps) {
+        if (group == null) {
+            return;
+        }
+        for (EnterpriseDefaultApps typeOfDefault : EnterpriseDefaultApps.values()) {
+            final List<ApplicationInfo> appsForCategory = apps.get(typeOfDefault);
+            if (appsForCategory == null || appsForCategory.isEmpty()) {
+                continue;
+            }
+            final Preference preference = new Preference(prefContext);
+            preference.setTitle(getTitle(prefContext, typeOfDefault, appsForCategory.size()));
+            preference.setSummary(buildSummaryString(prefContext, appsForCategory));
+            preference.setOrder(typeOfDefault.ordinal());
+            preference.setSelectable(false);
+            group.addPreference(preference);
+        }
+    }
+
+    private CharSequence buildSummaryString(Context context, List<ApplicationInfo> apps) {
+        final CharSequence[] appNames = new String[apps.size()];
+        for (int i = 0; i < apps.size(); i++) {
+            appNames[i] = apps.get(i).loadLabel(mPm);
+        }
+        if (apps.size() == 1) {
+            return appNames[0];
+        } else if (apps.size() == 2) {
+            return context.getString(R.string.app_names_concatenation_template_2, appNames[0],
+                    appNames[1]);
+        } else {
+            return context.getString(R.string.app_names_concatenation_template_3, appNames[0],
+                    appNames[1], appNames[2]);
+        }
+    }
+
+    private String getTitle(Context context, EnterpriseDefaultApps typeOfDefault, int appCount) {
+        switch (typeOfDefault) {
+            case BROWSER:
+                return context.getString(R.string.default_browser_title);
+            case CALENDAR:
+                return context.getString(R.string.default_calendar_app_title);
+            case CONTACTS:
+                return context.getString(R.string.default_contacts_app_title);
+            case PHONE:
+                return context.getResources()
+                        .getQuantityString(R.plurals.default_phone_app_title, appCount);
+            case MAP:
+                return context.getString(R.string.default_map_app_title);
+            case EMAIL:
+                return context.getResources()
+                        .getQuantityString(R.plurals.default_email_app_title, appCount);
+            case CAMERA:
+                return context.getResources()
+                        .getQuantityString(R.plurals.default_camera_app_title, appCount);
+            default:
+                throw new IllegalStateException("Unknown type of default " + typeOfDefault);
+        }
+    }
+
+}
diff --git a/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceController.java b/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceController.java
index ae76d63..2f43a61 100644
--- a/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceController.java
+++ b/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceController.java
@@ -14,28 +14,29 @@
 package com.android.settings.enterprise;
 
 import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.provider.ContactsContract;
-import android.provider.MediaStore;
+import android.os.UserHandle;
 import android.support.v7.preference.Preference;
 
 import com.android.settings.R;
 import com.android.settings.applications.ApplicationFeatureProvider;
+import com.android.settings.applications.EnterpriseDefaultApps;
 import com.android.settings.core.DynamicAvailabilityPreferenceController;
 import com.android.settings.core.lifecycle.Lifecycle;
 import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.users.UserFeatureProvider;
 
 public class EnterpriseSetDefaultAppsPreferenceController
         extends DynamicAvailabilityPreferenceController {
 
     private static final String KEY_DEFAULT_APPS = "number_enterprise_set_default_apps";
-    private final ApplicationFeatureProvider mFeatureProvider;
+    private final ApplicationFeatureProvider mApplicationFeatureProvider;
+    private final UserFeatureProvider mUserFeatureProvider;
 
     public EnterpriseSetDefaultAppsPreferenceController(Context context, Lifecycle lifecycle) {
         super(context, lifecycle);
-        mFeatureProvider = FeatureFactory.getFactory(context)
-                .getApplicationFeatureProvider(context);
+        final FeatureFactory factory = FeatureFactory.getFactory(context);
+        mApplicationFeatureProvider = factory.getApplicationFeatureProvider(context);
+        mUserFeatureProvider = factory.getUserFeatureProvider(context);
     }
 
     @Override
@@ -56,47 +57,14 @@
     }
 
     private int getNumberOfEnterpriseSetDefaultApps() {
-        // Browser
-        int num = mFeatureProvider.findPersistentPreferredActivities(new Intent[] {
-                buildIntent(Intent.ACTION_VIEW, Intent.CATEGORY_BROWSABLE, "http:", null)}).size();
-        // Camera
-        num += mFeatureProvider.findPersistentPreferredActivities(new Intent[] {
-                new Intent(MediaStore.ACTION_IMAGE_CAPTURE),
-                new Intent(MediaStore.ACTION_VIDEO_CAPTURE)}).size();
-        // Map
-        num += mFeatureProvider.findPersistentPreferredActivities(new Intent[] {
-                buildIntent(Intent.ACTION_VIEW, null, "geo:", null)}).size();
-        // E-mail
-        num += mFeatureProvider.findPersistentPreferredActivities(new Intent[] {
-                new Intent(Intent.ACTION_SENDTO), new Intent(Intent.ACTION_SEND),
-                new Intent(Intent.ACTION_SEND_MULTIPLE)}).size();
-        // Calendar
-        num += mFeatureProvider.findPersistentPreferredActivities(new Intent[] {
-                buildIntent(Intent.ACTION_INSERT, null, null, "vnd.android.cursor.dir/event")})
-                .size();
-        // Contacts
-        num += mFeatureProvider.findPersistentPreferredActivities(new Intent[] {
-                buildIntent(Intent.ACTION_PICK, null, null,
-                        ContactsContract.Contacts.CONTENT_TYPE)}).size();
-        // Dialer
-        num += mFeatureProvider.findPersistentPreferredActivities(new Intent[] {
-                new Intent(Intent.ACTION_DIAL), new Intent(Intent.ACTION_CALL)}).size();
-
+        int num = 0;
+        for (UserHandle user : mUserFeatureProvider.getUserProfiles()) {
+            for (EnterpriseDefaultApps app : EnterpriseDefaultApps.values()) {
+                num += mApplicationFeatureProvider
+                        .findPersistentPreferredActivities(user.getIdentifier(),
+                                app.getIntents()).size();
+            }
+        }
         return num;
     }
-
-    private static Intent buildIntent(String action, String category, String protocol,
-            String type) {
-        final Intent intent = new Intent(action);
-        if (category != null) {
-            intent.addCategory(category);
-        }
-        if (protocol != null) {
-            intent.setData(Uri.parse(protocol));
-        }
-        if (type != null) {
-            intent.setType(type);
-        }
-        return intent;
-    }
 }
diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
index a73902a..eede37a 100644
--- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
+++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
@@ -28,6 +28,7 @@
 import android.support.annotation.VisibleForTesting;
 import android.support.v14.preference.PreferenceFragment;
 import android.support.v7.preference.Preference;
+import android.text.TextUtils;
 import android.view.View;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -82,6 +83,8 @@
     ApplicationsState mState;
     @VisibleForTesting
     ApplicationsState.AppEntry mAppEntry;
+    @VisibleForTesting
+    BatteryUtils mBatteryUtils;
 
     private Preference mForegroundPreference;
     private Preference mBackgroundPreference;
@@ -137,6 +140,7 @@
                 (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE));
         mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE);
         mPackageManager = activity.getPackageManager();
+        mBatteryUtils = BatteryUtils.getInstance(getContext());
     }
 
     @Override
diff --git a/src/com/android/settings/fuelgauge/BatteryUtils.java b/src/com/android/settings/fuelgauge/BatteryUtils.java
index 48e2421..c8af4a0 100644
--- a/src/com/android/settings/fuelgauge/BatteryUtils.java
+++ b/src/com/android/settings/fuelgauge/BatteryUtils.java
@@ -15,14 +15,13 @@
  */
 package com.android.settings.fuelgauge;
 
-import android.annotation.IntDef;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.BatteryStats;
 import android.os.SystemClock;
+import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
 import android.support.annotation.VisibleForTesting;
-import android.text.format.DateUtils;
 import android.util.Log;
 
 import com.android.internal.os.BatterySipper;
@@ -64,7 +63,8 @@
         return sInstance;
     }
 
-    private BatteryUtils(Context context) {
+    @VisibleForTesting
+    BatteryUtils(Context context) {
         mPackageManager = context.getPackageManager();
         mPowerUsageFeatureProvider = FeatureFactory.getFactory(
                 context).getPowerUsageFeatureProvider(context);
diff --git a/src/com/android/settings/fuelgauge/PowerGaugePreference.java b/src/com/android/settings/fuelgauge/PowerGaugePreference.java
index bdadf4c..b124c81 100644
--- a/src/com/android/settings/fuelgauge/PowerGaugePreference.java
+++ b/src/com/android/settings/fuelgauge/PowerGaugePreference.java
@@ -79,11 +79,15 @@
         return mProgress.toString();
     }
 
-    public void setSubtitle(String subtitle) {
+    public void setSubtitle(CharSequence subtitle) {
         mProgress = subtitle;
         notifyChanged();
     }
 
+    public CharSequence getSubtitle() {
+        return mProgress;
+    }
+
     BatteryEntry getInfo() {
         return mInfo;
     }
diff --git a/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java b/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java
index b718f8f..28dfe1d 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java
@@ -29,6 +29,7 @@
 import android.support.annotation.VisibleForTesting;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceGroup;
+import android.text.TextUtils;
 
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.internal.os.BatterySipper;
@@ -67,13 +68,15 @@
             UsageType.APP,
             UsageType.UNACCOUNTED,
             UsageType.OVERCOUNTED};
+
+    @VisibleForTesting
+    BatteryUtils mBatteryUtils;
     private BatteryHistoryPreference mHistPref;
     private PreferenceGroup mUsageListGroup;
     private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
     private PackageManager mPackageManager;
     private UserManager mUserManager;
     private Map<Integer, PowerUsageData> mBatteryDataMap;
-    private BatteryUtils mBatteryUtils;
 
     Handler mHandler = new Handler() {
 
@@ -270,8 +273,10 @@
             return;
         }
         if (usageData.usageList.size() <= 1) {
-            usageData.summary = getString(R.string.battery_used_for,
-                    Utils.formatElapsedTime(getContext(), usageData.totalUsageTimeMs, false));
+            CharSequence timeSequence = Utils.formatElapsedTime(getContext(),
+                    usageData.totalUsageTimeMs, false);
+            usageData.summary = TextUtils.expandTemplate(getText(R.string.battery_used_for),
+                    timeSequence);
         } else {
             BatterySipper sipper = findBatterySipperWithMaxBatteryUsage(usageData.usageList);
             BatteryEntry batteryEntry = new BatteryEntry(getContext(), mHandler, mUserManager,
@@ -346,7 +351,7 @@
 
         @StringRes
         public int titleResId;
-        public String summary;
+        public CharSequence summary;
         public double percentage;
         public double totalPowerMah;
         public long totalUsageTimeMs;
diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
index 078a9b3..1bc67af 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
@@ -418,15 +418,16 @@
         final TypedValue value = new TypedValue();
         context.getTheme().resolveAttribute(android.R.attr.colorControlNormal, value, true);
         final int colorControl = context.getColor(value.resourceId);
-        final String usedTime = context.getString(R.string.battery_used_for);
         final int dischargeAmount = USE_FAKE_DATA ? 5000
                 : stats != null ? stats.getDischargeAmount(mStatsType) : 0;
 
         final long runningTime = calculateRunningTimeBasedOnStatsType();
         updateScreenPreference();
         updateLastFullChargePreference(runningTime);
-        mAppListGroup.setTitle(getString(R.string.power_usage_list_summary,
-                Utils.formatElapsedTime(context, runningTime, false)));
+
+        final CharSequence timeSequence = Utils.formatElapsedTime(context, runningTime, false);
+        mAppListGroup.setTitle(
+                TextUtils.expandTemplate(getText(R.string.power_usage_list_summary), timeSequence));
 
         if (averagePower >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP || USE_FAKE_DATA) {
             final List<BatterySipper> usageList = getCoalescedUsageList(
@@ -497,7 +498,7 @@
                     sipper.usageTimeMs = mBatteryUtils.getProcessTimeMs(
                             BatteryUtils.StatusType.FOREGROUND, sipper.uidObj, mStatsType);
                 }
-                setUsageSummary(pref, usedTime, sipper.usageTimeMs);
+                setUsageSummary(pref, sipper.usageTimeMs);
                 if ((sipper.drainType != DrainType.APP
                         || sipper.uidObj.getUid() == Process.ROOT_UID)
                         && sipper.drainType != DrainType.USER) {
@@ -534,16 +535,17 @@
     void updateScreenPreference() {
         final BatterySipper sipper = findBatterySipperByType(
                 mStatsHelper.getUsageList(), DrainType.SCREEN);
-        final Context context = getContext();
         final long usageTimeMs = sipper != null ? sipper.usageTimeMs : 0;
 
-        mScreenUsagePref.setSubtitle(Utils.formatElapsedTime(context, usageTimeMs, false));
+        mScreenUsagePref.setSubtitle(Utils.formatElapsedTime(getContext(), usageTimeMs, false));
     }
 
     @VisibleForTesting
     void updateLastFullChargePreference(long timeMs) {
-        mLastFullChargePref.setSubtitle(getString(R.string.power_last_full_charge_summary,
-                Utils.formatElapsedTime(getContext(), timeMs, false)));
+        final CharSequence timeSequence = Utils.formatElapsedTime(getContext(), timeMs, false);
+        mLastFullChargePref.setSubtitle(
+                TextUtils.expandTemplate(getText(R.string.power_last_full_charge_summary),
+                        timeSequence));
     }
 
     @VisibleForTesting
@@ -583,11 +585,13 @@
     }
 
     @VisibleForTesting
-    void setUsageSummary(Preference preference, String usedTimePrefix, long usageTimeMs) {
+    void setUsageSummary(Preference preference, long usageTimeMs) {
         // Only show summary when usage time is longer than one minute
         if (usageTimeMs >= DateUtils.MINUTE_IN_MILLIS) {
-            preference.setSummary(String.format(usedTimePrefix,
-                    Utils.formatElapsedTime(getContext(), usageTimeMs, false)));
+            final CharSequence timeSequence = Utils.formatElapsedTime(getContext(), usageTimeMs,
+                    false);
+            preference.setSummary(
+                    TextUtils.expandTemplate(getText(R.string.battery_used_for), timeSequence));
         }
     }
 
diff --git a/src/com/android/settings/overlay/FeatureFactory.java b/src/com/android/settings/overlay/FeatureFactory.java
index dcd2b51..e7d5088 100644
--- a/src/com/android/settings/overlay/FeatureFactory.java
+++ b/src/com/android/settings/overlay/FeatureFactory.java
@@ -31,6 +31,7 @@
 import com.android.settings.localepicker.LocaleFeatureProvider;
 import com.android.settings.security.SecurityFeatureProvider;
 import com.android.settings.search2.SearchFeatureProvider;
+import com.android.settings.users.UserFeatureProvider;
 
 /**
  * Abstract class for creating feature controllers. Allows OEM implementations to define their own
@@ -94,6 +95,8 @@
 
     public abstract SecurityFeatureProvider getSecurityFeatureProvider();
 
+    public abstract UserFeatureProvider getUserFeatureProvider(Context context);
+
     public static final class FactoryNotFoundException extends RuntimeException {
         public FactoryNotFoundException(Throwable throwable) {
             super("Unable to create factory. Did you misconfigure Proguard?", throwable);
diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.java b/src/com/android/settings/overlay/FeatureFactoryImpl.java
index 5b039b2..b39a2a0 100644
--- a/src/com/android/settings/overlay/FeatureFactoryImpl.java
+++ b/src/com/android/settings/overlay/FeatureFactoryImpl.java
@@ -45,6 +45,8 @@
 import com.android.settings.search2.SearchFeatureProviderImpl;
 import com.android.settings.security.SecurityFeatureProvider;
 import com.android.settings.security.SecurityFeatureProviderImpl;
+import com.android.settings.users.UserFeatureProvider;
+import com.android.settings.users.UserFeatureProviderImpl;
 import com.android.settings.vpn2.ConnectivityManagerWrapperImpl;
 
 /**
@@ -63,6 +65,7 @@
     private SuggestionFeatureProvider mSuggestionFeatureProvider;
     private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
     private AssistGestureFeatureProvider mAssistGestureFeatureProvider;
+    private UserFeatureProvider mUserFeatureProvider;
 
     @Override
     public SupportFeatureProvider getSupportFeatureProvider(Context context) {
@@ -158,6 +161,14 @@
     }
 
     @Override
+    public UserFeatureProvider getUserFeatureProvider(Context context) {
+        if (mUserFeatureProvider == null) {
+            mUserFeatureProvider = new UserFeatureProviderImpl(context);
+        }
+        return mUserFeatureProvider;
+    }
+
+    @Override
     public AssistGestureFeatureProvider getAssistGestureFeatureProvider() {
         if (mAssistGestureFeatureProvider == null) {
             mAssistGestureFeatureProvider = new AssistGestureFeatureProviderImpl();
diff --git a/src/com/android/settings/search2/DatabaseIndexingManager.java b/src/com/android/settings/search2/DatabaseIndexingManager.java
index c2e6ba8..b39aa05 100644
--- a/src/com/android/settings/search2/DatabaseIndexingManager.java
+++ b/src/com/android/settings/search2/DatabaseIndexingManager.java
@@ -751,7 +751,6 @@
                     .setEntries(null)
                     .setClassName(fragmentName)
                     .setScreenTitle(screenTitle)
-                    .setIconResId(iconResId)
                     .setRank(rank)
                     .setIntentAction(intentAction)
                     .setIntentTargetPackage(intentTargetPackage)
diff --git a/src/com/android/settings/search2/SearchViewHolder.java b/src/com/android/settings/search2/SearchViewHolder.java
index 1439833..54e78fb 100644
--- a/src/com/android/settings/search2/SearchViewHolder.java
+++ b/src/com/android/settings/search2/SearchViewHolder.java
@@ -69,9 +69,11 @@
             iconView.setImageDrawable(appResult.info.loadIcon(pm));
         } else if (result.icon != null) {
             iconView.setImageDrawable(result.icon);
+            // TODO set color of icon
         } else {
             iconView.setBackgroundResource(R.drawable.empty_icon);
         }
+
         bindBreadcrumbView(result);
     }
 
diff --git a/src/com/android/settings/users/UserFeatureProvider.java b/src/com/android/settings/users/UserFeatureProvider.java
new file mode 100644
index 0000000..9853255
--- /dev/null
+++ b/src/com/android/settings/users/UserFeatureProvider.java
@@ -0,0 +1,31 @@
+/*
+ * 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.users;
+
+import android.os.UserHandle;
+
+import java.util.List;
+
+public interface UserFeatureProvider {
+    /**
+     * Returns a list of UserHandles for profiles associated with the user that the calling process
+     * is running on, including the user itself.
+     *
+     * @return A non-empty list of UserHandles associated with the calling user.
+     */
+    List<UserHandle> getUserProfiles();
+}
diff --git a/src/com/android/settings/users/UserFeatureProviderImpl.java b/src/com/android/settings/users/UserFeatureProviderImpl.java
new file mode 100644
index 0000000..b88c831
--- /dev/null
+++ b/src/com/android/settings/users/UserFeatureProviderImpl.java
@@ -0,0 +1,36 @@
+/*
+ * 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.users;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import java.util.List;
+
+public class UserFeatureProviderImpl implements UserFeatureProvider {
+    UserManager mUm;
+
+    public UserFeatureProviderImpl(Context context) {
+        mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
+    }
+
+    @Override
+    public List<UserHandle> getUserProfiles() {
+        return mUm.getUserProfiles();
+    }
+}
diff --git a/tests/robotests/assets/grandfather_not_implementing_index_provider b/tests/robotests/assets/grandfather_not_implementing_index_provider
index 240b8eb..8613dd1 100644
--- a/tests/robotests/assets/grandfather_not_implementing_index_provider
+++ b/tests/robotests/assets/grandfather_not_implementing_index_provider
@@ -7,3 +7,8 @@
 com.android.settings.fuelgauge.AdvancedPowerUsageDetail
 com.android.settings.deviceinfo.StorageProfileFragment
 com.android.settings.wifi.details.WifiNetworkDetailsFragment
+com.android.settings.enterprise.ApplicationListFragment$AdminGrantedPermissionCamera
+com.android.settings.enterprise.ApplicationListFragment$AdminGrantedPermissionLocation
+com.android.settings.enterprise.ApplicationListFragment$AdminGrantedPermissionMicrophone
+com.android.settings.enterprise.ApplicationListFragment$EnterpriseInstalledPackages
+com.android.settings.enterprise.EnterpriseSetDefaultAppsListFragment
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/UtilsTest.java b/tests/robotests/src/com/android/settings/UtilsTest.java
index 8b7605c..8dfd32a 100644
--- a/tests/robotests/src/com/android/settings/UtilsTest.java
+++ b/tests/robotests/src/com/android/settings/UtilsTest.java
@@ -19,7 +19,10 @@
 import android.os.storage.DiskInfo;
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
 import android.text.format.DateUtils;
+import android.text.style.TtsSpan;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -35,10 +38,14 @@
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public class UtilsTest {
 
+    private static final String TIME_DESCRIPTION = "1 day 20 hours 30 minutes";
     private Context mContext;
-    @Mock private WifiManager wifiManager;
-    @Mock private Network network;
-    @Mock private ConnectivityManager connectivityManager;
+    @Mock
+    private WifiManager wifiManager;
+    @Mock
+    private Network network;
+    @Mock
+    private ConnectivityManager connectivityManager;
 
     @Before
     public void setUp() {
@@ -47,7 +54,7 @@
         mContext = spy(RuntimeEnvironment.application);
         when(mContext.getSystemService(WifiManager.class)).thenReturn(wifiManager);
         when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE))
-            .thenReturn(connectivityManager);
+                .thenReturn(connectivityManager);
     }
 
     @Test
@@ -89,7 +96,8 @@
         final double testMillis = 5 * DateUtils.MINUTE_IN_MILLIS;
         final String expectedTime = "5m 0s";
 
-        assertThat(Utils.formatElapsedTime(mContext, testMillis, true)).isEqualTo(expectedTime);
+        assertThat(Utils.formatElapsedTime(mContext, testMillis, true).toString()).isEqualTo(
+                expectedTime);
     }
 
     @Test
@@ -97,7 +105,8 @@
         final double testMillis = 5 * DateUtils.MINUTE_IN_MILLIS;
         final String expectedTime = "5m";
 
-        assertThat(Utils.formatElapsedTime(mContext, testMillis, false)).isEqualTo(expectedTime);
+        assertThat(Utils.formatElapsedTime(mContext, testMillis, false).toString()).isEqualTo(
+                expectedTime);
     }
 
     @Test
@@ -106,7 +115,23 @@
                 + 4 * DateUtils.HOUR_IN_MILLIS + 15 * DateUtils.MINUTE_IN_MILLIS;
         final String expectedTime = "2d 4h 15m";
 
-        assertThat(Utils.formatElapsedTime(mContext, testMillis, false)).isEqualTo(expectedTime);
+        assertThat(Utils.formatElapsedTime(mContext, testMillis, false).toString()).isEqualTo(
+                expectedTime);
+    }
+
+    @Test
+    public void testFormatElapsedTime_onlyContainsMinute_hasTtsSpan() {
+        final double testMillis = 15 * DateUtils.MINUTE_IN_MILLIS;
+
+        final CharSequence charSequence = Utils.formatElapsedTime(mContext, testMillis, false);
+        assertThat(charSequence).isInstanceOf(SpannableStringBuilder.class);
+
+        final SpannableStringBuilder expectedString = (SpannableStringBuilder) charSequence;
+        final TtsSpan[] ttsSpans = expectedString.getSpans(0, expectedString.length(),
+                TtsSpan.class);
+
+        assertThat(ttsSpans).asList().hasSize(1);
+        assertThat(ttsSpans[0].getType()).isEqualTo(TtsSpan.TYPE_MEASURE);
     }
 
     @Test
@@ -119,13 +144,13 @@
     }
 
     @Test
-    public void getInstallationStatus_notInstalled_shouldReturnUninstalled() {
+    public void testGetInstallationStatus_notInstalled_shouldReturnUninstalled() {
         assertThat(Utils.getInstallationStatus(new ApplicationInfo()))
                 .isEqualTo(R.string.not_installed);
     }
 
     @Test
-    public void getInstallationStatus_enabled_shouldReturnInstalled() {
+    public void testGetInstallationStatus_enabled_shouldReturnInstalled() {
         final ApplicationInfo info = new ApplicationInfo();
         info.flags = ApplicationInfo.FLAG_INSTALLED;
         info.enabled = true;
@@ -134,7 +159,7 @@
     }
 
     @Test
-    public void getInstallationStatus_disabled_shouldReturnDisabled() {
+    public void testGetInstallationStatus_disabled_shouldReturnDisabled() {
         final ApplicationInfo info = new ApplicationInfo();
         info.flags = ApplicationInfo.FLAG_INSTALLED;
         info.enabled = false;
diff --git a/tests/robotests/src/com/android/settings/applications/AppStorageSizesControllerTest.java b/tests/robotests/src/com/android/settings/applications/AppStorageSizesControllerTest.java
index 127100f..36d78c1 100644
--- a/tests/robotests/src/com/android/settings/applications/AppStorageSizesControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/AppStorageSizesControllerTest.java
@@ -110,4 +110,22 @@
         assertThat(mDataPreference.getSummary()).isEqualTo("100B");
         assertThat(mTotalPreference.getSummary()).isEqualTo("101B");
     }
+
+    @Test
+    public void fakeDataFlagSetsDataAndCacheToZero() {
+        AppStorageStats result = mock(AppStorageStats.class);
+        when(result.getCodeBytes()).thenReturn(1L);
+        when(result.getCacheBytes()).thenReturn(10L);
+        when(result.getDataBytes()).thenReturn(100L);
+        when(result.getTotalBytes()).thenReturn(111L);
+
+        mController.setResult(result);
+        mController.setDataCleared(true);
+        mController.updateUi(mContext);
+
+        assertThat(mAppPreference.getSummary()).isEqualTo("1.00B");
+        assertThat(mCachePreference.getSummary()).isEqualTo("0.00B");
+        assertThat(mDataPreference.getSummary()).isEqualTo("0.00B");
+        assertThat(mTotalPreference.getSummary()).isEqualTo("1.00B");
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounterTest.java b/tests/robotests/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounterTest.java
index aeb3826..4a7dc8f 100644
--- a/tests/robotests/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounterTest.java
+++ b/tests/robotests/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounterTest.java
@@ -18,15 +18,18 @@
 
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.os.Build;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
 
 import com.android.settings.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
 import com.android.settings.enterprise.DevicePolicyManagerWrapper;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -83,102 +86,31 @@
     @Mock private DevicePolicyManagerWrapper mDevicePolicyManager;
 
     private int mAppCount = -1;
+    private ApplicationInfo mApp1;
+    private ApplicationInfo mApp2;
+    private ApplicationInfo mApp3;
+    private ApplicationInfo mApp4;
+    private ApplicationInfo mApp5;
+    private ApplicationInfo mApp6;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+
+        mApp1 = buildInfo(APP_1_UID, APP_1, 0 /* flags */, Build.VERSION_CODES.M);
+        mApp2 = buildInfo(APP_2_UID, APP_2, 0 /* flags */, Build.VERSION_CODES.M);
+        mApp3 = buildInfo(APP_3_UID, APP_3, 0 /* flags */, Build.VERSION_CODES.LOLLIPOP);
+        mApp4 = buildInfo(APP_4_UID, APP_4, 0 /* flags */, Build.VERSION_CODES.LOLLIPOP);
+        mApp5 = buildInfo(APP_5_UID, APP_5, 0 /* flags */, Build.VERSION_CODES.LOLLIPOP);
+        mApp6 = buildInfo(APP_6_UID, APP_6, 0 /* flags */, Build.VERSION_CODES.M);
     }
 
     private void verifyCountInstalledApps(boolean async) throws Exception {
-        // There are two users.
-        when(mUserManager.getProfiles(UserHandle.myUserId())).thenReturn(Arrays.asList(
-                new UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_ADMIN),
-                new UserInfo(MANAGED_PROFILE_ID, "managed profile", 0)));
-
-        // The first user has five apps installed:
-        // * app1 uses run-time permissions. It has been granted one of the permissions by the
-        //        admin. It should be counted.
-        // * app2 uses run-time permissions. It has not been granted any of the permissions by the
-        //        admin. It should not be counted.
-        // * app3 uses install-time permissions. It was installed by the admin and requested one of
-        //        the permissions. It should be counted.
-        // * app4 uses install-time permissions. It was not installed by the admin but did request
-        //        one of the permissions. It should not be counted.
-        // * app5 uses install-time permissions. It was installed by the admin but did not request
-        //        any of the permissions. It should not be counted.
-        when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
-                | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS
-                | PackageManager.MATCH_ANY_USER,
-                MAIN_USER_ID)).thenReturn(Arrays.asList(
-                        buildInfo(APP_1_UID, APP_1, 0 /* flags */, Build.VERSION_CODES.M),
-                        buildInfo(APP_2_UID, APP_2, 0 /* flags */, Build.VERSION_CODES.M),
-                        buildInfo(APP_3_UID, APP_3, 0 /* flags */, Build.VERSION_CODES.LOLLIPOP),
-                        buildInfo(APP_4_UID, APP_4, 0 /* flags */, Build.VERSION_CODES.LOLLIPOP),
-                        buildInfo(APP_5_UID, APP_5, 0 /* flags */, Build.VERSION_CODES.LOLLIPOP)));
-
-        // Grant run-time permissions as appropriate.
-        when(mDevicePolicyManager.getPermissionGrantState(null, APP_1, PERMISSION_1))
-                .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
-        when(mDevicePolicyManager.getPermissionGrantState(null, APP_1, PERMISSION_2))
-                .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
-        when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_2), anyObject()))
-                .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
-        when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_3), anyObject()))
-                .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
-        when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_4), anyObject()))
-                .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
-        when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_5), anyObject()))
-                .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
-
-        // Grant install-time permissions as appropriate.
-        when(mPackageManagerService.checkUidPermission(anyObject(), eq(APP_1_UID)))
-                .thenReturn(PackageManager.PERMISSION_DENIED);
-        when(mPackageManagerService.checkUidPermission(anyObject(), eq(APP_2_UID)))
-                .thenReturn(PackageManager.PERMISSION_DENIED);
-        when(mPackageManagerService.checkUidPermission(PERMISSION_1, APP_3_UID))
-                .thenReturn(PackageManager.PERMISSION_DENIED);
-        when(mPackageManagerService.checkUidPermission(PERMISSION_2, APP_3_UID))
-                .thenReturn(PackageManager.PERMISSION_GRANTED);
-        when(mPackageManagerService.checkUidPermission(PERMISSION_1, APP_4_UID))
-                .thenReturn(PackageManager.PERMISSION_DENIED);
-        when(mPackageManagerService.checkUidPermission(PERMISSION_2, APP_4_UID))
-                .thenReturn(PackageManager.PERMISSION_GRANTED);
-        when(mPackageManagerService.checkUidPermission(anyObject(), eq(APP_5_UID)))
-                .thenReturn(PackageManager.PERMISSION_DENIED);
-
-        // app3 and app5 were installed by enterprise policy.
-        final UserHandle mainUser = new UserHandle(MAIN_USER_ID);
-        when(mPackageManager.getInstallReason(APP_1, mainUser))
-                .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
-        when(mPackageManager.getInstallReason(APP_2, mainUser))
-                .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
-        when(mPackageManager.getInstallReason(APP_3, mainUser))
-                .thenReturn(PackageManager.INSTALL_REASON_POLICY);
-        when(mPackageManager.getInstallReason(APP_4, mainUser))
-                .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
-        when(mPackageManager.getInstallReason(APP_5, mainUser))
-                .thenReturn(PackageManager.INSTALL_REASON_POLICY);
-
-        // The second user has one app installed. This app uses run-time permissions. It has been
-        // granted both permissions by the admin. It should be counted.
-        when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
-                | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
-                MANAGED_PROFILE_ID)).thenReturn(Arrays.asList(
-                        buildInfo(APP_6_UID, APP_6, 0 /* flags */, Build.VERSION_CODES.M)));
-
-        // Grant run-time permissions as appropriate.
-        when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_6), anyObject()))
-                .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
-
-        // Grant install-time permissions as appropriate.
-        when(mPackageManagerService.checkUidPermission(anyObject(), eq(APP_6_UID)))
-                .thenReturn(PackageManager.PERMISSION_DENIED);
-
-        // app6 was not installed by enterprise policy.
-        final UserHandle managedProfileUser = new UserHandle(MANAGED_PROFILE_ID);
-        when(mPackageManager.getInstallReason(APP_6, managedProfileUser))
-                .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+        configureUserManager();
+        configurePackageManager();
+        configureRunTimePermissions();
+        configureInstallTimePermissions();
 
         // Count the number of all apps installed that were granted on or more permissions by the
         // admin.
@@ -201,6 +133,28 @@
     }
 
     @Test
+    public void testIncludeInCount() throws Exception {
+        configurePackageManager();
+        configureRunTimePermissions();
+        configureInstallTimePermissions();
+
+        assertThat(AppWithAdminGrantedPermissionsCounter.includeInCount(PERMISSIONS,
+                mDevicePolicyManager, mPackageManager, mPackageManagerService, mApp1)).isTrue();
+
+        assertThat(AppWithAdminGrantedPermissionsCounter.includeInCount(PERMISSIONS,
+                mDevicePolicyManager, mPackageManager, mPackageManagerService, mApp2)).isFalse();
+
+        assertThat(AppWithAdminGrantedPermissionsCounter.includeInCount(PERMISSIONS,
+                mDevicePolicyManager, mPackageManager, mPackageManagerService, mApp3)).isTrue();
+
+        assertThat(AppWithAdminGrantedPermissionsCounter.includeInCount(PERMISSIONS,
+                mDevicePolicyManager, mPackageManager, mPackageManagerService, mApp4)).isFalse();
+
+        assertThat(AppWithAdminGrantedPermissionsCounter.includeInCount(PERMISSIONS,
+                mDevicePolicyManager, mPackageManager, mPackageManagerService, mApp5)).isFalse();
+    }
+
+    @Test
     public void testCountInstalledAppsSync() throws Exception {
         verifyCountInstalledApps(false /* async */);
     }
@@ -210,6 +164,90 @@
         verifyCountInstalledApps(true /* async */);
     }
 
+    private void configureInstallTimePermissions() throws RemoteException {
+        when(mPackageManagerService.checkUidPermission(anyObject(), eq(APP_1_UID)))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+        when(mPackageManagerService.checkUidPermission(anyObject(), eq(APP_2_UID)))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+        when(mPackageManagerService.checkUidPermission(PERMISSION_1, APP_3_UID))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+        when(mPackageManagerService.checkUidPermission(PERMISSION_2, APP_3_UID))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+        when(mPackageManagerService.checkUidPermission(PERMISSION_1, APP_4_UID))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+        when(mPackageManagerService.checkUidPermission(PERMISSION_2, APP_4_UID))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+        when(mPackageManagerService.checkUidPermission(anyObject(), eq(APP_5_UID)))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+        when(mPackageManagerService.checkUidPermission(anyObject(), eq(APP_6_UID)))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+    }
+
+    private void configureRunTimePermissions() {
+        when(mDevicePolicyManager.getPermissionGrantState(null, APP_1, PERMISSION_1))
+                .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
+        when(mDevicePolicyManager.getPermissionGrantState(null, APP_1, PERMISSION_2))
+                .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
+        when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_2), anyObject()))
+                .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
+        when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_3), anyObject()))
+                .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
+        when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_4), anyObject()))
+                .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
+        when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_5), anyObject()))
+                .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
+        when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_6), anyObject()))
+                .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
+    }
+
+    private void configurePackageManager() {
+        // The first user has five apps installed:
+        // * app1 uses run-time permissions. It has been granted one of the permissions by the
+        //        admin. It should be counted.
+        // * app2 uses run-time permissions. It has not been granted any of the permissions by the
+        //        admin. It should not be counted.
+        // * app3 uses install-time permissions. It was installed by the admin and requested one of
+        //        the permissions. It should be counted.
+        // * app4 uses install-time permissions. It was not installed by the admin but did request
+        //        one of the permissions. It should not be counted.
+        // * app5 uses install-time permissions. It was installed by the admin but did not request
+        //        any of the permissions. It should not be counted.
+        when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
+                | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS
+                | PackageManager.MATCH_ANY_USER,
+                MAIN_USER_ID)).thenReturn(Arrays.asList(mApp1, mApp2, mApp3, mApp4, mApp5));
+        // The second user has one app installed. This app uses run-time permissions. It has been
+        // granted both permissions by the admin. It should be counted.
+        when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
+                | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
+                MANAGED_PROFILE_ID)).thenReturn(Arrays.asList(mApp6));
+
+        // app3 and app5 were installed by enterprise policy.
+        final UserHandle mainUser = new UserHandle(MAIN_USER_ID);
+        when(mPackageManager.getInstallReason(APP_1, mainUser))
+                .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+        when(mPackageManager.getInstallReason(APP_2, mainUser))
+                .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+        when(mPackageManager.getInstallReason(APP_3, mainUser))
+                .thenReturn(PackageManager.INSTALL_REASON_POLICY);
+        when(mPackageManager.getInstallReason(APP_4, mainUser))
+                .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+        when(mPackageManager.getInstallReason(APP_5, mainUser))
+                .thenReturn(PackageManager.INSTALL_REASON_POLICY);
+        // app6 was not installed by enterprise policy.
+        final UserHandle managedProfileUser = new UserHandle(MANAGED_PROFILE_ID);
+        when(mPackageManager.getInstallReason(APP_6, managedProfileUser))
+                .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+
+    }
+
+    private void configureUserManager() {
+        // There are two users.
+        when(mUserManager.getProfiles(UserHandle.myUserId())).thenReturn(Arrays.asList(
+                new UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_ADMIN),
+                new UserInfo(MANAGED_PROFILE_ID, "managed profile", 0)));
+    }
+
     private class AppWithAdminGrantedPermissionsCounterTestable extends
             AppWithAdminGrantedPermissionsCounter {
         public AppWithAdminGrantedPermissionsCounterTestable(String[] permissions) {
diff --git a/tests/robotests/src/com/android/settings/applications/AppWithAdminGrantedPermissionsListerTest.java b/tests/robotests/src/com/android/settings/applications/AppWithAdminGrantedPermissionsListerTest.java
new file mode 100644
index 0000000..73bba04
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/AppWithAdminGrantedPermissionsListerTest.java
@@ -0,0 +1,223 @@
+/*
+ * 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.applications;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.os.Build;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.enterprise.DevicePolicyManagerWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static com.android.settings.testutils.ApplicationTestUtils.buildInfo;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for {@link AppWithAdminGrantedPermissionsLister}.
+ */
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public final class AppWithAdminGrantedPermissionsListerTest {
+
+    private final String APP_1 = "app1";
+    private final String APP_2 = "app2";
+    private final String APP_3 = "app3";
+    private final String APP_4 = "app4";
+    private final String APP_5 = "app5";
+    private final String APP_6 = "app6";
+
+    private final int MAIN_USER_ID = 0;
+    private final int MANAGED_PROFILE_ID = 10;
+
+    private final int PER_USER_UID_RANGE = 100000;
+    private final int APP_1_UID = MAIN_USER_ID * PER_USER_UID_RANGE + 1;
+    private final int APP_2_UID = MAIN_USER_ID * PER_USER_UID_RANGE + 2;
+    private final int APP_3_UID = MAIN_USER_ID * PER_USER_UID_RANGE + 3;
+    private final int APP_4_UID = MAIN_USER_ID * PER_USER_UID_RANGE + 4;
+    private final int APP_5_UID = MAIN_USER_ID * PER_USER_UID_RANGE + 5;
+    private final int APP_6_UID = MANAGED_PROFILE_ID * PER_USER_UID_RANGE + 1;
+
+    private final String PERMISSION_1 = "some.permission.1";
+    private final String PERMISSION_2 = "some.permission.2";
+    private final String[] PERMISSIONS = {PERMISSION_1, PERMISSION_2};
+
+    @Mock private UserManager mUserManager;
+    @Mock private PackageManagerWrapper mPackageManager;
+    @Mock private IPackageManagerWrapper mPackageManagerService;
+    @Mock private DevicePolicyManagerWrapper mDevicePolicyManager;
+
+    private List<UserAppInfo> mAppList = Collections.emptyList();
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void verifyListInstalledApps() throws Exception {
+        // There are two users.
+        when(mUserManager.getProfiles(UserHandle.myUserId())).thenReturn(Arrays.asList(
+                new UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_ADMIN),
+                new UserInfo(MANAGED_PROFILE_ID, "managed profile", 0)));
+
+        // The first user has five apps installed:
+        // * app1 uses run-time permissions. It has been granted one of the permissions by the
+        //        admin. It should be listed.
+        // * app2 uses run-time permissions. It has not been granted any of the permissions by the
+        //        admin. It should not be listed.
+        // * app3 uses install-time permissions. It was installed by the admin and requested one of
+        //        the permissions. It should be listed.
+        // * app4 uses install-time permissions. It was not installed by the admin but did request
+        //        one of the permissions. It should not be listed.
+        // * app5 uses install-time permissions. It was installed by the admin but did not request
+        //        any of the permissions. It should not be listed.
+        when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
+                | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS
+                | PackageManager.MATCH_ANY_USER,
+                MAIN_USER_ID)).thenReturn(Arrays.asList(
+                        buildInfo(APP_1_UID, APP_1, 0 /* flags */, Build.VERSION_CODES.M),
+                        buildInfo(APP_2_UID, APP_2, 0 /* flags */, Build.VERSION_CODES.M),
+                        buildInfo(APP_3_UID, APP_3, 0 /* flags */, Build.VERSION_CODES.LOLLIPOP),
+                        buildInfo(APP_4_UID, APP_4, 0 /* flags */, Build.VERSION_CODES.LOLLIPOP),
+                        buildInfo(APP_5_UID, APP_5, 0 /* flags */, Build.VERSION_CODES.LOLLIPOP)));
+
+        // Grant run-time permissions as appropriate.
+        when(mDevicePolicyManager.getPermissionGrantState(null, APP_1, PERMISSION_1))
+                .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
+        when(mDevicePolicyManager.getPermissionGrantState(null, APP_1, PERMISSION_2))
+                .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
+        when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_2), anyObject()))
+                .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
+        when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_3), anyObject()))
+                .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
+        when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_4), anyObject()))
+                .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
+        when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_5), anyObject()))
+                .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
+
+        // Grant install-time permissions as appropriate.
+        when(mPackageManagerService.checkUidPermission(anyObject(), eq(APP_1_UID)))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+        when(mPackageManagerService.checkUidPermission(anyObject(), eq(APP_2_UID)))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+        when(mPackageManagerService.checkUidPermission(PERMISSION_1, APP_3_UID))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+        when(mPackageManagerService.checkUidPermission(PERMISSION_2, APP_3_UID))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+        when(mPackageManagerService.checkUidPermission(PERMISSION_1, APP_4_UID))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+        when(mPackageManagerService.checkUidPermission(PERMISSION_2, APP_4_UID))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+        when(mPackageManagerService.checkUidPermission(anyObject(), eq(APP_5_UID)))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+
+        // app3 and app5 were installed by enterprise policy.
+        final UserHandle mainUser = new UserHandle(MAIN_USER_ID);
+        when(mPackageManager.getInstallReason(APP_1, mainUser))
+                .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+        when(mPackageManager.getInstallReason(APP_2, mainUser))
+                .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+        when(mPackageManager.getInstallReason(APP_3, mainUser))
+                .thenReturn(PackageManager.INSTALL_REASON_POLICY);
+        when(mPackageManager.getInstallReason(APP_4, mainUser))
+                .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+        when(mPackageManager.getInstallReason(APP_5, mainUser))
+                .thenReturn(PackageManager.INSTALL_REASON_POLICY);
+
+        // The second user has one app installed. This app uses run-time permissions. It has been
+        // granted both permissions by the admin. It should be listed.
+        when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
+                | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
+                MANAGED_PROFILE_ID)).thenReturn(Arrays.asList(
+                        buildInfo(APP_6_UID, APP_6, 0 /* flags */, Build.VERSION_CODES.M)));
+
+        // Grant run-time permissions as appropriate.
+        when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_6), anyObject()))
+                .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
+
+        // Grant install-time permissions as appropriate.
+        when(mPackageManagerService.checkUidPermission(anyObject(), eq(APP_6_UID)))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+
+        // app6 was not installed by enterprise policy.
+        final UserHandle managedProfileUser = new UserHandle(MANAGED_PROFILE_ID);
+        when(mPackageManager.getInstallReason(APP_6, managedProfileUser))
+                .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+
+        // List all apps installed that were granted one or more permissions by the
+        // admin.
+        (new AppWithAdminGrantedPermissionsListerTestable(PERMISSIONS)).execute();
+
+        // Wait for the background task to finish.
+        ShadowApplication.runBackgroundTasks();
+        assertThat(mAppList.size()).isEqualTo(3);
+        InstalledAppListerTest.verifyListUniqueness(mAppList);
+
+        assertThat(InstalledAppListerTest.checkAppFound(mAppList, APP_1, MAIN_USER_ID)).isTrue();
+        assertThat(InstalledAppListerTest.checkAppFound(mAppList, APP_2, MAIN_USER_ID)).isFalse();
+        assertThat(InstalledAppListerTest.checkAppFound(mAppList, APP_3, MAIN_USER_ID)).isTrue();
+        assertThat(InstalledAppListerTest.checkAppFound(mAppList, APP_4, MAIN_USER_ID)).isFalse();
+        assertThat(InstalledAppListerTest.checkAppFound(mAppList, APP_5, MAIN_USER_ID)).isFalse();
+        assertThat(InstalledAppListerTest.checkAppFound(mAppList, APP_6, MANAGED_PROFILE_ID)).
+                isTrue();
+
+        // Verify that installed packages were retrieved the current user and the user's managed
+        // profile only.
+        verify(mPackageManager).getInstalledApplicationsAsUser(anyInt(), eq(MAIN_USER_ID));
+        verify(mPackageManager).getInstalledApplicationsAsUser(anyInt(),
+                eq(MANAGED_PROFILE_ID));
+        verify(mPackageManager, atLeast(0)).getInstallReason(anyObject(), anyObject());
+        verifyNoMoreInteractions(mPackageManager);
+    }
+
+    private class AppWithAdminGrantedPermissionsListerTestable extends
+            AppWithAdminGrantedPermissionsLister {
+
+        public AppWithAdminGrantedPermissionsListerTestable(String[] permissions) {
+            super(permissions, mPackageManager, mPackageManagerService,
+                    mDevicePolicyManager, mUserManager);
+        }
+
+        @Override
+        protected void onAppListBuilt(List<UserAppInfo> list) {
+            mAppList = list;
+        }
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java
index f46bb90..4c4ec46 100644
--- a/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java
+++ b/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java
@@ -20,19 +20,20 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
 import android.os.Build;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.util.ArraySet;
 
 import com.android.settings.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
 import com.android.settings.enterprise.DevicePolicyManagerWrapper;
 import com.android.settings.testutils.ApplicationTestUtils;
 import com.android.settings.testutils.shadow.ShadowUserManager;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -41,8 +42,9 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowApplication;
 
+import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Set;
+import java.util.List;
 
 import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.Mockito.when;
@@ -76,6 +78,7 @@
     private ApplicationFeatureProvider mProvider;
 
     private int mAppCount = -1;
+    private List<UserAppInfo> mAppList = null;
 
     @Before
     public void setUp() {
@@ -106,6 +109,22 @@
     }
 
     @Test
+    public void testListPolicyInstalledApps() {
+        setUpUsersAndInstalledApps();
+
+        when(mPackageManager.getInstallReason(APP_1, new UserHandle(MAIN_USER_ID)))
+                .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+        when(mPackageManager.getInstallReason(APP_2, new UserHandle(MANAGED_PROFILE_ID)))
+                .thenReturn(PackageManager.INSTALL_REASON_POLICY);
+
+        mAppList = null;
+        mProvider.listPolicyInstalledApps((list) -> mAppList = list);
+        assertThat(mAppList).isNotNull();
+        assertThat(mAppList.size()).isEqualTo(1);
+        assertThat(mAppList.get(0).appInfo.packageName).isEqualTo(APP_2);
+    }
+
+    @Test
     public void testCalculateNumberOfInstalledAppsSync() {
         verifyCalculateNumberOfPolicyInstalledApps(false /* async */);
     }
@@ -139,7 +158,6 @@
             ShadowApplication.runBackgroundTasks();
         }
         assertThat(mAppCount).isEqualTo(2);
-
     }
 
     @Test
@@ -153,9 +171,43 @@
     }
 
     @Test
+    public void testListAppsWithAdminGrantedPermissions()
+            throws Exception {
+        setUpUsersAndInstalledApps();
+
+        when(mDevicePolicyManager.getPermissionGrantState(null, APP_1, PERMISSION))
+                .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
+        when(mDevicePolicyManager.getPermissionGrantState(null, APP_2, PERMISSION))
+                .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
+        when(mPackageManagerService.checkUidPermission(PERMISSION, APP_1_UID))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+        when(mPackageManagerService.checkUidPermission(PERMISSION, APP_2_UID))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+        when(mPackageManager.getInstallReason(APP_1, new UserHandle(MAIN_USER_ID)))
+                .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+        when(mPackageManager.getInstallReason(APP_2, new UserHandle(MANAGED_PROFILE_ID)))
+                .thenReturn(PackageManager.INSTALL_REASON_POLICY);
+
+        mAppList = null;
+        mProvider.listAppsWithAdminGrantedPermissions(new String[] {PERMISSION},
+                (list) -> mAppList = list);
+        assertThat(mAppList).isNotNull();
+        assertThat(mAppList.size()).isEqualTo(2);
+        assertThat(Arrays.asList(mAppList.get(0).appInfo.packageName,
+                mAppList.get(1).appInfo.packageName).containsAll(Arrays.asList(APP_1, APP_2)))
+                .isTrue();
+    }
+
+    @Test
     public void testFindPersistentPreferredActivities() throws Exception {
+        final UserInfo mainUser = new UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_ADMIN);
+        final UserInfo managedUser = new UserInfo(MANAGED_PROFILE_ID, "managed",
+                UserInfo.FLAG_MANAGED_PROFILE);
+
         when(mUserManager.getUserProfiles()).thenReturn(Arrays.asList(new UserHandle(MAIN_USER_ID),
                 new UserHandle(MANAGED_PROFILE_ID)));
+        when(mUserManager.getUserInfo(MAIN_USER_ID)).thenReturn(mainUser);
+        when(mUserManager.getUserInfo(MANAGED_PROFILE_ID)).thenReturn(managedUser);
 
         final Intent viewIntent = new Intent(Intent.ACTION_VIEW);
         final Intent editIntent = new Intent(Intent.ACTION_EDIT);
@@ -176,17 +228,21 @@
         when(mPackageManagerService.findPersistentPreferredActivity(sendIntent, MANAGED_PROFILE_ID))
                 .thenReturn(null);
 
-        final Set<ApplicationFeatureProvider.PersistentPreferredActivityInfo> expectedActivities
-                = new ArraySet<>();
-        expectedActivities.add(new ApplicationFeatureProvider.PersistentPreferredActivityInfo(APP_1,
-                MAIN_USER_ID));
-        expectedActivities.add(new ApplicationFeatureProvider.PersistentPreferredActivityInfo(APP_1,
-                MANAGED_PROFILE_ID));
-        expectedActivities.add(new ApplicationFeatureProvider.PersistentPreferredActivityInfo(APP_2,
-                MANAGED_PROFILE_ID));
+        final List<UserAppInfo> expectedMainUserActivities = new ArrayList<>();
+        expectedMainUserActivities.add(new UserAppInfo(mainUser,
+                new ApplicationInfo(app1.activityInfo.applicationInfo)));
+        final List<UserAppInfo> expectedManagedUserActivities = new ArrayList<>();
+        expectedManagedUserActivities.add(new UserAppInfo(managedUser,
+                new ApplicationInfo(app1.activityInfo.applicationInfo)));
+        expectedManagedUserActivities.add(new UserAppInfo(managedUser,
+                new ApplicationInfo(app2.activityInfo.applicationInfo)));
 
-        assertThat(mProvider.findPersistentPreferredActivities(
-                new Intent[] {viewIntent, editIntent, sendIntent})).isEqualTo(expectedActivities);
+        assertThat(mProvider.findPersistentPreferredActivities(MAIN_USER_ID,
+                new Intent[] {viewIntent, editIntent, sendIntent}))
+                .isEqualTo(expectedMainUserActivities);
+        assertThat(mProvider.findPersistentPreferredActivities(MANAGED_PROFILE_ID,
+                new Intent[] {viewIntent, editIntent, sendIntent}))
+                .isEqualTo(expectedManagedUserActivities);
     }
 
     private void setUpUsersAndInstalledApps() {
@@ -208,8 +264,11 @@
     }
 
     private ResolveInfo createResolveInfo(String packageName) {
+        final ApplicationInfo applicationInfo = new ApplicationInfo();
+        applicationInfo.packageName = packageName;
         final ActivityInfo activityInfo = new ActivityInfo();
         activityInfo.packageName = packageName;
+        activityInfo.applicationInfo = applicationInfo;
         final ResolveInfo resolveInfo = new ResolveInfo();
         resolveInfo.activityInfo = activityInfo;
         return resolveInfo;
diff --git a/tests/robotests/src/com/android/settings/applications/EnterpriseDefaultAppsTest.java b/tests/robotests/src/com/android/settings/applications/EnterpriseDefaultAppsTest.java
new file mode 100644
index 0000000..ad05c27
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/EnterpriseDefaultAppsTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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.applications;
+
+import com.android.settings.R;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import static junit.framework.Assert.assertTrue;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class EnterpriseDefaultAppsTest {
+    @Test
+    public void testNumberOfIntentsCorrelateWithUI() throws Exception {
+        final int concatenation_templates[] =
+                new int[]{0 /* no need for single app name */,
+                        R.string.app_names_concatenation_template_2,
+                        R.string.app_names_concatenation_template_3};
+        for (EnterpriseDefaultApps app : EnterpriseDefaultApps.values()) {
+            assertTrue("Number of intents should be limited by number of apps the UI can show",
+                    app.getIntents().length <= concatenation_templates.length);
+        }
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/applications/InstalledAppCounterTest.java b/tests/robotests/src/com/android/settings/applications/InstalledAppCounterTest.java
index 46a3359..37fa9d7 100644
--- a/tests/robotests/src/com/android/settings/applications/InstalledAppCounterTest.java
+++ b/tests/robotests/src/com/android/settings/applications/InstalledAppCounterTest.java
@@ -22,12 +22,14 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
+import android.os.Build;
 import android.os.UserHandle;
 import android.os.UserManager;
 
 import com.android.settings.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
 import com.android.settings.testutils.shadow.ShadowUserManager;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -75,16 +77,38 @@
     private final int MAIN_USER_APP_UID = MAIN_USER_ID * PER_USER_UID_RANGE;
     private final int MANAGED_PROFILE_APP_UID = MANAGED_PROFILE_ID * PER_USER_UID_RANGE;
 
-    @Mock private UserManager mUserManager;
-    @Mock private Context mContext;
-    @Mock private PackageManagerWrapper mPackageManager;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private Context mContext;
+    @Mock
+    private PackageManagerWrapper mPackageManager;
 
     private int mInstalledAppCount = -1;
+    private ApplicationInfo mApp1;
+    private ApplicationInfo mApp2;
+    private ApplicationInfo mApp3;
+    private ApplicationInfo mApp4;
+    private ApplicationInfo mApp5;
+    private ApplicationInfo mApp6;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+
+        mApp1 = buildInfo(MAIN_USER_APP_UID, APP_1,
+                ApplicationInfo.FLAG_UPDATED_SYSTEM_APP, 0 /* targetSdkVersion */);
+        mApp2 = buildInfo(MAIN_USER_APP_UID, APP_2, 0 /* flags */,
+                0 /* targetSdkVersion */);
+        mApp3 = buildInfo(MAIN_USER_APP_UID, APP_3, ApplicationInfo.FLAG_SYSTEM,
+                0 /* targetSdkVersion */);
+        mApp4 = buildInfo(MAIN_USER_APP_UID, APP_4, ApplicationInfo.FLAG_SYSTEM,
+                0 /* targetSdkVersion */);
+        mApp5 = buildInfo(MANAGED_PROFILE_APP_UID, APP_5, 0 /* flags */,
+                0 /* targetSdkVersion */);
+        mApp6 = buildInfo(MANAGED_PROFILE_APP_UID, APP_6, ApplicationInfo.FLAG_SYSTEM,
+                0 /* targetSdkVersion */);
     }
 
     private void expectQueryIntentActivities(int userId, String packageName, boolean launchable) {
@@ -93,7 +117,7 @@
                 eq(PackageManager.GET_DISABLED_COMPONENTS | PackageManager.MATCH_DIRECT_BOOT_AWARE
                         | PackageManager.MATCH_DIRECT_BOOT_UNAWARE),
                 eq(userId))).thenReturn(launchable ? Arrays.asList(new ResolveInfo())
-                        : new ArrayList<ResolveInfo>());
+                : new ArrayList<ResolveInfo>());
     }
 
     private void testCountInstalledAppsAcrossAllUsers(boolean async) {
@@ -101,58 +125,7 @@
         when(mUserManager.getProfiles(UserHandle.myUserId())).thenReturn(Arrays.asList(
                 new UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_ADMIN),
                 new UserInfo(MANAGED_PROFILE_ID, "managed profile", 0)));
-
-        // The first user has four apps installed:
-        // * app1 is an updated system app. It should be counted.
-        // * app2 is a user-installed app. It should be counted.
-        // * app3 is a system app that provides a launcher icon. It should be counted.
-        // * app4 is a system app that provides no launcher icon. It should not be counted.
-        when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
-                | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS
-                | PackageManager.MATCH_ANY_USER,
-                MAIN_USER_ID)).thenReturn(Arrays.asList(
-                        buildInfo(MAIN_USER_APP_UID, APP_1,
-                                ApplicationInfo.FLAG_UPDATED_SYSTEM_APP, 0 /* targetSdkVersion */),
-                        buildInfo(MAIN_USER_APP_UID, APP_2, 0 /* flags */,
-                                0 /* targetSdkVersion */),
-                        buildInfo(MAIN_USER_APP_UID, APP_3, ApplicationInfo.FLAG_SYSTEM,
-                                0 /* targetSdkVersion */),
-                        buildInfo(MAIN_USER_APP_UID, APP_4, ApplicationInfo.FLAG_SYSTEM,
-                                0 /* targetSdkVersion */)));
-        // For system apps, InstalledAppCounter checks whether they handle the default launcher
-        // intent to decide whether to include them in the count of installed apps or not.
-        expectQueryIntentActivities(MAIN_USER_ID, APP_3, true /* launchable */);
-        expectQueryIntentActivities(MAIN_USER_ID, APP_4, false /* launchable */);
-
-        // app1, app3 and app4 are installed by enterprise policy.
-        final UserHandle mainUser = new UserHandle(MAIN_USER_ID);
-        when(mPackageManager.getInstallReason(APP_1, mainUser))
-                .thenReturn(PackageManager.INSTALL_REASON_POLICY);
-        when(mPackageManager.getInstallReason(APP_2, mainUser))
-                .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
-        when(mPackageManager.getInstallReason(APP_3, mainUser))
-                .thenReturn(PackageManager.INSTALL_REASON_POLICY);
-        when(mPackageManager.getInstallReason(APP_4, mainUser))
-                .thenReturn(PackageManager.INSTALL_REASON_POLICY);
-
-        // The second user has two apps installed:
-        // * app5 is a user-installed app. It should be counted.
-        // * app6 is a system app that provides a launcher icon. It should be counted.
-        when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
-                | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
-                MANAGED_PROFILE_ID)).thenReturn(Arrays.asList(
-                        buildInfo(MANAGED_PROFILE_APP_UID, APP_5, 0 /* flags */,
-                                0 /* targetSdkVersion */),
-                        buildInfo(MANAGED_PROFILE_APP_UID, APP_6, ApplicationInfo.FLAG_SYSTEM,
-                                0 /* targetSdkVersion */)));
-        expectQueryIntentActivities(MANAGED_PROFILE_ID, APP_6, true /* launchable */);
-
-        // app5 is installed by enterprise policy.
-        final UserHandle managedProfileUser = new UserHandle(MANAGED_PROFILE_ID);
-        when(mPackageManager.getInstallReason(APP_5, managedProfileUser))
-                .thenReturn(PackageManager.INSTALL_REASON_POLICY);
-        when(mPackageManager.getInstallReason(APP_6, managedProfileUser))
-                .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+        configurePackageManager();
 
         // Count the number of all apps installed, irrespective of install reason.
         count(InstalledAppCounter.IGNORE_INSTALL_REASON, async);
@@ -173,6 +146,36 @@
     }
 
     @Test
+    public void testIncludeInCount() {
+        configurePackageManager();
+        assertThat(InstalledAppCounter.includeInCount(InstalledAppCounter.IGNORE_INSTALL_REASON,
+                mPackageManager, mApp1)).isTrue();
+        assertThat(InstalledAppCounter.includeInCount(InstalledAppCounter.IGNORE_INSTALL_REASON,
+                mPackageManager, mApp2)).isTrue();
+        assertThat(InstalledAppCounter.includeInCount(InstalledAppCounter.IGNORE_INSTALL_REASON,
+                mPackageManager, mApp3)).isTrue();
+        assertThat(InstalledAppCounter.includeInCount(InstalledAppCounter.IGNORE_INSTALL_REASON,
+                mPackageManager, mApp4)).isFalse();
+        assertThat(InstalledAppCounter.includeInCount(InstalledAppCounter.IGNORE_INSTALL_REASON,
+                mPackageManager, mApp5)).isTrue();
+        assertThat(InstalledAppCounter.includeInCount(InstalledAppCounter.IGNORE_INSTALL_REASON,
+                mPackageManager, mApp6)).isTrue();
+
+        assertThat(InstalledAppCounter.includeInCount(PackageManager.INSTALL_REASON_POLICY,
+                mPackageManager, mApp1)).isTrue();
+        assertThat(InstalledAppCounter.includeInCount(PackageManager.INSTALL_REASON_POLICY,
+                mPackageManager, mApp2)).isFalse();
+        assertThat(InstalledAppCounter.includeInCount(PackageManager.INSTALL_REASON_POLICY,
+                mPackageManager, mApp3)).isTrue();
+        assertThat(InstalledAppCounter.includeInCount(PackageManager.INSTALL_REASON_POLICY,
+                mPackageManager, mApp4)).isFalse();
+        assertThat(InstalledAppCounter.includeInCount(PackageManager.INSTALL_REASON_POLICY,
+                mPackageManager, mApp5)).isTrue();
+        assertThat(InstalledAppCounter.includeInCount(PackageManager.INSTALL_REASON_POLICY,
+                mPackageManager, mApp6)).isFalse();
+    }
+
+    @Test
     public void testCountInstalledAppsAcrossAllUsersSync() {
         testCountInstalledAppsAcrossAllUsers(false /* async */);
     }
@@ -194,6 +197,48 @@
         }
     }
 
+    private void configurePackageManager() {
+        // The first user has four apps installed:
+        // * app1 is an updated system app. It should be counted.
+        // * app2 is a user-installed app. It should be counted.
+        // * app3 is a system app that provides a launcher icon. It should be counted.
+        // * app4 is a system app that provides no launcher icon. It should not be counted.
+        when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
+                | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS
+                | PackageManager.MATCH_ANY_USER,
+                MAIN_USER_ID)).thenReturn(Arrays.asList(mApp1, mApp2, mApp3, mApp4));
+        // For system apps, InstalledAppCounter checks whether they handle the default launcher
+        // intent to decide whether to include them in the count of installed apps or not.
+        expectQueryIntentActivities(MAIN_USER_ID, APP_3, true /* launchable */);
+        expectQueryIntentActivities(MAIN_USER_ID, APP_4, false /* launchable */);
+
+        // app1, app3 and app4 are installed by enterprise policy.
+        final UserHandle mainUser = new UserHandle(MAIN_USER_ID);
+        when(mPackageManager.getInstallReason(APP_1, mainUser))
+                .thenReturn(PackageManager.INSTALL_REASON_POLICY);
+        when(mPackageManager.getInstallReason(APP_2, mainUser))
+                .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+        when(mPackageManager.getInstallReason(APP_3, mainUser))
+                .thenReturn(PackageManager.INSTALL_REASON_POLICY);
+        when(mPackageManager.getInstallReason(APP_4, mainUser))
+                .thenReturn(PackageManager.INSTALL_REASON_POLICY);
+
+        // The second user has two apps installed:
+        // * app5 is a user-installed app. It should be counted.
+        // * app6 is a system app that provides a launcher icon. It should be counted.
+        when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
+                | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,MANAGED_PROFILE_ID))
+                .thenReturn(Arrays.asList(mApp5, mApp6));
+        expectQueryIntentActivities(MANAGED_PROFILE_ID, APP_6, true /* launchable */);
+
+        // app5 is installed by enterprise policy.
+        final UserHandle managedProfileUser = new UserHandle(MANAGED_PROFILE_ID);
+        when(mPackageManager.getInstallReason(APP_5, managedProfileUser))
+                .thenReturn(PackageManager.INSTALL_REASON_POLICY);
+        when(mPackageManager.getInstallReason(APP_6, managedProfileUser))
+                .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+    }
+
 
     private class InstalledAppCounterTestable extends InstalledAppCounter {
         public InstalledAppCounterTestable(int installReason) {
diff --git a/tests/robotests/src/com/android/settings/applications/InstalledAppDetailsTest.java b/tests/robotests/src/com/android/settings/applications/InstalledAppDetailsTest.java
index f372836..9c91ccf 100644
--- a/tests/robotests/src/com/android/settings/applications/InstalledAppDetailsTest.java
+++ b/tests/robotests/src/com/android/settings/applications/InstalledAppDetailsTest.java
@@ -87,6 +87,8 @@
     private BatteryStatsHelper mBatteryStatsHelper;
     @Mock
     private BatteryStats.Uid mUid;
+    @Mock
+    private PackageManager mPackageManager;
 
     private InstalledAppDetails mAppDetail;
     private Context mShadowContext;
@@ -94,14 +96,15 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mShadowContext = RuntimeEnvironment.application;
 
+        mShadowContext = RuntimeEnvironment.application;
         mAppDetail = spy(new InstalledAppDetails());
 
         mBatterySipper.drainType = BatterySipper.DrainType.IDLE;
         mBatterySipper.uidObj = mUid;
         doReturn(mActivity).when(mAppDetail).getActivity();
         doReturn(mShadowContext).when(mAppDetail).getContext();
+        doReturn(mPackageManager).when(mActivity).getPackageManager();
 
         // Default to not considering any apps to be instant (individual tests can override this).
         ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
diff --git a/tests/robotests/src/com/android/settings/applications/InstalledAppListerTest.java b/tests/robotests/src/com/android/settings/applications/InstalledAppListerTest.java
new file mode 100644
index 0000000..76421c2
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/InstalledAppListerTest.java
@@ -0,0 +1,233 @@
+/*
+ * 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.applications;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static com.android.settings.testutils.ApplicationTestUtils.buildInfo;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for {@link InstalledAppLister}.
+ */
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public final class InstalledAppListerTest {
+
+    private final String APP_1 = "app1";
+    private final String APP_2 = "app2";
+    private final String APP_3 = "app3";
+    private final String APP_4 = "app4";
+    private final String APP_5 = "app5";
+    private final String APP_6 = "app6";
+
+    private final int MAIN_USER_ID = 0;
+    private final int MANAGED_PROFILE_ID = 10;
+
+    private final int PER_USER_UID_RANGE = 100000;
+    private final int MAIN_USER_APP_UID = MAIN_USER_ID * PER_USER_UID_RANGE;
+    private final int MANAGED_PROFILE_APP_UID = MANAGED_PROFILE_ID * PER_USER_UID_RANGE;
+
+    @Mock private UserManager mUserManager;
+    @Mock private PackageManagerWrapper mPackageManager;
+
+    private List<UserAppInfo> mInstalledAppList = Collections.emptyList();
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    private void expectQueryIntentActivities(int userId, String packageName, boolean launchable) {
+        when(mPackageManager.queryIntentActivitiesAsUser(
+                argThat(new IsLaunchIntentFor(packageName)),
+                eq(PackageManager.GET_DISABLED_COMPONENTS | PackageManager.MATCH_DIRECT_BOOT_AWARE
+                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE),
+                eq(userId))).thenReturn(launchable ? Arrays.asList(new ResolveInfo())
+                        : new ArrayList<ResolveInfo>());
+    }
+
+    @Test
+    public void testCountInstalledAppsAcrossAllUsers() {
+        // There are two users.
+        when(mUserManager.getProfiles(UserHandle.myUserId())).thenReturn(Arrays.asList(
+                new UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_ADMIN),
+                new UserInfo(MANAGED_PROFILE_ID, "managed profile", 0)));
+
+        // The first user has four apps installed:
+        // * app1 is an updated system app. It should be listed.
+        // * app2 is a user-installed app. It should be listed.
+        // * app3 is a system app that provides a launcher icon. It should be listed.
+        // * app4 is a system app that provides no launcher icon. It should not be listed.
+        when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
+                | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS
+                | PackageManager.MATCH_ANY_USER,
+                MAIN_USER_ID)).thenReturn(Arrays.asList(
+                        buildInfo(MAIN_USER_APP_UID, APP_1,
+                                ApplicationInfo.FLAG_UPDATED_SYSTEM_APP, 0 /* targetSdkVersion */),
+                        buildInfo(MAIN_USER_APP_UID, APP_2, 0 /* flags */,
+                                0 /* targetSdkVersion */),
+                        buildInfo(MAIN_USER_APP_UID, APP_3, ApplicationInfo.FLAG_SYSTEM,
+                                0 /* targetSdkVersion */),
+                        buildInfo(MAIN_USER_APP_UID, APP_4, ApplicationInfo.FLAG_SYSTEM,
+                                0 /* targetSdkVersion */)));
+        // For system apps, InstalledAppLister checks whether they handle the default launcher
+        // intent to decide whether to include them in the list of installed apps or not.
+        expectQueryIntentActivities(MAIN_USER_ID, APP_3, true /* launchable */);
+        expectQueryIntentActivities(MAIN_USER_ID, APP_4, false /* launchable */);
+
+        // app1, app3 and app4 are installed by enterprise policy.
+        final UserHandle mainUser = new UserHandle(MAIN_USER_ID);
+        when(mPackageManager.getInstallReason(APP_1, mainUser))
+                .thenReturn(PackageManager.INSTALL_REASON_POLICY);
+        when(mPackageManager.getInstallReason(APP_2, mainUser))
+                .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+        when(mPackageManager.getInstallReason(APP_3, mainUser))
+                .thenReturn(PackageManager.INSTALL_REASON_POLICY);
+        when(mPackageManager.getInstallReason(APP_4, mainUser))
+                .thenReturn(PackageManager.INSTALL_REASON_POLICY);
+
+        // The second user has two apps installed:
+        // * app5 is a user-installed app. It should be listed.
+        // * app6 is a system app that provides a launcher icon. It should be listed.
+        when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
+                | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
+                MANAGED_PROFILE_ID)).thenReturn(Arrays.asList(
+                        buildInfo(MANAGED_PROFILE_APP_UID, APP_5, 0 /* flags */,
+                                0 /* targetSdkVersion */),
+                        buildInfo(MANAGED_PROFILE_APP_UID, APP_6, ApplicationInfo.FLAG_SYSTEM,
+                                0 /* targetSdkVersion */)));
+        expectQueryIntentActivities(MANAGED_PROFILE_ID, APP_6, true /* launchable */);
+
+        // app5 is installed by enterprise policy.
+        final UserHandle managedProfileUser = new UserHandle(MANAGED_PROFILE_ID);
+        when(mPackageManager.getInstallReason(APP_5, managedProfileUser))
+                .thenReturn(PackageManager.INSTALL_REASON_POLICY);
+        when(mPackageManager.getInstallReason(APP_6, managedProfileUser))
+                .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+
+        // List apps, considering apps installed by enterprise policy only.
+        mInstalledAppList = Collections.emptyList();
+        final InstalledAppListerTestable counter = new InstalledAppListerTestable();
+        counter.execute();
+        // Wait for the background task to finish.
+        ShadowApplication.runBackgroundTasks();
+
+        assertThat(mInstalledAppList.size()).isEqualTo(3);
+
+        assertThat(checkAppFound(mInstalledAppList, APP_1, MAIN_USER_ID)).isTrue();
+        assertThat(checkAppFound(mInstalledAppList, APP_2, MAIN_USER_ID)).isFalse();
+        assertThat(checkAppFound(mInstalledAppList, APP_3, MAIN_USER_ID)).isTrue();
+        assertThat(checkAppFound(mInstalledAppList, APP_4, MAIN_USER_ID)).isFalse();
+        assertThat(checkAppFound(mInstalledAppList, APP_5, MANAGED_PROFILE_ID)).isTrue();
+        assertThat(checkAppFound(mInstalledAppList, APP_6, MANAGED_PROFILE_ID)).isFalse();
+
+        // Verify that installed packages were retrieved for the current user and the user's
+        // managed profile.
+        verify(mPackageManager).getInstalledApplicationsAsUser(anyInt(), eq(MAIN_USER_ID));
+        verify(mPackageManager).getInstalledApplicationsAsUser(anyInt(),
+                eq(MANAGED_PROFILE_ID));
+        verify(mPackageManager, atLeast(0)).queryIntentActivitiesAsUser(anyObject(), anyInt(),
+                anyInt());
+    }
+
+    public static boolean checkAppFound(List<UserAppInfo> mInstalledAppList, String appId,
+            int userId) {
+       for (UserAppInfo info : mInstalledAppList) {
+           if (appId.equals(info.appInfo.packageName) && (info.userInfo.id == userId)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static void verifyListUniqueness(List<UserAppInfo> list) {
+        assertThat((new HashSet<>(list)).size()).isEqualTo(list.size());
+    }
+
+    private class InstalledAppListerTestable extends InstalledAppLister {
+        public InstalledAppListerTestable() {
+            super(mPackageManager, mUserManager);
+        }
+
+        @Override
+        protected void onAppListBuilt(List<UserAppInfo> list) {
+            mInstalledAppList = list;
+        }
+    }
+
+    private static class IsLaunchIntentFor extends ArgumentMatcher<Intent> {
+        private final String mPackageName;
+
+        IsLaunchIntentFor(String packageName) {
+            mPackageName = packageName;
+        }
+
+        @Override
+        public boolean matches(Object i) {
+            final Intent intent = (Intent) i;
+            if (intent == null) {
+                return false;
+            }
+            if (!Intent.ACTION_MAIN.equals(intent.getAction())) {
+                return false;
+            }
+            final Set<String> categories = intent.getCategories();
+            if (categories == null || categories.size() != 1 ||
+                    !categories.contains(Intent.CATEGORY_LAUNCHER)) {
+                return false;
+            }
+            if (!mPackageName.equals(intent.getPackage())) {
+                return false;
+            }
+            return true;
+        }
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothSummaryUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothSummaryUpdaterTest.java
index cc059ad..79daa0e 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothSummaryUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothSummaryUpdaterTest.java
@@ -16,7 +16,10 @@
 
 package com.android.settings.bluetooth;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
 import android.content.Context;
 
 import com.android.settings.R;
@@ -35,29 +38,38 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowBluetoothAdapter;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
+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;
 
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public class BluetoothSummaryUpdaterTest {
+    private static final String DEVICE_NAME = "Nightshade";
+    private static final String DEVICE_KEYBOARD_NAME = "Bluetooth Keyboard";
 
     private Context mContext;
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private LocalBluetoothManager mBluetoothManager;
     @Mock
     private LocalBluetoothAdapter mBtAdapter;
-
-    private BluetoothSummaryUpdater mSummaryUpdater;
+    @Mock
+    private BluetoothDevice mConnectedDevice;
+    @Mock
+    private BluetoothDevice mConnectedKeyBoardDevice;
     @Mock
     private SummaryListener mListener;
 
+    private BluetoothSummaryUpdater mSummaryUpdater;
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -84,10 +96,12 @@
 
     @Test
     public void register_true_shouldSendSummaryChange() {
+        prepareConnectedDevice(false);
+
         mSummaryUpdater.register(true);
 
         verify(mListener).onSummaryChanged(
-            mContext.getString(R.string.bluetooth_connected_summary));
+                mContext.getString(R.string.bluetooth_connected_summary, DEVICE_NAME));
     }
 
     @Test
@@ -100,11 +114,13 @@
 
     @Test
     public void onBluetoothStateChanged_btEnabled_connected_shouldSendConnectedSummary() {
+        prepareConnectedDevice(false);
+
         mSummaryUpdater.register(true);
         mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_ON);
 
         verify(mListener).onSummaryChanged(
-            mContext.getString(R.string.bluetooth_connected_summary));
+                mContext.getString(R.string.bluetooth_connected_summary, DEVICE_NAME));
     }
 
     @Test
@@ -114,7 +130,7 @@
         mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_TURNING_ON);
 
         verify(mListener).onSummaryChanged(
-            mContext.getString(R.string.disconnected));
+                mContext.getString(R.string.disconnected));
     }
 
     @Test
@@ -123,26 +139,28 @@
         devices.add(mock(CachedBluetoothDevice.class));
         when(devices.get(0).isConnected()).thenReturn(true);
         when(mBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy())
-            .thenReturn(devices);
+                .thenReturn(devices);
         when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_DISCONNECTED);
+        prepareConnectedDevice(false);
+
         mSummaryUpdater.register(true);
 
         when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_CONNECTED);
         mSummaryUpdater.onConnectionStateChanged(null /* device */,
-            BluetoothAdapter.STATE_CONNECTED);
+                BluetoothAdapter.STATE_CONNECTED);
 
         verify(mListener).onSummaryChanged(
-            mContext.getString(R.string.bluetooth_connected_summary));
+                mContext.getString(R.string.bluetooth_connected_summary, DEVICE_NAME));
     }
 
     @Test
     public void onConnectionStateChanged_inconsistentState_shouldSendDisconnectedMessage() {
         mSummaryUpdater.register(true);
         mSummaryUpdater.onConnectionStateChanged(null /* device */,
-            BluetoothAdapter.STATE_CONNECTED);
+                BluetoothAdapter.STATE_CONNECTED);
 
         verify(mListener).onSummaryChanged(
-            mContext.getString(R.string.disconnected));
+                mContext.getString(R.string.disconnected));
     }
 
     @Test
@@ -150,7 +168,7 @@
         mSummaryUpdater.register(true);
         when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_CONNECTING);
         mSummaryUpdater.onConnectionStateChanged(null /* device */,
-            BluetoothAdapter.STATE_CONNECTING);
+                BluetoothAdapter.STATE_CONNECTING);
 
         verify(mListener).onSummaryChanged(mContext.getString(R.string.bluetooth_connecting));
     }
@@ -160,11 +178,44 @@
         mSummaryUpdater.register(true);
         when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_DISCONNECTING);
         mSummaryUpdater.onConnectionStateChanged(null /* device */,
-            BluetoothAdapter.STATE_DISCONNECTING);
+                BluetoothAdapter.STATE_DISCONNECTING);
 
         verify(mListener).onSummaryChanged(mContext.getString(R.string.bluetooth_disconnecting));
     }
 
+    @Test
+    public void getConnectedDeviceSummary_hasConnectedDevice_returnOneDeviceSummary() {
+        prepareConnectedDevice(false);
+        final String expectedSummary = mContext.getString(R.string.bluetooth_connected_summary,
+                DEVICE_NAME);
+
+        assertThat(mSummaryUpdater.getConnectedDeviceSummary()).isEqualTo(expectedSummary);
+    }
+
+    @Test
+    public void getConnectedDeviceSummary_multipleDevices_returnMultipleDevicesSummary() {
+        prepareConnectedDevice(true);
+        final String expectedSummary = mContext.getString(
+                R.string.bluetooth_connected_multiple_devices_summary);
+
+        assertThat(mSummaryUpdater.getConnectedDeviceSummary()).isEqualTo(expectedSummary);
+    }
+
+    private void prepareConnectedDevice(boolean multipleDevices) {
+        final Set<BluetoothDevice> devices = new HashSet<>();
+        doReturn(DEVICE_NAME).when(mConnectedDevice).getName();
+        doReturn(true).when(mConnectedDevice).isConnected();
+        devices.add(mConnectedDevice);
+        if (multipleDevices) {
+            // Add one more device if we need to test multiple devices
+            doReturn(DEVICE_KEYBOARD_NAME).when(mConnectedKeyBoardDevice).getName();
+            doReturn(true).when(mConnectedKeyBoardDevice).isConnected();
+            devices.add(mConnectedKeyBoardDevice);
+        }
+
+        doReturn(devices).when(mBtAdapter).getBondedDevices();
+    }
+
     private class SummaryListener implements OnSummaryChangeListener {
         String summary;
 
diff --git a/tests/robotests/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerTestBase.java b/tests/robotests/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerTestBase.java
index f001bd1..96ce081 100644
--- a/tests/robotests/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerTestBase.java
+++ b/tests/robotests/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerTestBase.java
@@ -17,8 +17,6 @@
 package com.android.settings.enterprise;
 
 import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
 import android.support.v7.preference.Preference;
 
 import com.android.settings.R;
@@ -28,24 +26,21 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Answers;
-import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
 import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.anyObject;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 /**
  * Common base for testing subclasses of {@link AdminGrantedPermissionsPreferenceControllerBase}.
  */
 public abstract class AdminGrantedPermissionsPreferenceControllerTestBase {
-
     protected final String mKey;
     protected final String[] mPermissions;
     protected final String mPermissionGroup;
@@ -123,19 +118,8 @@
 
     @Test
     public void testHandlePreferenceTreeClick() {
-        final Preference preference = new Preference(mContext, null, 0, 0);
-        preference.setKey(mKey);
-
-        assertThat(mController.handlePreferenceTreeClick(preference)).isTrue();
-
-        final ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class);
-        verify(mContext).startActivity(argumentCaptor.capture());
-
-        final Intent intent = argumentCaptor.getValue();
-
-        assertThat(intent.getAction()).isEqualTo(Intent.ACTION_MANAGE_PERMISSION_APPS);
-        assertThat(intent.getStringExtra(Intent.EXTRA_PERMISSION_NAME)).
-                isEqualTo(mPermissionGroup);
+        assertThat(mController.handlePreferenceTreeClick(new Preference(mContext, null, 0, 0)))
+                .isFalse();
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/enterprise/ApplicationListFragmentTest.java b/tests/robotests/src/com/android/settings/enterprise/ApplicationListFragmentTest.java
new file mode 100644
index 0000000..1936f80
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/enterprise/ApplicationListFragmentTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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.enterprise;
+
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.support.v7.preference.PreferenceManager;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.applications.ApplicationFeatureProvider;
+import com.android.settings.applications.UserAppInfo;
+import com.android.settings.core.PreferenceController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.android.settings.testutils.ApplicationTestUtils.buildInfo;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Answers.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class ApplicationListFragmentTest {
+    private static final int USER_ID = 0;
+    private static final int USER_APP_UID = 0;
+
+    private static final String APP = "APP";
+
+    @Mock(answer = RETURNS_DEEP_STUBS)
+    private PreferenceScreen mScreen;
+    @Mock(answer = RETURNS_DEEP_STUBS)
+    private PreferenceManager mPreferenceManager;
+
+    private ApplicationListFragment mFragment;
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = ShadowApplication.getInstance().getApplicationContext();
+        when(mPreferenceManager.getContext()).thenReturn(mContext);
+
+        mFragment = new ApplicationListFragmentTestable(mPreferenceManager, mScreen);
+    }
+
+    @Test
+    public void getLogTag() {
+        assertThat(mFragment.getLogTag())
+                .isEqualTo("EnterprisePrivacySettings");
+    }
+
+    @Test
+    public void getScreenResource() {
+        assertThat(mFragment.getPreferenceScreenResId())
+                .isEqualTo(R.xml.app_list_disclosure_settings);
+    }
+
+    @Test
+    public void getPreferenceControllers() {
+        final List<PreferenceController> controllers = mFragment.getPreferenceControllers(mContext);
+        assertThat(controllers).isNotNull();
+        assertThat(controllers.size()).isEqualTo(1);
+        int position = 0;
+        assertThat(controllers.get(position++)).isInstanceOf(
+                ApplicationListPreferenceController.class);
+    }
+
+    @Test public void getCategories() {
+        assertThat(new ApplicationListFragment.AdminGrantedPermissionCamera().getMetricsCategory())
+                .isEqualTo(MetricsEvent.ENTERPRISE_PRIVACY_PERMISSIONS);
+        assertThat(new ApplicationListFragment.AdminGrantedPermissionLocation().
+                getMetricsCategory()).isEqualTo(MetricsEvent.ENTERPRISE_PRIVACY_PERMISSIONS);
+        assertThat(new ApplicationListFragment.AdminGrantedPermissionMicrophone().
+                getMetricsCategory()).isEqualTo(MetricsEvent.ENTERPRISE_PRIVACY_PERMISSIONS);
+        assertThat(new ApplicationListFragment.EnterpriseInstalledPackages().getMetricsCategory())
+                .isEqualTo(MetricsEvent.ENTERPRISE_PRIVACY_INSTALLED_APPS);
+    }
+
+    private static class ApplicationListFragmentTestable extends ApplicationListFragment {
+
+        private final PreferenceManager mPreferenceManager;
+        private final PreferenceScreen mPreferenceScreen;
+
+        public ApplicationListFragmentTestable(PreferenceManager preferenceManager,
+                PreferenceScreen screen) {
+            this.mPreferenceManager = preferenceManager;
+            this.mPreferenceScreen = screen;
+        }
+
+        @Override
+        public void buildApplicationList(Context context,
+                ApplicationFeatureProvider.ListOfAppsCallback callback) {
+            final List<UserAppInfo> apps = new ArrayList<>();
+            final UserInfo user = new UserInfo(USER_ID, "main", UserInfo.FLAG_ADMIN);
+            apps.add(new UserAppInfo(user, buildInfo(USER_APP_UID, APP, 0, 0)));
+            callback.onListOfAppsResult(apps);
+        }
+
+        @Override
+        public PreferenceManager getPreferenceManager() {
+            return mPreferenceManager;
+        }
+
+        @Override
+        public PreferenceScreen getPreferenceScreen() {
+            return mPreferenceScreen;
+        }
+
+        @Override
+        public int getMetricsCategory() {
+            return MetricsEvent.VIEW_UNKNOWN;
+        }
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/enterprise/ApplicationListPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/ApplicationListPreferenceControllerTest.java
new file mode 100644
index 0000000..56a6c62
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/enterprise/ApplicationListPreferenceControllerTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.enterprise;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.applications.ApplicationFeatureProvider;
+import com.android.settings.applications.UserAppInfo;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static com.android.settings.testutils.ApplicationTestUtils.buildInfo;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Answers.RETURNS_DEEP_STUBS;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class ApplicationListPreferenceControllerTest {
+
+    private static final int MAIN_USER_ID = 0;
+
+    private static final int MANAGED_PROFILE_ID = 10;
+    private static final int PER_USER_UID_RANGE = 100000;
+    private static final int MAIN_USER_APP_UID = MAIN_USER_ID * PER_USER_UID_RANGE;
+    private static final int MANAGED_PROFILE_APP_UID = MANAGED_PROFILE_ID * PER_USER_UID_RANGE;
+
+    private static final String APP_1 = "APP_1";
+    private static final String APP_2 = "APP_2";
+    private static final String APP_3 = "APP_3";
+
+    @Mock(answer = RETURNS_DEEP_STUBS)
+    private PreferenceScreen mScreen;
+    @Mock(answer = RETURNS_DEEP_STUBS)
+    private PackageManager mPackageManager;
+    @Mock(answer = RETURNS_DEEP_STUBS)
+    private SettingsPreferenceFragment mFragment;
+
+    private Context mContext;
+    private ApplicationListPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        final ShadowApplication shadowContext = ShadowApplication.getInstance();
+        mContext = shadowContext.getApplicationContext();
+        when(mFragment.getPreferenceScreen()).thenReturn(mScreen);
+        when(mFragment.getPreferenceManager().getContext()).thenReturn(mContext);
+        when(mPackageManager.getText(eq(APP_1), anyInt(), any())).thenReturn(APP_1);
+        when(mPackageManager.getText(eq(APP_2), anyInt(), any())).thenReturn(APP_2);
+        when(mPackageManager.getText(eq(APP_3), anyInt(), any())).thenReturn(APP_3);
+
+        mController = new ApplicationListPreferenceController(mContext, new ThreeAppsBuilder(),
+                mPackageManager, mFragment);
+    }
+
+    @Test
+    public void checkNumberAndTitlesOfApps() {
+        ArgumentCaptor<Preference> apps = ArgumentCaptor.forClass(Preference.class);
+        verify(mScreen, times(3)).addPreference(apps.capture());
+        final Set<String> expectedPackages = new HashSet<>(Arrays.asList(APP_1, APP_2, APP_3));
+        final Set<String> packages = new HashSet<>();
+
+        for (Preference p : apps.getAllValues()) {
+            packages.add(p.getTitle().toString());
+        }
+        assertThat(packages).isEqualTo(expectedPackages);
+    }
+
+    @Test
+    public void isAvailable() {
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void getPreferenceKey() {
+        assertThat(mController.getPreferenceKey()).isNull();
+    }
+
+    private static class ThreeAppsBuilder
+            implements ApplicationListPreferenceController.ApplicationListBuilder {
+        @Override
+        public void buildApplicationList(Context context,
+                                         ApplicationFeatureProvider.ListOfAppsCallback callback) {
+            final List<UserAppInfo> apps = new ArrayList<>();
+            final UserInfo user = new UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_ADMIN);
+            apps.add(new UserAppInfo(user, buildInfo(MAIN_USER_APP_UID, APP_1, 0, 0)));
+            apps.add(new UserAppInfo(user, buildInfo(MAIN_USER_APP_UID, APP_2, 0, 0)));
+            apps.add(new UserAppInfo(user, buildInfo(MANAGED_PROFILE_APP_UID, APP_3, 0, 0)));
+            callback.onListOfAppsResult(apps);
+        }
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListFragmentTest.java b/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListFragmentTest.java
new file mode 100644
index 0000000..e5c877f
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListFragmentTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.enterprise;
+
+import android.content.Context;
+import android.support.v7.preference.PreferenceManager;
+import android.support.v7.preference.PreferenceScreen;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.core.PreferenceController;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import java.util.List;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Answers.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class EnterpriseSetDefaultAppsListFragmentTest {
+    @Mock(answer = RETURNS_DEEP_STUBS)
+    private PreferenceScreen mScreen;
+    @Mock(answer = RETURNS_DEEP_STUBS)
+    private PreferenceManager mPreferenceManager;
+
+    private EnterpriseSetDefaultAppsListFragment mFragment;
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = ShadowApplication.getInstance().getApplicationContext();
+        when(mPreferenceManager.getContext()).thenReturn(mContext);
+        when(mScreen.getPreferenceManager()).thenReturn(mPreferenceManager);
+        mFragment = new EnterpriseSetDefaultAppsListFragmentTestable(mPreferenceManager, mScreen);
+    }
+
+    @Test
+    public void getMetricsCategory() {
+        assertThat(mFragment.getMetricsCategory())
+                .isEqualTo(MetricsEvent.ENTERPRISE_PRIVACY_DEFAULT_APPS);
+    }
+
+    @Test
+    public void getLogTag() {
+        assertThat(mFragment.getLogTag()).isEqualTo("EnterprisePrivacySettings");
+    }
+
+    @Test
+    public void getScreenResource() {
+        assertThat(mFragment.getPreferenceScreenResId())
+                .isEqualTo(R.xml.enterprise_set_default_apps_settings);
+    }
+
+    @Test
+    public void getPreferenceControllers() {
+        final List<PreferenceController> controllers = mFragment.getPreferenceControllers(mContext);
+        assertThat(controllers).isNotNull();
+        assertThat(controllers.size()).isEqualTo(1);
+        int position = 0;
+        assertThat(controllers.get(position++)).isInstanceOf(
+                EnterpriseSetDefaultAppsListPreferenceController.class);
+    }
+
+    private static class EnterpriseSetDefaultAppsListFragmentTestable extends
+            EnterpriseSetDefaultAppsListFragment {
+
+        private final PreferenceManager mPreferenceManager;
+        private final PreferenceScreen mPreferenceScreen;
+
+        public EnterpriseSetDefaultAppsListFragmentTestable(PreferenceManager preferenceManager,
+                PreferenceScreen screen) {
+            this.mPreferenceManager = preferenceManager;
+            this.mPreferenceScreen = screen;
+        }
+
+        @Override
+        public PreferenceManager getPreferenceManager() {
+            return mPreferenceManager;
+        }
+
+        @Override
+        public PreferenceScreen getPreferenceScreen() {
+            return mPreferenceScreen;
+        }
+    }
+
+}
diff --git a/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListPreferenceControllerTest.java
new file mode 100644
index 0000000..6a1a7f7
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListPreferenceControllerTest.java
@@ -0,0 +1,167 @@
+/*
+ * 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.enterprise;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.os.UserHandle;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceManager;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.applications.EnterpriseDefaultApps;
+import com.android.settings.applications.UserAppInfo;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.testutils.ApplicationTestUtils;
+import com.android.settings.testutils.FakeFeatureFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Answers.RETURNS_DEEP_STUBS;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class EnterpriseSetDefaultAppsListPreferenceControllerTest {
+    private static final int USER_ID = 0;
+    private static final int APP_UID = 0;
+
+    private static final String APP_1 = "APP_1";
+    private static final String APP_2 = "APP_2";
+    private static final String BROWSER_TITLE = "Browser app";
+    private static final String PHONE_TITLE = "Phone apps";
+
+    @Mock(answer = RETURNS_DEEP_STUBS)
+    private PreferenceScreen mScreen;
+    @Mock(answer = RETURNS_DEEP_STUBS)
+    private PreferenceManager mPrefenceManager;
+    @Mock(answer = RETURNS_DEEP_STUBS)
+    private PackageManager mPackageManager;
+    @Mock(answer = RETURNS_DEEP_STUBS)
+    private SettingsPreferenceFragment mFragment;
+
+    private Context mContext;
+    private FakeFeatureFactory mFeatureFactory;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowContext = ShadowApplication.getInstance();
+        mContext = spy(shadowContext.getApplicationContext());
+        FakeFeatureFactory.setupForTest(mContext);
+        mFeatureFactory = (FakeFeatureFactory) FeatureFactory.getFactory(mContext);
+        when(mFragment.getPreferenceScreen()).thenReturn(mScreen);
+        when(mPrefenceManager.getContext()).thenReturn(mContext);
+        when(mFragment.getPreferenceManager()).thenReturn(mPrefenceManager);
+
+        when(mContext.getString(R.string.default_browser_title)).thenReturn(BROWSER_TITLE);
+        Resources resources = spy(mContext.getResources());
+        when(mContext.getResources()).thenReturn(resources);
+        when(resources.getQuantityString(R.plurals.default_phone_app_title, 2))
+                .thenReturn(PHONE_TITLE);
+        when(mContext.getString(R.string.app_names_concatenation_template_2))
+                .thenReturn("%1$s, %2$s");
+
+        when(mPackageManager.getText(eq(APP_1), anyInt(), any())).thenReturn(APP_1);
+        when(mPackageManager.getText(eq(APP_2), anyInt(), any())).thenReturn(APP_2);
+    }
+
+    @Test
+    public void testMultipleAppsForOneTypeOfDefault() {
+        final UserInfo user = new UserInfo(USER_ID, "main", UserInfo.FLAG_ADMIN);
+        final ApplicationInfo appInfo1 = ApplicationTestUtils.buildInfo(APP_UID, APP_1, 0, 0);
+        final ApplicationInfo appInfo2 = ApplicationTestUtils.buildInfo(APP_UID, APP_2, 0, 0);
+
+        when(mFeatureFactory.userFeatureProvider.getUserProfiles())
+                .thenReturn(Arrays.asList(new UserHandle(USER_ID)));
+        when(mFeatureFactory.enterprisePrivacyFeatureProvider.isInCompMode()).thenReturn(false);
+        when(mFeatureFactory.applicationFeatureProvider
+                .findPersistentPreferredActivities(anyInt(), any()))
+                .thenReturn(Collections.emptyList());
+        when(mFeatureFactory.applicationFeatureProvider
+                .findPersistentPreferredActivities(eq(USER_ID),
+                        eq(EnterpriseDefaultApps.BROWSER.getIntents())))
+                .thenReturn(Arrays.asList(new UserAppInfo(user, appInfo1)));
+        when(mFeatureFactory.applicationFeatureProvider
+                .findPersistentPreferredActivities(eq(USER_ID),
+                        eq(EnterpriseDefaultApps.PHONE.getIntents()))).thenReturn(
+                                Arrays.asList(new UserAppInfo(user, appInfo1),
+                                        new UserAppInfo(user, appInfo2)));
+
+        new EnterpriseSetDefaultAppsListPreferenceController(mContext, mFragment, mPackageManager);
+        ShadowApplication.runBackgroundTasks();
+
+        ArgumentCaptor<Preference> apps = ArgumentCaptor.forClass(Preference.class);
+        verify(mScreen, times(2)).addPreference(apps.capture());
+
+        assertThat(apps.getAllValues().get(0).getTitle()).isEqualTo(BROWSER_TITLE);
+        assertThat(apps.getAllValues().get(0).getSummary()).isEqualTo(APP_1);
+
+        assertThat(apps.getAllValues().get(1).getTitle()).isEqualTo(PHONE_TITLE);
+        assertThat(apps.getAllValues().get(1).getSummary()).isEqualTo(APP_1 + ", " + APP_2);
+    }
+
+    @Test
+    public void isAvailable() {
+        when(mFeatureFactory.userFeatureProvider.getUserProfiles())
+                .thenReturn(Arrays.asList(new UserHandle(USER_ID)));
+        when(mFeatureFactory.applicationFeatureProvider
+                .findPersistentPreferredActivities(anyInt(), any()))
+                .thenReturn(Collections.emptyList());
+        final EnterpriseSetDefaultAppsListPreferenceController controller =
+                new EnterpriseSetDefaultAppsListPreferenceController(mContext, mFragment,
+                        mPackageManager);
+        assertThat(controller.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void getPreferenceKey() {
+        when(mFeatureFactory.userFeatureProvider.getUserProfiles())
+                .thenReturn(Arrays.asList(new UserHandle(USER_ID)));
+        when(mFeatureFactory.applicationFeatureProvider
+                .findPersistentPreferredActivities(anyInt(), any()))
+                .thenReturn(Collections.emptyList());
+        final EnterpriseSetDefaultAppsListPreferenceController controller =
+                new EnterpriseSetDefaultAppsListPreferenceController(mContext, mFragment,
+                        mPackageManager);
+        assertThat(controller.getPreferenceKey()).isNull();
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceControllerTest.java
index 3455e80..34d9b24 100644
--- a/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceControllerTest.java
@@ -18,32 +18,35 @@
 
 import android.content.Context;
 import android.content.Intent;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.provider.ContactsContract;
-import android.provider.MediaStore;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.os.UserManager;
 import android.support.v7.preference.Preference;
-import android.util.ArraySet;
 
 import com.android.settings.R;
 import com.android.settings.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
-import com.android.settings.applications.ApplicationFeatureProvider;
+import com.android.settings.applications.EnterpriseDefaultApps;
+import com.android.settings.applications.UserAppInfo;
 import com.android.settings.testutils.FakeFeatureFactory;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatcher;
 import org.mockito.Answers;
+import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.annotation.Config;
 
-import java.util.Set;
+import java.util.ArrayList;
+import java.util.List;
 
 import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.argThat;
+import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.anyObject;
 import static org.mockito.Mockito.when;
 
@@ -56,6 +59,8 @@
 
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private Context mContext;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private UserManager mUm;
     private FakeFeatureFactory mFeatureFactory;
 
     private EnterpriseSetDefaultAppsPreferenceController mController;
@@ -69,50 +74,40 @@
                 null /* lifecycle */);
     }
 
-    private static Intent buildIntent(String action, String category, String protocol,
-            String type) {
-        final Intent intent = new Intent(action);
-        if (category != null) {
-            intent.addCategory(category);
+    private void setEnterpriseSetDefaultApps(Intent[] intents, int number) {
+        final ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.packageName = "app";
+        for (int i = 0; i < number; i++) {
+            final List<UserAppInfo> apps = new ArrayList<>(number);
+            apps.add(new UserAppInfo(new UserInfo(i, "user." + i, UserInfo.FLAG_ADMIN), appInfo));
+            when(mFeatureFactory.applicationFeatureProvider.findPersistentPreferredActivities(eq(i),
+                    argThat(new MatchesIntents(intents)))).thenReturn(apps);
         }
-        if (protocol != null) {
-            intent.setData(Uri.parse(protocol));
-        }
-        if (type != null) {
-            intent.setType(type);
-        }
-        return intent;
     }
 
-    private void setEnterpriseSetDefaultApps(Intent[] intents, int number) {
-        final Set<ApplicationFeatureProvider.PersistentPreferredActivityInfo> apps
-                = new ArraySet<>(number);
-        for (int i = 0; i < number; i++) {
-            apps.add(new ApplicationFeatureProvider.PersistentPreferredActivityInfo("app", i));
+    private void configureUsers(int number) {
+        final List<UserHandle> users = new ArrayList<>(number);
+        for (int i = 0; i < 64; i++) {
+            users.add(new UserHandle(i));
         }
-        when(mFeatureFactory.applicationFeatureProvider.findPersistentPreferredActivities(
-                argThat(new MatchesIntents(intents)))).thenReturn(apps);
+        when(mFeatureFactory.userFeatureProvider.getUserProfiles()).thenReturn(users);
     }
 
     @Test
     public void testUpdateState() {
-        setEnterpriseSetDefaultApps(new Intent[] {buildIntent(Intent.ACTION_VIEW,
-                Intent.CATEGORY_BROWSABLE, "http:", null)}, 1);
-        setEnterpriseSetDefaultApps(new Intent[] {new Intent(MediaStore.ACTION_IMAGE_CAPTURE),
-                new Intent(MediaStore.ACTION_VIDEO_CAPTURE)}, 2);
-        setEnterpriseSetDefaultApps(new Intent[] {buildIntent(Intent.ACTION_VIEW, null, "geo:",
-                null)}, 4);
-        setEnterpriseSetDefaultApps(new Intent[] {new Intent(Intent.ACTION_SENDTO),
-                new Intent(Intent.ACTION_SEND), new Intent(Intent.ACTION_SEND_MULTIPLE)}, 8);
-        setEnterpriseSetDefaultApps(new Intent[] {buildIntent(Intent.ACTION_INSERT, null, null,
-                "vnd.android.cursor.dir/event")}, 16);
-        setEnterpriseSetDefaultApps(new Intent[] {buildIntent(Intent.ACTION_PICK, null, null,
-                ContactsContract.Contacts.CONTENT_TYPE)}, 32);
-        setEnterpriseSetDefaultApps(new Intent[] {new Intent(Intent.ACTION_DIAL),
-                new Intent(Intent.ACTION_CALL)}, 64);
+        setEnterpriseSetDefaultApps(EnterpriseDefaultApps.BROWSER.getIntents(), 1);
+        setEnterpriseSetDefaultApps(EnterpriseDefaultApps.CAMERA.getIntents(), 2);
+        setEnterpriseSetDefaultApps(EnterpriseDefaultApps.MAP.getIntents(), 4);
+        setEnterpriseSetDefaultApps(EnterpriseDefaultApps.EMAIL.getIntents(), 8);
+        setEnterpriseSetDefaultApps(EnterpriseDefaultApps.CALENDAR.getIntents(), 16);
+        setEnterpriseSetDefaultApps(EnterpriseDefaultApps.CONTACTS.getIntents(), 32);
+        setEnterpriseSetDefaultApps(EnterpriseDefaultApps.PHONE.getIntents(), 64);
         when(mContext.getResources().getQuantityString(R.plurals.enterprise_privacy_number_packages,
                 127, 127)).thenReturn("127 apps");
 
+        // As setEnterpriseSetDefaultApps uses fake Users, we need to list them via UserManager.
+        configureUsers(64);
+
         final Preference preference = new Preference(mContext, null, 0, 0);
         mController.updateState(preference);
         assertThat(preference.getSummary()).isEqualTo("127 apps");
@@ -120,13 +115,12 @@
 
     @Test
     public void testIsAvailable() {
-        when(mFeatureFactory.applicationFeatureProvider.findPersistentPreferredActivities(
-                anyObject())).thenReturn(
-                        new ArraySet<ApplicationFeatureProvider.PersistentPreferredActivityInfo>());
+        when(mFeatureFactory.applicationFeatureProvider.findPersistentPreferredActivities(anyInt(),
+                anyObject())).thenReturn(new ArrayList<UserAppInfo>());
         assertThat(mController.isAvailable()).isFalse();
 
-        setEnterpriseSetDefaultApps(new Intent[] {buildIntent(Intent.ACTION_VIEW,
-                Intent.CATEGORY_BROWSABLE, "http:", null)}, 1);
+        setEnterpriseSetDefaultApps(EnterpriseDefaultApps.BROWSER.getIntents(), 1);
+        configureUsers(1);
         assertThat(mController.isAvailable()).isTrue();
     }
 
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java
index 15fec3c..4305dc7 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java
@@ -57,6 +57,8 @@
 import org.mockito.MockitoAnnotations;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
+import org.robolectric.RuntimeEnvironment;
+
 import org.robolectric.annotation.Config;
 import org.robolectric.util.ReflectionHelpers;
 
@@ -143,6 +145,7 @@
 
         mFragment.mHeaderPreference = mHeaderPreference;
         mFragment.mState = mState;
+        mFragment.mBatteryUtils = new BatteryUtils(RuntimeEnvironment.application);
         mAppEntry.info = mock(ApplicationInfo.class);
 
         mTestActivity = spy(new SettingsActivity());
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
index e5383c8..52cfe4c 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
@@ -33,6 +33,9 @@
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import static android.os.BatteryStats.Uid.PROCESS_STATE_BACKGROUND;
 import static android.os.BatteryStats.Uid.PROCESS_STATE_FOREGROUND;
 import static android.os.BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE;
@@ -47,9 +50,7 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.when;
-
-import java.util.ArrayList;
-import java.util.List;
+import static org.mockito.Mockito.spy;
 
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
@@ -127,6 +128,8 @@
 
         mBatteryUtils = BatteryUtils.getInstance(RuntimeEnvironment.application);
         mBatteryUtils.mPowerUsageFeatureProvider = mProvider;
+
+        mBatteryUtils = spy(new BatteryUtils(RuntimeEnvironment.application));
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedTest.java
index 41b1c96..7a1284a 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedTest.java
@@ -18,6 +18,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.UserManager;
+import android.text.TextUtils;
 
 import com.android.internal.os.BatterySipper;
 import com.android.internal.os.BatterySipper.DrainType;
@@ -105,9 +106,12 @@
         when(mPowerUsageAdvanced.getContext()).thenReturn(mShadowContext);
         doReturn(STUB_STRING).when(mPowerUsageAdvanced).getString(anyInt(), any(), any());
         doReturn(STUB_STRING).when(mPowerUsageAdvanced).getString(anyInt(), any());
+        doReturn(mShadowContext.getText(R.string.battery_used_for)).when(
+                mPowerUsageAdvanced).getText(R.string.battery_used_for);
         mPowerUsageAdvanced.setPackageManager(mPackageManager);
         mPowerUsageAdvanced.setPowerUsageFeatureProvider(mPowerUsageFeatureProvider);
         mPowerUsageAdvanced.setUserManager(mUserManager);
+        mPowerUsageAdvanced.mBatteryUtils = spy(new BatteryUtils(mShadowContext));
 
         mPowerUsageData = new PowerUsageData(UsageType.APP);
         mMaxBatterySipper.totalPowerMah = TYPE_BLUETOOTH_USAGE;
@@ -176,10 +180,12 @@
 
     @Test
     public void testUpdateUsageDataSummary_onlyOneApp_showUsageTime() {
+        final String expectedSummary = "Used for 0m";
         mPowerUsageData.usageList.add(mNormalBatterySipper);
+
         mPowerUsageAdvanced.updateUsageDataSummary(mPowerUsageData, TOTAL_POWER, DISCHARGE_AMOUNT);
 
-        verify(mPowerUsageAdvanced).getString(eq(R.string.battery_used_for), any());
+        assertThat(mPowerUsageData.summary.toString()).isEqualTo(expectedSummary);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
index 5611371..5cb60d2 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
@@ -88,15 +88,16 @@
 public class PowerUsageSummaryTest {
     private static final String[] PACKAGE_NAMES = {"com.app1", "com.app2"};
     private static final String TIME_LEFT = "2h30min";
+    private static final String STUB_STRING = "stub_string";
     private static final int BATTERY_LEVEL = 55;
     private static final int UID = 123;
     private static final int POWER_MAH = 100;
     private static final long REMAINING_TIME_US = 100000;
-    private static final long TIME_SINCE_LAST_FULL_CHARGE_MS = 25000;
+    private static final long TIME_SINCE_LAST_FULL_CHARGE_MS = 120 * 60 * 1000;
     private static final long TIME_SINCE_LAST_FULL_CHARGE_US =
             TIME_SINCE_LAST_FULL_CHARGE_MS * 1000;
     private static final int DISCHARGE_AMOUNT = 100;
-    private static final long USAGE_TIME_MS = 10000;
+    private static final long USAGE_TIME_MS = 65 * 60 * 1000;
     private static final double TOTAL_POWER = 200;
     private static final double BATTERY_SCREEN_USAGE = 300;
     private static final double BATTERY_SYSTEM_USAGE = 600;
@@ -125,8 +126,6 @@
     @Mock
     private BatterySipper mCellBatterySipper;
     @Mock
-    private PowerGaugePreference mPreference;
-    @Mock
     private LayoutPreference mBatteryLayoutPref;
     @Mock
     private TextView mBatteryPercentText;
@@ -134,10 +133,6 @@
     private TextView mSummary1;
     @Mock
     private BatteryInfo mBatteryInfo;
-    @Mock
-    private PowerGaugePreference mScreenUsagePref;
-    @Mock
-    private PowerGaugePreference mLastFullChargePref;
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private BatteryStatsHelper mBatteryHelper;
     @Mock
@@ -150,6 +145,9 @@
     private TestFragment mFragment;
     private FakeFeatureFactory mFeatureFactory;
     private BatteryMeterView mBatteryMeterView;
+    private PowerGaugePreference mPreference;
+    private PowerGaugePreference mScreenUsagePref;
+    private PowerGaugePreference mLastFullChargePref;
 
     @Before
     public void setUp() {
@@ -160,6 +158,9 @@
         mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
         when(mContext.getSystemService(Context.POWER_SERVICE)).thenReturn(mPowerManager);
 
+        mPreference = new PowerGaugePreference(mRealContext);
+        mScreenUsagePref = new PowerGaugePreference(mRealContext);
+        mLastFullChargePref = new PowerGaugePreference(mRealContext);
         mFragment = spy(new TestFragment(mContext));
         mFragment.initFeatureProvider();
         mBatteryMeterView = new BatteryMeterView(mRealContext);
@@ -202,6 +203,7 @@
         when(mBatteryHelper.getUsageList()).thenReturn(mUsageList);
         mFragment.mScreenUsagePref = mScreenUsagePref;
         mFragment.mLastFullChargePref = mLastFullChargePref;
+        mFragment.mBatteryUtils = spy(new BatteryUtils(mRealContext));
 
         mBatteryInfo.batteryLevel = BATTERY_LEVEL;
     }
@@ -306,16 +308,21 @@
     public void testSetUsageSummary_timeLessThanOneMinute_DoNotSetSummary() {
         final long usageTimeMs = 59 * DateUtils.SECOND_IN_MILLIS;
 
-        mFragment.setUsageSummary(mPreference, "", usageTimeMs);
-        verify(mPreference, never()).setSummary(anyString());
+        mFragment.setUsageSummary(mPreference, usageTimeMs);
+        assertThat(mPreference.getSummary()).isNull();
     }
 
     @Test
     public void testSetUsageSummary_timeMoreThanOneMinute_setSummary() {
         final long usageTimeMs = 2 * DateUtils.MINUTE_IN_MILLIS;
+        doReturn(mRealContext.getText(R.string.battery_used_for)).when(mFragment).getText(
+                R.string.battery_used_for);
+        doReturn(mRealContext).when(mFragment).getContext();
+        final String expectedSummary = "Used for 2m";
 
-        mFragment.setUsageSummary(mPreference, "", usageTimeMs);
-        verify(mPreference).setSummary(anyString());
+        mFragment.setUsageSummary(mPreference, usageTimeMs);
+
+        assertThat(mPreference.getSummary().toString()).isEqualTo(expectedSummary);
     }
 
     @Test
@@ -382,32 +389,37 @@
 
     @Test
     public void testUpdateScreenPreference_showCorrectSummary() {
-        final String expectedUsedTime = Utils.formatElapsedTime(mRealContext, USAGE_TIME_MS, false);
         doReturn(mScreenBatterySipper).when(mFragment).findBatterySipperByType(any(), any());
         doReturn(mRealContext).when(mFragment).getContext();
+        final CharSequence expectedSummary = Utils.formatElapsedTime(mRealContext, USAGE_TIME_MS,
+                false);
 
         mFragment.updateScreenPreference();
 
-        verify(mScreenUsagePref).setSubtitle(expectedUsedTime);
+        assertThat(mScreenUsagePref.getSubtitle()).isEqualTo(expectedSummary);
     }
 
     @Test
     public void testUpdateLastFullChargePreference_showCorrectSummary() {
+        final CharSequence formattedString = mRealContext.getText(
+                R.string.power_last_full_charge_summary);
+        final CharSequence timeSequence = Utils.formatElapsedTime(mRealContext,
+                TIME_SINCE_LAST_FULL_CHARGE_MS, false);
+        final CharSequence expectedSummary = TextUtils.expandTemplate(
+                formattedString, timeSequence);
+        doReturn(formattedString).when(mFragment).getText(R.string.power_last_full_charge_summary);
         doReturn(mRealContext).when(mFragment).getContext();
-        final String expected = mRealContext.getString(R.string.power_last_full_charge_summary,
-                Utils.formatElapsedTime(mRealContext, TIME_SINCE_LAST_FULL_CHARGE_MS, false));
-        doReturn(expected).when(mFragment).getString(eq(R.string.power_last_full_charge_summary),
-                any());
 
         mFragment.updateLastFullChargePreference(TIME_SINCE_LAST_FULL_CHARGE_MS);
 
-        verify(mLastFullChargePref).setSubtitle(expected);
+        assertThat(mLastFullChargePref.getSubtitle()).isEqualTo(expectedSummary);
     }
 
     @Test
     public void testUpdatePreference_usageListEmpty_shouldNotCrash() {
         when(mBatteryHelper.getUsageList()).thenReturn(new ArrayList<BatterySipper>());
-        doReturn("").when(mFragment).getString(anyInt(), any());
+        doReturn(STUB_STRING).when(mFragment).getString(anyInt(), any());
+        doReturn(mRealContext).when(mFragment).getContext();
 
         // Should not crash when update
         mFragment.updateScreenPreference();
diff --git a/tests/robotests/src/com/android/settings/search2/DatabaseIndexingManagerTest.java b/tests/robotests/src/com/android/settings/search2/DatabaseIndexingManagerTest.java
index 0e89e04..0d24eb4 100644
--- a/tests/robotests/src/com/android/settings/search2/DatabaseIndexingManagerTest.java
+++ b/tests/robotests/src/com/android/settings/search2/DatabaseIndexingManagerTest.java
@@ -306,7 +306,7 @@
         // Class Name
         assertThat(cursor.getString(11)).isEqualTo(className);
         // Icon
-        assertThat(cursor.getInt(12)).isEqualTo(iconResId);
+        assertThat(cursor.getInt(12)).isEqualTo(0);
         // Intent Action
         assertThat(cursor.getString(13)).isEqualTo(action);
         // Target Package
diff --git a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
index f4f1c63..68333e7 100644
--- a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
@@ -30,6 +30,7 @@
 import com.android.settings.security.SecurityFeatureProvider;
 import com.android.settings.search2.SearchFeatureProvider;
 import com.android.settings.overlay.SurveyFeatureProvider;
+import com.android.settings.users.UserFeatureProvider;
 
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.mock;
@@ -52,6 +53,7 @@
     public final SurveyFeatureProvider surveyFeatureProvider;
     public final SecurityFeatureProvider securityFeatureProvider;
     public final SuggestionFeatureProvider suggestionsFeatureProvider;
+    public final UserFeatureProvider userFeatureProvider;
     public final AssistGestureFeatureProvider assistGestureFeatureProvider;
 
     /**
@@ -86,6 +88,7 @@
         surveyFeatureProvider = mock(SurveyFeatureProvider.class);
         securityFeatureProvider = mock(SecurityFeatureProvider.class);
         suggestionsFeatureProvider = mock(SuggestionFeatureProvider.class);
+        userFeatureProvider = mock(UserFeatureProvider.class);
         assistGestureFeatureProvider = mock(AssistGestureFeatureProvider.class);
     }
 
@@ -145,6 +148,11 @@
     }
 
     @Override
+    public UserFeatureProvider getUserFeatureProvider(Context context) {
+        return userFeatureProvider;
+    }
+
+    @Override
     public AssistGestureFeatureProvider getAssistGestureFeatureProvider() {
         return assistGestureFeatureProvider;
     }
diff --git a/tests/robotests/src/com/android/settings/users/UserFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/users/UserFeatureProviderImplTest.java
new file mode 100644
index 0000000..c794cdd
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/users/UserFeatureProviderImplTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.users;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class UserFeatureProviderImplTest {
+    public static final int FIRST_USER_ID = 0;
+    public static final int SECOND_USER_ID = 4;
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private UserManager mUserManager;
+
+    private UserFeatureProviderImpl mFeatureProvider;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+        mFeatureProvider = new UserFeatureProviderImpl(mContext);
+    }
+
+    @Test
+    public void getUserProfiles() {
+        final List<UserHandle> expected =
+                Arrays.asList(new UserHandle(FIRST_USER_ID), new UserHandle(SECOND_USER_ID));
+        when(mUserManager.getUserProfiles()).thenReturn(expected);
+        final List<UserHandle> userProfiles = mFeatureProvider.getUserProfiles();
+        assertThat(userProfiles).isEqualTo(expected);
+    }
+}