Merge "Support CATEGORY_LEANBACK_LAUNCHER on tv devices." into pi-dev
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 3a0ae9f..093b1bd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -16,6 +16,7 @@
 
 package com.android.settingslib.applications;
 
+import android.annotation.IntDef;
 import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.app.Application;
@@ -59,6 +60,8 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.text.Collator;
 import java.text.Normalizer;
 import java.text.Normalizer.Form;
@@ -135,6 +138,38 @@
     final BackgroundHandler mBackgroundHandler;
     final MainHandler mMainHandler = new MainHandler(Looper.getMainLooper());
 
+    /** Requests that the home app is loaded. */
+    public static final int FLAG_SESSION_REQUEST_HOME_APP = 1 << 0;
+
+    /** Requests that icons are loaded. */
+    public static final int FLAG_SESSION_REQUEST_ICONS = 1 << 1;
+
+    /** Requests that sizes are loaded. */
+    public static final int FLAG_SESSION_REQUEST_SIZES = 1 << 2;
+
+    /** Requests that launcher intents are resolved. */
+    public static final int FLAG_SESSION_REQUEST_LAUNCHER = 1 << 3;
+
+    /** Requests that leanback launcher intents are resolved. */
+    public static final int FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER = 1 << 4;
+
+    /**
+     * Flags to configure the session to request various types of info.
+     */
+    @IntDef(prefix = { "FLAG_SESSION_" }, value = {
+            FLAG_SESSION_REQUEST_HOME_APP,
+            FLAG_SESSION_REQUEST_ICONS,
+            FLAG_SESSION_REQUEST_SIZES,
+            FLAG_SESSION_REQUEST_LAUNCHER,
+            FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SessionFlags {}
+
+    public static final @SessionFlags int DEFAULT_SESSION_FLAGS =
+            FLAG_SESSION_REQUEST_HOME_APP | FLAG_SESSION_REQUEST_ICONS |
+            FLAG_SESSION_REQUEST_SIZES | FLAG_SESSION_REQUEST_LAUNCHER;
+
     private ApplicationsState(Application app) {
         mContext = app;
         mPm = mContext.getPackageManager();
@@ -265,7 +300,8 @@
         }
     }
 
-    private void clearEntries() {
+    @VisibleForTesting
+    void clearEntries() {
         for (int i = 0; i < mEntriesMap.size(); i++) {
             mEntriesMap.valueAt(i).clear();
         }
@@ -346,7 +382,7 @@
         if (DEBUG_LOCKING) Log.v(TAG, "requestSize about to acquire lock...");
         synchronized (mEntriesMap) {
             AppEntry entry = mEntriesMap.get(userId).get(packageName);
-            if (entry != null && (entry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
+            if (entry != null && hasFlag(entry.info.flags, ApplicationInfo.FLAG_INSTALLED)) {
                 mBackgroundHandler.post(
                         () -> {
                             try {
@@ -595,6 +631,7 @@
     }
 
     public class Session implements LifecycleObserver {
+
         final Callbacks mCallbacks;
         boolean mResumed;
 
@@ -609,6 +646,7 @@
         boolean mRebuildForeground;
 
         private final boolean mHasLifecycle;
+        @SessionFlags private int mFlags = DEFAULT_SESSION_FLAGS;
 
         Session(Callbacks callbacks, Lifecycle lifecycle) {
             mCallbacks = callbacks;
@@ -620,6 +658,14 @@
             }
         }
 
+        public @SessionFlags int getSessionFlags() {
+            return mFlags;
+        }
+
+        public void setSessionFlags(@SessionFlags int flags) {
+            mFlags = flags;
+        }
+
         @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
         public void onResume() {
             if (DEBUG_LOCKING) Log.v(TAG, "resume about to acquire lock...");
@@ -833,10 +879,11 @@
     private class BackgroundHandler extends Handler {
         static final int MSG_REBUILD_LIST = 1;
         static final int MSG_LOAD_ENTRIES = 2;
-        static final int MSG_LOAD_ICONS = 3;
-        static final int MSG_LOAD_SIZES = 4;
-        static final int MSG_LOAD_LAUNCHER = 5;
-        static final int MSG_LOAD_HOME_APP = 6;
+        static final int MSG_LOAD_HOME_APP = 3;
+        static final int MSG_LOAD_LAUNCHER = 4;
+        static final int MSG_LOAD_LEANBACK_LAUNCHER = 5;
+        static final int MSG_LOAD_ICONS = 6;
+        static final int MSG_LOAD_SIZES = 7;
 
         boolean mRunning;
 
@@ -860,6 +907,8 @@
                 }
             }
 
+            int flags = getCombinedSessionFlags(mSessions);
+
             switch (msg.what) {
                 case MSG_REBUILD_LIST: {
                 } break;
@@ -889,8 +938,8 @@
                                 // happens because of the way we generate the list in
                                 // doResumeIfNeededLocked.
                                 AppEntry entry = mEntriesMap.get(0).get(info.packageName);
-                                if (entry != null &&
-                                        (entry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
+                                if (entry != null && !hasFlag(entry.info.flags,
+                                        ApplicationInfo.FLAG_INSTALLED)) {
                                     mEntriesMap.get(0).remove(info.packageName);
                                     mAppEntries.remove(entry);
                                 }
@@ -909,166 +958,206 @@
                     }
                 } break;
                 case MSG_LOAD_HOME_APP: {
-                    final List<ResolveInfo> homeActivities = new ArrayList<>();
-                    mPm.getHomeActivities(homeActivities);
-                    synchronized (mEntriesMap) {
-                        final int entryCount = mEntriesMap.size();
-                        for (int i = 0; i < entryCount; i++) {
-                            if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP acquired lock");
-                            final HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i);
-                            for (ResolveInfo activity : homeActivities) {
-                                String packageName = activity.activityInfo.packageName;
-                                AppEntry entry = userEntries.get(packageName);
-                                if (entry != null) {
-                                    entry.isHomeApp = true;
+                    if (hasFlag(flags, FLAG_SESSION_REQUEST_HOME_APP)) {
+                        final List<ResolveInfo> homeActivities = new ArrayList<>();
+                        mPm.getHomeActivities(homeActivities);
+                        synchronized (mEntriesMap) {
+                            final int entryCount = mEntriesMap.size();
+                            for (int i = 0; i < entryCount; i++) {
+                                if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP acquired lock");
+                                final HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(
+                                        i);
+                                for (ResolveInfo activity : homeActivities) {
+                                    String packageName = activity.activityInfo.packageName;
+                                    AppEntry entry = userEntries.get(packageName);
+                                    if (entry != null) {
+                                        entry.isHomeApp = true;
+                                    }
                                 }
+                                if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP releasing lock");
                             }
-                            if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP releasing lock");
                         }
                     }
                     sendEmptyMessage(MSG_LOAD_LAUNCHER);
-                }
-                break;
-                case MSG_LOAD_LAUNCHER: {
-                    Intent launchIntent = new Intent(Intent.ACTION_MAIN, null)
-                            .addCategory(Intent.CATEGORY_LAUNCHER);
-                    for (int i = 0; i < mEntriesMap.size(); i++) {
-                        int userId = mEntriesMap.keyAt(i);
-                        // If we do not specify MATCH_DIRECT_BOOT_AWARE or
-                        // MATCH_DIRECT_BOOT_UNAWARE, system will derive and update the flags
-                        // according to the user's lock state. When the user is locked, components
-                        // with ComponentInfo#directBootAware == false will be filtered. We should
-                        // explicitly include both direct boot aware and unaware components here.
-                        List<ResolveInfo> intents = mPm.queryIntentActivitiesAsUser(
-                                launchIntent,
-                                PackageManager.MATCH_DISABLED_COMPONENTS
-                                        | PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
-                                userId
-                        );
-                        synchronized (mEntriesMap) {
-                            if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER acquired lock");
-                            HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i);
-                            final int N = intents.size();
-                            for (int j = 0; j < N; j++) {
-                                String packageName = intents.get(j).activityInfo.packageName;
-                                AppEntry entry = userEntries.get(packageName);
-                                if (entry != null) {
-                                    entry.hasLauncherEntry = true;
-                                } else {
-                                    Log.w(TAG, "Cannot find pkg: " + packageName
-                                            + " on user " + userId);
+                } break;
+                case MSG_LOAD_LAUNCHER:
+                case MSG_LOAD_LEANBACK_LAUNCHER: {
+                    if ((msg.what == MSG_LOAD_LAUNCHER &&
+                            hasFlag(flags, FLAG_SESSION_REQUEST_LAUNCHER))
+                            || (msg.what == MSG_LOAD_LEANBACK_LAUNCHER &&
+                            hasFlag(flags, FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER))) {
+
+                        Intent launchIntent = new Intent(Intent.ACTION_MAIN, null);
+                        launchIntent.addCategory(msg.what == MSG_LOAD_LAUNCHER
+                                ? Intent.CATEGORY_LAUNCHER : Intent.CATEGORY_LEANBACK_LAUNCHER);
+                        for (int i = 0; i < mEntriesMap.size(); i++) {
+                            int userId = mEntriesMap.keyAt(i);
+                            // If we do not specify MATCH_DIRECT_BOOT_AWARE or
+                            // MATCH_DIRECT_BOOT_UNAWARE, system will derive and update the flags
+                            // according to the user's lock state. When the user is locked,
+                            // components
+                            // with ComponentInfo#directBootAware == false will be filtered. We should
+                            // explicitly include both direct boot aware and unaware components here.
+                            List<ResolveInfo> intents = mPm.queryIntentActivitiesAsUser(
+                                    launchIntent,
+                                    PackageManager.MATCH_DISABLED_COMPONENTS
+                                            | PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+                                    userId
+                            );
+                            synchronized (mEntriesMap) {
+                                if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER acquired lock");
+                                HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i);
+                                final int N = intents.size();
+                                for (int j = 0; j < N; j++) {
+                                    ResolveInfo resolveInfo = intents.get(j);
+                                    String packageName = resolveInfo.activityInfo.packageName;
+                                    AppEntry entry = userEntries.get(packageName);
+                                    if (entry != null) {
+                                        entry.hasLauncherEntry = true;
+                                        entry.launcherEntryEnabled |=
+                                                resolveInfo.activityInfo.enabled;
+                                    } else {
+                                        Log.w(TAG, "Cannot find pkg: " + packageName
+                                                + " on user " + userId);
+                                    }
                                 }
+                                if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER releasing lock");
                             }
-                            if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER releasing lock");
+                        }
+
+                        if (!mMainHandler.hasMessages(MainHandler.MSG_LAUNCHER_INFO_CHANGED)) {
+                            mMainHandler.sendEmptyMessage(MainHandler.MSG_LAUNCHER_INFO_CHANGED);
                         }
                     }
-
-                    if (!mMainHandler.hasMessages(MainHandler.MSG_LAUNCHER_INFO_CHANGED)) {
-                        mMainHandler.sendEmptyMessage(MainHandler.MSG_LAUNCHER_INFO_CHANGED);
+                    if (msg.what == MSG_LOAD_LAUNCHER) {
+                        sendEmptyMessage(MSG_LOAD_LEANBACK_LAUNCHER);
+                    } else {
+                        sendEmptyMessage(MSG_LOAD_ICONS);
                     }
-                    sendEmptyMessage(MSG_LOAD_ICONS);
                 } break;
                 case MSG_LOAD_ICONS: {
-                    int numDone = 0;
-                    synchronized (mEntriesMap) {
-                        if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS acquired lock");
-                        for (int i=0; i<mAppEntries.size() && numDone<2; i++) {
-                            AppEntry entry = mAppEntries.get(i);
-                            if (entry.icon == null || !entry.mounted) {
-                                synchronized (entry) {
-                                    if (entry.ensureIconLocked(mContext, mDrawableFactory)) {
+                    if (hasFlag(flags, FLAG_SESSION_REQUEST_ICONS)) {
+                        int numDone = 0;
+                        synchronized (mEntriesMap) {
+                            if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS acquired lock");
+                            for (int i = 0; i < mAppEntries.size() && numDone < 2; i++) {
+                                AppEntry entry = mAppEntries.get(i);
+                                if (entry.icon == null || !entry.mounted) {
+                                    synchronized (entry) {
+                                        if (entry.ensureIconLocked(mContext, mDrawableFactory)) {
+                                            if (!mRunning) {
+                                                mRunning = true;
+                                                Message m = mMainHandler.obtainMessage(
+                                                        MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
+                                                mMainHandler.sendMessage(m);
+                                            }
+                                            numDone++;
+                                        }
+                                    }
+                                }
+                            }
+                            if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS releasing lock");
+                        }
+                        if (numDone > 0) {
+                            if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_ICON_CHANGED)) {
+                                mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_ICON_CHANGED);
+                            }
+                        }
+                        if (numDone >= 2) {
+                            sendEmptyMessage(MSG_LOAD_ICONS);
+                            break;
+                        }
+                    }
+                    sendEmptyMessage(MSG_LOAD_SIZES);
+                } break;
+                case MSG_LOAD_SIZES: {
+                    if (hasFlag(flags, FLAG_SESSION_REQUEST_SIZES)) {
+                        synchronized (mEntriesMap) {
+                            if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES acquired lock");
+                            if (mCurComputingSizePkg != null) {
+                                if (DEBUG_LOCKING) Log.v(TAG,
+                                        "MSG_LOAD_SIZES releasing: currently computing");
+                                return;
+                            }
+
+                            long now = SystemClock.uptimeMillis();
+                            for (int i = 0; i < mAppEntries.size(); i++) {
+                                AppEntry entry = mAppEntries.get(i);
+                                if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_INSTALLED)
+                                        && (entry.size == SIZE_UNKNOWN || entry.sizeStale)) {
+                                    if (entry.sizeLoadStart == 0 ||
+                                            (entry.sizeLoadStart < (now - 20 * 1000))) {
                                         if (!mRunning) {
                                             mRunning = true;
                                             Message m = mMainHandler.obtainMessage(
                                                     MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
                                             mMainHandler.sendMessage(m);
                                         }
-                                        numDone++;
+                                        entry.sizeLoadStart = now;
+                                        mCurComputingSizeUuid = entry.info.storageUuid;
+                                        mCurComputingSizePkg = entry.info.packageName;
+                                        mCurComputingSizeUserId = UserHandle.getUserId(
+                                                entry.info.uid);
+
+                                        mBackgroundHandler.post(() -> {
+                                            try {
+                                                final StorageStats stats =
+                                                        mStats.queryStatsForPackage(
+                                                                mCurComputingSizeUuid,
+                                                                mCurComputingSizePkg,
+                                                                UserHandle.of(
+                                                                        mCurComputingSizeUserId));
+                                                final PackageStats legacy = new PackageStats(
+                                                        mCurComputingSizePkg,
+                                                        mCurComputingSizeUserId);
+                                                legacy.codeSize = stats.getCodeBytes();
+                                                legacy.dataSize = stats.getDataBytes();
+                                                legacy.cacheSize = stats.getCacheBytes();
+                                                try {
+                                                    mStatsObserver.onGetStatsCompleted(legacy,
+                                                            true);
+                                                } catch (RemoteException ignored) {
+                                                }
+                                            } catch (NameNotFoundException | IOException e) {
+                                                Log.w(TAG, "Failed to query stats: " + e);
+                                                try {
+                                                    mStatsObserver.onGetStatsCompleted(null, false);
+                                                } catch (RemoteException ignored) {
+                                                }
+                                            }
+
+                                        });
                                     }
+                                    if (DEBUG_LOCKING) Log.v(TAG,
+                                            "MSG_LOAD_SIZES releasing: now computing");
+                                    return;
                                 }
                             }
-                        }
-                        if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS releasing lock");
-                    }
-                    if (numDone > 0) {
-                        if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_ICON_CHANGED)) {
-                            mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_ICON_CHANGED);
-                        }
-                    }
-                    if (numDone >= 2) {
-                        sendEmptyMessage(MSG_LOAD_ICONS);
-                    } else {
-                        sendEmptyMessage(MSG_LOAD_SIZES);
-                    }
-                } break;
-                case MSG_LOAD_SIZES: {
-                    synchronized (mEntriesMap) {
-                        if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES acquired lock");
-                        if (mCurComputingSizePkg != null) {
-                            if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: currently computing");
-                            return;
-                        }
-
-                        long now = SystemClock.uptimeMillis();
-                        for (int i=0; i<mAppEntries.size(); i++) {
-                            AppEntry entry = mAppEntries.get(i);
-                            if ((entry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0
-                                    && (entry.size == SIZE_UNKNOWN || entry.sizeStale)) {
-                                if (entry.sizeLoadStart == 0 ||
-                                        (entry.sizeLoadStart < (now-20*1000))) {
-                                    if (!mRunning) {
-                                        mRunning = true;
-                                        Message m = mMainHandler.obtainMessage(
-                                                MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
-                                        mMainHandler.sendMessage(m);
-                                    }
-                                    entry.sizeLoadStart = now;
-                                    mCurComputingSizeUuid = entry.info.storageUuid;
-                                    mCurComputingSizePkg = entry.info.packageName;
-                                    mCurComputingSizeUserId = UserHandle.getUserId(entry.info.uid);
-
-                                    mBackgroundHandler.post(() -> {
-                                        try {
-                                            final StorageStats stats = mStats.queryStatsForPackage(
-                                                    mCurComputingSizeUuid, mCurComputingSizePkg,
-                                                    UserHandle.of(mCurComputingSizeUserId));
-                                            final PackageStats legacy = new PackageStats(
-                                                    mCurComputingSizePkg, mCurComputingSizeUserId);
-                                            legacy.codeSize = stats.getCodeBytes();
-                                            legacy.dataSize = stats.getDataBytes();
-                                            legacy.cacheSize = stats.getCacheBytes();
-                                            try {
-                                                mStatsObserver.onGetStatsCompleted(legacy, true);
-                                            } catch (RemoteException ignored) {
-                                            }
-                                        } catch (NameNotFoundException | IOException e) {
-                                            Log.w(TAG, "Failed to query stats: " + e);
-                                            try {
-                                                mStatsObserver.onGetStatsCompleted(null, false);
-                                            } catch (RemoteException ignored) {
-                                            }
-                                        }
-
-                                    });
-                                }
-                                if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: now computing");
-                                return;
+                            if (!mMainHandler.hasMessages(MainHandler.MSG_ALL_SIZES_COMPUTED)) {
+                                mMainHandler.sendEmptyMessage(MainHandler.MSG_ALL_SIZES_COMPUTED);
+                                mRunning = false;
+                                Message m = mMainHandler.obtainMessage(
+                                        MainHandler.MSG_RUNNING_STATE_CHANGED, 0);
+                                mMainHandler.sendMessage(m);
                             }
+                            if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing lock");
                         }
-                        if (!mMainHandler.hasMessages(MainHandler.MSG_ALL_SIZES_COMPUTED)) {
-                            mMainHandler.sendEmptyMessage(MainHandler.MSG_ALL_SIZES_COMPUTED);
-                            mRunning = false;
-                            Message m = mMainHandler.obtainMessage(
-                                    MainHandler.MSG_RUNNING_STATE_CHANGED, 0);
-                            mMainHandler.sendMessage(m);
-                        }
-                        if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing lock");
                     }
                 } break;
             }
         }
 
+        private @SessionFlags int getCombinedSessionFlags(List<Session> sessions) {
+            synchronized (mEntriesMap) {
+                int flags = 0;
+                for (Session session : sessions) {
+                    flags |= session.mFlags;
+                }
+                return flags;
+            }
+        }
+
         final IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver.Stub() {
             public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
                 if (!succeeded) {
@@ -1257,6 +1346,11 @@
         public boolean hasLauncherEntry;
 
         /**
+         * Whether the component that has a launcher intent filter is enabled.
+         */
+        public boolean launcherEntryEnabled;
+
+        /**
          * Whether or not it's a Home app.
          */
         public boolean isHomeApp;
@@ -1283,7 +1377,7 @@
         // A location where extra info can be placed to be used by custom filters.
         public Object extraInfo;
 
-        @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+        @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
         public AppEntry(Context context, ApplicationInfo info, long id) {
             apkFile = new File(info.sourceDir);
             this.id = id;
@@ -1336,6 +1430,10 @@
         }
     }
 
+    private static boolean hasFlag(int flags, int flag) {
+        return (flags & flag) != 0;
+    }
+
     /**
      * Compare by label, then package name, then uid.
      */
@@ -1449,13 +1547,13 @@
         public boolean filterApp(AppEntry entry) {
             if (AppUtils.isInstant(entry.info)) {
                 return false;
-            } else if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+            } else if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) {
                 return true;
-            } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+            } else if (!hasFlag(entry.info.flags, ApplicationInfo.FLAG_SYSTEM)) {
                 return true;
             } else if (entry.hasLauncherEntry) {
                 return true;
-            } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0 && entry.isHomeApp) {
+            } else if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_SYSTEM) && entry.isHomeApp) {
                 return true;
             }
             return false;
@@ -1486,9 +1584,9 @@
 
         @Override
         public boolean filterApp(AppEntry entry) {
-            if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+            if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) {
                 return true;
-            } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+            } else if (!hasFlag(entry.info.flags, ApplicationInfo.FLAG_SYSTEM)) {
                 return true;
             }
             return false;
@@ -1547,7 +1645,7 @@
         @Override
         public boolean filterApp(AppEntry entry) {
             return !AppUtils.isInstant(entry.info)
-                && (entry.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
+                && hasFlag(entry.info.privateFlags, ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS);
         }
     };
 
@@ -1589,7 +1687,7 @@
             // TODO: Update for the new game category.
             boolean isGame;
             synchronized (info.info) {
-                isGame = ((info.info.flags & ApplicationInfo.FLAG_IS_GAME) != 0)
+                isGame = hasFlag(info.info.flags, ApplicationInfo.FLAG_IS_GAME)
                         || info.info.category == ApplicationInfo.CATEGORY_GAME;
             }
             return isGame;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
new file mode 100644
index 0000000..2dbabe0
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
@@ -0,0 +1,303 @@
+/*
+ * 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.settingslib.applications;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.robolectric.shadow.api.Shadow.extract;
+
+import android.annotation.UserIdInt;
+import android.app.ApplicationPackageManager;
+import android.app.usage.IStorageStatsManager;
+import android.app.usage.StorageStats;
+import android.app.usage.StorageStatsManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.util.IconDrawableFactory;
+
+import com.android.settingslib.applications.ApplicationsState.AppEntry;
+import com.android.settingslib.applications.ApplicationsState.Callbacks;
+import com.android.settingslib.applications.ApplicationsState.Session;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.testutils.shadow.ShadowUserManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowContextImpl;
+import org.robolectric.shadows.ShadowLooper;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(shadows = {ShadowUserManager.class,
+        ApplicationsStateRoboTest.ShadowIconDrawableFactory.class,
+        ApplicationsStateRoboTest.ShadowPackageManager.class})
+public class ApplicationsStateRoboTest {
+
+    private final static String HOME_PACKAGE_NAME = "com.android.home";
+    private final static String LAUNCHABLE_PACKAGE_NAME = "com.android.launchable";
+
+    /** Class under test */
+    private ApplicationsState mApplicationsState;
+
+    @Mock
+    private Callbacks mCallbacks;
+    @Captor
+    private ArgumentCaptor<ArrayList<AppEntry>> mAppEntriesCaptor;
+    @Mock
+    private StorageStatsManager mStorageStatsManager;
+
+    @Implements(value = IconDrawableFactory.class, inheritImplementationMethods = true)
+    public static class ShadowIconDrawableFactory {
+
+        @Implementation
+        public Drawable getBadgedIcon(ApplicationInfo appInfo) {
+            return new ColorDrawable(0);
+        }
+    }
+
+    @Implements(value = ApplicationPackageManager.class, inheritImplementationMethods = true)
+    public static class ShadowPackageManager extends
+            org.robolectric.shadows.ShadowApplicationPackageManager {
+
+        @Implementation
+        public ComponentName getHomeActivities(List<ResolveInfo> outActivities) {
+            ResolveInfo resolveInfo = new ResolveInfo();
+            resolveInfo.activityInfo = new ActivityInfo();
+            resolveInfo.activityInfo.packageName = HOME_PACKAGE_NAME;
+            resolveInfo.activityInfo.enabled = true;
+            outActivities.add(resolveInfo);
+            return ComponentName.createRelative(resolveInfo.activityInfo.packageName, "foo");
+        }
+
+        public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent,
+                @PackageManager.ResolveInfoFlags int flags, @UserIdInt int userId) {
+            List<ResolveInfo> resolveInfos = new ArrayList<>();
+            ResolveInfo resolveInfo = new ResolveInfo();
+            resolveInfo.activityInfo = new ActivityInfo();
+            resolveInfo.activityInfo.packageName = LAUNCHABLE_PACKAGE_NAME;
+            resolveInfo.activityInfo.enabled = true;
+            resolveInfo.filter = new IntentFilter();
+            resolveInfo.filter.addCategory(Intent.CATEGORY_LAUNCHER);
+            resolveInfos.add(resolveInfo);
+            return resolveInfos;
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        // Robolectric does not know about the StorageStatsManager as a system service.
+        // Registering a mock of this service as a replacement.
+        ShadowContextImpl shadowContext = Shadow.extract(
+                RuntimeEnvironment.application.getBaseContext());
+        shadowContext.setSystemService(Context.STORAGE_STATS_SERVICE, mStorageStatsManager);
+        StorageStats storageStats = new StorageStats();
+        storageStats.codeBytes = 10;
+        storageStats.dataBytes = 20;
+        storageStats.cacheBytes = 30;
+        when(mStorageStatsManager.queryStatsForPackage(ArgumentMatchers.any(UUID.class),
+                anyString(), ArgumentMatchers.any(UserHandle.class))).thenReturn(storageStats);
+
+        mApplicationsState = ApplicationsState.getInstance(RuntimeEnvironment.application);
+        mApplicationsState.clearEntries();
+    }
+
+    private ApplicationInfo createApplicationInfo(String packageName) {
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.sourceDir = "foo";
+        appInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
+        appInfo.storageUuid = UUID.randomUUID();
+        appInfo.packageName = packageName;
+        return appInfo;
+    }
+
+    private AppEntry createAppEntry(ApplicationInfo appInfo, int id) {
+        AppEntry appEntry = new AppEntry(RuntimeEnvironment.application, appInfo, id);
+        appEntry.label = "label";
+        appEntry.mounted = true;
+        return appEntry;
+    }
+
+    private void addApp(String packageName, int id) {
+        ApplicationInfo appInfo = createApplicationInfo(packageName);
+        AppEntry appEntry = createAppEntry(appInfo, id);
+        mApplicationsState.mAppEntries.add(appEntry);
+        mApplicationsState.mEntriesMap.get(0).put(appInfo.packageName, appEntry);
+    }
+
+    private void processAllMessages() {
+        Handler mainHandler = mApplicationsState.mMainHandler;
+        Handler bkgHandler = mApplicationsState.mBackgroundHandler;
+        ShadowLooper shadowBkgLooper = extract(bkgHandler.getLooper());
+        ShadowLooper shadowMainLooper = extract(mainHandler.getLooper());
+        shadowBkgLooper.idle();
+        shadowMainLooper.idle();
+    }
+
+    private AppEntry findAppEntry(List<AppEntry> appEntries, long id) {
+        for (AppEntry appEntry : appEntries) {
+            if (appEntry.id == id) {
+                return appEntry;
+            }
+        }
+        return null;
+    }
+
+    @Test
+    public void testDefaultSessionLoadsAll() {
+        Session session = mApplicationsState.newSession(mCallbacks);
+        session.onResume();
+
+        addApp(HOME_PACKAGE_NAME,1);
+        addApp(LAUNCHABLE_PACKAGE_NAME,2);
+        session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+        processAllMessages();
+        verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
+
+        List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
+        assertThat(appEntries.size()).isEqualTo(2);
+
+        for (AppEntry appEntry : appEntries) {
+            assertThat(appEntry.size).isGreaterThan(0L);
+            assertThat(appEntry.icon).isNotNull();
+        }
+
+        AppEntry homeEntry = findAppEntry(appEntries, 1);
+        assertThat(homeEntry.isHomeApp).isTrue();
+        assertThat(homeEntry.hasLauncherEntry).isFalse();
+
+        AppEntry launchableEntry = findAppEntry(appEntries, 2);
+        assertThat(launchableEntry.hasLauncherEntry).isTrue();
+        assertThat(launchableEntry.launcherEntryEnabled).isTrue();
+        session.onDestroy();
+    }
+
+    @Test
+    public void testCustomSessionLoadsIconsOnly() {
+        Session session = mApplicationsState.newSession(mCallbacks);
+        session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_ICONS);
+        session.onResume();
+
+        addApp(LAUNCHABLE_PACKAGE_NAME,1);
+        session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+        processAllMessages();
+        verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
+
+        List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
+        assertThat(appEntries.size()).isEqualTo(1);
+
+        AppEntry launchableEntry = findAppEntry(appEntries, 1);
+        assertThat(launchableEntry.icon).isNotNull();
+        assertThat(launchableEntry.size).isEqualTo(-1);
+        assertThat(launchableEntry.hasLauncherEntry).isFalse();
+        session.onDestroy();
+    }
+
+    @Test
+    public void testCustomSessionLoadsSizesOnly() {
+        Session session = mApplicationsState.newSession(mCallbacks);
+        session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_SIZES);
+        session.onResume();
+
+        addApp(LAUNCHABLE_PACKAGE_NAME,1);
+        session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+        processAllMessages();
+        verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
+
+        List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
+        assertThat(appEntries.size()).isEqualTo(1);
+
+        AppEntry launchableEntry = findAppEntry(appEntries, 1);
+        assertThat(launchableEntry.icon).isNull();
+        assertThat(launchableEntry.hasLauncherEntry).isFalse();
+        assertThat(launchableEntry.size).isGreaterThan(0L);
+        session.onDestroy();
+    }
+
+    @Test
+    public void testCustomSessionLoadsHomeOnly() {
+        Session session = mApplicationsState.newSession(mCallbacks);
+        session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_HOME_APP);
+        session.onResume();
+
+        addApp(HOME_PACKAGE_NAME,1);
+        session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+        processAllMessages();
+        verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
+
+        List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
+        assertThat(appEntries.size()).isEqualTo(1);
+
+        AppEntry launchableEntry = findAppEntry(appEntries, 1);
+        assertThat(launchableEntry.icon).isNull();
+        assertThat(launchableEntry.hasLauncherEntry).isFalse();
+        assertThat(launchableEntry.size).isEqualTo(-1);
+        assertThat(launchableEntry.isHomeApp).isTrue();
+        session.onDestroy();
+    }
+
+    @Test
+    public void testCustomSessionLoadsLeanbackOnly() {
+        Session session = mApplicationsState.newSession(mCallbacks);
+        session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER);
+        session.onResume();
+
+        addApp(LAUNCHABLE_PACKAGE_NAME,1);
+        session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+        processAllMessages();
+        verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
+
+        List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
+        assertThat(appEntries.size()).isEqualTo(1);
+
+        AppEntry launchableEntry = findAppEntry(appEntries, 1);
+        assertThat(launchableEntry.icon).isNull();
+        assertThat(launchableEntry.size).isEqualTo(-1);
+        assertThat(launchableEntry.isHomeApp).isFalse();
+        assertThat(launchableEntry.hasLauncherEntry).isTrue();
+        assertThat(launchableEntry.launcherEntryEnabled).isTrue();
+        session.onDestroy();
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java
index a3e1bc8..bbd3a53 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java
@@ -17,6 +17,7 @@
 package com.android.settingslib.testutils.shadow;
 
 import android.content.Context;
+import android.content.pm.UserInfo;
 import android.os.UserManager;
 
 import org.robolectric.RuntimeEnvironment;
@@ -25,6 +26,9 @@
 import org.robolectric.annotation.Resetter;
 import org.robolectric.shadow.api.Shadow;
 
+import java.util.ArrayList;
+import java.util.List;
+
 @Implements(value = UserManager.class, inheritImplementationMethods = true)
 public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager {
 
@@ -49,6 +53,20 @@
         return (UserManager) context.getSystemService(Context.USER_SERVICE);
     }
 
+    @Implementation
+    public int[] getProfileIdsWithDisabled(int userId) {
+        return new int[] { 0 };
+    }
+
+    @Implementation
+    public List<UserInfo> getProfiles() {
+        UserInfo userInfo = new UserInfo();
+        userInfo.id = 0;
+        List<UserInfo> userInfos = new ArrayList<>();
+        userInfos.add(userInfo);
+        return userInfos;
+    }
+
     public static ShadowUserManager getShadow() {
         return (ShadowUserManager) Shadow.extract(
                 RuntimeEnvironment.application.getSystemService(UserManager.class));