Merge "Remove char limit from summary strings."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index ed511ef..b14856c 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -6,6 +6,7 @@
 
     <original-package android:name="com.android.settings" />
 
+    <uses-permission android:name="android.permission.REQUEST_NETWORK_SCORES" />
     <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
@@ -2905,6 +2906,17 @@
                 android:value="com.android.settings.applications.ManageApplications" />
         </activity>
 
+        <activity android:name="Settings$ManageExternalSourcesActivity"
+                android:label="@string/install_other_apps"
+                android:taskAffinity="">
+            <intent-filter android:priority="1">
+                <action android:name="android.settings.action.MANAGE_EXTERNAL_SOURCES" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+                android:value="com.android.settings.applications.ManageApplications" />
+        </activity>
+
         <activity android:name="Settings$AppWriteSettingsActivity"
                 android:label="@string/write_settings_title"
                 android:taskAffinity="">
diff --git a/res/drawable/wifi_friction.xml b/res/drawable/wifi_friction.xml
index cd13dac..c46f5b4 100644
--- a/res/drawable/wifi_friction.xml
+++ b/res/drawable/wifi_friction.xml
@@ -16,9 +16,10 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:settings="http://schemas.android.com/apk/res/com.android.settings">
-    <item settings:state_encrypted="true"
-          settings:state_saved="true"
-          android:drawable="@drawable/ic_friction_lock_open"/>
+    <item
+        settings:state_encrypted="true"
+        settings:state_saved="true"
+        android:drawable="@drawable/ic_friction_lock_closed"/>
     <item
         settings:state_encrypted="true"
         settings:state_saved="false"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index da5ce16..b4e6339 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -6329,6 +6329,9 @@
     <!-- [CHAR LIMIT=NONE] App notification settings: channels title -->
     <string name="notification_channels">Channels</string>
 
+    <!-- [CHAR LIMIT=60] App notification settings: Text to display for deleted channels -->
+    <string name="deleted_channel_name"><xliff:g id="channel_name" example="Promotions">%1$s</xliff:g> (deleted)</string>
+
     <!-- [CHAR LIMIT=NONE] App notification settings: Block option title -->
     <string name="app_notification_block_title">Block all</string>
 
@@ -7224,6 +7227,12 @@
     <!-- Summary of app not allowed to draw overlay [CHAR LIMIT=60] -->
     <string name="system_alert_window_off">No</string>
 
+    <!-- Title for settings screen for controlling apps that can install other apps on device [CHAR LIMIT=30] -->
+    <string name="install_other_apps">Install other apps</string>
+    <!-- Keywords for setting screen for controlling apps that can install other apps on device -->
+    <string name="keywords_install_other_apps">install apps external unknown sources</string>
+    <!-- Label for setting which controls whether app is trusted to install apps on the device [CHAR LIMIT=45] -->
+    <string name="permit_install_other_apps">Allows to install other apps</string>
 
     <!-- Write Settings settings -->
     <!-- Settings title in main settings screen for WRITE_SETTINGS [CHAR LIMIT=30] -->
@@ -7233,6 +7242,8 @@
     <!-- Summary of number of apps currently can draw overlays [CHAR LIMIT=60] -->
     <string name="write_settings_summary"><xliff:g id="count" example="10">%1$d</xliff:g> of <xliff:g id="count" example="10">%2$d</xliff:g> apps allowed to modify system settings</string>
 
+    <!-- Label for showing apps that can install other apps [CHAR LIMIT=45] -->
+    <string name="filter_install_sources_apps">Can install other apps</string>
     <!-- Label for showing apps that can write system settings [CHAR LIMIT=45] -->
     <string name="filter_write_settings_apps">Can modify system settings</string>
     <!-- Title for the apps that are allowed to write system settings [CHAR LIMIT=60] -->
@@ -7249,6 +7260,12 @@
     <string name="write_settings_on">Yes</string>
     <!-- Summary of app not allowed to write system settings [CHAR LIMIT=45] -->
     <string name="write_settings_off">No</string>
+    <!-- Summary of app trusted to install apps [CHAR LIMIT=45] -->
+    <string name="external_source_trusted">Yes</string>
+    <!-- Summary of app not trusted to install apps [CHAR LIMIT=45] -->
+    <string name="external_source_untrusted">No</string>
+    <!-- Title of switch preference that controls whether an external app source is trusted or not [CHAR LIMIT=100] -->
+    <string name="external_source_switch_title">Trust apps from this source</string>
 
     <!-- Title of setting that controls gesture to open camera [CHAR LIMIT=40] -->
     <string name="camera_gesture_title">Double twist for camera</string>
diff --git a/res/values/themes.xml b/res/values/themes.xml
index 8f37bbe..0241e1a 100644
--- a/res/values/themes.xml
+++ b/res/values/themes.xml
@@ -122,7 +122,7 @@
          layouts against a remote context using our local theme colors. Due to the implementation
          details of Theme, we can't reference any local resources and MUST instead use the values
          directly. So use #ff263238 instead of @color/theme_primary and so on. -->
-    <style name="Theme.SettingsBase" parent="@*android:style/Theme.DeviceDefault.Settings.LightActionBar" />
+    <style name="Theme.SettingsBase" parent="@android:style/Theme.DeviceDefault.Settings" />
 
     <style name="Theme.Settings" parent="Theme.SettingsBase">
         <item name="preferenceTheme">@style/PreferenceTheme</item>
@@ -144,7 +144,6 @@
 
         <!-- Redefine the ActionBar style for contentInsetStart -->
         <item name="android:actionBarStyle">@style/Theme.ActionBar</item>
-        <item name="*android:actionBarSize">@dimen/actionbar_size</item>
 
         <item name="switchBarTheme">@style/ThemeOverlay.SwitchBar.Settings</item>
 
@@ -157,8 +156,6 @@
         <item name="*android:regularColor">@color/lock_pattern_view_regular_color</item>
         <item name="*android:successColor">@color/lock_pattern_view_success_color</item>
         <item name="*android:errorColor">@color/lock_pattern_view_error_color</item>
-
-        <item name="android:statusBarColor">@color/status_bar_color</item>
     </style>
 
     <style name="Theme.SubSettings" parent="Theme.Settings">
@@ -178,7 +175,7 @@
         <item name="android:backgroundDimEnabled">false</item>
     </style>
 
-    <style name="Theme.ActionBar" parent="@android:style/Widget.DeviceDefault.Light.ActionBar">
+    <style name="Theme.ActionBar" parent="@android:style/Widget.Material.ActionBar.Solid">
         <item name="android:contentInsetStart">@dimen/actionbar_contentInsetStart</item>
     </style>
 
@@ -186,16 +183,16 @@
         <item name="android:contentInsetStart">@dimen/actionbar_subsettings_contentInsetStart</item>
     </style>
 
-    <style name="ThemeOverlay.SwitchBar.Settings" parent="@*android:style/ThemeOverlay.DeviceDefault.ActionBar.Accent">
+    <style name="ThemeOverlay.SwitchBar.Settings" parent="@android:style/ThemeOverlay.Material.ActionBar">
         <item name="switchBarMarginStart">@dimen/switchbar_subsettings_margin_start</item>
         <item name="switchBarMarginEnd">@dimen/switchbar_subsettings_margin_end</item>
-        <item name="switchBarBackgroundColor">?android:attr/colorBackground</item>
+        <item name="switchBarBackgroundColor">?android:attr/colorSecondary</item>
     </style>
 
-    <style name="ThemeOverlay.SwitchBar.SubSettings" parent="@*android:style/ThemeOverlay.DeviceDefault.ActionBar.Accent">
+    <style name="ThemeOverlay.SwitchBar.SubSettings" parent="@android:style/ThemeOverlay.Material.ActionBar">
         <item name="switchBarMarginStart">@dimen/switchbar_subsettings_margin_start</item>
         <item name="switchBarMarginEnd">@dimen/switchbar_subsettings_margin_end</item>
-        <item name="switchBarBackgroundColor">?android:attr/colorBackground</item>
+        <item name="switchBarBackgroundColor">?android:attr/colorSecondary</item>
     </style>
 
     <style name="Theme.DialogWhenLarge" parent="@*android:style/Theme.DeviceDefault.Settings.DialogWhenLarge">
@@ -220,7 +217,7 @@
     <style name="Theme.SubSettingsDialogWhenLarge" parent="Theme.DialogWhenLarge">
         <item name="preferenceTheme">@style/PreferenceTheme</item>
         <item name="android:actionBarWidgetTheme">@null</item>
-        <item name="android:actionBarTheme">@*android:style/ThemeOverlay.DeviceDefault.ActionBar.Accent</item>
+        <item name="android:actionBarTheme">@android:style/ThemeOverlay.Material.ActionBar</item>
         <item name="preferenceBackgroundColor">@drawable/preference_background</item>
     </style>
 
diff --git a/res/xml/double_twist_gesture_settings.xml b/res/xml/double_twist_gesture_settings.xml
index e8d0abe..f77d778 100644
--- a/res/xml/double_twist_gesture_settings.xml
+++ b/res/xml/double_twist_gesture_settings.xml
@@ -16,7 +16,8 @@
   -->
 
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
-                  xmlns:app="http://schemas.android.com/apk/res-auto">
+                  xmlns:app="http://schemas.android.com/apk/res-auto"
+                  android:title="@string/double_twist_for_camera_mode_title">
 
     <com.android.settings.widget.VideoPreference
         android:key="gesture_double_twist_video"
diff --git a/res/xml/external_sources_details.xml b/res/xml/external_sources_details.xml
new file mode 100644
index 0000000..fb443f4
--- /dev/null
+++ b/res/xml/external_sources_details.xml
@@ -0,0 +1,26 @@
+<?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">
+
+    <SwitchPreference
+        android:key="external_sources_settings_switch" />
+
+    <Preference
+        android:key="external_sources_settings_description"
+        android:selectable="false" />
+
+</PreferenceScreen>
diff --git a/res/xml/input_and_gesture.xml b/res/xml/input_and_gesture.xml
index c30178b..9abc797 100644
--- a/res/xml/input_and_gesture.xml
+++ b/res/xml/input_and_gesture.xml
@@ -15,7 +15,9 @@
   limitations under the License.
   -->
 
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:title="@string/input_gesture_settings_title">
 
     <PreferenceCategory
         android:title="@string/keyboard_and_input_methods_category">
diff --git a/res/xml/special_access.xml b/res/xml/special_access.xml
index 4de167a..f8a5bf4 100644
--- a/res/xml/special_access.xml
+++ b/res/xml/special_access.xml
@@ -92,4 +92,15 @@
             android:name="classname"
             android:value="com.android.settings.Settings$UsageAccessSettingsActivity" />
     </Preference>
+
+    <Preference
+        android:key="manage_external_sources"
+        android:title="@string/install_other_apps"
+        android:fragment="com.android.settings.applications.ManageApplications"
+        settings:keywords="@string/keywords_install_other_apps">
+        <extra
+            android:name="classname"
+            android:value="com.android.settings.Settings$ManageExternalSourcesActivity" />
+    </Preference>
+
 </PreferenceScreen>
diff --git a/src/com/android/settings/ConfirmDeviceCredentialBaseFragment.java b/src/com/android/settings/ConfirmDeviceCredentialBaseFragment.java
index 5aa66bb..cabc805 100644
--- a/src/com/android/settings/ConfirmDeviceCredentialBaseFragment.java
+++ b/src/com/android/settings/ConfirmDeviceCredentialBaseFragment.java
@@ -90,9 +90,6 @@
         final UserManager userManager = UserManager.get(getActivity());
         mEffectiveUserId = userManager.getCredentialOwnerProfile(mUserId);
         mLockPatternUtils = new LockPatternUtils(getActivity());
-        mIsStrongAuthRequired = isFingerprintDisallowedByStrongAuth();
-        mAllowFpAuthentication = mAllowFpAuthentication && !isFingerprintDisabledByAdmin()
-                && !mReturnCredentials && !mIsStrongAuthRequired;
     }
 
     @Override
@@ -141,6 +138,10 @@
     @Override
     public void onResume() {
         super.onResume();
+        mIsStrongAuthRequired = isFingerprintDisallowedByStrongAuth();
+        mAllowFpAuthentication = getActivity().getIntent().getBooleanExtra(
+                        ALLOW_FP_AUTHENTICATION, false)
+                && !isFingerprintDisabledByAdmin() && !mReturnCredentials && !mIsStrongAuthRequired;
         refreshLockScreen();
     }
 
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 97e53e5..a393436 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -138,6 +138,9 @@
     public static class AppWriteSettingsActivity extends SettingsActivity { /* empty */ }
     public static class AdvancedAppsActivity extends SettingsActivity { /* empty */ }
 
+    public static class ManageExternalSourcesActivity extends SettingsActivity {
+        /* empty */ }
+
     public static class WifiCallingSuggestionActivity extends SettingsActivity { /* empty */ }
     public static class ZenModeAutomationSuggestionActivity extends SettingsActivity { /* empty */ }
     public static class FingerprintSuggestionActivity extends FingerprintSettings { /* empty */ }
diff --git a/src/com/android/settings/applications/AppStateInstallAppsBridge.java b/src/com/android/settings/applications/AppStateInstallAppsBridge.java
new file mode 100644
index 0000000..6d22e25
--- /dev/null
+++ b/src/com/android/settings/applications/AppStateInstallAppsBridge.java
@@ -0,0 +1,156 @@
+/*
+ * 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.Manifest;
+import android.app.AppGlobals;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.settings.R;
+import com.android.settingslib.applications.ApplicationsState;
+import com.android.settingslib.applications.ApplicationsState.AppEntry;
+import com.android.settingslib.applications.ApplicationsState.AppFilter;
+
+import java.util.List;
+
+/**
+ * Connects app op info to the ApplicationsState. Wraps around the generic AppStateBaseBridge
+ * class to tailor to the semantics of {@link AppOpsManager#OP_REQUEST_INSTALL_PACKAGES}
+ * Also provides app filters that can use the info.
+ */
+public class AppStateInstallAppsBridge extends AppStateBaseBridge {
+
+    private static final String TAG = AppStateInstallAppsBridge.class.getSimpleName();
+
+    private final IPackageManager mIpm;
+    private final AppOpsManager mAppOpsManager;
+    private final Context mContext;
+
+    public AppStateInstallAppsBridge(Context context, ApplicationsState appState,
+            Callback callback) {
+        super(appState, callback);
+        mContext = context;
+        mIpm = AppGlobals.getPackageManager();
+        mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+    }
+
+    @Override
+    protected void updateExtraInfo(AppEntry app, String packageName, int uid) {
+        app.extraInfo = createInstallAppsStateFor(packageName, uid);
+    }
+
+    @Override
+    protected void loadAllExtraInfo() {
+        // TODO: consider making this a batch operation with a single binder call
+        final List<AppEntry> allApps = mAppSession.getAllApps();
+        for (int i = 0; i < allApps.size(); i++) {
+            AppEntry currentEntry = allApps.get(i);
+            updateExtraInfo(currentEntry, currentEntry.info.packageName, currentEntry.info.uid);
+        }
+    }
+
+    private boolean hasRequestedAppOpPermission(String permission, String packageName) {
+        try {
+            String[] packages = mIpm.getAppOpPermissionPackages(permission);
+            return ArrayUtils.contains(packages, packageName);
+        } catch (RemoteException exc) {
+            Log.e(TAG, "PackageManager dead. Cannot get permission info");
+            return false;
+        }
+    }
+
+    private boolean hasPermission(String permission, int uid) {
+        try {
+            int result = mIpm.checkUidPermission(permission, uid);
+            return result == PackageManager.PERMISSION_GRANTED;
+        } catch (RemoteException e) {
+            Log.e(TAG, "PackageManager dead. Cannot get permission info");
+            return false;
+        }
+    }
+
+    private int getAppOpMode(int appOpCode, int uid, String packageName) {
+        return mAppOpsManager.checkOpNoThrow(appOpCode, uid, packageName);
+    }
+
+    InstallAppsState createInstallAppsStateFor(String packageName, int uid) {
+        final InstallAppsState appState = new InstallAppsState();
+        appState.permissionRequested = hasRequestedAppOpPermission(
+                Manifest.permission.REQUEST_INSTALL_PACKAGES, packageName);
+        appState.permissionGranted = hasPermission(Manifest.permission.REQUEST_INSTALL_PACKAGES,
+                uid);
+        appState.appOpMode = getAppOpMode(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES, uid,
+                packageName);
+        return appState;
+    }
+
+    /**
+     * Collection of information to be used as {@link AppEntry#extraInfo} objects
+     */
+    public static class InstallAppsState {
+        boolean permissionRequested;
+        boolean permissionGranted;
+        int appOpMode;
+
+        public InstallAppsState() {
+            this.appOpMode = AppOpsManager.MODE_DEFAULT;
+        }
+
+        public boolean canInstallApps() {
+            if (appOpMode == AppOpsManager.MODE_DEFAULT) {
+                return permissionGranted;
+            } else {
+                return appOpMode == AppOpsManager.MODE_ALLOWED;
+            }
+        }
+
+        public int getSummary() {
+            return canInstallApps() ? R.string.external_source_trusted
+                    : R.string.external_source_untrusted;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder("[permissionGranted: " + permissionGranted);
+            sb.append(", permissionRequested: " + permissionRequested);
+            sb.append(", appOpMode: " + appOpMode);
+            sb.append("]");
+            return sb.toString();
+        }
+    }
+
+    static final AppFilter FILTER_APP_SOURCES = new AppFilter() {
+
+        @Override
+        public void init() {
+        }
+
+        @Override
+        public boolean filterApp(AppEntry info) {
+            if (info.extraInfo == null || !(info.extraInfo instanceof InstallAppsState)) {
+                return false;
+            }
+            InstallAppsState state = (InstallAppsState) info.extraInfo;
+            return (state.appOpMode != AppOpsManager.MODE_DEFAULT) || state.permissionRequested;
+        }
+    };
+}
diff --git a/src/com/android/settings/applications/ExternalSourcesDetails.java b/src/com/android/settings/applications/ExternalSourcesDetails.java
new file mode 100644
index 0000000..fd8221c
--- /dev/null
+++ b/src/com/android/settings/applications/ExternalSourcesDetails.java
@@ -0,0 +1,102 @@
+/*
+ * 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.internal.logging.nano.MetricsProto.MetricsEvent;
+
+import android.app.AlertDialog;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.Preference.OnPreferenceChangeListener;
+
+import com.android.settings.R;
+import com.android.settings.applications.AppStateInstallAppsBridge.InstallAppsState;
+
+public class ExternalSourcesDetails extends AppInfoWithHeader
+        implements OnPreferenceChangeListener {
+
+    private static final String KEY_EXTERNAL_SOURCES_SETTINGS_SWITCH =
+            "external_sources_settings_switch";
+    private static final String KEY_EXTERNAL_SOURCES_SETTINGS_DESC =
+            "external_sources_settings_description";
+
+    private AppStateInstallAppsBridge mAppBridge;
+    private AppOpsManager mAppOpsManager;
+    private SwitchPreference mSwitchPref;
+    private Preference mExternalSourcesSettingsDesc;
+    private InstallAppsState mInstallAppsState;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        final Context context = getActivity();
+        mAppBridge = new AppStateInstallAppsBridge(context, mState, null);
+        mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+
+        addPreferencesFromResource(R.xml.external_sources_details);
+        mSwitchPref = (SwitchPreference) findPreference(KEY_EXTERNAL_SOURCES_SETTINGS_SWITCH);
+        mExternalSourcesSettingsDesc = findPreference(KEY_EXTERNAL_SOURCES_SETTINGS_DESC);
+
+        getPreferenceScreen().setTitle(R.string.install_other_apps);
+        mSwitchPref.setTitle(R.string.external_source_switch_title);
+        mExternalSourcesSettingsDesc.setSummary(R.string.install_all_warning);
+
+        mSwitchPref.setOnPreferenceChangeListener(this);
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        final boolean checked = (Boolean) newValue;
+        if (preference == mSwitchPref) {
+            if (mInstallAppsState != null && checked != mInstallAppsState.canInstallApps()) {
+                setCanInstallApps(checked);
+                refreshUi();
+            }
+            return true;
+        }
+        return false;
+    }
+
+    private void setCanInstallApps(boolean newState) {
+        mAppOpsManager.setMode(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES,
+                mPackageInfo.applicationInfo.uid, mPackageName,
+                newState ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_ERRORED);
+    }
+
+    @Override
+    protected boolean refreshUi() {
+        mInstallAppsState = mAppBridge.createInstallAppsStateFor(mPackageName,
+                mPackageInfo.applicationInfo.uid);
+
+        final boolean canWrite = mInstallAppsState.canInstallApps();
+        mSwitchPref.setChecked(canWrite);
+        return true;
+    }
+
+    @Override
+    protected AlertDialog createDialog(int id, int errorCode) {
+        return null;
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsEvent.MANAGE_EXTERNAL_SOURCES;
+    }
+}
diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java
index b5f0e76..930957e 100755
--- a/src/com/android/settings/applications/InstalledAppDetails.java
+++ b/src/com/android/settings/applications/InstalledAppDetails.java
@@ -743,6 +743,8 @@
     }
 
     private void forceStopPackage(String pkgName) {
+        FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider().action(getContext(),
+                MetricsEvent.ACTION_APP_FORCE_STOP, pkgName);
         ActivityManager am = (ActivityManager) getActivity().getSystemService(
                 Context.ACTIVITY_SERVICE);
         am.forceStopPackage(pkgName);
diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java
index 43bb02a..a87ba53 100644
--- a/src/com/android/settings/applications/ManageApplications.java
+++ b/src/com/android/settings/applications/ManageApplications.java
@@ -58,6 +58,7 @@
 import com.android.settings.R;
 import com.android.settings.Settings.AllApplicationsActivity;
 import com.android.settings.Settings.HighPowerApplicationsActivity;
+import com.android.settings.Settings.ManageExternalSourcesActivity;
 import com.android.settings.Settings.NotificationAppListActivity;
 import com.android.settings.Settings.OverlaySettingsActivity;
 import com.android.settings.Settings.StorageUseActivity;
@@ -66,6 +67,7 @@
 import com.android.settings.SettingsActivity;
 import com.android.settings.Utils;
 import com.android.settings.applications.AppStateAppOpsBridge.PermissionState;
+import com.android.settings.applications.AppStateInstallAppsBridge.InstallAppsState;
 import com.android.settings.applications.AppStateUsageBridge.UsageState;
 import com.android.settings.core.InstrumentedPreferenceFragment;
 import com.android.settings.dashboard.SummaryLoader;
@@ -136,6 +138,7 @@
     public static final int FILTER_APPS_USAGE_ACCESS = 8;
     public static final int FILTER_APPS_WITH_OVERLAY = 9;
     public static final int FILTER_APPS_WRITE_SETTINGS = 10;
+    public static final int FILTER_APPS_INSTALL_SOURCES = 12;
 
     // This is the string labels for the filter modes above, the order must be kept in sync.
     public static final int[] FILTER_LABELS = new int[]{
@@ -151,6 +154,7 @@
             R.string.filter_all_apps,      // Usage access screen, never displayed
             R.string.filter_overlay_apps,   // Apps with overlay permission
             R.string.filter_write_settings_apps,   // Apps that can write system settings
+            R.string.filter_install_sources_apps, // Apps that are trusted sources of apks
     };
     // This is the actual mapping to filters from FILTER_ constants above, the order must
     // be kept in sync.
@@ -169,6 +173,7 @@
             AppStateUsageBridge.FILTER_APP_USAGE, // Apps with Domain URLs
             AppStateOverlayBridge.FILTER_SYSTEM_ALERT_WINDOW,   // Apps that can draw overlays
             AppStateWriteSettingsBridge.FILTER_WRITE_SETTINGS,  // Apps that can write system settings
+            AppStateInstallAppsBridge.FILTER_APP_SOURCES,
     };
 
     // sort order
@@ -210,6 +215,7 @@
     public static final int LIST_TYPE_HIGH_POWER = 5;
     public static final int LIST_TYPE_OVERLAY = 6;
     public static final int LIST_TYPE_WRITE_SETTINGS = 7;
+    public static final int LIST_TYPE_MANAGE_SOURCES = 8;
 
     private View mRootView;
 
@@ -259,6 +265,8 @@
             mListType = LIST_TYPE_OVERLAY;
         } else if (className.equals(WriteSettingsActivity.class.getName())) {
             mListType = LIST_TYPE_WRITE_SETTINGS;
+        } else if (className.equals(ManageExternalSourcesActivity.class.getName())) {
+            mListType = LIST_TYPE_MANAGE_SOURCES;
         } else {
             mListType = LIST_TYPE_MAIN;
         }
@@ -379,6 +387,8 @@
                 return FILTER_APPS_WITH_OVERLAY;
             case LIST_TYPE_WRITE_SETTINGS:
                 return FILTER_APPS_WRITE_SETTINGS;
+            case LIST_TYPE_MANAGE_SOURCES:
+                return FILTER_APPS_INSTALL_SOURCES;
             default:
                 return FILTER_APPS_ALL;
         }
@@ -412,6 +422,8 @@
                 return MetricsEvent.SYSTEM_ALERT_WINDOW_APPS;
             case LIST_TYPE_WRITE_SETTINGS:
                 return MetricsEvent.SYSTEM_ALERT_WINDOW_APPS;
+            case LIST_TYPE_MANAGE_SOURCES:
+                return MetricsEvent.MANAGE_EXTERNAL_SOURCES;
             default:
                 return MetricsEvent.VIEW_UNKNOWN;
         }
@@ -503,6 +515,9 @@
             case LIST_TYPE_WRITE_SETTINGS:
                 startAppInfoFragment(WriteSettingsDetails.class, R.string.write_system_settings);
                 break;
+            case LIST_TYPE_MANAGE_SOURCES:
+                startAppInfoFragment(ExternalSourcesDetails.class, R.string.install_other_apps);
+                break;
             // TODO: Figure out if there is a way where we can spin up the profile's settings
             // process ahead of time, to avoid a long load of data when user clicks on a managed app.
             // Maybe when they load the list of apps that contains managed profile apps.
@@ -796,6 +811,8 @@
                 mExtraInfoBridge = new AppStateOverlayBridge(mContext, mState, this);
             } else if (mManageApplications.mListType == LIST_TYPE_WRITE_SETTINGS) {
                 mExtraInfoBridge = new AppStateWriteSettingsBridge(mContext, mState, this);
+            } else if (mManageApplications.mListType == LIST_TYPE_MANAGE_SOURCES) {
+                mExtraInfoBridge = new AppStateInstallAppsBridge(mContext, mState, this);
             } else {
                 mExtraInfoBridge = null;
             }
@@ -1206,6 +1223,11 @@
                             holder.entry));
                     break;
 
+                case LIST_TYPE_MANAGE_SOURCES:
+                    holder.summary
+                            .setText(((InstallAppsState) holder.entry.extraInfo).getSummary());
+                    break;
+
                 default:
                     holder.updateSizeText(mManageApplications.mInvalidSizeStr, mWhichSize);
                     break;
diff --git a/src/com/android/settings/dashboard/SuggestionFeatureProvider.java b/src/com/android/settings/dashboard/SuggestionFeatureProvider.java
index 834e63d..769c6e8 100644
--- a/src/com/android/settings/dashboard/SuggestionFeatureProvider.java
+++ b/src/com/android/settings/dashboard/SuggestionFeatureProvider.java
@@ -26,4 +26,10 @@
      */
     boolean isSmartSuggestionEnabled(Context context);
 
+    /** Return true if className is the name of a class of one of your newly added suggestion. */
+    boolean isPresent(String className);
+
+    /** Return true if the suggestion has already been completed and does not need to be shown */
+    boolean isSuggestionCompleted(Context context);
+
 }
\ No newline at end of file
diff --git a/src/com/android/settings/dashboard/SuggestionFeatureProviderImpl.java b/src/com/android/settings/dashboard/SuggestionFeatureProviderImpl.java
index 189dfa5..afaf36e 100644
--- a/src/com/android/settings/dashboard/SuggestionFeatureProviderImpl.java
+++ b/src/com/android/settings/dashboard/SuggestionFeatureProviderImpl.java
@@ -25,4 +25,14 @@
         return false;
     }
 
+    @Override
+    public boolean isPresent(String className) {
+        return false;
+    }
+
+    @Override
+    public boolean isSuggestionCompleted(Context context) {
+        return false;
+    }
+
 }
diff --git a/src/com/android/settings/dashboard/SuggestionsChecks.java b/src/com/android/settings/dashboard/SuggestionsChecks.java
index 78f21ea..f355f83 100644
--- a/src/com/android/settings/dashboard/SuggestionsChecks.java
+++ b/src/com/android/settings/dashboard/SuggestionsChecks.java
@@ -37,6 +37,7 @@
 import com.android.settings.Settings.ZenModeAutomationSuggestionActivity;
 import com.android.settings.Utils;
 import com.android.settings.WallpaperSuggestionActivity;
+import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.drawer.Tile;
 
 import java.util.Collection;
@@ -67,6 +68,13 @@
         } else if (className.equals(FingerprintEnrollSuggestionActivity.class.getName())) {
             return isDeviceSecured() || !isFingerprintEnabled();
         }
+
+        SuggestionFeatureProvider provider =
+            FeatureFactory.getFactory(mContext).getSuggestionFeatureProvider();
+        if (provider != null && provider.isPresent(className)) {
+            return provider.isSuggestionCompleted(mContext);
+        }
+
         return false;
     }
 
diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
index c4b97d4..3d49719 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
@@ -319,7 +319,7 @@
             final int numSippers = usageList.size();
             for (int i = 0; i < numSippers; i++) {
                 final BatterySipper sipper = usageList.get(i);
-                if ((sipper.totalPowerMah * SECONDS_IN_HOUR) < MIN_POWER_THRESHOLD_MILLI_AMP) {
+                if (shouldHideSipper(sipper)) {
                     continue;
                 }
                 double totalPower = USE_FAKE_DATA ? 4000 : mStatsHelper.getTotalPower();
@@ -375,7 +375,7 @@
                 pref.setTitle(entry.getLabel());
                 pref.setOrder(i + 1);
                 pref.setPercent(percentOfMax, percentOfTotal);
-                if ((sipper.drainType != DrainType.APP || sipper.uidObj.getUid() == 0)
+                if ((sipper.drainType != DrainType.APP || sipper.uidObj.getUid() == Process.ROOT_UID)
                          && sipper.drainType != DrainType.USER) {
                     pref.setTint(colorControl);
                 }
@@ -396,6 +396,16 @@
     }
 
     @VisibleForTesting
+    boolean shouldHideSipper(BatterySipper sipper) {
+        final DrainType drainType = sipper.drainType;
+        final int uid = sipper.getUid();
+
+        return drainType == DrainType.IDLE || drainType == DrainType.CELL
+                || uid == Process.ROOT_UID || uid == Process.SYSTEM_UID
+                || (sipper.totalPowerMah * SECONDS_IN_HOUR) < MIN_POWER_THRESHOLD_MILLI_AMP;
+    }
+
+    @VisibleForTesting
     String extractKeyFromSipper(BatterySipper sipper) {
         if (sipper.uidObj != null) {
             return Integer.toString(sipper.getUid());
diff --git a/src/com/android/settings/inputmethod/InputAndGestureSettings.java b/src/com/android/settings/inputmethod/InputAndGestureSettings.java
index 43d8b230b..4ad5af2 100644
--- a/src/com/android/settings/inputmethod/InputAndGestureSettings.java
+++ b/src/com/android/settings/inputmethod/InputAndGestureSettings.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.os.UserHandle;
+import android.provider.SearchIndexableResource;
 import android.support.annotation.VisibleForTesting;
 
 import com.android.internal.hardware.AmbientDisplayConfiguration;
@@ -31,9 +32,12 @@
 import com.android.settings.gestures.DoubleTwistPreferenceController;
 import com.android.settings.gestures.PickupGesturePreferenceController;
 import com.android.settings.gestures.SwipeToNotificationPreferenceController;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settingslib.drawer.CategoryKey;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 public class InputAndGestureSettings extends DashboardFragment {
@@ -90,4 +94,19 @@
     void setAmbientDisplayConfig(AmbientDisplayConfiguration ambientConfig) {
         mAmbientDisplayConfig = ambientConfig;
     }
+
+    public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+            new BaseSearchIndexProvider() {
+                @Override
+                public List<SearchIndexableResource> getXmlResourcesToIndex(
+                        Context context, boolean enabled) {
+                    if (!FeatureFactory.getFactory(context).getDashboardFeatureProvider(context)
+                            .isEnabled()) {
+                        return null;
+                    }
+                    final SearchIndexableResource sir = new SearchIndexableResource(context);
+                    sir.xmlResId = R.xml.input_and_gesture;
+                    return Arrays.asList(sir);
+                }
+            };
 }
diff --git a/src/com/android/settings/notification/AppNotificationSettings.java b/src/com/android/settings/notification/AppNotificationSettings.java
index c720bb5..b740b92 100644
--- a/src/com/android/settings/notification/AppNotificationSettings.java
+++ b/src/com/android/settings/notification/AppNotificationSettings.java
@@ -19,7 +19,6 @@
 import android.app.Activity;
 import android.app.Notification;
 import android.app.NotificationChannel;
-import android.app.NotificationManager;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
@@ -42,7 +41,11 @@
 import com.android.settingslib.RestrictedPreference;
 import com.android.settingslib.RestrictedSwitchPreference;
 
+import java.text.Collator;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
+import java.util.Objects;
 
 /** These settings are per app, so should not be returned in global search results. */
 public class AppNotificationSettings extends NotificationSettingsBase {
@@ -94,6 +97,7 @@
             rows.put(mAppRow.pkg, mAppRow);
             collectConfigActivities(rows);
             mChannelList = mBackend.getChannels(mPkg, mUid).getList();
+            Collections.sort(mChannelList, mChannelComparator);
 
             if (mChannelList.isEmpty()) {
                 setVisible(mChannels, false);
@@ -105,16 +109,21 @@
                     channelPref.setDisabledByAdmin(mSuspendedAppsAdmin);
                     channelPref.setKey(channel.getId());
                     channelPref.setTitle(channel.getName());
-                    Bundle channelArgs = new Bundle();
-                    channelArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, mUid);
-                    channelArgs.putBoolean(AppHeader.EXTRA_HIDE_INFO_BUTTON, true);
-                    channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, mPkg);
-                    channelArgs.putString(ARG_CHANNEL, channel.getId());
 
-                    Intent topicIntent = Utils.onBuildStartFragmentIntent(getActivity(),
-                            ChannelNotificationSettings.class.getName(),
-                            channelArgs, null, 0, null, false);
-                    channelPref.setIntent(topicIntent);
+                    if (channel.isDeleted()) {
+                        channelPref.setTitle(
+                                getString(R.string.deleted_channel_name, channel.getName()));
+                    } else {
+                        Bundle channelArgs = new Bundle();
+                        channelArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, mUid);
+                        channelArgs.putBoolean(AppHeader.EXTRA_HIDE_INFO_BUTTON, true);
+                        channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, mPkg);
+                        channelArgs.putString(ARG_CHANNEL, channel.getId());
+                        Intent channelIntent = Utils.onBuildStartFragmentIntent(getActivity(),
+                                ChannelNotificationSettings.class.getName(),
+                                channelArgs, null, 0, null, false);
+                        channelPref.setIntent(channelIntent);
+                    }
                     mChannels.addPreference(channelPref);
                 }
             }
@@ -212,4 +221,20 @@
                     .setClassName(activityInfo.packageName, activityInfo.name);
         }
     }
+
+    private Comparator<NotificationChannel> mChannelComparator =
+            new Comparator<NotificationChannel>() {
+        private final Collator sCollator = Collator.getInstance();
+
+        @Override
+        public int compare(NotificationChannel left, NotificationChannel right) {
+            if (left.isDeleted() != right.isDeleted()) {
+                return Boolean.compare(left.isDeleted(), right.isDeleted());
+            }
+            if (!Objects.equals(left.getName(), right.getName())) {
+                return sCollator.compare(left.getName().toString(), right.getName().toString());
+            }
+            return left.getId().compareTo(right.getId());
+        }
+    };
 }
diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java
index 4013971..692e1f6 100644
--- a/src/com/android/settings/notification/NotificationBackend.java
+++ b/src/com/android/settings/notification/NotificationBackend.java
@@ -92,7 +92,7 @@
             return null;
         }
         try {
-            return sINM.getNotificationChannelForPackage(pkg, uid, channelId);
+            return sINM.getNotificationChannelForPackage(pkg, uid, channelId, true);
         } catch (Exception e) {
             Log.w(TAG, "Error calling NoMan", e);
             return null;
@@ -101,7 +101,7 @@
 
     public ParceledListSlice<NotificationChannel> getChannels(String pkg, int uid) {
         try {
-            return sINM.getNotificationChannelsForPackage(pkg, uid);
+            return sINM.getNotificationChannelsForPackage(pkg, uid, true);
         } catch (Exception e) {
             Log.w(TAG, "Error calling NoMan", e);
             return ParceledListSlice.emptyList();
diff --git a/src/com/android/settings/notification/WorkSoundPreferenceController.java b/src/com/android/settings/notification/WorkSoundPreferenceController.java
index cc671ad..96d88c9 100644
--- a/src/com/android/settings/notification/WorkSoundPreferenceController.java
+++ b/src/com/android/settings/notification/WorkSoundPreferenceController.java
@@ -20,8 +20,11 @@
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.FragmentManager;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.media.AudioSystem;
 import android.media.Ringtone;
 import android.media.RingtoneManager;
@@ -46,11 +49,11 @@
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
 import com.android.settings.core.lifecycle.Lifecycle;
 import com.android.settings.core.lifecycle.LifecycleObserver;
+import com.android.settings.core.lifecycle.events.OnPause;
 import com.android.settings.core.lifecycle.events.OnResume;
 
-
 public class WorkSoundPreferenceController extends PreferenceController implements
-    OnPreferenceChangeListener, LifecycleObserver, OnResume {
+    OnPreferenceChangeListener, LifecycleObserver, OnResume, OnPause {
 
     private static final String TAG = "WorkSoundPrefController";
     private static final String KEY_WORK_CATEGORY = "sound_work_settings_section";
@@ -97,18 +100,18 @@
 
     @Override
     public void onResume() {
-        if (isAvailable()) {
-            if ((mWorkPreferenceCategory == null)) {
-                // Work preferences not yet set
-                mParent.addPreferencesFromResource(R.xml.sound_work_settings);
-                initWorkPreferences();
-            }
-            if (!mWorkUsePersonalSounds.isChecked()) {
-                updateWorkRingtoneSummaries();
-            }
-        } else {
-            maybeRemoveWorkPreferences();
-        }
+        IntentFilter managedProfileFilter = new IntentFilter();
+        managedProfileFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
+        managedProfileFilter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
+        mContext.registerReceiver(mManagedProfileReceiver, managedProfileFilter);
+
+        mManagedProfileId = mHelper.getManagedProfileId(mUserManager);
+        initWorkPreferences();
+    }
+
+    @Override
+    public void onPause() {
+        mContext.unregisterReceiver(mManagedProfileReceiver);
     }
 
     @Override
@@ -118,8 +121,8 @@
 
     @Override
     public boolean isAvailable() {
-        mManagedProfileId = mHelper.getManagedProfileId(mUserManager);
-        return mManagedProfileId != UserHandle.USER_NULL && shouldShowRingtoneSettings();
+        return mHelper.getManagedProfileId(mUserManager) != UserHandle.USER_NULL
+                && shouldShowRingtoneSettings();
     }
 
     @Override
@@ -184,17 +187,37 @@
     }
 
     private void initWorkPreferences() {
-        mWorkPreferenceCategory = (PreferenceGroup) mParent.getPreferenceScreen()
-            .findPreference(KEY_WORK_CATEGORY);
-        mWorkUsePersonalSounds = (TwoStatePreference) mParent.getPreferenceScreen()
-            .findPreference(KEY_WORK_USE_PERSONAL_SOUNDS);
-        mWorkPhoneRingtonePreference = initWorkPreference(KEY_WORK_PHONE_RINGTONE);
-        mWorkNotificationRingtonePreference = initWorkPreference(KEY_WORK_NOTIFICATION_RINGTONE);
-        mWorkAlarmRingtonePreference = initWorkPreference(KEY_WORK_ALARM_RINGTONE);
+        if (mManagedProfileId == UserHandle.USER_NULL || !isAvailable()) {
+            maybeRemoveWorkPreferences();
+            return;
+        }
 
-        if (!mVoiceCapable) {
-            mWorkPreferenceCategory.removePreference(mWorkPhoneRingtonePreference);
-            mWorkPhoneRingtonePreference = null;
+        if (mWorkPreferenceCategory == null) {
+            mParent.addPreferencesFromResource(R.xml.sound_work_settings);
+            final PreferenceScreen screen = mParent.getPreferenceScreen();
+
+            mWorkPreferenceCategory = (PreferenceGroup) screen.findPreference(KEY_WORK_CATEGORY);
+            mWorkUsePersonalSounds = (TwoStatePreference)
+                    screen.findPreference(KEY_WORK_USE_PERSONAL_SOUNDS);
+            mWorkPhoneRingtonePreference = initWorkPreference(KEY_WORK_PHONE_RINGTONE);
+            mWorkNotificationRingtonePreference = initWorkPreference(
+                    KEY_WORK_NOTIFICATION_RINGTONE);
+            mWorkAlarmRingtonePreference = initWorkPreference(KEY_WORK_ALARM_RINGTONE);
+
+            mWorkUsePersonalSounds.setOnPreferenceChangeListener((Preference p, Object value) -> {
+                if ((boolean) value) {
+                    UnifyWorkDialogFragment.show(mParent);
+                    return false;
+                } else {
+                    disableWorkSync();
+                    return true;
+                }
+            });
+
+            if (!mVoiceCapable) {
+                mWorkPreferenceCategory.removePreference(mWorkPhoneRingtonePreference);
+                mWorkPhoneRingtonePreference = null;
+            }
         }
 
         Context managedProfileContext = getManagedProfileContext();
@@ -204,19 +227,6 @@
         } else {
             disableWorkSyncSettings();
         }
-
-        mWorkUsePersonalSounds.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                if ((boolean) newValue) {
-                    UnifyWorkDialogFragment.show(mParent);
-                    return false;
-                } else {
-                    disableWorkSync();
-                    return true;
-                }
-            }
-        });
     }
 
     void enableWorkSync() {
@@ -267,7 +277,6 @@
 
     private void maybeRemoveWorkPreferences() {
         if (mWorkPreferenceCategory == null) {
-            // No work preferences to remove
             return;
         }
         mParent.getPreferenceScreen().removePreference(mWorkPreferenceCategory);
@@ -277,6 +286,37 @@
         mWorkAlarmRingtonePreference = null;
     }
 
+    public void onManagedProfileAdded(@UserIdInt int profileId) {
+        if (mManagedProfileId == UserHandle.USER_NULL) {
+            mManagedProfileId = profileId;
+            initWorkPreferences();
+        }
+    }
+
+    public void onManagedProfileRemoved(@UserIdInt int profileId) {
+        if (mManagedProfileId == profileId) {
+            mManagedProfileId = mHelper.getManagedProfileId(mUserManager);
+            initWorkPreferences();
+        }
+    }
+
+    private final BroadcastReceiver mManagedProfileReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final int userId = ((UserHandle) intent.getExtra(Intent.EXTRA_USER)).getIdentifier();
+            switch (intent.getAction()) {
+                case Intent.ACTION_MANAGED_PROFILE_ADDED: {
+                    onManagedProfileAdded(userId);
+                    return;
+                }
+                case Intent.ACTION_MANAGED_PROFILE_REMOVED: {
+                    onManagedProfileRemoved(userId);
+                    return;
+                }
+            }
+        }
+    };
+
     public static class UnifyWorkDialogFragment extends InstrumentedDialogFragment
         implements DialogInterface.OnClickListener {
         private static final String TAG = "UnifyWorkDialogFragment";
diff --git a/src/com/android/settings/search/SearchIndexableResources.java b/src/com/android/settings/search/SearchIndexableResources.java
index 8a158cd..cb848d8 100644
--- a/src/com/android/settings/search/SearchIndexableResources.java
+++ b/src/com/android/settings/search/SearchIndexableResources.java
@@ -55,6 +55,7 @@
 import com.android.settings.gestures.PickupGestureSettings;
 import com.android.settings.gestures.SwipeToNotificationSettings;
 import com.android.settings.inputmethod.AvailableVirtualKeyboardFragment;
+import com.android.settings.inputmethod.InputAndGestureSettings;
 import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
 import com.android.settings.inputmethod.PhysicalKeyboardFragment;
 import com.android.settings.inputmethod.VirtualKeyboardFragment;
@@ -136,6 +137,7 @@
         addIndex(DoubleTwistGestureSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_gestures);
         addIndex(SwipeToNotificationSettings.class, NO_DATA_RES_ID,
                 R.drawable.ic_settings_gestures);
+        addIndex(InputAndGestureSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_language);
         addIndex(LocationSettings.class, R.xml.location_settings, R.drawable.ic_settings_location);
         addIndex(ScanningSettings.class, R.xml.location_scanning, R.drawable.ic_settings_location);
         addIndex(SecuritySettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_security);
diff --git a/src/com/android/settings/security/SecurityFeatureProviderImpl.java b/src/com/android/settings/security/SecurityFeatureProviderImpl.java
index 65fc373..72d0f81 100644
--- a/src/com/android/settings/security/SecurityFeatureProviderImpl.java
+++ b/src/com/android/settings/security/SecurityFeatureProviderImpl.java
@@ -30,6 +30,7 @@
 import android.support.v7.preference.PreferenceScreen;
 import android.text.TextUtils;
 import android.util.ArrayMap;
+import android.util.Pair;
 
 import com.android.settingslib.drawer.Tile;
 import com.android.settingslib.drawer.TileUtils;
@@ -68,37 +69,23 @@
             String summaryUri =
                     tile.metaData.getString(TileUtils.META_DATA_PREFERENCE_SUMMARY_URI, null);
             if (!TextUtils.isEmpty(iconUri)) {
-                int icon = TileUtils.getIconFromUri(context, iconUri, providerMap);
-                boolean updateIcon = true;
                 String packageName = null;
-                // Dynamic icon has to come from the same package that the preference launches.
                 if (tile.intent != null) {
-                        Intent intent = tile.intent;
-                        if (!TextUtils.isEmpty(intent.getPackage())) {
-                            packageName = intent.getPackage();
-                        } else if (intent.getComponent() != null) {
-                            packageName = intent.getComponent().getPackageName();
-                        }
-                }
-                if (TextUtils.isEmpty(packageName)) {
-                    updateIcon = false;
-                } else {
-                    if (tile.icon == null) {
-                        // If the tile does not have an icon already, only update if the suggested
-                        // icon is non-zero.
-                        updateIcon = (icon != 0);
-                    } else {
-                        // If the existing icon has the same resource package and resource id, the
-                        // icon does not need to be updated.
-                        updateIcon = !(packageName.equals(tile.icon.getResPackage())
-                                && (icon == tile.icon.getResId()));
+                    Intent intent = tile.intent;
+                    if (!TextUtils.isEmpty(intent.getPackage())) {
+                        packageName = intent.getPackage();
+                    } else if (intent.getComponent() != null) {
+                        packageName = intent.getComponent().getPackageName();
                     }
                 }
-                if (updateIcon) {
+                Pair<String, Integer> icon =
+                        TileUtils.getIconFromUri(context, packageName, iconUri, providerMap);
+                if (icon != null) {
+                    // Icon is only returned if the icon belongs to Settings or the target app.
                     try {
                         matchingPref.setIcon(context.getPackageManager()
-                                .getResourcesForApplication(packageName)
-                                        .getDrawable(icon, context.getTheme()));
+                                .getResourcesForApplication(icon.first /* package name */)
+                                        .getDrawable(icon.second /* res id */, context.getTheme()));
                     } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
                         // Intentionally ignored. If icon resources cannot be found, do not update.
                     }
diff --git a/tests/robotests/assets/grandfather_not_implementing_indexable b/tests/robotests/assets/grandfather_not_implementing_indexable
index 81adf8b..cd0822b 100644
--- a/tests/robotests/assets/grandfather_not_implementing_indexable
+++ b/tests/robotests/assets/grandfather_not_implementing_indexable
@@ -89,4 +89,5 @@
 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment
 com.android.settings.applications.ConvertToFbe
 com.android.settings.localepicker.LocaleListEditor
-com.android.settings.qstile.DevelopmentTileConfigActivity$DevelopmentTileConfigFragment
\ No newline at end of file
+com.android.settings.qstile.DevelopmentTileConfigActivity$DevelopmentTileConfigFragment
+com.android.settings.applications.ExternalSourcesDetails
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
index 1cf83f1..35df218 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
@@ -17,6 +17,7 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.os.Process;
 import android.text.TextUtils;
 import android.view.Menu;
 import android.view.MenuInflater;
@@ -28,7 +29,6 @@
 import com.android.settings.testutils.FakeFeatureFactory;
 import org.junit.Before;
 import org.junit.Test;
-
 import org.junit.runner.RunWith;
 import org.mockito.Answers;
 import org.mockito.Mock;
@@ -52,6 +52,7 @@
 public class PowerUsageSummaryTest {
     private static final String[] PACKAGE_NAMES = {"com.app1", "com.app2"};
     private static final int UID = 123;
+    private static final int POWER_MAH = 100;
 
     private static final Intent ADDITIONAL_BATTERY_INFO_INTENT =
             new Intent("com.example.app.ADDITIONAL_BATTERY_INFO");
@@ -96,6 +97,7 @@
 
         when(mBatterySipper.getPackages()).thenReturn(PACKAGE_NAMES);
         when(mBatterySipper.getUid()).thenReturn(UID);
+        mBatterySipper.totalPowerMah = POWER_MAH;
     }
 
     @Test
@@ -152,6 +154,39 @@
         assertThat(key).isEqualTo(Integer.toString(mBatterySipper.getUid()));
     }
 
+    @Test
+    public void testShouldHideSipper_TypeIdle_ReturnTrue() {
+        mBatterySipper.drainType = BatterySipper.DrainType.IDLE;
+        assertThat(mPowerUsageSummary.shouldHideSipper(mBatterySipper)).isTrue();
+    }
+
+    @Test
+    public void testShouldHideSipper_TypeCell_ReturnTrue() {
+        mBatterySipper.drainType = BatterySipper.DrainType.CELL;
+        assertThat(mPowerUsageSummary.shouldHideSipper(mBatterySipper)).isTrue();
+    }
+
+    @Test
+    public void testShouldHideSipper_UidRoot_ReturnTrue() {
+        mBatterySipper.drainType = BatterySipper.DrainType.APP;
+        when(mBatterySipper.getUid()).thenReturn(Process.ROOT_UID);
+        assertThat(mPowerUsageSummary.shouldHideSipper(mBatterySipper)).isTrue();
+    }
+
+    @Test
+    public void testShouldHideSipper_UidSystem_ReturnTrue() {
+        mBatterySipper.drainType = BatterySipper.DrainType.APP;
+        when(mBatterySipper.getUid()).thenReturn(Process.SYSTEM_UID);
+        assertThat(mPowerUsageSummary.shouldHideSipper(mBatterySipper)).isTrue();
+    }
+
+    @Test
+    public void testShouldHideSipper_UidNormal_ReturnFalse() {
+        mBatterySipper.drainType = BatterySipper.DrainType.APP;
+        when(mBatterySipper.getUid()).thenReturn(UID);
+        assertThat(mPowerUsageSummary.shouldHideSipper(mBatterySipper)).isFalse();
+    }
+
     public static class TestFragment extends PowerUsageSummary {
 
         private Context mContext;
diff --git a/tests/robotests/src/com/android/settings/notification/WorkSoundPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/WorkSoundPreferenceControllerTest.java
index 4d8101f..bb19533 100644
--- a/tests/robotests/src/com/android/settings/notification/WorkSoundPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/WorkSoundPreferenceControllerTest.java
@@ -114,17 +114,8 @@
                 .thenReturn(UserHandle.myUserId());
         when(mAudioHelper.isSingleVolume()).thenReturn(false);
         when(mFragment.getPreferenceScreen()).thenReturn(mScreen);
-        when(mScreen.findPreference(KEY_WORK_CATEGORY))
-            .thenReturn(mock(PreferenceGroup.class));
-        when(mScreen.findPreference(KEY_WORK_USE_PERSONAL_SOUNDS))
-            .thenReturn(mock(TwoStatePreference.class));
-        when(mScreen.findPreference(KEY_WORK_PHONE_RINGTONE))
-            .thenReturn(mock(DefaultRingtonePreference.class));
-        when(mScreen.findPreference(KEY_WORK_NOTIFICATION_RINGTONE))
-            .thenReturn(mock(DefaultRingtonePreference.class));
-        when(mScreen.findPreference(KEY_WORK_ALARM_RINGTONE))
-            .thenReturn(mock(DefaultRingtonePreference.class));
         when(mAudioHelper.createPackageContextAsUser(anyInt())).thenReturn(mContext);
+        mockWorkCategory();
 
         mController.onResume();
 
@@ -132,6 +123,50 @@
     }
 
     @Test
+    public void onManagedProfileAdded_shouldAddPreferenceCategory() {
+        // Given a device without any managed profiles:
+        when(mAudioHelper.isSingleVolume()).thenReturn(false);
+        when(mFragment.getPreferenceScreen()).thenReturn(mScreen);
+        when(mAudioHelper.createPackageContextAsUser(anyInt())).thenReturn(mContext);
+        when(mAudioHelper.getManagedProfileId(any(UserManager.class)))
+                .thenReturn(UserHandle.USER_NULL);
+        mockWorkCategory();
+
+        // When the fragment first resumes, the category should not appear.
+        mController.onResume();
+
+        verify(mFragment, never()).addPreferencesFromResource(R.xml.sound_work_settings);
+
+        // However, when a managed profile is added after resuming, the category should appear.
+        when(mAudioHelper.getManagedProfileId(any(UserManager.class)))
+                .thenReturn(UserHandle.myUserId());
+        mController.onManagedProfileAdded(UserHandle.myUserId());
+
+        verify(mFragment).addPreferencesFromResource(R.xml.sound_work_settings);
+    }
+
+    @Test
+    public void onManagedProfileRemoved_shouldRemovePreferenceCategory() {
+        // Given a device with a managed profile:
+        when(mAudioHelper.isSingleVolume()).thenReturn(false);
+        when(mFragment.getPreferenceScreen()).thenReturn(mScreen);
+        when(mAudioHelper.createPackageContextAsUser(anyInt())).thenReturn(mContext);
+        when(mAudioHelper.getManagedProfileId(any(UserManager.class)))
+                .thenReturn(UserHandle.myUserId());
+        mockWorkCategory();
+
+        // Which is in resumed state:
+        mController.onResume();
+
+        // When a managed profile is removed, the category should be removed.
+        when(mAudioHelper.getManagedProfileId(any(UserManager.class)))
+                .thenReturn(UserHandle.USER_NULL);
+        mController.onManagedProfileRemoved(UserHandle.myUserId());
+
+        verify(mScreen).removePreference(mScreen.findPreference(KEY_WORK_CATEGORY));
+    }
+
+    @Test
     public void onResume_notAvailable_shouldNotAddPreferenceCategory() {
         when(mTelephonyManager.isVoiceCapable()).thenReturn(true);
         when(mAudioHelper.getManagedProfileId(any(UserManager.class)))
@@ -154,4 +189,16 @@
         verify(preference).setSummary(anyString());
     }
 
+    private void mockWorkCategory() {
+        when(mScreen.findPreference(KEY_WORK_CATEGORY))
+            .thenReturn(mock(PreferenceGroup.class));
+        when(mScreen.findPreference(KEY_WORK_USE_PERSONAL_SOUNDS))
+            .thenReturn(mock(TwoStatePreference.class));
+        when(mScreen.findPreference(KEY_WORK_PHONE_RINGTONE))
+            .thenReturn(mock(DefaultRingtonePreference.class));
+        when(mScreen.findPreference(KEY_WORK_NOTIFICATION_RINGTONE))
+            .thenReturn(mock(DefaultRingtonePreference.class));
+        when(mScreen.findPreference(KEY_WORK_ALARM_RINGTONE))
+            .thenReturn(mock(DefaultRingtonePreference.class));
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/security/SecurityFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/security/SecurityFeatureProviderImplTest.java
index f64054c..293c0ae 100644
--- a/tests/robotests/src/com/android/settings/security/SecurityFeatureProviderImplTest.java
+++ b/tests/robotests/src/com/android/settings/security/SecurityFeatureProviderImplTest.java
@@ -25,6 +25,7 @@
 import android.os.Bundle;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
+import android.util.Pair;
 
 import com.android.settings.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
@@ -77,9 +78,9 @@
     @Implements(com.android.settingslib.drawer.TileUtils.class)
     public static class TileUtilsMock {
         @Implementation
-        public static int getIconFromUri(Context context, String uriString,
+        public static Pair getIconFromUri(Context context, String packageName, String uriString,
                 Map<String, IContentProvider> providerMap) {
-            return 161803;
+            return Pair.create("package", 161803);
         }
 
         @Implementation