Merge "Hook up the Games storage category preference to a games view."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 6698814..a68b2a7 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2564,6 +2564,20 @@
                        android:value="com.android.settings.applications.VrListenerSettings" />
         </activity>
 
+        <activity android:name="Settings$PictureInPictureSettingsActivity"
+            android:label="@string/picture_in_picture_title"
+            android:taskAffinity="">
+            <intent-filter android:priority="1">
+                <action android:name="android.settings.PICTURE_IN_PICTURE_SETTINGS" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+                android:value="com.android.settings.applications.PictureInPictureSettings" />
+        </activity>
 
         <activity android:name="Settings$ZenAccessSettingsActivity"
                   android:label="@string/manage_zen_access_title"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a63366a..e5f4438 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -6324,6 +6324,21 @@
          flicker while in VR mode. -->
     <string name="display_vr_pref_off">Reduce flicker</string>
 
+    <!-- Special access > Title for managing Picture-in-picture settings. [CHAR LIMIT=50] -->
+    <string name="picture_in_picture_title">Picture-in-picture</string>
+
+    <!-- Special access > Picture-in-picture > Text to display when the list is empty. [CHAR LIMIT=NONE] -->
+    <string name="picture_in_picture_empty_text">No installed apps support Picture-in-picture</string>
+
+    <!-- Special access > Picture-in-picture > Additional keywords to search for. [CHAR LIMIT=NONE] -->
+    <string name="picture_in_picture_keywords">pip picture in</string>
+
+    <!-- Apps > App Details > Advanced section string title. [CHAR LIMIT=NONE] -->
+    <string name="picture_in_picture_app_detail_title">Picture-in-picture</string>
+
+    <!-- Apps > App Details > Advanced section string description. [CHAR LIMIT=NONE] -->
+    <string name="picture_in_picture_app_detail_summary">Permit entering picture-in-picture when leaving app</string>
+
     <!-- Sound & notification > Advanced section: Title for managing Do Not Disturb access option. [CHAR LIMIT=40] -->
     <string name="manage_zen_access_title">Do Not Disturb access</string>
 
diff --git a/res/xml/special_access.xml b/res/xml/special_access.xml
index 7d85195..8bf5c56 100644
--- a/res/xml/special_access.xml
+++ b/res/xml/special_access.xml
@@ -75,6 +75,11 @@
         android:fragment="com.android.settings.notification.NotificationAccessSettings" />
 
     <Preference
+        android:key="picture_in_picture"
+        android:title="@string/picture_in_picture_title"
+        android:fragment="com.android.settings.applications.PictureInPictureSettings"
+        settings:keywords="@string/picture_in_picture_keywords" />
+    <Preference
         android:key="premium_sms"
         android:title="@string/premium_sms_access"
         android:fragment="com.android.settings.applications.PremiumSmsAccess" />
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 5c69f2e..12b86b6 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -105,6 +105,7 @@
     public static class UserSettingsActivity extends SettingsActivity { /* empty */ }
     public static class NotificationAccessSettingsActivity extends SettingsActivity { /* empty */ }
     public static class VrListenersSettingsActivity extends SettingsActivity { /* empty */ }
+    public static class PictureInPictureSettingsActivity extends SettingsActivity { /* empty */ }
     public static class ZenAccessSettingsActivity extends SettingsActivity { /* empty */ }
     public static class ConditionProviderSettingsActivity extends SettingsActivity { /* empty */ }
     public static class UsbSettingsActivity extends SettingsActivity { /* empty */ }
diff --git a/src/com/android/settings/applications/ActivityInfoWrapper.java b/src/com/android/settings/applications/ActivityInfoWrapper.java
new file mode 100644
index 0000000..c6920ca
--- /dev/null
+++ b/src/com/android/settings/applications/ActivityInfoWrapper.java
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+/**
+ * This interface replicates a subset of the android.content.pm.ActivityInfo. The interface
+ * exists so that we can use a thin wrapper around the ActivityInfo in production code and a mock in
+ * tests.
+ */
+public interface ActivityInfoWrapper {
+
+    /**
+     * Returns the resizeMode of the activity.
+     */
+    int getResizeMode();
+}
diff --git a/src/com/android/settings/applications/ActivityInfoWrapperImpl.java b/src/com/android/settings/applications/ActivityInfoWrapperImpl.java
new file mode 100644
index 0000000..e7a20bc
--- /dev/null
+++ b/src/com/android/settings/applications/ActivityInfoWrapperImpl.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.ActivityInfo;
+
+public class ActivityInfoWrapperImpl implements ActivityInfoWrapper {
+
+    private final ActivityInfo mInfo;
+
+    public ActivityInfoWrapperImpl(ActivityInfo info) {
+        mInfo = info;
+    }
+
+    @Override
+    public int getResizeMode() {
+        return mInfo.resizeMode;
+    }
+}
diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java
index 8bde62a..873c5fd 100755
--- a/src/com/android/settings/applications/InstalledAppDetails.java
+++ b/src/com/android/settings/applications/InstalledAppDetails.java
@@ -51,6 +51,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.support.annotation.VisibleForTesting;
+import android.support.v14.preference.SwitchPreference;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.Preference.OnPreferenceClickListener;
 import android.support.v7.preference.PreferenceCategory;
@@ -934,9 +935,23 @@
                     AdvancedAppSettings.class, "default_sms_app", R.string.sms_application_title,
                     R.string.configure_apps));
         }
+
+        // Get the package info with the activities
+        PackageInfo packageInfoWithActivities = null;
+        try {
+            packageInfoWithActivities = mPm.getPackageInfoAsUser(mPackageName,
+                    PackageManager.GET_ACTIVITIES, UserHandle.myUserId());
+        } catch (NameNotFoundException e) {
+            Log.e(TAG, "Exception while retrieving the package info of " + mPackageName, e);
+        }
+
         boolean hasDrawOverOtherApps = hasPermission(permission.SYSTEM_ALERT_WINDOW);
         boolean hasWriteSettings = hasPermission(permission.WRITE_SETTINGS);
-        if (hasDrawOverOtherApps || hasWriteSettings) {
+        boolean hasPictureInPictureActivities = (packageInfoWithActivities != null) &&
+                PictureInPictureSettings.checkPackageHasPictureInPictureActivities(
+                        packageInfoWithActivities.packageName,
+                        packageInfoWithActivities.activities);
+        if (hasDrawOverOtherApps || hasWriteSettings || hasPictureInPictureActivities) {
             PreferenceCategory category = new PreferenceCategory(getPrefContext());
             category.setTitle(R.string.advanced_apps);
             screen.addPreference(category);
@@ -969,6 +984,23 @@
                 });
                 category.addPreference(pref);
             }
+            if (hasPictureInPictureActivities) {
+                final SwitchPreference pref = new SwitchPreference(getPrefContext());
+                pref.setPersistent(false);
+                pref.setTitle(R.string.picture_in_picture_app_detail_title);
+                pref.setSummary(R.string.picture_in_picture_app_detail_summary);
+                pref.setChecked(PictureInPictureSettings.getEnterPipOnHideStateForPackage(
+                        getContext(), mPackageInfo.applicationInfo.uid, mPackageName));
+                pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+                    @Override
+                    public boolean onPreferenceChange(Preference preference, Object newValue) {
+                        PictureInPictureSettings.setEnterPipOnHideStateForPackage(getContext(),
+                                mPackageInfo.applicationInfo.uid, mPackageName, (Boolean) newValue);
+                        return true;
+                    }
+                });
+                category.addPreference(pref);
+            }
         }
 
         addAppInstallerInfoPref(screen);
diff --git a/src/com/android/settings/applications/PictureInPictureSettings.java b/src/com/android/settings/applications/PictureInPictureSettings.java
new file mode 100644
index 0000000..a17c894
--- /dev/null
+++ b/src/com/android/settings/applications/PictureInPictureSettings.java
@@ -0,0 +1,201 @@
+/*
+ * 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 static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_ERRORED;
+import static android.app.AppOpsManager.OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE;
+import static android.content.pm.PackageManager.GET_ACTIVITIES;
+
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.util.ArrayMap;
+import android.view.View;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+import com.android.settings.notification.EmptyTextSettings;
+import com.android.settings.overlay.FeatureFactory;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class PictureInPictureSettings extends EmptyTextSettings {
+
+    private static final String TAG = PictureInPictureSettings.class.getSimpleName();
+    @VisibleForTesting
+    static final List<String> IGNORE_PACKAGE_LIST = new ArrayList<>();
+    static {
+        IGNORE_PACKAGE_LIST.add("com.android.systemui");
+    }
+
+    private Context mContext;
+    private PackageManager mPackageManager;
+
+    /**
+     * @return true if the package has any activities that declare that they support
+     *         picture-in-picture.
+     */
+    static boolean checkPackageHasPictureInPictureActivities(String packageName,
+            ActivityInfo[] activities) {
+        ActivityInfoWrapper[] wrappedActivities = null;
+        if (activities != null) {
+            wrappedActivities = new ActivityInfoWrapper[activities.length];
+            for (int i = 0; i < activities.length; i++) {
+                wrappedActivities[i] = new ActivityInfoWrapperImpl(activities[i]);
+            }
+        }
+        return checkPackageHasPictureInPictureActivities(packageName, wrappedActivities);
+    }
+
+    /**
+     * @return true if the package has any activities that declare that they support
+     *         picture-in-picture.
+     */
+    @VisibleForTesting
+    static boolean checkPackageHasPictureInPictureActivities(String packageName,
+            ActivityInfoWrapper[] activities) {
+        // Skip if it's in the ignored list
+        if (IGNORE_PACKAGE_LIST.contains(packageName)) {
+            return false;
+        }
+
+        // Iterate through all the activities and check if it is resizeable and supports
+        // picture-in-picture
+        if (activities != null) {
+            for (int i = activities.length - 1; i >= 0; i--) {
+                if (activities[i].getResizeMode() == RESIZE_MODE_RESIZEABLE_AND_PIPABLE) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Sets whether the app associated with the given {@param packageName} is allowed to enter
+     * picture-in-picture when it is hidden.
+     */
+    static void setEnterPipOnHideStateForPackage(Context context, int uid, String packageName,
+            boolean value) {
+        final AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
+        final int newMode = value ? MODE_ALLOWED : MODE_ERRORED;
+        appOps.setMode(OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE,
+                uid, packageName, newMode);
+    }
+
+    /**
+     * @return whether the app associated with the given {@param packageName} is allowed to enter
+     *         picture-in-picture when it is hidden.
+     */
+    static boolean getEnterPipOnHideStateForPackage(Context context, int uid, String packageName) {
+        final AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
+        return appOps.checkOpNoThrow(OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE,
+                uid, packageName) == MODE_ALLOWED;
+    }
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        mContext = getActivity();
+        mPackageManager = mContext.getPackageManager();
+        setPreferenceScreen(getPreferenceManager().createPreferenceScreen(mContext));
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        // Clear the prefs
+        final PreferenceScreen screen = getPreferenceScreen();
+        screen.removeAll();
+
+        // Fetch the set of applications which have at least one activity that declare that they
+        // support picture-in-picture
+        final ArrayMap<String, Boolean> packageToState = new ArrayMap<>();
+        final ArrayList<ApplicationInfo> pipApps = new ArrayList<>();
+        final List<PackageInfo> installedPackages = mPackageManager.getInstalledPackagesAsUser(
+                GET_ACTIVITIES, UserHandle.myUserId());
+        for (PackageInfo packageInfo : installedPackages) {
+            if (checkPackageHasPictureInPictureActivities(packageInfo.packageName,
+                    packageInfo.activities)) {
+                final String packageName = packageInfo.applicationInfo.packageName;
+                final boolean state = getEnterPipOnHideStateForPackage(mContext,
+                        packageInfo.applicationInfo.uid, packageName);
+                pipApps.add(packageInfo.applicationInfo);
+                packageToState.put(packageName, state);
+            }
+        }
+        Collections.sort(pipApps, new PackageItemInfo.DisplayNameComparator(mPackageManager));
+
+        // Rebuild the list of prefs
+        final Context prefContext = getPrefContext();
+        for (final ApplicationInfo appInfo : pipApps) {
+            final String packageName = appInfo.packageName;
+            final CharSequence label = appInfo.loadLabel(mPackageManager);
+            final SwitchPreference pref = new SwitchPreference(prefContext);
+            pref.setPersistent(false);
+            pref.setIcon(appInfo.loadIcon(mPackageManager));
+            pref.setTitle(label);
+            pref.setChecked(packageToState.get(packageName));
+            pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+                @Override
+                public boolean onPreferenceChange(Preference preference, Object newValue) {
+                    logSpecialPermissionChange((Boolean) newValue, packageName);
+                    setEnterPipOnHideStateForPackage(mContext, appInfo.uid, packageName,
+                            (Boolean) newValue);
+                    return true;
+                }
+            });
+            screen.addPreference(pref);
+        }
+    }
+
+    @Override
+    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        setEmptyText(R.string.picture_in_picture_empty_text);
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsEvent.SETTINGS_MANAGE_PICTURE_IN_PICTURE;
+    }
+
+    @VisibleForTesting
+    void logSpecialPermissionChange(boolean newState, String packageName) {
+        int logCategory = newState
+                ? MetricsEvent.APP_PICTURE_IN_PICTURE_ON_HIDE_ALLOW
+                : MetricsEvent.APP_PICTURE_IN_PICTURE_ON_HIDE_DENY;
+        FeatureFactory.getFactory(getContext())
+                .getMetricsFeatureProvider().action(getContext(), logCategory, packageName);
+    }
+}
diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java
index 6b08af8..76132ef 100644
--- a/src/com/android/settings/core/gateway/SettingsGateway.java
+++ b/src/com/android/settings/core/gateway/SettingsGateway.java
@@ -57,6 +57,7 @@
 import com.android.settings.applications.ManageAssist;
 import com.android.settings.applications.ManageDomainUrls;
 import com.android.settings.applications.NotificationApps;
+import com.android.settings.applications.PictureInPictureSettings;
 import com.android.settings.applications.ProcessStatsSummary;
 import com.android.settings.applications.ProcessStatsUi;
 import com.android.settings.applications.UsageAccessDetails;
@@ -225,6 +226,7 @@
             AdvancedAppSettings.class.getName(),
             WallpaperTypeSettings.class.getName(),
             VrListenerSettings.class.getName(),
+            PictureInPictureSettings.class.getName(),
             ManagedProfileSettings.class.getName(),
             ChooseAccountActivity.class.getName(),
             IccLockSettings.class.getName(),
diff --git a/src/com/android/settings/datausage/BillingCycleSettings.java b/src/com/android/settings/datausage/BillingCycleSettings.java
index 9b39840..dda984b 100644
--- a/src/com/android/settings/datausage/BillingCycleSettings.java
+++ b/src/com/android/settings/datausage/BillingCycleSettings.java
@@ -44,14 +44,16 @@
 
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
 import static android.net.NetworkPolicy.WARNING_DISABLED;
-import static android.net.TrafficStats.GB_IN_BYTES;
-import static android.net.TrafficStats.MB_IN_BYTES;
 
 public class BillingCycleSettings extends DataUsageBase implements
         Preference.OnPreferenceChangeListener, DataUsageEditController {
 
     private static final String TAG = "BillingCycleSettings";
     private static final boolean LOGD = false;
+    public static final long KB_IN_BYTES = 1000;
+    public static final long MB_IN_BYTES = KB_IN_BYTES * 1000;
+    public static final long GB_IN_BYTES = MB_IN_BYTES * 1000;
+
     private static final long MAX_DATA_LIMIT_BYTES = 50000 * GB_IN_BYTES;
 
     private static final String TAG_CONFIRM_LIMIT = "confirmLimit";
diff --git a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
index d05d088..8fabd8d 100644
--- a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
+++ b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
@@ -21,9 +21,6 @@
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
 import android.provider.SearchIndexableResource;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
 
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
@@ -62,7 +59,8 @@
 
         // Initialize the storage sizes that we can quickly calc.
         StorageManager sm = context.getSystemService(StorageManager.class);
-        String volumeId = getArguments().getString(VolumeInfo.EXTRA_VOLUME_ID);
+        String volumeId = getArguments().getString(VolumeInfo.EXTRA_VOLUME_ID,
+                VolumeInfo.ID_PRIVATE_INTERNAL);
         mVolume = sm.findVolumeById(volumeId);
         if (!isVolumeValid()) {
             getActivity().finish();
diff --git a/src/com/android/settings/search2/DatabaseIndexingManager.java b/src/com/android/settings/search2/DatabaseIndexingManager.java
index c75f93f..d7c8746 100644
--- a/src/com/android/settings/search2/DatabaseIndexingManager.java
+++ b/src/com/android/settings/search2/DatabaseIndexingManager.java
@@ -516,10 +516,7 @@
                 nonIndexableKeys.addAll(resNonIndxableKeys);
             }
 
-            indexFromResource(sir.context, database, localeStr,
-                    sir.xmlResId, sir.className, sir.iconResId, sir.rank,
-                    sir.intentAction, sir.intentTargetPackage, sir.intentTargetClass,
-                    nonIndexableKeys);
+            indexFromResource(database, localeStr, sir, nonIndexableKeys);
         } else {
             if (TextUtils.isEmpty(sir.className)) {
                 Log.w(LOG_TAG, "Cannot index an empty Search Provider name!");
@@ -543,20 +540,17 @@
                     nonIndexableKeys.addAll(providerNonIndexableKeys);
                 }
 
-                indexFromProvider(mContext, database, localeStr, provider, sir.className,
-                        sir.iconResId, sir.rank, sir.enabled, nonIndexableKeys);
+                indexFromProvider(database, localeStr, provider, sir, nonIndexableKeys);
             }
         }
     }
 
-    private void indexFromResource(Context context, SQLiteDatabase database, String localeStr,
-            int xmlResId, String fragmentName, int iconResId, int rank,
-            String intentAction, String intentTargetPackage, String intentTargetClass,
-            List<String> nonIndexableKeys) {
-
+    private void indexFromResource(SQLiteDatabase database, String localeStr,
+            SearchIndexableResource sir, List<String> nonIndexableKeys) {
+        final Context context = sir.context;
         XmlResourceParser parser = null;
         try {
-            parser = context.getResources().getXml(xmlResId);
+            parser = context.getResources().getXml(sir.xmlResId);
 
             int type;
             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -583,6 +577,13 @@
             String keywords;
             String childFragment;
             ResultPayload payload;
+            boolean enabled;
+            final String fragmentName = sir.className;
+            final int iconResId = sir.iconResId;
+            final int rank = sir.rank;
+            final String intentAction = sir.intentAction;
+            final String intentTargetPackage = sir.intentTargetPackage;
+            final String intentTargetClass = sir.intentTargetClass;
 
             Map<String, PreferenceController> controllerUriMap = null;
 
@@ -593,28 +594,28 @@
 
             // Insert rows for the main PreferenceScreen node. Rewrite the data for removing
             // hyphens.
-            if (!nonIndexableKeys.contains(key)) {
-                title = XmlParserUtils.getDataTitle(context, attrs);
-                summary = XmlParserUtils.getDataSummary(context, attrs);
-                keywords = XmlParserUtils.getDataKeywords(context, attrs);
 
-                DatabaseRow.Builder builder = new DatabaseRow.Builder();
-                builder.setLocale(localeStr)
-                        .setEntries(null)
-                        .setClassName(fragmentName)
-                        .setScreenTitle(screenTitle)
-                        .setIconResId(iconResId)
-                        .setRank(rank)
-                        .setIntentAction(intentAction)
-                        .setIntentTargetPackage(intentTargetPackage)
-                        .setIntentTargetClass(intentTargetClass)
-                        .setEnabled(true)
-                        .setKey(key)
-                        .setUserId(-1 /* default user id */);
+            title = XmlParserUtils.getDataTitle(context, attrs);
+            summary = XmlParserUtils.getDataSummary(context, attrs);
+            keywords = XmlParserUtils.getDataKeywords(context, attrs);
+            enabled = !nonIndexableKeys.contains(key);
 
-                updateOneRowWithFilteredData(database, builder, title, summary,
-                        null /* summary off */, keywords);
-            }
+            DatabaseRow.Builder builder = new DatabaseRow.Builder();
+            builder.setLocale(localeStr)
+                    .setEntries(null)
+                    .setClassName(fragmentName)
+                    .setScreenTitle(screenTitle)
+                    .setIconResId(iconResId)
+                    .setRank(rank)
+                    .setIntentAction(intentAction)
+                    .setIntentTargetPackage(intentTargetPackage)
+                    .setIntentTargetClass(intentTargetClass)
+                    .setEnabled(enabled)
+                    .setKey(key)
+                    .setUserId(-1 /* default user id */);
+
+            updateOneRowWithFilteredData(database, builder, title, summary,
+                    null /* summary off */, keywords);
 
             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                     && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
@@ -625,14 +626,11 @@
                 nodeName = parser.getName();
 
                 key = XmlParserUtils.getDataKey(context, attrs);
-                if (nonIndexableKeys.contains(key)) {
-                    continue;
-                }
-
+                enabled = ! nonIndexableKeys.contains(key);
                 title = XmlParserUtils.getDataTitle(context, attrs);
                 keywords = XmlParserUtils.getDataKeywords(context, attrs);
 
-                DatabaseRow.Builder builder = new DatabaseRow.Builder();
+                builder = new DatabaseRow.Builder();
                 builder.setLocale(localeStr)
                         .setClassName(fragmentName)
                         .setScreenTitle(screenTitle)
@@ -641,7 +639,7 @@
                         .setIntentAction(intentAction)
                         .setIntentTargetPackage(intentTargetPackage)
                         .setIntentTargetClass(intentTargetClass)
-                        .setEnabled(true)
+                        .setEnabled(enabled)
                         .setKey(key)
                         .setUserId(-1 /* default user id */);
 
@@ -685,16 +683,21 @@
         }
     }
 
-    private void indexFromProvider(Context context, SQLiteDatabase database, String localeStr,
-            Indexable.SearchIndexProvider provider, String className, int iconResId, int rank,
-            boolean enabled, List<String> nonIndexableKeys) {
+    private void indexFromProvider(SQLiteDatabase database, String localeStr,
+            Indexable.SearchIndexProvider provider, SearchIndexableResource sir,
+            List<String> nonIndexableKeys) {
+
+        final String className = sir.className;
+        final int iconResId = sir.iconResId;
+        final int rank = sir.rank;
 
         if (provider == null) {
             Log.w(LOG_TAG, "Cannot find provider: " + className);
             return;
         }
 
-        final List<SearchIndexableRaw> rawList = provider.getRawDataToIndex(context, enabled);
+        final List<SearchIndexableRaw> rawList = provider.getRawDataToIndex(mContext,
+                true /* enabled */);
 
         if (rawList != null) {
 
@@ -706,10 +709,7 @@
                 if (!raw.locale.toString().equalsIgnoreCase(localeStr)) {
                     continue;
                 }
-
-                if (nonIndexableKeys.contains(raw.key)) {
-                    continue;
-                }
+                boolean enabled = !nonIndexableKeys.contains(raw.key);
 
                 DatabaseRow.Builder builder = new DatabaseRow.Builder();
                 builder.setLocale(localeStr)
@@ -721,7 +721,7 @@
                         .setIntentAction(raw.intentAction)
                         .setIntentTargetPackage(raw.intentTargetPackage)
                         .setIntentTargetClass(raw.intentTargetClass)
-                        .setEnabled(raw.enabled)
+                        .setEnabled(enabled)
                         .setKey(raw.key)
                         .setUserId(raw.userId);
 
@@ -731,7 +731,7 @@
         }
 
         final List<SearchIndexableResource> resList =
-                provider.getXmlResourcesToIndex(context, enabled);
+                provider.getXmlResourcesToIndex(mContext, true);
         if (resList != null) {
             final int resSize = resList.size();
             for (int i = 0; i < resSize; i++) {
@@ -742,15 +742,10 @@
                     continue;
                 }
 
-                final int itemIconResId = (item.iconResId == 0) ? iconResId : item.iconResId;
-                final int itemRank = (item.rank == 0) ? rank : item.rank;
-                String itemClassName = (TextUtils.isEmpty(item.className))
-                        ? className : item.className;
+                item.iconResId = (item.iconResId == 0) ? iconResId : item.iconResId;
+                item.className = (TextUtils.isEmpty(item.className)) ? className : item.className;
 
-                indexFromResource(context, database, localeStr,
-                        item.xmlResId, itemClassName, itemIconResId, itemRank,
-                        item.intentAction, item.intentTargetPackage,
-                        item.intentTargetClass, nonIndexableKeys);
+                indexFromResource(database, localeStr, item, nonIndexableKeys);
             }
         }
     }
@@ -1164,4 +1159,4 @@
             }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/src/com/android/settings/search2/DatabaseResultLoader.java b/src/com/android/settings/search2/DatabaseResultLoader.java
index 6c8def6..0bbded2 100644
--- a/src/com/android/settings/search2/DatabaseResultLoader.java
+++ b/src/com/android/settings/search2/DatabaseResultLoader.java
@@ -129,7 +129,6 @@
         secondaryResults = query(MATCH_COLUMNS_SECONDARY, BASE_RANKS[1]);
         tertiaryResults = query(MATCH_COLUMNS_TERTIARY, BASE_RANKS[2]);
 
-
         final List<SearchResult> results = new ArrayList<>(primaryResults.size()
                 + secondaryResults.size()
                 + tertiaryResults.size());
@@ -167,7 +166,7 @@
     }
 
     private static String buildWhereClause(String[] matchColumns) {
-        StringBuilder sb = new StringBuilder(" ");
+        StringBuilder sb = new StringBuilder(" (");
         final int count = matchColumns.length;
         for (int n = 0; n < count; n++) {
             sb.append(matchColumns[n]);
@@ -176,6 +175,7 @@
                 sb.append(" OR ");
             }
         }
+        sb.append(") AND enabled = 1");
         return sb.toString();
     }
 }
diff --git a/tests/robotests/assets/grandfather_not_implementing_indexable b/tests/robotests/assets/grandfather_not_implementing_indexable
index a178596..d774779 100644
--- a/tests/robotests/assets/grandfather_not_implementing_indexable
+++ b/tests/robotests/assets/grandfather_not_implementing_indexable
@@ -91,3 +91,4 @@
 com.android.settings.localepicker.LocaleListEditor
 com.android.settings.qstile.DevelopmentTileConfigActivity$DevelopmentTileConfigFragment
 com.android.settings.applications.ExternalSourcesDetails
+com.android.settings.applications.PictureInPictureSettings
diff --git a/tests/robotests/src/com/android/settings/applications/PictureInPictureSettingsTest.java b/tests/robotests/src/com/android/settings/applications/PictureInPictureSettingsTest.java
new file mode 100644
index 0000000..daed00d
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/PictureInPictureSettingsTest.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.applications;
+
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageInfo;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+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;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.lang.reflect.Field;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.argThat;
+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;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class PictureInPictureSettingsTest {
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Context mContext;
+
+    private FakeFeatureFactory mFeatureFactory;
+    private PictureInPictureSettings mFragment;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        FakeFeatureFactory.setupForTest(mContext);
+        mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
+        mFragment = new PictureInPictureSettings();
+    }
+
+    @Test
+    public void testIgnoredApp() {
+        for (String ignoredPackage : mFragment.IGNORE_PACKAGE_LIST) {
+            assertThat(checkPackageHasPictureInPictureActivities(ignoredPackage, true))
+                            .isFalse();
+        }
+    }
+
+    @Test
+    public void testNonPippableApp() {
+        assertThat(checkPackageHasPictureInPictureActivities("com.android.dummypackage")).isFalse();
+        assertThat(checkPackageHasPictureInPictureActivities("com.android.dummypackage",
+                false, false, false)).isFalse();
+    }
+
+    @Test
+    public void testPippableApp() {
+        assertThat(checkPackageHasPictureInPictureActivities("com.android.dummypackage",
+                true)).isTrue();
+        assertThat(checkPackageHasPictureInPictureActivities("com.android.dummypackage",
+                false, true)).isTrue();
+        assertThat(checkPackageHasPictureInPictureActivities("com.android.dummypackage",
+                true, false)).isTrue();
+    }
+
+    @Test
+    public void logSpecialPermissionChange() {
+        mFragment.logSpecialPermissionChange(true, "app");
+        verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
+                eq(MetricsProto.MetricsEvent.APP_PICTURE_IN_PICTURE_ON_HIDE_ALLOW), eq("app"));
+
+        mFragment.logSpecialPermissionChange(false, "app");
+        verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
+                eq(MetricsProto.MetricsEvent.APP_PICTURE_IN_PICTURE_ON_HIDE_DENY), eq("app"));
+    }
+
+    private boolean checkPackageHasPictureInPictureActivities(String packageName,
+            boolean... resizeableActivityState) {
+        ActivityInfoWrapper[] activities = null;
+        if (resizeableActivityState.length > 0) {
+            activities = new ActivityInfoWrapper[resizeableActivityState.length];
+            for (int i = 0; i < activities.length; i++) {
+                activities[i] = new MockActivityInfo(resizeableActivityState[i]
+                        ? ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE
+                        : ActivityInfo.RESIZE_MODE_UNRESIZEABLE);
+            }
+        }
+        return PictureInPictureSettings.checkPackageHasPictureInPictureActivities(packageName,
+                activities);
+    }
+
+    private class MockActivityInfo implements ActivityInfoWrapper {
+
+        private int mResizeMode;
+
+        public MockActivityInfo(int resizeMode) {
+            mResizeMode = resizeMode;
+        }
+
+        @Override
+        public int getResizeMode() {
+            return mResizeMode;
+        }
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/search/DatabaseIndexingManagerTest.java b/tests/robotests/src/com/android/settings/search/DatabaseIndexingManagerTest.java
index 1cf72ea..e89f009 100644
--- a/tests/robotests/src/com/android/settings/search/DatabaseIndexingManagerTest.java
+++ b/tests/robotests/src/com/android/settings/search/DatabaseIndexingManagerTest.java
@@ -41,6 +41,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 
 import static com.android.settings.dashboard.SiteMapManager.SITE_MAP_COLUMNS;
 import static com.google.common.truth.Truth.assertThat;
@@ -51,7 +52,7 @@
 public class DatabaseIndexingManagerTest {
     private final String localeStr = "en_US";
 
-    private final int rank = 42;
+    private final int rank = 8;
     private final String title = "title\u2011title";
     private final String updatedTitle = "title-title";
     private final String normalizedTitle = "titletitle";
@@ -213,6 +214,20 @@
     }
 
     @Test
+    public void testAddResourceWithNIKs_RowsInsertedDisabled() {
+        SearchIndexableResource resource = getFakeResource(R.xml.gesture_settings);
+        // Only add 2 of 6 items to be disabled.
+        String[] keys = {"gesture_double_tap_power", "gesture_swipe_down_fingerprint"};
+        Map<String, List<String>> niks = getNonIndexableKeys(keys);
+        mManager.indexOneSearchIndexableData(mDb, localeStr, resource, niks);
+
+        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 0", null);
+        assertThat(cursor.getCount()).isEqualTo(2);
+        cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 1", null);
+        assertThat(cursor.getCount()).isEqualTo(4);
+    }
+
+    @Test
     public void testAddResourceHeader_RowsMatch() {
         SearchIndexableResource resource = getFakeResource(R.xml.application_settings);
         mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
@@ -529,21 +544,19 @@
 
     @Test
     public void testResourceProvider_ResourceRowInserted() {
-        SearchIndexableResource resource = getFakeResource(R.xml.gesture_settings);
-        resource.xmlResId = 0;
+        SearchIndexableResource resource = getFakeResource(0);
         resource.className = "com.android.settings.LegalSettings";
 
         mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
                 new HashMap<>());
         Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
-        assertThat(cursor.getCount()).isEqualTo(2);
+        assertThat(cursor.getCount()).isEqualTo(6);
     }
 
     @Test
     public void testResourceProvider_ResourceRowMatches() {
-        SearchIndexableResource resource = getFakeResource(R.xml.gesture_settings);
-        resource.xmlResId = 0;
-        resource.className = "com.android.settings.LegalSettings";
+        SearchIndexableResource resource = getFakeResource(0);
+        resource.className = "com.android.settings.display.ScreenZoomSettings";
 
         mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
                 new HashMap<>());
@@ -555,9 +568,9 @@
         // Data Rank
         assertThat(cursor.getInt(1)).isEqualTo(rank);
         // Data Title
-        assertThat(cursor.getString(2)).isEqualTo("Legal information");
+        assertThat(cursor.getString(2)).isEqualTo("Display size");
         // Normalized Title
-        assertThat(cursor.getString(3)).isEqualTo("legal information");
+        assertThat(cursor.getString(3)).isEqualTo("display size");
         // Summary On
         assertThat(cursor.getString(4)).isEmpty();
         // Summary On Normalized
@@ -569,12 +582,13 @@
         // Entries - only on for list preferences
         assertThat(cursor.getString(8)).isNull();
         // Keywords
-        assertThat(cursor.getString(9)).isEmpty();
+        assertThat(cursor.getString(9)).isEqualTo(
+                "display density screen zoom scale scaling");
         // Screen Title
-        assertThat(cursor.getString(10)).isEqualTo("Legal information");
+        assertThat(cursor.getString(10)).isEqualTo("Display size");
         // Class Name
         assertThat(cursor.getString(11))
-                .isEqualTo("com.android.settings.LegalSettings");
+                .isEqualTo("com.android.settings.display.ScreenZoomSettings");
         // Icon
         assertThat(cursor.getInt(12)).isEqualTo(iconResId);
         // Intent Action
@@ -595,6 +609,20 @@
         assertThat(cursor.getBlob(20)).isNull();
     }
 
+    @Test
+    public void testResourceProvider_DisabledResourceRowsInserted() {
+        SearchIndexableResource resource = getFakeResource(0);
+        resource.className = "com.android.settings.LegalSettings";
+
+        mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
+                new HashMap<String, List<String>>());
+
+        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 1", null);
+        assertThat(cursor.getCount()).isEqualTo(2);
+        cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 0", null);
+        assertThat(cursor.getCount()).isEqualTo(4);
+    }
+
     // Util functions
 
     private SearchIndexableRaw getFakeRaw() {
@@ -636,4 +664,11 @@
         sir.enabled = enabled;
         return sir;
     }
-}
+
+    private Map<String, List<String>> getNonIndexableKeys(String[] keys) {
+        Map<String, List<String>> niks = new HashMap<>();
+        List<String> keysList = new ArrayList<>(Arrays.asList(keys));
+        niks.put(packageName, keysList);
+        return niks;
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/search/DatabaseResultLoaderTest.java b/tests/robotests/src/com/android/settings/search/DatabaseResultLoaderTest.java
index c749a00..31e6e6c 100644
--- a/tests/robotests/src/com/android/settings/search/DatabaseResultLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/search/DatabaseResultLoaderTest.java
@@ -80,26 +80,26 @@
     @Test
     public void testMatchTitle() {
         loader = new DatabaseResultLoader(mContext, "title");
-        assertThat(loader.loadInBackground().size()).isEqualTo(3);
-        verify(mSiteMapManager, times(3)).buildBreadCrumb(eq(mContext), anyString(), anyString());
+        assertThat(loader.loadInBackground().size()).isEqualTo(2);
+        verify(mSiteMapManager, times(2)).buildBreadCrumb(eq(mContext), anyString(), anyString());
     }
 
     @Test
     public void testMatchSummary() {
         loader = new DatabaseResultLoader(mContext, "summary");
-        assertThat(loader.loadInBackground().size()).isEqualTo(3);
+        assertThat(loader.loadInBackground().size()).isEqualTo(2);
     }
 
     @Test
     public void testMatchKeywords() {
         loader = new DatabaseResultLoader(mContext, "keywords");
-        assertThat(loader.loadInBackground().size()).isEqualTo(3);
+        assertThat(loader.loadInBackground().size()).isEqualTo(2);
     }
 
     @Test
     public void testMatchEntries() {
         loader = new DatabaseResultLoader(mContext, "entries");
-        assertThat(loader.loadInBackground().size()).isEqualTo(3);
+        assertThat(loader.loadInBackground().size()).isEqualTo(2);
     }
 
     @Test
@@ -167,7 +167,7 @@
         values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE, "");
         values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS, "");
         values.put(IndexDatabaseHelper.IndexColumns.ICON, "");
-        values.put(IndexDatabaseHelper.IndexColumns.ENABLED, "");
+        values.put(IndexDatabaseHelper.IndexColumns.ENABLED, true);
         values.put(IndexDatabaseHelper.IndexColumns.DATA_KEY_REF, "gesture_double_tap_power");
         values.put(IndexDatabaseHelper.IndexColumns.USER_ID, 0);
         values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD_TYPE, 0);
@@ -196,7 +196,7 @@
         values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE, "");
         values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS, "");
         values.put(IndexDatabaseHelper.IndexColumns.ICON, "");
-        values.put(IndexDatabaseHelper.IndexColumns.ENABLED, "");
+        values.put(IndexDatabaseHelper.IndexColumns.ENABLED, true);
         values.put(IndexDatabaseHelper.IndexColumns.DATA_KEY_REF, "gesture_double_tap_power");
         values.put(IndexDatabaseHelper.IndexColumns.USER_ID, 0);
         values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD_TYPE, 0);
@@ -223,7 +223,7 @@
         values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE, "");
         values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS, "");
         values.put(IndexDatabaseHelper.IndexColumns.ICON, "");
-        values.put(IndexDatabaseHelper.IndexColumns.ENABLED, "");
+        values.put(IndexDatabaseHelper.IndexColumns.ENABLED, true);
         values.put(IndexDatabaseHelper.IndexColumns.DATA_KEY_REF, "gesture_double_tap_power");
         values.put(IndexDatabaseHelper.IndexColumns.USER_ID, 0);
         values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD_TYPE, 0);
@@ -249,7 +249,7 @@
         values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE, "");
         values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS, "");
         values.put(IndexDatabaseHelper.IndexColumns.ICON, "");
-        values.put(IndexDatabaseHelper.IndexColumns.ENABLED, "");
+        values.put(IndexDatabaseHelper.IndexColumns.ENABLED, false);
         values.put(IndexDatabaseHelper.IndexColumns.DATA_KEY_REF, "gesture_double_tap_power");
         values.put(IndexDatabaseHelper.IndexColumns.USER_ID, 0);
         values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD_TYPE, 0);