Launcher3 multi-profile support

Use LauncherApps API and badging APIs instead of PackageManager.
With compatability layer that uses PackageManager pre L.

Adds support to show apps from current user and any managed profiles.

Background: Managed profiles are user sandboxes that are visible from
the primary user and can be launched as if they are a part of this user.
A launcher should now be capable of listing apps from this user as well
as related profiles of this user.

Launching of activities is now via the LauncherApps interface, to allow
for cross-profile app launching. Only activities with category LAUNCHER
can be added as a shortcut on the workspace for a managed profile.

Widgets and non-application shortcuts are only supported for the
current profile. Widgets from the managed profile are not available.

Change-Id: I5f396b1bf7f91ad91a5710ea4a0fd14573972eb9
diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/AllAppsList.java
index 89b291f..c25aa40 100644
--- a/src/com/android/launcher3/AllAppsList.java
+++ b/src/com/android/launcher3/AllAppsList.java
@@ -23,6 +23,10 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 
+import com.android.launcher3.compat.LauncherActivityInfoCompat;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.UserHandleCompat;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -66,7 +70,7 @@
         if (mAppFilter != null && !mAppFilter.shouldShowApp(info.componentName)) {
             return;
         }
-        if (findActivity(data, info.componentName)) {
+        if (findActivity(data, info.componentName, info.user)) {
             return;
         }
         data.add(info);
@@ -92,12 +96,14 @@
     /**
      * Add the icons for the supplied apk called packageName.
      */
-    public void addPackage(Context context, String packageName) {
-        final List<ResolveInfo> matches = findActivitiesForPackage(context, packageName);
+    public void addPackage(Context context, String packageName, UserHandleCompat user) {
+        final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
+        final List<LauncherActivityInfoCompat> matches = launcherApps.getActivityList(packageName,
+                user);
 
         if (matches.size() > 0) {
-            for (ResolveInfo info : matches) {
-                add(new AppInfo(context.getPackageManager(), info, mIconCache, null));
+            for (LauncherActivityInfoCompat info : matches) {
+                add(new AppInfo(context, info, user, mIconCache, null));
             }
         }
     }
@@ -105,34 +111,37 @@
     /**
      * Remove the apps for the given apk identified by packageName.
      */
-    public void removePackage(String packageName) {
+    public void removePackage(String packageName, UserHandleCompat user) {
         final List<AppInfo> data = this.data;
         for (int i = data.size() - 1; i >= 0; i--) {
             AppInfo info = data.get(i);
             final ComponentName component = info.intent.getComponent();
-            if (packageName.equals(component.getPackageName())) {
+            if (info.user.equals(user) && packageName.equals(component.getPackageName())) {
                 removed.add(info);
                 data.remove(i);
             }
         }
-        mIconCache.remove(packageName);
+        mIconCache.remove(packageName, user);
     }
 
     /**
      * Add and remove icons for this package which has been updated.
      */
-    public void updatePackage(Context context, String packageName) {
-        final List<ResolveInfo> matches = findActivitiesForPackage(context, packageName);
+    public void updatePackage(Context context, String packageName, UserHandleCompat user) {
+        final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
+        final List<LauncherActivityInfoCompat> matches = launcherApps.getActivityList(packageName,
+                user);
         if (matches.size() > 0) {
             // Find disabled/removed activities and remove them from data and add them
             // to the removed list.
             for (int i = data.size() - 1; i >= 0; i--) {
                 final AppInfo applicationInfo = data.get(i);
                 final ComponentName component = applicationInfo.intent.getComponent();
-                if (packageName.equals(component.getPackageName())) {
+                if (user.equals(applicationInfo.user)
+                        && packageName.equals(component.getPackageName())) {
                     if (!findActivity(matches, component)) {
                         removed.add(applicationInfo);
-                        mIconCache.remove(component);
+                        mIconCache.remove(component, user);
                         data.remove(i);
                     }
                 }
@@ -142,14 +151,14 @@
             // Also updates existing activities with new labels/icons
             int count = matches.size();
             for (int i = 0; i < count; i++) {
-                final ResolveInfo info = matches.get(i);
+                final LauncherActivityInfoCompat info = matches.get(i);
                 AppInfo applicationInfo = findApplicationInfoLocked(
-                        info.activityInfo.applicationInfo.packageName,
-                        info.activityInfo.name);
+                        info.getComponentName().getPackageName(), user,
+                        info.getComponentName().getShortClassName());
                 if (applicationInfo == null) {
-                    add(new AppInfo(context.getPackageManager(), info, mIconCache, null));
+                    add(new AppInfo(context, info, user, mIconCache, null));
                 } else {
-                    mIconCache.remove(applicationInfo.componentName);
+                    mIconCache.remove(applicationInfo.componentName, user);
                     mIconCache.getTitleAndIcon(applicationInfo, info, null);
                     modified.add(applicationInfo);
                 }
@@ -159,37 +168,24 @@
             for (int i = data.size() - 1; i >= 0; i--) {
                 final AppInfo applicationInfo = data.get(i);
                 final ComponentName component = applicationInfo.intent.getComponent();
-                if (packageName.equals(component.getPackageName())) {
+                if (user.equals(applicationInfo.user)
+                        && packageName.equals(component.getPackageName())) {
                     removed.add(applicationInfo);
-                    mIconCache.remove(component);
+                    mIconCache.remove(component, user);
                     data.remove(i);
                 }
             }
         }
     }
 
-    /**
-     * Query the package manager for MAIN/LAUNCHER activities in the supplied package.
-     */
-    static List<ResolveInfo> findActivitiesForPackage(Context context, String packageName) {
-        final PackageManager packageManager = context.getPackageManager();
-
-        final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
-        mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
-        mainIntent.setPackage(packageName);
-
-        final List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0);
-        return apps != null ? apps : new ArrayList<ResolveInfo>();
-    }
 
     /**
      * Returns whether <em>apps</em> contains <em>component</em>.
      */
-    private static boolean findActivity(List<ResolveInfo> apps, ComponentName component) {
-        final String className = component.getClassName();
-        for (ResolveInfo info : apps) {
-            final ActivityInfo activityInfo = info.activityInfo;
-            if (activityInfo.name.equals(className)) {
+    private static boolean findActivity(List<LauncherActivityInfoCompat> apps,
+            ComponentName component) {
+        for (LauncherActivityInfoCompat info : apps) {
+            if (info.getComponentName().equals(component)) {
                 return true;
             }
         }
@@ -197,13 +193,24 @@
     }
 
     /**
+     * Query the launcher apps service for whether the supplied package has
+     * MAIN/LAUNCHER activities in the supplied package.
+     */
+    static boolean packageHasActivities(Context context, String packageName,
+            UserHandleCompat user) {
+        final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
+        return launcherApps.getActivityList(packageName, user).size() > 0;
+    }
+
+    /**
      * Returns whether <em>apps</em> contains <em>component</em>.
      */
-    private static boolean findActivity(ArrayList<AppInfo> apps, ComponentName component) {
+    private static boolean findActivity(ArrayList<AppInfo> apps, ComponentName component,
+            UserHandleCompat user) {
         final int N = apps.size();
-        for (int i=0; i<N; i++) {
+        for (int i = 0; i < N; i++) {
             final AppInfo info = apps.get(i);
-            if (info.componentName.equals(component)) {
+            if (info.user.equals(user) && info.componentName.equals(component)) {
                 return true;
             }
         }
@@ -213,10 +220,11 @@
     /**
      * Find an ApplicationInfo object for the given packageName and className.
      */
-    private AppInfo findApplicationInfoLocked(String packageName, String className) {
+    private AppInfo findApplicationInfoLocked(String packageName, UserHandleCompat user,
+            String className) {
         for (AppInfo info: data) {
             final ComponentName component = info.intent.getComponent();
-            if (packageName.equals(component.getPackageName())
+            if (user.equals(info.user) && packageName.equals(component.getPackageName())
                     && className.equals(component.getClassName())) {
                 return info;
             }
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index c16e98f..40e8e6d 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -17,14 +17,18 @@
 package com.android.launcher3;
 
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.graphics.Bitmap;
 import android.util.Log;
 
+import com.android.launcher3.compat.LauncherActivityInfoCompat;
+import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.compat.UserHandleCompat;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -72,28 +76,24 @@
     /**
      * Must not hold the Context.
      */
-    public AppInfo(PackageManager pm, ResolveInfo info, IconCache iconCache,
-            HashMap<Object, CharSequence> labelCache) {
-        final String packageName = info.activityInfo.applicationInfo.packageName;
-
-        this.componentName = new ComponentName(packageName, info.activityInfo.name);
+    public AppInfo(Context context, LauncherActivityInfoCompat info, UserHandleCompat user,
+            IconCache iconCache, HashMap<Object, CharSequence> labelCache) {
+        this.componentName = info.getComponentName();
         this.container = ItemInfo.NO_ID;
-        this.setActivity(componentName,
-                Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
 
-        try {
-            PackageInfo pi = pm.getPackageInfo(packageName, 0);
-            flags = initFlags(pi);
-            firstInstallTime = initFirstInstallTime(pi);
-        } catch (NameNotFoundException e) {
-            Log.d(TAG, "PackageManager.getApplicationInfo failed for " + packageName);
-        }
-
+        flags = initFlags(info);
+        firstInstallTime = info.getFirstInstallTime();
         iconCache.getTitleAndIcon(this, info, labelCache);
+        intent = new Intent(Intent.ACTION_MAIN);
+        intent.addCategory(Intent.CATEGORY_LAUNCHER);
+        intent.setComponent(info.getComponentName());
+        long serialNumber = UserManagerCompat.getInstance(context).getSerialNumberForUser(user);
+        intent.putExtra(EXTRA_PROFILE, serialNumber);
+        this.user = user;
     }
 
-    public static int initFlags(PackageInfo pi) {
-        int appFlags = pi.applicationInfo.flags;
+    private static int initFlags(LauncherActivityInfoCompat info) {
+        int appFlags = info.getApplicationFlags();
         int flags = 0;
         if ((appFlags & android.content.pm.ApplicationInfo.FLAG_SYSTEM) == 0) {
             flags |= DOWNLOADED_FLAG;
@@ -105,10 +105,6 @@
         return flags;
     }
 
-    public static long initFirstInstallTime(PackageInfo pi) {
-        return pi.firstInstallTime;
-    }
-
     public AppInfo(AppInfo info) {
         super(info);
         componentName = info.componentName;
@@ -116,21 +112,7 @@
         intent = new Intent(info.intent);
         flags = info.flags;
         firstInstallTime = info.firstInstallTime;
-    }
-
-    /**
-     * Creates the application intent based on a component name and various launch flags.
-     * Sets {@link #itemType} to {@link LauncherSettings.BaseLauncherColumns#ITEM_TYPE_APPLICATION}.
-     *
-     * @param className the class name of the component representing the intent
-     * @param launchFlags the launch flags
-     */
-    final void setActivity(ComponentName className, int launchFlags) {
-        intent = new Intent(Intent.ACTION_MAIN);
-        intent.addCategory(Intent.CATEGORY_LAUNCHER);
-        intent.setComponent(className);
-        intent.setFlags(launchFlags);
-        itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_APPLICATION;
+        iconBitmap = info.iconBitmap;
     }
 
     @Override
@@ -139,7 +121,7 @@
                 + " type=" + this.itemType + " container=" + this.container
                 + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY
                 + " spanX=" + spanX + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos)
-                + ")";
+                + " user=" + user + ")";
     }
 
     public static void dumpApplicationInfoList(String tag, String label, ArrayList<AppInfo> list) {
diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java
index d6e0bb4..9d86959 100644
--- a/src/com/android/launcher3/AppsCustomizePagedView.java
+++ b/src/com/android/launcher3/AppsCustomizePagedView.java
@@ -1575,7 +1575,8 @@
         int length = list.size();
         for (int i = 0; i < length; ++i) {
             AppInfo info = list.get(i);
-            if (info.intent.getComponent().equals(removeComponent)) {
+            if (info.user.equals(item.user)
+                    && info.intent.getComponent().equals(removeComponent)) {
                 return i;
             }
         }
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 75d906b..20546b8 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -38,6 +38,9 @@
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.LinearInterpolator;
 
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.UserHandleCompat;
+
 import java.util.List;
 import java.util.Set;
 
@@ -184,6 +187,11 @@
         if (!willAcceptDrop(info) || isAllAppsWidget(source, info)) {
             isVisible = false;
         }
+        if (useUninstallLabel &&
+                !(((ItemInfo) info).user.equals(UserHandleCompat.myUserHandle()))) {
+            // Don't support uninstall for apps from other profiles.
+            isVisible = false;
+        }
 
         if (useUninstallLabel) {
             setCompoundDrawablesRelativeWithIntrinsicBounds(mUninstallDrawable, null, null, null);
@@ -279,25 +287,27 @@
         if (isAllAppsApplication(d.dragSource, item)) {
             // Uninstall the application if it is being dragged from AppsCustomize
             AppInfo appInfo = (AppInfo) item;
-            mLauncher.startApplicationUninstallActivity(appInfo.componentName, appInfo.flags);
+            // We don't support uninstalling apps from other profiles.
+            if (item.user.equals(UserHandleCompat.myUserHandle())) {
+                mLauncher.startApplicationUninstallActivity(appInfo.componentName, appInfo.flags);
+            }
         } else if (isUninstallFromWorkspace(d)) {
             ShortcutInfo shortcut = (ShortcutInfo) item;
-            if (shortcut.intent != null && shortcut.intent.getComponent() != null) {
+            // We don't support uninstalling apps from other profiles.
+            if (shortcut.intent != null && shortcut.intent.getComponent() != null &&
+                    shortcut.user.equals(UserHandleCompat.myUserHandle())) {
                 final ComponentName componentName = shortcut.intent.getComponent();
                 final DragSource dragSource = d.dragSource;
-                int flags = AppInfo.initFlags(
-                    ShortcutInfo.getPackageInfo(getContext(), componentName.getPackageName()));
                 mWaitingForUninstall =
-                    mLauncher.startApplicationUninstallActivity(componentName, flags);
+                        mLauncher.startApplicationUninstallActivity(componentName, shortcut.flags);
                 if (mWaitingForUninstall) {
                     final Runnable checkIfUninstallWasSuccess = new Runnable() {
                         @Override
                         public void run() {
                             mWaitingForUninstall = false;
                             String packageName = componentName.getPackageName();
-                            List<ResolveInfo> activities =
-                                    AllAppsList.findActivitiesForPackage(getContext(), packageName);
-                            boolean uninstallSuccessful = activities.size() == 0;
+                            boolean uninstallSuccessful = !AllAppsList.packageHasActivities(
+                                    getContext(), packageName, UserHandleCompat.myUserHandle());
                             if (dragSource instanceof Folder) {
                                 ((Folder) dragSource).
                                     onUninstallActivityReturned(uninstallSuccessful);
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index 61fa7e5..85a792f 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -17,6 +17,9 @@
 package com.android.launcher3;
 
 import android.content.ContentValues;
+import android.content.Context;
+
+import com.android.launcher3.compat.UserHandleCompat;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -40,6 +43,7 @@
 
     FolderInfo() {
         itemType = LauncherSettings.Favorites.ITEM_TYPE_FOLDER;
+        user = UserHandleCompat.myUserHandle();
     }
 
     /**
@@ -76,8 +80,8 @@
     }
 
     @Override
-    void onAddToDatabase(ContentValues values) {
-        super.onAddToDatabase(values);
+    void onAddToDatabase(Context context, ContentValues values) {
+        super.onAddToDatabase(context, values);
         values.put(LauncherSettings.Favorites.TITLE, title.toString());
     }
 
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index ee9f4d4..05be214 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -31,8 +31,14 @@
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.util.Log;
 
+import com.android.launcher3.compat.LauncherActivityInfoCompat;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.compat.UserManagerCompat;
+
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
@@ -61,11 +67,35 @@
         public String title;
     }
 
-    private final Bitmap mDefaultIcon;
+    private static class CacheKey {
+        public ComponentName componentName;
+        public UserHandleCompat user;
+
+        CacheKey(ComponentName componentName, UserHandleCompat user) {
+            this.componentName = componentName;
+            this.user = user;
+        }
+
+        @Override
+        public int hashCode() {
+            return componentName.hashCode() + user.hashCode();
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            CacheKey other = (CacheKey) o;
+            return other.componentName.equals(componentName) && other.user.equals(user);
+        }
+    }
+
+    private final HashMap<UserHandleCompat, Bitmap> mDefaultIcons =
+            new HashMap<UserHandleCompat, Bitmap>();
     private final Context mContext;
     private final PackageManager mPackageManager;
-    private final HashMap<ComponentName, CacheEntry> mCache =
-            new HashMap<ComponentName, CacheEntry>(INITIAL_ICON_CACHE_CAPACITY);
+    private final UserManagerCompat mUserManager;
+    private final LauncherAppsCompat mLauncherApps;
+    private final HashMap<CacheKey, CacheEntry> mCache =
+            new HashMap<CacheKey, CacheEntry>(INITIAL_ICON_CACHE_CAPACITY);
     private int mIconDpi;
 
     public IconCache(Context context) {
@@ -74,10 +104,13 @@
 
         mContext = context;
         mPackageManager = context.getPackageManager();
+        mUserManager = UserManagerCompat.getInstance(mContext);
+        mLauncherApps = LauncherAppsCompat.getInstance(mContext);
         mIconDpi = activityManager.getLauncherLargeIconDensity();
 
         // need to set mIconDpi before getting default icon
-        mDefaultIcon = makeDefaultIcon();
+        UserHandleCompat myUser = UserHandleCompat.myUserHandle();
+        mDefaultIcons.put(myUser, makeDefaultIcon(myUser));
     }
 
     public Drawable getFullResDefaultActivityIcon() {
@@ -134,8 +167,9 @@
         return getFullResDefaultActivityIcon();
     }
 
-    private Bitmap makeDefaultIcon() {
-        Drawable d = getFullResDefaultActivityIcon();
+    private Bitmap makeDefaultIcon(UserHandleCompat user) {
+        Drawable unbadged = getFullResDefaultActivityIcon();
+        Drawable d = mUserManager.getBadgedDrawableForUser(unbadged, user);
         Bitmap b = Bitmap.createBitmap(Math.max(d.getIntrinsicWidth(), 1),
                 Math.max(d.getIntrinsicHeight(), 1),
                 Bitmap.Config.ARGB_8888);
@@ -149,24 +183,25 @@
     /**
      * Remove any records for the supplied ComponentName.
      */
-    public void remove(ComponentName componentName) {
+    public void remove(ComponentName componentName, UserHandleCompat user) {
         synchronized (mCache) {
-            mCache.remove(componentName);
+            mCache.remove(new CacheKey(componentName, user));
         }
     }
 
     /**
      * Remove any records for the supplied package name.
      */
-    public void remove(String packageName) {
-        HashSet<ComponentName> forDeletion = new HashSet<ComponentName>();
-        for (ComponentName componentName: mCache.keySet()) {
-            if (componentName.getPackageName().equals(packageName)) {
-                forDeletion.add(componentName);
+    public void remove(String packageName, UserHandleCompat user) {
+        HashSet<CacheKey> forDeletion = new HashSet<CacheKey>();
+        for (CacheKey key: mCache.keySet()) {
+            if (key.componentName.getPackageName().equals(packageName)
+                    && key.user.equals(user)) {
+                forDeletion.add(key);
             }
         }
-        for (ComponentName condemned: forDeletion) {
-            remove(condemned);
+        for (CacheKey condemned: forDeletion) {
+            mCache.remove(condemned);
         }
     }
 
@@ -184,7 +219,7 @@
      */
     public void flushInvalidIcons(DeviceProfile grid) {
         synchronized (mCache) {
-            Iterator<Entry<ComponentName, CacheEntry>> it = mCache.entrySet().iterator();
+            Iterator<Entry<CacheKey, CacheEntry>> it = mCache.entrySet().iterator();
             while (it.hasNext()) {
                 final CacheEntry e = it.next().getValue();
                 if (e.icon.getWidth() < grid.iconSizePx || e.icon.getHeight() < grid.iconSizePx) {
@@ -197,30 +232,32 @@
     /**
      * Fill in "application" with the icon and label for "info."
      */
-    public void getTitleAndIcon(AppInfo application, ResolveInfo info,
+    public void getTitleAndIcon(AppInfo application, LauncherActivityInfoCompat info,
             HashMap<Object, CharSequence> labelCache) {
         synchronized (mCache) {
-            CacheEntry entry = cacheLocked(application.componentName, info, labelCache);
+            CacheEntry entry = cacheLocked(application.componentName, info, labelCache,
+                    info.getUser());
 
             application.title = entry.title;
             application.iconBitmap = entry.icon;
         }
     }
 
-    public Bitmap getIcon(Intent intent) {
-        return getIcon(intent, null);
+    public Bitmap getIcon(Intent intent, UserHandleCompat user) {
+        return getIcon(intent, null, user);
     }
 
-    public Bitmap getIcon(Intent intent, String title) {
+    public Bitmap getIcon(Intent intent, String title, UserHandleCompat user) {
         synchronized (mCache) {
-            final ResolveInfo resolveInfo = mPackageManager.resolveActivity(intent, 0);
+            final LauncherActivityInfoCompat launcherActInfo =
+                    mLauncherApps.resolveActivity(intent, user);
             ComponentName component = intent.getComponent();
 
-            if (component == null) {
-                return mDefaultIcon;
+            if (launcherActInfo == null || component == null) {
+                return getDefaultIcon(user);
             }
 
-            CacheEntry entry = cacheLocked(component, resolveInfo, null);
+            CacheEntry entry = cacheLocked(component, launcherActInfo, null, user);
             if (title != null) {
                 entry.title = title;
             }
@@ -228,49 +265,54 @@
         }
     }
 
-    public Bitmap getIcon(ComponentName component, ResolveInfo resolveInfo,
+    public Bitmap getDefaultIcon(UserHandleCompat user) {
+        if (!mDefaultIcons.containsKey(user)) {
+            mDefaultIcons.put(user, makeDefaultIcon(user));
+        }
+        return mDefaultIcons.get(user);
+    }
+
+    public Bitmap getIcon(ComponentName component, LauncherActivityInfoCompat info,
             HashMap<Object, CharSequence> labelCache) {
         synchronized (mCache) {
-            if (resolveInfo == null || component == null) {
+            if (info == null || component == null) {
                 return null;
             }
 
-            CacheEntry entry = cacheLocked(component, resolveInfo, labelCache);
+            CacheEntry entry = cacheLocked(component, info, labelCache, info.getUser());
             return entry.icon;
         }
     }
 
-    public boolean isDefaultIcon(Bitmap icon) {
-        return mDefaultIcon == icon;
+    public boolean isDefaultIcon(Bitmap icon, UserHandleCompat user) {
+        return mDefaultIcons.get(user) == icon;
     }
 
-    private CacheEntry cacheLocked(ComponentName componentName, ResolveInfo info,
-            HashMap<Object, CharSequence> labelCache) {
-        CacheEntry entry = mCache.get(componentName);
+    private CacheEntry cacheLocked(ComponentName componentName, LauncherActivityInfoCompat info,
+            HashMap<Object, CharSequence> labelCache, UserHandleCompat user) {
+        CacheKey cacheKey = new CacheKey(componentName, user);
+        CacheEntry entry = mCache.get(cacheKey);
         if (entry == null) {
             entry = new CacheEntry();
 
-            mCache.put(componentName, entry);
+            mCache.put(cacheKey, entry);
 
             if (info != null) {
-                ComponentName key = LauncherModel.getComponentNameFromResolveInfo(info);
-                if (labelCache != null && labelCache.containsKey(key)) {
-                    entry.title = labelCache.get(key).toString();
+                ComponentName labelKey = info.getComponentName();
+                if (labelCache != null && labelCache.containsKey(labelKey)) {
+                    entry.title = labelCache.get(labelKey).toString();
                 } else {
-                    entry.title = info.loadLabel(mPackageManager).toString();
+                    entry.title = info.getLabel().toString();
                     if (labelCache != null) {
-                        labelCache.put(key, entry.title);
+                        labelCache.put(labelKey, entry.title);
                     }
                 }
-                if (entry.title == null) {
-                    entry.title = info.activityInfo.name;
-                }
 
                 entry.icon = Utilities.createIconBitmap(
-                        getFullResIcon(info), mContext);
+                        info.getBadgedIcon(mIconDpi), mContext);
             } else {
                 entry.title = "";
-                Bitmap preloaded = getPreloadedIcon(componentName);
+                Bitmap preloaded = getPreloadedIcon(componentName, user);
                 if (preloaded != null) {
                     if (DEBUG) Log.d(TAG, "using preloaded icon for " +
                             componentName.toShortString());
@@ -278,7 +320,7 @@
                 } else {
                     if (DEBUG) Log.d(TAG, "using default icon for " +
                             componentName.toShortString());
-                    entry.icon = mDefaultIcon;
+                    entry.icon = getDefaultIcon(user);
                 }
             }
         }
@@ -288,9 +330,9 @@
     public HashMap<ComponentName,Bitmap> getAllIcons() {
         synchronized (mCache) {
             HashMap<ComponentName,Bitmap> set = new HashMap<ComponentName,Bitmap>();
-            for (ComponentName cn : mCache.keySet()) {
-                final CacheEntry e = mCache.get(cn);
-                set.put(cn, e.icon);
+            for (CacheKey ck : mCache.keySet()) {
+                final CacheEntry e = mCache.get(ck);
+                set.put(ck.componentName, e.icon);
             }
             return set;
         }
@@ -353,9 +395,14 @@
      * @param componentName the component that should own the icon
      * @returns a bitmap if one is cached, or null.
      */
-    private Bitmap getPreloadedIcon(ComponentName componentName) {
+    private Bitmap getPreloadedIcon(ComponentName componentName, UserHandleCompat user) {
         final String key = componentName.flattenToShortString();
 
+        // We don't keep icons for other profiles in persistent cache.
+        if (!user.equals(UserHandleCompat.myUserHandle())) {
+            return null;
+        }
+
         if (DEBUG) Log.v(TAG, "looking for pre-load icon for " + key);
         Bitmap icon = null;
         FileInputStream resourceFile = null;
@@ -396,7 +443,11 @@
      * @param componentName the component that should own the icon
      * @returns true on success
      */
-    public boolean deletePreloadedIcon(ComponentName componentName) {
+    public boolean deletePreloadedIcon(ComponentName componentName, UserHandleCompat user) {
+        // We don't keep icons for other profiles in persistent cache.
+        if (!user.equals(UserHandleCompat.myUserHandle())) {
+            return false;
+        }
         if (componentName == null) {
             return false;
         }
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index 12bbee7..74f16e3 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -17,10 +17,14 @@
 package com.android.launcher3;
 
 import android.content.ContentValues;
+import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.util.Log;
 
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.compat.UserManagerCompat;
+
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.util.Arrays;
@@ -29,6 +33,11 @@
  * Represents an item in the launcher.
  */
 public class ItemInfo {
+
+    /**
+     * Intent extra to store the profile. Format: UserHandle
+     */
+    static final String EXTRA_PROFILE = "profile";
     
     static final int NO_ID = -1;
     
@@ -103,6 +112,8 @@
      */
     int[] dropPos = null;
 
+    UserHandleCompat user;
+
     ItemInfo() {
     }
 
@@ -115,6 +126,7 @@
         screenId = info.screenId;
         itemType = info.itemType;
         container = info.container;
+        user = info.user;
         // tempdebug:
         LauncherModel.checkItemInfo(this);
     }
@@ -130,9 +142,11 @@
     /**
      * Write the fields of this item to the DB
      * 
+     * @param context A context object to use for getting UserManagerCompat
      * @param values
      */
-    void onAddToDatabase(ContentValues values) {
+
+    void onAddToDatabase(Context context, ContentValues values) {
         values.put(LauncherSettings.BaseLauncherColumns.ITEM_TYPE, itemType);
         values.put(LauncherSettings.Favorites.CONTAINER, container);
         values.put(LauncherSettings.Favorites.SCREEN, screenId);
@@ -140,6 +154,8 @@
         values.put(LauncherSettings.Favorites.CELLY, cellY);
         values.put(LauncherSettings.Favorites.SPANX, spanX);
         values.put(LauncherSettings.Favorites.SPANY, spanY);
+        long serialNumber = UserManagerCompat.getInstance(context).getSerialNumberForUser(user);
+        values.put(LauncherSettings.Favorites.PROFILE_ID, serialNumber);
 
         if (screenId == Workspace.EXTRA_EMPTY_SCREEN_ID) {
             // We should never persist an item on the extra empty screen.
@@ -188,6 +204,7 @@
     public String toString() {
         return "Item(id=" + this.id + " type=" + this.itemType + " container=" + this.container
             + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX
-            + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos) + ")";
+            + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos)
+            + " user=" + user + ")";
     }
 }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index ff5b1eb..2172194 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -95,6 +95,10 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
+import com.android.launcher3.compat.LauncherActivityInfoCompat;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.PagedView.PageSwitchListener;
 
@@ -134,7 +138,6 @@
 
     private static final int REQUEST_CREATE_SHORTCUT = 1;
     private static final int REQUEST_CREATE_APPWIDGET = 5;
-    private static final int REQUEST_PICK_APPLICATION = 6;
     private static final int REQUEST_PICK_SHORTCUT = 7;
     private static final int REQUEST_PICK_APPWIDGET = 9;
     private static final int REQUEST_PICK_WALLPAPER = 10;
@@ -734,10 +737,6 @@
     private boolean completeAdd(PendingAddArguments args) {
         boolean result = false;
         switch (args.requestCode) {
-            case REQUEST_PICK_APPLICATION:
-                completeAddApplication(args.intent, args.container, args.screenId, args.cellX,
-                        args.cellY);
-                break;
             case REQUEST_PICK_SHORTCUT:
                 processShortcut(args.intent);
                 break;
@@ -1368,38 +1367,6 @@
     }
 
     /**
-     * Add an application shortcut to the workspace.
-     *
-     * @param data The intent describing the application.
-     * @param cellInfo The position on screen where to create the shortcut.
-     */
-    void completeAddApplication(Intent data, long container, long screenId, int cellX, int cellY) {
-        final int[] cellXY = mTmpAddItemCellCoordinates;
-        final CellLayout layout = getCellLayout(container, screenId);
-
-        // First we check if we already know the exact location where we want to add this item.
-        if (cellX >= 0 && cellY >= 0) {
-            cellXY[0] = cellX;
-            cellXY[1] = cellY;
-        } else if (!layout.findCellForSpan(cellXY, 1, 1)) {
-            showOutOfSpaceMessage(isHotseatLayout(layout));
-            return;
-        }
-
-        final ShortcutInfo info = mModel.getShortcutInfo(getPackageManager(), data, this);
-
-        if (info != null) {
-            info.setActivity(this, data.getComponent(), Intent.FLAG_ACTIVITY_NEW_TASK |
-                    Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-            info.container = ItemInfo.NO_ID;
-            mWorkspace.addApplicationShortcut(info, layout, container, screenId, cellXY[0], cellXY[1],
-                    isWorkspaceLocked(), cellX, cellY);
-        } else {
-            Log.e(TAG, "Couldn't find ActivityInfo for selected application: " + data);
-        }
-    }
-
-    /**
      * Add a shortcut to the workspace.
      *
      * @param data The intent describing the shortcut.
@@ -2220,21 +2187,7 @@
     }
 
     void processShortcut(Intent intent) {
-        // Handle case where user selected "Applications"
-        String applicationName = getResources().getString(R.string.group_applications);
-        String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
-
-        if (applicationName != null && applicationName.equals(shortcutName)) {
-            Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
-            mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
-
-            Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
-            pickIntent.putExtra(Intent.EXTRA_INTENT, mainIntent);
-            pickIntent.putExtra(Intent.EXTRA_TITLE, getText(R.string.title_select_application));
-            Utilities.startActivityForResultSafely(this, pickIntent, REQUEST_PICK_APPLICATION);
-        } else {
-            Utilities.startActivityForResultSafely(this, intent, REQUEST_CREATE_SHORTCUT);
-        }
+        Utilities.startActivityForResultSafely(this, intent, REQUEST_CREATE_SHORTCUT);
     }
 
     void processWallpaper(Intent intent) {
@@ -2651,18 +2604,29 @@
     }
 
     boolean startActivity(View v, Intent intent, Object tag) {
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
         try {
             // Only launch using the new animation if the shortcut has not opted out (this is a
             // private contract between launcher and may be ignored in the future).
             boolean useLaunchAnimation = (v != null) &&
                     !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
+            LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(this);
+            UserManagerCompat userManager = UserManagerCompat.getInstance(this);
+            long serialNumber = intent.getLongExtra(AppInfo.EXTRA_PROFILE, 0);
+            UserHandleCompat user = serialNumber == 0 ? null :
+                    userManager.getUserForSerialNumber(serialNumber);
+
             if (useLaunchAnimation) {
                 ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(v, 0, 0,
                         v.getMeasuredWidth(), v.getMeasuredHeight());
-
-                startActivity(intent, opts.toBundle());
+                if (user == null || user.equals(UserHandleCompat.myUserHandle())) {
+                    // Could be launching some bookkeeping activity
+                    startActivity(intent, opts.toBundle());
+                } else {
+                    launcherApps.startActivityForProfile(intent.getComponent(),
+                            intent.getSourceBounds(),
+                            opts.toBundle(), user);
+                }
             } else {
                 startActivity(intent);
             }
@@ -4270,10 +4234,10 @@
      * Implementation of the method from LauncherModel.Callbacks.
      */
     public void bindComponentsRemoved(final ArrayList<String> packageNames,
-                                      final ArrayList<AppInfo> appInfos) {
+            final ArrayList<AppInfo> appInfos, final UserHandleCompat user) {
         Runnable r = new Runnable() {
             public void run() {
-                bindComponentsRemoved(packageNames, appInfos);
+                bindComponentsRemoved(packageNames, appInfos, user);
             }
         };
         if (waitUntilResume(r)) {
@@ -4281,10 +4245,10 @@
         }
 
         if (!packageNames.isEmpty()) {
-            mWorkspace.removeItemsByPackageName(packageNames);
+            mWorkspace.removeItemsByPackageName(packageNames, user);
         }
         if (!appInfos.isEmpty()) {
-            mWorkspace.removeItemsByApplicationInfo(appInfos);
+            mWorkspace.removeItemsByApplicationInfo(appInfos, user);
         }
 
         // Notify the drag controller
@@ -4555,16 +4519,27 @@
     }
 
     public ItemInfo createAppDragInfo(Intent appLaunchIntent) {
-        ResolveInfo ri = getPackageManager().resolveActivity(appLaunchIntent, 0);
-        if (ri == null) {
+        // Called from search suggestion, not supported in other profiles.
+        final UserHandleCompat myUser = UserHandleCompat.myUserHandle();
+        LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(this);
+        LauncherActivityInfoCompat activityInfo = launcherApps.resolveActivity(appLaunchIntent,
+                myUser);
+        if (activityInfo == null) {
             return null;
         }
-        return new AppInfo(getPackageManager(), ri, mIconCache, null);
+        return new AppInfo(this, activityInfo, myUser, mIconCache, null);
     }
 
     public ItemInfo createShortcutDragInfo(Intent shortcutIntent, CharSequence caption,
             Bitmap icon) {
-        return new ShortcutInfo(shortcutIntent, caption, icon);
+        // Called from search suggestion, not supported in other profiles.
+        return createShortcutDragInfo(shortcutIntent, caption, icon,
+                UserHandleCompat.myUserHandle());
+    }
+
+    public ItemInfo createShortcutDragInfo(Intent shortcutIntent, CharSequence caption,
+            Bitmap icon, UserHandleCompat user) {
+        return new ShortcutInfo(shortcutIntent, caption, icon, user);
     }
 
     protected void moveWorkspaceToDefaultScreen() {
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index ad01019..0725a65 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -24,6 +24,8 @@
 import android.os.Handler;
 import android.util.Log;
 
+import com.android.launcher3.compat.LauncherAppsCompat;
+
 import java.lang.ref.WeakReference;
 
 public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks {
@@ -92,16 +94,11 @@
         mAppFilter = AppFilter.loadByName(sContext.getString(R.string.app_filter_class));
         mBuildInfo = BuildInfo.loadByName(sContext.getString(R.string.build_info_class));
         mModel = new LauncherModel(this, mIconCache, mAppFilter);
+        final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(sContext);
+        launcherApps.addOnAppsChangedListener(mModel);
 
         // Register intent receivers
-        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
-        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-        filter.addDataScheme("package");
-        sContext.registerReceiver(mModel, filter);
-        filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
-        filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+        IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_LOCALE_CHANGED);
         filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
         sContext.registerReceiver(mModel, filter);
diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java
index 950828e..bec1f9e 100644
--- a/src/com/android/launcher3/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java
@@ -19,6 +19,9 @@
 import android.appwidget.AppWidgetHostView;
 import android.content.ComponentName;
 import android.content.ContentValues;
+import android.content.Context;
+
+import com.android.launcher3.compat.UserHandleCompat;
 
 /**
  * Represents a widget (either instantiated or about to be) in the Launcher.
@@ -59,11 +62,13 @@
         // to indicate that they should be calculated based on the layout and minWidth/minHeight
         spanX = -1;
         spanY = -1;
+        // We only support app widgets on current user.
+        user = UserHandleCompat.myUserHandle();
     }
 
     @Override
-    void onAddToDatabase(ContentValues values) {
-        super.onAddToDatabase(values);
+    void onAddToDatabase(Context context, ContentValues values) {
+        super.onAddToDatabase(context, values);
         values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId);
         values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER, providerName.flattenToString());
     }
diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java
index cab55c7..275fccc 100644
--- a/src/com/android/launcher3/LauncherBackupHelper.java
+++ b/src/com/android/launcher3/LauncherBackupHelper.java
@@ -28,6 +28,8 @@
 import com.android.launcher3.backup.BackupProtos.Resource;
 import com.android.launcher3.backup.BackupProtos.Screen;
 import com.android.launcher3.backup.BackupProtos.Widget;
+import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.compat.UserHandleCompat;
 
 import android.app.backup.BackupDataInputStream;
 import android.app.backup.BackupDataOutput;
@@ -459,10 +461,15 @@
         Set<String> savedIds = getSavedIdsByType(Key.ICON, in);
         if (DEBUG) Log.d(TAG, "icon savedIds.size()=" + savedIds.size());
 
+        // Don't backup apps in other profiles for now.
+        UserHandleCompat myUserHandle = UserHandleCompat.myUserHandle();
+        long userSerialNumber =
+                UserManagerCompat.getInstance(mContext).getSerialNumberForUser(myUserHandle);
         int startRows = out.rows;
         if (DEBUG) Log.d(TAG, "starting here: " + startRows);
-        String where = Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_APPLICATION + " OR " +
-                Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_SHORTCUT;
+        String where = "(" + Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_APPLICATION + " OR " +
+                Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_SHORTCUT + ") AND" +
+                Favorites.PROFILE_ID + "=" + userSerialNumber;
         Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION,
                 where, null, null);
         Set<String> currentIds = new HashSet<String>(cursor.getCount());
@@ -492,9 +499,9 @@
                         if (DEBUG) Log.d(TAG, "I can count this high: " + out.rows);
                         if ((out.rows - startRows) < MAX_ICONS_PER_PASS) {
                             if (VERBOSE) Log.v(TAG, "saving icon " + backupKey);
-                            Bitmap icon = mIconCache.getIcon(intent);
+                            Bitmap icon = mIconCache.getIcon(intent, myUserHandle);
                             keys.add(key);
-                            if (icon != null && !mIconCache.isDefaultIcon(icon)) {
+                            if (icon != null && !mIconCache.isDefaultIcon(icon, myUserHandle)) {
                                 byte[] blob = packIcon(dpi, icon);
                                 writeRowToBackup(key, blob, out, data);
                             }
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 044ddbb..15db057 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -44,6 +44,10 @@
 import android.util.Log;
 import android.util.Pair;
 
+import com.android.launcher3.compat.LauncherActivityInfoCompat;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.InstallWidgetReceiver.WidgetMimeTypeHandlerData;
 
 import java.lang.ref.WeakReference;
@@ -67,7 +71,8 @@
  * LauncherModel object held in a static. Also provide APIs for updating the database state
  * for the Launcher.
  */
-public class LauncherModel extends BroadcastReceiver {
+public class LauncherModel extends BroadcastReceiver
+        implements LauncherAppsCompat.OnAppsChangedListenerCompat {
     static final boolean DEBUG_LOADERS = false;
     private static final boolean DEBUG_RECEIVER = true;  // STOPSHIP(cwren) temporary for debugging
 
@@ -153,10 +158,12 @@
     // </ only access in worker thread >
 
     private IconCache mIconCache;
-    private Bitmap mDefaultIcon;
 
     protected int mPreviousConfigMcc;
 
+    private final LauncherAppsCompat mLauncherApps;
+    private final UserManagerCompat mUserManager;
+
     public interface Callbacks {
         public boolean setLoadOnResume();
         public int getCurrentWorkspaceScreen();
@@ -176,7 +183,7 @@
         public void bindAppsUpdated(ArrayList<AppInfo> apps);
         public void updatePackageState(String pkgName, int state);
         public void bindComponentsRemoved(ArrayList<String> packageNames,
-                        ArrayList<AppInfo> appInfos);
+                        ArrayList<AppInfo> appInfos, UserHandleCompat user);
         public void bindPackagesUpdated(ArrayList<Object> widgetsAndShortcuts);
         public void bindSearchablesChanged();
         public boolean isAllAppsButtonRank(int rank);
@@ -206,6 +213,8 @@
         final Resources res = context.getResources();
         Configuration config = res.getConfiguration();
         mPreviousConfigMcc = config.mcc;
+        mLauncherApps = LauncherAppsCompat.getInstance(context);
+        mUserManager = UserManagerCompat.getInstance(context);
     }
 
     /** Runs the specified runnable immediately if called from the main thread, otherwise it is
@@ -325,7 +334,7 @@
         Iterator<AppInfo> iter = allAppsApps.iterator();
         while (iter.hasNext()) {
             ItemInfo a = iter.next();
-            if (LauncherModel.appWasRestored(ctx, a.getIntent())) {
+            if (LauncherModel.appWasRestored(ctx, a.getIntent(), a.user)) {
                 restoredAppsFinal.add((AppInfo) a);
             }
         }
@@ -341,7 +350,8 @@
                                 for (AppInfo info : restoredAppsFinal) {
                                     final Intent intent = info.getIntent();
                                     if (intent != null) {
-                                        mIconCache.deletePreloadedIcon(intent.getComponent());
+                                        mIconCache.deletePreloadedIcon(intent.getComponent(),
+                                                info.user);
                                     }
                                 }
                                 callbacks.bindAppsUpdated(restoredAppsFinal);
@@ -393,7 +403,7 @@
                         if (LauncherModel.shortcutExists(context, name, launchIntent)) {
                             // Only InstallShortcutReceiver sends us shortcutInfos, ignore them
                             if (a instanceof AppInfo &&
-                                    LauncherModel.appWasRestored(context, launchIntent)) {
+                                    LauncherModel.appWasRestored(context, launchIntent, a.user)) {
                                 restoredAppsFinal.add((AppInfo) a);
                             }
                             continue;
@@ -483,15 +493,6 @@
         runOnWorkerThread(r);
     }
 
-    public Bitmap getFallbackIcon() {
-        if (mDefaultIcon == null) {
-            final Context context = LauncherAppState.getInstance().getContext();
-            mDefaultIcon = Utilities.createIconBitmap(
-                    mIconCache.getFullResDefaultActivityIcon(), context);
-        }
-        return Bitmap.createBitmap(mDefaultIcon);
-    }
-
     public void unbindItemInfosAndClearQueuedBindRunnables() {
         if (sWorkerThread.getThreadId() == Process.myTid()) {
             throw new RuntimeException("Expected unbindLauncherItemInfos() to be called from the " +
@@ -817,7 +818,7 @@
      */
     static void updateItemInDatabase(Context context, final ItemInfo item) {
         final ContentValues values = new ContentValues();
-        item.onAddToDatabase(values);
+        item.onAddToDatabase(context, values);
         item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
         updateItemInDatabaseHelper(context, values, item, "updateItemInDatabase");
     }
@@ -842,18 +843,22 @@
 
     /**
      * Returns true if the shortcuts already exists in the database.
-     * we identify a shortcut by the component name of the intent.
+     * we identify a shortcut by the component name of the intent
+     * and the user.
      */
-    static boolean appWasRestored(Context context, Intent intent) {
+    static boolean appWasRestored(Context context, Intent intent, UserHandleCompat user) {
         final ContentResolver cr = context.getContentResolver();
         final ComponentName component = intent.getComponent();
         if (component == null) {
             return false;
         }
         String componentName = component.flattenToString();
-        final String where = "intent glob \"*component=" + componentName + "*\" and restored = 1";
+        long serialNumber = UserManagerCompat.getInstance(context)
+                .getSerialNumberForUser(user);
+        final String where = "intent glob \"*component=" + componentName + "*\" and restored = 1"
+                + " and profileId = " + serialNumber;
         Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
-                new String[]{"intent", "restored"}, where, null, null);
+                new String[]{"intent", "restored", "profileId"}, where, null, null);
         boolean result = false;
         try {
             result = c.moveToFirst();
@@ -873,8 +878,10 @@
         final ContentResolver cr = context.getContentResolver();
         Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, new String[] {
                 LauncherSettings.Favorites.ITEM_TYPE, LauncherSettings.Favorites.CONTAINER,
-                LauncherSettings.Favorites.SCREEN, LauncherSettings.Favorites.CELLX, LauncherSettings.Favorites.CELLY,
-                LauncherSettings.Favorites.SPANX, LauncherSettings.Favorites.SPANY }, null, null, null);
+                LauncherSettings.Favorites.SCREEN,
+                LauncherSettings.Favorites.CELLX, LauncherSettings.Favorites.CELLY,
+                LauncherSettings.Favorites.SPANX, LauncherSettings.Favorites.SPANY,
+                LauncherSettings.Favorites.PROFILE_ID }, null, null, null);
 
         final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
         final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
@@ -883,7 +890,8 @@
         final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
         final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
         final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
-
+        final int profileIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.PROFILE_ID);
+        UserManagerCompat userManager = UserManagerCompat.getInstance(context);
         try {
             while (c.moveToNext()) {
                 ItemInfo item = new ItemInfo();
@@ -894,8 +902,12 @@
                 item.container = c.getInt(containerIndex);
                 item.itemType = c.getInt(itemTypeIndex);
                 item.screenId = c.getInt(screenIndex);
-
-                items.add(item);
+                int serialNumber = c.getInt(profileIdIndex);
+                item.user = userManager.getUserForSerialNumber(serialNumber);
+                // Skip if user has been deleted.
+                if (item.user != null) {
+                    items.add(item);
+                }
             }
         } catch (Exception e) {
             items.clear();
@@ -968,7 +980,7 @@
 
         final ContentValues values = new ContentValues();
         final ContentResolver cr = context.getContentResolver();
-        item.onAddToDatabase(values);
+        item.onAddToDatabase(context, values);
 
         item.id = LauncherAppState.getLauncherProvider().generateNewItemId();
         values.put(LauncherSettings.Favorites._ID, item.id);
@@ -1158,6 +1170,57 @@
         }
     }
 
+    @Override
+    public void onPackageChanged(UserHandleCompat user, String packageName) {
+        int op = PackageUpdatedTask.OP_UPDATE;
+        enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
+                user));
+    }
+
+    @Override
+    public void onPackageRemoved(UserHandleCompat user, String packageName) {
+        int op = PackageUpdatedTask.OP_REMOVE;
+        enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
+                user));
+    }
+
+    @Override
+    public void onPackageAdded(UserHandleCompat user, String packageName) {
+        int op = PackageUpdatedTask.OP_ADD;
+        enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
+                user));
+    }
+
+    @Override
+    public void onPackagesAvailable(UserHandleCompat user, String[] packageNames,
+            boolean replacing) {
+        if (!replacing) {
+            enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_ADD, packageNames,
+                    user));
+            if (mAppsCanBeOnRemoveableStorage) {
+                // Only rebind if we support removable storage. It catches the
+                // case where
+                // apps on the external sd card need to be reloaded
+                startLoaderFromBackground();
+            }
+        } else {
+            // If we are replacing then just update the packages in the list
+            enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE,
+                    packageNames, user));
+        }
+    }
+
+    @Override
+    public void onPackagesUnavailable(UserHandleCompat user, String[] packageNames,
+            boolean replacing) {
+        if (!replacing) {
+            enqueuePackageUpdated(new PackageUpdatedTask(
+                    PackageUpdatedTask.OP_UNAVAILABLE, packageNames,
+                    user));
+        }
+
+    }
+
     /**
      * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
      * ACTION_PACKAGE_CHANGED.
@@ -1167,65 +1230,7 @@
         if (DEBUG_RECEIVER) Log.d(TAG, "onReceive intent=" + intent);
 
         final String action = intent.getAction();
-
-        if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
-                || Intent.ACTION_PACKAGE_REMOVED.equals(action)
-                || Intent.ACTION_PACKAGE_ADDED.equals(action)) {
-            final String packageName = intent.getData().getSchemeSpecificPart();
-            final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
-
-            int op = PackageUpdatedTask.OP_NONE;
-
-            if (packageName == null || packageName.length() == 0) {
-                // they sent us a bad intent
-                return;
-            }
-
-            if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
-                op = PackageUpdatedTask.OP_UPDATE;
-            } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
-                if (!replacing) {
-                    op = PackageUpdatedTask.OP_REMOVE;
-                }
-                // else, we are replacing the package, so a PACKAGE_ADDED will be sent
-                // later, we will update the package at this time
-            } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
-                if (!replacing) {
-                    op = PackageUpdatedTask.OP_ADD;
-                } else {
-                    op = PackageUpdatedTask.OP_UPDATE;
-                }
-            }
-
-            if (op != PackageUpdatedTask.OP_NONE) {
-                enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName }));
-            }
-
-        } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
-            final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
-            String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
-            if (!replacing) {
-                enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_ADD, packages));
-                if (mAppsCanBeOnRemoveableStorage) {
-                    // Only rebind if we support removable storage.  It catches the case where
-                    // apps on the external sd card need to be reloaded
-                    startLoaderFromBackground();
-                }
-            } else {
-                // If we are replacing then just update the packages in the list
-                enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE,
-                        packages));
-            }
-        } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
-            final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
-            if (!replacing) {
-                String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
-                enqueuePackageUpdated(new PackageUpdatedTask(
-                            PackageUpdatedTask.OP_UNAVAILABLE, packages));
-            }
-            // else, we are replacing the packages, so ignore this event and wait for
-            // EXTERNAL_APPLICATIONS_AVAILABLE to update the packages at that time
-        } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
+        if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
             // If we have changed locale we need to clear out the labels in all apps/workspace.
             forceReload();
         } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
@@ -1659,7 +1664,7 @@
             ArrayList<ItemInfo> added = new ArrayList<ItemInfo>();
             synchronized (sBgLock) {
                 for (AppInfo app : mBgAllAppsList.data) {
-                    tmpInfos = getItemInfoForComponentName(app.componentName);
+                    tmpInfos = getItemInfoForComponentName(app.componentName, app.user);
                     if (tmpInfos.isEmpty()) {
                         // We are missing an application icon, so add this to the workspace
                         added.add(app);
@@ -1864,6 +1869,8 @@
                             LauncherSettings.Favorites.SPANY);
                     final int restoredIndex = c.getColumnIndexOrThrow(
                             LauncherSettings.Favorites.RESTORED);
+                    final int profileIdIndex = c.getColumnIndexOrThrow(
+                            LauncherSettings.Favorites.PROFILE_ID);
                     //final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
                     //final int displayModeIndex = c.getColumnIndexOrThrow(
                     //        LauncherSettings.Favorites.DISPLAY_MODE);
@@ -1874,6 +1881,7 @@
                     int container;
                     long id;
                     Intent intent;
+                    UserHandleCompat user;
 
                     while (!mStopped && c.moveToNext()) {
                         AtomicBoolean deleteOnInvalidPlacement = new AtomicBoolean(false);
@@ -1886,10 +1894,17 @@
                             case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                                 id = c.getLong(idIndex);
                                 intentDescription = c.getString(intentIndex);
+                                int serialNumber = c.getInt(profileIdIndex);
+                                user = mUserManager.getUserForSerialNumber(serialNumber);
+                                if (user == null) {
+                                    // User has been deleted remove the item.
+                                    itemsToRemove.add(id);
+                                    continue;
+                                }
                                 try {
                                     intent = Intent.parseUri(intentDescription, 0);
                                     ComponentName cn = intent.getComponent();
-                                    if (cn != null && !isValidPackageComponent(manager, cn)) {
+                                    if (cn != null && !isValidPackageActivity(context, cn, user)) {
                                         if (restored) {
                                             // might be installed later
                                             Launcher.addDumpLog(TAG,
@@ -1921,14 +1936,20 @@
                                 }
 
                                 if (restored) {
-                                    Launcher.addDumpLog(TAG,
-                                            "constructing info for partially restored package",
-                                            true);
-                                    info = getRestoredItemInfo(c, titleIndex, intent);
-                                    intent = getRestoredItemIntent(c, context, intent);
+                                    if (user.equals(UserHandleCompat.myUserHandle())) {
+                                        Launcher.addDumpLog(TAG,
+                                                "constructing info for partially restored package",
+                                                true);
+                                        info = getRestoredItemInfo(c, titleIndex, intent);
+                                        intent = getRestoredItemIntent(c, context, intent);
+                                    } else {
+                                        // Don't restore items for other profiles.
+                                        itemsToRemove.add(id);
+                                        continue;
+                                    }
                                 } else if (itemType ==
                                         LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
-                                    info = getShortcutInfo(manager, intent, context, c, iconIndex,
+                                    info = getShortcutInfo(manager, intent, user, context, c, iconIndex,
                                             titleIndex, mLabelCache);
                                 } else {
                                     info = getShortcutInfo(c, context, iconTypeIndex,
@@ -1958,6 +1979,7 @@
                                     info.cellY = c.getInt(cellYIndex);
                                     info.spanX = 1;
                                     info.spanY = 1;
+                                    info.intent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber);
 
                                     // check & update map of what's occupied
                                     deleteOnInvalidPlacement.set(false);
@@ -2612,42 +2634,42 @@
                 return;
             }
 
-            final PackageManager packageManager = mContext.getPackageManager();
             final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
             mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
 
+            final List<UserHandleCompat> profiles = mUserManager.getUserProfiles();
+
             // Clear the list of apps
             mBgAllAppsList.clear();
+            for (UserHandleCompat user : profiles) {
+                // Query for the set of apps
+                final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
+                List<LauncherActivityInfoCompat> apps = mLauncherApps.getActivityList(null, user);
+                if (DEBUG_LOADERS) {
+                    Log.d(TAG, "getActivityList took "
+                            + (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user);
+                    Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user);
+                }
+                // Fail if we don't have any apps
+                if (apps == null || apps.isEmpty()) {
+                    return;
+                }
+                // Sort the applications by name
+                final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
+                Collections.sort(apps,
+                        new LauncherModel.ShortcutNameComparator(mLabelCache));
+                if (DEBUG_LOADERS) {
+                    Log.d(TAG, "sort took "
+                            + (SystemClock.uptimeMillis()-sortTime) + "ms");
+                }
 
-            // Query for the set of apps
-            final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
-            List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0);
-            if (DEBUG_LOADERS) {
-                Log.d(TAG, "queryIntentActivities took "
-                        + (SystemClock.uptimeMillis()-qiaTime) + "ms");
-                Log.d(TAG, "queryIntentActivities got " + apps.size() + " apps");
+                // Create the ApplicationInfos
+                for (int i = 0; i < apps.size(); i++) {
+                    LauncherActivityInfoCompat app = apps.get(i);
+                    // This builds the icon bitmaps.
+                    mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache, mLabelCache));
+                }
             }
-            // Fail if we don't have any apps
-            if (apps == null || apps.isEmpty()) {
-                return;
-            }
-            // Sort the applications by name
-            final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
-            Collections.sort(apps,
-                    new LauncherModel.ShortcutNameComparator(packageManager, mLabelCache));
-            if (DEBUG_LOADERS) {
-                Log.d(TAG, "sort took "
-                        + (SystemClock.uptimeMillis()-sortTime) + "ms");
-            }
-
-            // Create the ApplicationInfos
-            for (int i = 0; i < apps.size(); i++) {
-                ResolveInfo app = apps.get(i);
-                // This builds the icon bitmaps.
-                mBgAllAppsList.add(new AppInfo(packageManager, app,
-                        mIconCache, mLabelCache));
-            }
-
             // Huh? Shouldn't this be inside the Runnable below?
             final ArrayList<AppInfo> added = mBgAllAppsList.added;
             mBgAllAppsList.added = new ArrayList<AppInfo>();
@@ -2693,6 +2715,7 @@
     private class PackageUpdatedTask implements Runnable {
         int mOp;
         String[] mPackages;
+        UserHandleCompat mUser;
 
         public static final int OP_NONE = 0;
         public static final int OP_ADD = 1;
@@ -2701,9 +2724,10 @@
         public static final int OP_UNAVAILABLE = 4; // external media unmounted
 
 
-        public PackageUpdatedTask(int op, String[] packages) {
+        public PackageUpdatedTask(int op, String[] packages, UserHandleCompat user) {
             mOp = op;
             mPackages = packages;
+            mUser = user;
         }
 
         public void run() {
@@ -2715,14 +2739,14 @@
                 case OP_ADD:
                     for (int i=0; i<N; i++) {
                         if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]);
-                        mIconCache.remove(packages[i]);
-                        mBgAllAppsList.addPackage(context, packages[i]);
+                        mIconCache.remove(packages[i], mUser);
+                        mBgAllAppsList.addPackage(context, packages[i], mUser);
                     }
                     break;
                 case OP_UPDATE:
                     for (int i=0; i<N; i++) {
                         if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
-                        mBgAllAppsList.updatePackage(context, packages[i]);
+                        mBgAllAppsList.updatePackage(context, packages[i], mUser);
                         WidgetPreviewLoader.removePackageFromDb(
                                 mApp.getWidgetPreviewCacheDb(), packages[i]);
                     }
@@ -2731,7 +2755,7 @@
                 case OP_UNAVAILABLE:
                     for (int i=0; i<N; i++) {
                         if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
-                        mBgAllAppsList.removePackage(packages[i]);
+                        mBgAllAppsList.removePackage(packages[i], mUser);
                         WidgetPreviewLoader.removePackageFromDb(
                                 mApp.getWidgetPreviewCacheDb(), packages[i]);
                     }
@@ -2777,7 +2801,7 @@
                 // Update the launcher db to reflect the changes
                 for (AppInfo a : modifiedFinal) {
                     ArrayList<ItemInfo> infos =
-                            getItemInfoForComponentName(a.componentName);
+                            getItemInfoForComponentName(a.componentName, mUser);
                     for (ItemInfo i : infos) {
                         if (isShortcutInfoUpdateable(i)) {
                             ShortcutInfo info = (ShortcutInfo) i;
@@ -2806,21 +2830,21 @@
                 // Mark disabled packages in the broadcast to be removed
                 final PackageManager pm = context.getPackageManager();
                 for (int i=0; i<N; i++) {
-                    if (isPackageDisabled(pm, packages[i])) {
+                    if (isPackageDisabled(context, packages[i], mUser)) {
                         removedPackageNames.add(packages[i]);
                     }
                 }
             }
             // Remove all the components associated with this package
             for (String pn : removedPackageNames) {
-                ArrayList<ItemInfo> infos = getItemInfoForPackageName(pn);
+                ArrayList<ItemInfo> infos = getItemInfoForPackageName(pn, mUser);
                 for (ItemInfo i : infos) {
                     deleteItemFromDatabase(context, i);
                 }
             }
             // Remove all the specific components
             for (AppInfo a : removedApps) {
-                ArrayList<ItemInfo> infos = getItemInfoForComponentName(a.componentName);
+                ArrayList<ItemInfo> infos = getItemInfoForComponentName(a.componentName, mUser);
                 for (ItemInfo i : infos) {
                     deleteItemFromDatabase(context, i);
                 }
@@ -2836,14 +2860,14 @@
                     public void run() {
                         Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
                         if (callbacks == cb && cb != null) {
-                            callbacks.bindComponentsRemoved(removedPackageNames, removedApps);
+                            callbacks.bindComponentsRemoved(removedPackageNames, removedApps, mUser);
                         }
                     }
                 });
             }
 
             final ArrayList<Object> widgetsAndShortcuts =
-                getSortedWidgetsAndShortcuts(context);
+                    getSortedWidgetsAndShortcuts(context);
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
@@ -2878,31 +2902,22 @@
         return widgetsAndShortcuts;
     }
 
-    private static boolean isPackageDisabled(PackageManager pm, String packageName) {
-        try {
-            PackageInfo pi = pm.getPackageInfo(packageName, 0);
-            return !pi.applicationInfo.enabled;
-        } catch (NameNotFoundException e) {
-            // Fall through
-        }
-        return false;
+    private static boolean isPackageDisabled(Context context, String packageName,
+            UserHandleCompat user) {
+        final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
+        return !launcherApps.isPackageEnabledForProfile(packageName, user);
     }
 
-    public static boolean isValidPackageComponent(PackageManager pm, ComponentName cn) {
+    public static boolean isValidPackageActivity(Context context, ComponentName cn,
+            UserHandleCompat user) {
         if (cn == null) {
             return false;
         }
-        if (isPackageDisabled(pm, cn.getPackageName())) {
+        final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
+        if (!launcherApps.isPackageEnabledForProfile(cn.getPackageName(), user)) {
             return false;
         }
-
-        try {
-            // Check the activity
-            PackageInfo pi = pm.getPackageInfo(cn.getPackageName(), 0);
-            return (pm.getActivityInfo(cn, 0) != null);
-        } catch (NameNotFoundException e) {
-            return false;
-        }
+        return launcherApps.isActivityEnabledForProfile(cn, user);
     }
 
     /**
@@ -2916,7 +2931,8 @@
         } else {
             info.title = "";
         }
-        info.setIcon(mIconCache.getIcon(intent, info.title.toString()));
+        info.user = UserHandleCompat.myUserHandle();
+        info.setIcon(mIconCache.getIcon(intent, info.title.toString(), info.user));
         info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
         info.restoredIntent = intent;
         return info;
@@ -2944,8 +2960,9 @@
      * This is called from the code that adds shortcuts from the intent receiver.  This
      * doesn't have a Cursor, but
      */
-    public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context) {
-        return getShortcutInfo(manager, intent, context, null, -1, -1, null);
+    public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent,
+            UserHandleCompat user, Context context) {
+        return getShortcutInfo(manager, intent, user, context, null, -1, -1, null);
     }
 
     /**
@@ -2953,54 +2970,37 @@
      *
      * If c is not null, then it will be used to fill in missing data like the title and icon.
      */
-    public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context,
-            Cursor c, int iconIndex, int titleIndex, HashMap<Object, CharSequence> labelCache) {
-        ComponentName componentName = intent.getComponent();
-        final ShortcutInfo info = new ShortcutInfo();
-        if (componentName != null && !isValidPackageComponent(manager, componentName)) {
-            Log.d(TAG, "Invalid package found in getShortcutInfo: " + componentName);
+    public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent,
+            UserHandleCompat user, Context context, Cursor c, int iconIndex, int titleIndex,
+            HashMap<Object, CharSequence> labelCache) {
+        if (user == null) {
+            Log.d(TAG, "Null user found in getShortcutInfo");
             return null;
-        } else {
-            try {
-                PackageInfo pi = manager.getPackageInfo(componentName.getPackageName(), 0);
-                info.initFlagsAndFirstInstallTime(pi);
-            } catch (NameNotFoundException e) {
-                Log.d(TAG, "getPackInfo failed for package " +
-                        componentName.getPackageName());
-            }
         }
 
-        // TODO: See if the PackageManager knows about this case.  If it doesn't
-        // then return null & delete this.
+        ComponentName componentName = intent.getComponent();
+        if (componentName == null) {
+            Log.d(TAG, "Missing component found in getShortcutInfo: " + componentName);
+            return null;
+        }
+
+        Intent newIntent = new Intent(intent.getAction(), null);
+        newIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+        newIntent.setComponent(componentName);
+        LauncherActivityInfoCompat lai = mLauncherApps.resolveActivity(newIntent, user);
+        if (lai == null) {
+            Log.d(TAG, "Missing activity found in getShortcutInfo: " + componentName);
+            return null;
+        }
+
+        final ShortcutInfo info = new ShortcutInfo();
 
         // the resource -- This may implicitly give us back the fallback icon,
         // but don't worry about that.  All we're doing with usingFallbackIcon is
         // to avoid saving lots of copies of that in the database, and most apps
         // have icons anyway.
+        Bitmap icon = mIconCache.getIcon(componentName, lai, labelCache);
 
-        // Attempt to use queryIntentActivities to get the ResolveInfo (with IntentFilter info) and
-        // if that fails, or is ambiguious, fallback to the standard way of getting the resolve info
-        // via resolveActivity().
-        Bitmap icon = null;
-        ResolveInfo resolveInfo = null;
-        ComponentName oldComponent = intent.getComponent();
-        Intent newIntent = new Intent(intent.getAction(), null);
-        newIntent.addCategory(Intent.CATEGORY_LAUNCHER);
-        newIntent.setPackage(oldComponent.getPackageName());
-        List<ResolveInfo> infos = manager.queryIntentActivities(newIntent, 0);
-        for (ResolveInfo i : infos) {
-            ComponentName cn = new ComponentName(i.activityInfo.packageName,
-                    i.activityInfo.name);
-            if (cn.equals(oldComponent)) {
-                resolveInfo = i;
-            }
-        }
-        if (resolveInfo == null) {
-            resolveInfo = manager.resolveActivity(intent, 0);
-        }
-        if (resolveInfo != null) {
-            icon = mIconCache.getIcon(componentName, resolveInfo, labelCache);
-        }
         // the db
         if (icon == null) {
             if (c != null) {
@@ -3009,21 +3009,21 @@
         }
         // the fallback icon
         if (icon == null) {
-            icon = getFallbackIcon();
+            icon = mIconCache.getDefaultIcon(user);
             info.usingFallbackIcon = true;
         }
         info.setIcon(icon);
 
+        // From the cache.
+        if (labelCache != null) {
+            info.title = labelCache.get(componentName);
+        }
+
         // from the resource
-        if (resolveInfo != null) {
-            ComponentName key = LauncherModel.getComponentNameFromResolveInfo(resolveInfo);
-            if (labelCache != null && labelCache.containsKey(key)) {
-                info.title = labelCache.get(key);
-            } else {
-                info.title = resolveInfo.activityInfo.loadLabel(manager);
-                if (labelCache != null) {
-                    labelCache.put(key, info.title);
-                }
+        if (info.title == null && lai != null) {
+            info.title = lai.getLabel();
+            if (labelCache != null) {
+                labelCache.put(componentName, info.title);
             }
         }
         // from the db
@@ -3037,6 +3037,7 @@
             info.title = componentName.getClassName();
         }
         info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+        info.user = user;
         return info;
     }
 
@@ -3069,21 +3070,27 @@
         return new ArrayList<ItemInfo>(filtered);
     }
 
-    private ArrayList<ItemInfo> getItemInfoForPackageName(final String pn) {
+    private ArrayList<ItemInfo> getItemInfoForPackageName(final String pn,
+            final UserHandleCompat user) {
         ItemInfoFilter filter  = new ItemInfoFilter() {
             @Override
             public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
-                return cn.getPackageName().equals(pn);
+                return cn.getPackageName().equals(pn) && info.user.equals(user);
             }
         };
         return filterItemInfos(sBgItemsIdMap.values(), filter);
     }
 
-    private ArrayList<ItemInfo> getItemInfoForComponentName(final ComponentName cname) {
+    private ArrayList<ItemInfo> getItemInfoForComponentName(final ComponentName cname,
+            final UserHandleCompat user) {
         ItemInfoFilter filter  = new ItemInfoFilter() {
             @Override
             public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
-                return cn.equals(cname);
+                if (info.user == null) {
+                    return cn.equals(cname);
+                } else {
+                    return cn.equals(cname) && info.user.equals(user);
+                }
             }
         };
         return filterItemInfos(sBgItemsIdMap.values(), filter);
@@ -3118,6 +3125,8 @@
 
         Bitmap icon = null;
         final ShortcutInfo info = new ShortcutInfo();
+        // Non-app shortcuts are only supported for current user.
+        info.user = UserHandleCompat.myUserHandle();
         info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
 
         // TODO: If there's an explicit component and we can't install that, delete it.
@@ -3148,14 +3157,14 @@
             }
             // the fallback icon
             if (icon == null) {
-                icon = getFallbackIcon();
+                icon = mIconCache.getDefaultIcon(info.user);
                 info.usingFallbackIcon = true;
             }
             break;
         case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
             icon = getIconFromCursor(c, iconIndex, context);
             if (icon == null) {
-                icon = getFallbackIcon();
+                icon = mIconCache.getDefaultIcon(info.user);
                 info.customIcon = false;
                 info.usingFallbackIcon = true;
             } else {
@@ -3163,7 +3172,7 @@
             }
             break;
         default:
-            icon = getFallbackIcon();
+            icon = mIconCache.getDefaultIcon(info.user);
             info.usingFallbackIcon = true;
             info.customIcon = false;
             break;
@@ -3280,7 +3289,8 @@
                             iconResource.packageName);
                     final int id = resources.getIdentifier(iconResource.resourceName, null, null);
                     icon = Utilities.createIconBitmap(
-                            mIconCache.getFullResIcon(resources, id), context);
+                            mIconCache.getFullResIcon(resources, id),
+                            context);
                 } catch (Exception e) {
                     Log.w(TAG, "Could not load shortcut icon: " + extra);
                 }
@@ -3289,11 +3299,14 @@
 
         final ShortcutInfo info = new ShortcutInfo();
 
+        // Only support intents for current user for now. Intents sent from other
+        // users wouldn't get here without intent forwarding anyway.
+        info.user = UserHandleCompat.myUserHandle();
         if (icon == null) {
             if (fallbackIcon != null) {
                 icon = fallbackIcon;
             } else {
-                icon = getFallbackIcon();
+                icon = mIconCache.getDefaultIcon(info.user);
                 info.usingFallbackIcon = true;
             }
         }
@@ -3365,12 +3378,18 @@
         final Collator collator = Collator.getInstance();
         return new Comparator<AppInfo>() {
             public final int compare(AppInfo a, AppInfo b) {
-                int result = collator.compare(a.title.toString().trim(),
-                        b.title.toString().trim());
-                if (result == 0) {
-                    result = a.componentName.compareTo(b.componentName);
+                if (a.user.equals(b.user)) {
+                    int result = collator.compare(a.title.toString().trim(),
+                            b.title.toString().trim());
+                    if (result == 0) {
+                        result = a.componentName.compareTo(b.componentName);
+                    }
+                    return result;
+                } else {
+                    // TODO Need to figure out rules for sorting
+                    // profiles, this puts work second.
+                    return a.user.toString().compareTo(b.user.toString());
                 }
-                return result;
             }
         };
     }
@@ -3397,35 +3416,32 @@
             return new ComponentName(info.serviceInfo.packageName, info.serviceInfo.name);
         }
     }
-    public static class ShortcutNameComparator implements Comparator<ResolveInfo> {
+    public static class ShortcutNameComparator implements Comparator<LauncherActivityInfoCompat> {
         private Collator mCollator;
-        private PackageManager mPackageManager;
         private HashMap<Object, CharSequence> mLabelCache;
         ShortcutNameComparator(PackageManager pm) {
-            mPackageManager = pm;
             mLabelCache = new HashMap<Object, CharSequence>();
             mCollator = Collator.getInstance();
         }
-        ShortcutNameComparator(PackageManager pm, HashMap<Object, CharSequence> labelCache) {
-            mPackageManager = pm;
+        ShortcutNameComparator(HashMap<Object, CharSequence> labelCache) {
             mLabelCache = labelCache;
             mCollator = Collator.getInstance();
         }
-        public final int compare(ResolveInfo a, ResolveInfo b) {
+        public final int compare(LauncherActivityInfoCompat a, LauncherActivityInfoCompat b) {
             CharSequence labelA, labelB;
-            ComponentName keyA = LauncherModel.getComponentNameFromResolveInfo(a);
-            ComponentName keyB = LauncherModel.getComponentNameFromResolveInfo(b);
+            ComponentName keyA = a.getComponentName();
+            ComponentName keyB = b.getComponentName();
             if (mLabelCache.containsKey(keyA)) {
                 labelA = mLabelCache.get(keyA);
             } else {
-                labelA = a.loadLabel(mPackageManager).toString().trim();
+                labelA = a.getLabel().toString().trim();
 
                 mLabelCache.put(keyA, labelA);
             }
             if (mLabelCache.containsKey(keyB)) {
                 labelB = mLabelCache.get(keyB);
             } else {
-                labelB = b.loadLabel(mPackageManager).toString().trim();
+                labelB = b.getLabel().toString().trim();
 
                 mLabelCache.put(keyB, labelB);
             }
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index e43b727..437c2ad 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -56,8 +56,10 @@
 import android.util.SparseArray;
 import android.util.Xml;
 
-import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.LauncherSettings.Favorites;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -75,7 +77,7 @@
 
     private static final String DATABASE_NAME = "launcher.db";
 
-    private static final int DATABASE_VERSION = 19;
+    private static final int DATABASE_VERSION = 20;
 
     static final String OLD_AUTHORITY = "com.android.launcher2.settings";
     static final String AUTHORITY = ProviderConfig.AUTHORITY;
@@ -429,6 +431,10 @@
             mMaxScreenId = 0;
             mNewDbCreated = true;
 
+            UserManagerCompat userManager = UserManagerCompat.getInstance(mContext);
+            long userSerialNumber = userManager.getSerialNumberForUser(
+                    UserHandleCompat.myUserHandle());
+
             db.execSQL("CREATE TABLE favorites (" +
                     "_id INTEGER PRIMARY KEY," +
                     "title TEXT," +
@@ -450,7 +456,8 @@
                     "displayMode INTEGER," +
                     "appWidgetProvider TEXT," +
                     "modified INTEGER NOT NULL DEFAULT 0," +
-                    "restored INTEGER NOT NULL DEFAULT 0" +
+                    "restored INTEGER NOT NULL DEFAULT 0," +
+                    "profileId INTEGER DEFAULT " + userSerialNumber +
                     ");");
             addWorkspacesTable(db);
 
@@ -847,6 +854,14 @@
                 version = 19;
             }
 
+            if (version < 20) {
+                // Add userId column
+                if (addProfileColumn(db)) {
+                    version = 20;
+                }
+                // else old version remains, which means we wipe old data
+            }
+
             if (version != DATABASE_VERSION) {
                 Log.w(TAG, "Destroying all old data.");
                 db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
@@ -856,6 +871,29 @@
             }
         }
 
+        private boolean addProfileColumn(SQLiteDatabase db) {
+            db.beginTransaction();
+            try {
+                UserManagerCompat userManager = UserManagerCompat.getInstance(mContext);
+                // Default to the serial number of this user, for older
+                // shortcuts.
+                long userSerialNumber = userManager.getSerialNumberForUser(
+                        UserHandleCompat.myUserHandle());
+                // Insert new column for holding user serial number
+                db.execSQL("ALTER TABLE favorites " +
+                        "ADD COLUMN profileId INTEGER DEFAULT "
+                                        + userSerialNumber + ";");
+                db.setTransactionSuccessful();
+            } catch (SQLException ex) {
+                // Old version remains, which means we wipe old data
+                Log.e(TAG, ex.getMessage(), ex);
+                return false;
+            } finally {
+                db.endTransaction();
+            }
+            return true;
+        }
+
         private boolean updateContactsShortcuts(SQLiteDatabase db) {
             final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE,
                     new int[] { Favorites.ITEM_TYPE_SHORTCUT });
@@ -1786,6 +1824,8 @@
                                 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
                         final int displayModeIndex
                                 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
+                        final int profileIndex
+                                = c.getColumnIndex(LauncherSettings.Favorites.PROFILE_ID);
 
                         int i = 0;
                         int curX = 0;
@@ -1817,6 +1857,19 @@
                             final int screen = c.getInt(screenIndex);
                             int container = c.getInt(containerIndex);
                             final String intentStr = c.getString(intentIndex);
+
+                            UserManagerCompat userManager = UserManagerCompat.getInstance(mContext);
+                            UserHandleCompat userHandle;
+                            final long userSerialNumber;
+                            if (profileIndex != -1 && !c.isNull(profileIndex)) {
+                                userSerialNumber = c.getInt(profileIndex);
+                                userHandle = userManager.getUserForSerialNumber(userSerialNumber);
+                            } else {
+                                // Default to the serial number of this user, for older
+                                // shortcuts.
+                                userHandle = UserHandleCompat.myUserHandle();
+                                userSerialNumber = userManager.getSerialNumberForUser(userHandle);
+                            }
                             Launcher.addDumpLog(TAG, "migrating \""
                                 + c.getString(titleIndex) + "\" ("
                                 + cellX + "," + cellY + "@"
@@ -1843,7 +1896,8 @@
                                     Launcher.addDumpLog(TAG, "skipping empty intent", true);
                                     continue;
                                 } else if (cn != null &&
-                                        !LauncherModel.isValidPackageComponent(pm, cn)) {
+                                        !LauncherModel.isValidPackageActivity(mContext, cn,
+                                                userHandle)) {
                                     // component no longer exists.
                                     Launcher.addDumpLog(TAG, "skipping item whose component " +
                                             "no longer exists.", true);
@@ -1882,6 +1936,7 @@
                             values.put(LauncherSettings.Favorites.URI, c.getString(uriIndex));
                             values.put(LauncherSettings.Favorites.DISPLAY_MODE,
                                     c.getInt(displayModeIndex));
+                            values.put(LauncherSettings.Favorites.PROFILE_ID, userSerialNumber);
 
                             if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
                                 hotseat.put(screen, values);
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 2a768a2..3553702 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -212,6 +212,14 @@
         static final String SPANY = "spanY";
 
         /**
+         * The profile id of the item in the cell.
+         * <P>
+         * Type: INTEGER
+         * </P>
+         */
+        static final String PROFILE_ID = "profileId";
+
+        /**
          * The favorite is a user created folder
          */
         static final int ITEM_TYPE_FOLDER = 2;
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index 92b582d..f40cf9f 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -26,6 +26,8 @@
 import android.graphics.Bitmap;
 import android.util.Log;
 
+import com.android.launcher3.compat.UserHandleCompat;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 
@@ -111,12 +113,12 @@
         }
     }
 
-
-    ShortcutInfo(Intent intent, CharSequence title, Bitmap icon) {
+    ShortcutInfo(Intent intent, CharSequence title, Bitmap icon, UserHandleCompat user) {
         this();
         this.intent = intent;
         this.title = title;
         mIcon = icon;
+        this.user = user;
     }
 
     public ShortcutInfo(Context context, ShortcutInfo info) {
@@ -130,8 +132,9 @@
         }
         mIcon = info.mIcon; // TODO: should make a copy here.  maybe we don't need this ctor at all
         customIcon = info.customIcon;
-        initFlagsAndFirstInstallTime(
-                getPackageInfo(context, intent.getComponent().getPackageName()));
+        flags = info.flags;
+        firstInstallTime = info.firstInstallTime;
+        user = info.user;
     }
 
     /** TODO: Remove this.  It's only called by ApplicationInfo.makeShortcut. */
@@ -144,22 +147,6 @@
         firstInstallTime = info.firstInstallTime;
     }
 
-    public static PackageInfo getPackageInfo(Context context, String packageName) {
-        PackageInfo pi = null;
-        try {
-            PackageManager pm = context.getPackageManager();
-            pi = pm.getPackageInfo(packageName, 0);
-        } catch (NameNotFoundException e) {
-            Log.d("ShortcutInfo", "PackageManager.getPackageInfo failed for " + packageName);
-        }
-        return pi;
-    }
-
-    void initFlagsAndFirstInstallTime(PackageInfo pi) {
-        flags = AppInfo.initFlags(pi);
-        firstInstallTime = AppInfo.initFirstInstallTime(pi);
-    }
-
     public void setIcon(Bitmap b) {
         mIcon = b;
     }
@@ -172,30 +159,13 @@
     }
 
     public void updateIcon(IconCache iconCache) {
-        mIcon = iconCache.getIcon(intent);
-        usingFallbackIcon = iconCache.isDefaultIcon(mIcon);
-    }
-
-    /**
-     * Creates the application intent based on a component name and various launch flags.
-     * Sets {@link #itemType} to {@link LauncherSettings.BaseLauncherColumns#ITEM_TYPE_APPLICATION}.
-     *
-     * @param className the class name of the component representing the intent
-     * @param launchFlags the launch flags
-     */
-    final void setActivity(Context context, ComponentName className, int launchFlags) {
-        intent = new Intent(Intent.ACTION_MAIN);
-        intent.addCategory(Intent.CATEGORY_LAUNCHER);
-        intent.setComponent(className);
-        intent.setFlags(launchFlags);
-        itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_APPLICATION;
-        initFlagsAndFirstInstallTime(
-                getPackageInfo(context, intent.getComponent().getPackageName()));
+        mIcon = iconCache.getIcon(intent, user);
+        usingFallbackIcon = iconCache.isDefaultIcon(mIcon, user);
     }
 
     @Override
-    void onAddToDatabase(ContentValues values) {
-        super.onAddToDatabase(values);
+    void onAddToDatabase(Context context, ContentValues values) {
+        super.onAddToDatabase(context, values);
 
         String titleStr = title != null ? title.toString() : null;
         values.put(LauncherSettings.BaseLauncherColumns.TITLE, titleStr);
@@ -224,10 +194,10 @@
 
     @Override
     public String toString() {
-        return "ShortcutInfo(title=" + title.toString() + "intent=" + intent + "id=" + this.id
+        return "ShortcutInfo(title=" + title + "intent=" + intent + "id=" + this.id
                 + " type=" + this.itemType + " container=" + this.container + " screen=" + screenId
                 + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX + " spanY=" + spanY
-                + " dropPos=" + Arrays.toString(dropPos) + ")";
+                + " dropPos=" + Arrays.toString(dropPos) + " user=" + user + ")";
     }
 
     public static void dumpShortcutInfoList(String tag, String label,
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index f7ca141..9bf2c23 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -60,6 +60,8 @@
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 import android.widget.TextView;
+
+import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.FolderIcon.FolderRingAnimator;
 import com.android.launcher3.Launcher.CustomContentCallbacks;
 import com.android.launcher3.LauncherSettings.Favorites;
@@ -4621,7 +4623,7 @@
     // Removes ALL items that match a given package name, this is usually called when a package
     // has been removed and we want to remove all components (widgets, shortcuts, apps) that
     // belong to that package.
-    void removeItemsByPackageName(final ArrayList<String> packages) {
+    void removeItemsByPackageName(final ArrayList<String> packages, final UserHandleCompat user) {
         final HashSet<String> packageNames = new HashSet<String>();
         packageNames.addAll(packages);
 
@@ -4641,7 +4643,8 @@
             @Override
             public boolean filterItem(ItemInfo parent, ItemInfo info,
                                       ComponentName cn) {
-                if (packageNames.contains(cn.getPackageName())) {
+                if (packageNames.contains(cn.getPackageName())
+                        && info.user.equals(user)) {
                     cns.add(cn);
                     return true;
                 }
@@ -4651,13 +4654,13 @@
         LauncherModel.filterItemInfos(infos, filter);
 
         // Remove the affected components
-        removeItemsByComponentName(cns);
+        removeItemsByComponentName(cns, user);
     }
 
     // Removes items that match the application info specified, when applications are removed
     // as a part of an update, this is called to ensure that other widgets and application
     // shortcuts are not removed.
-    void removeItemsByApplicationInfo(final ArrayList<AppInfo> appInfos) {
+    void removeItemsByApplicationInfo(final ArrayList<AppInfo> appInfos, UserHandleCompat user) {
         // Just create a hash table of all the specific components that this will affect
         HashSet<ComponentName> cns = new HashSet<ComponentName>();
         for (AppInfo info : appInfos) {
@@ -4665,10 +4668,11 @@
         }
 
         // Remove all the things
-        removeItemsByComponentName(cns);
+        removeItemsByComponentName(cns, user);
     }
 
-    void removeItemsByComponentName(final HashSet<ComponentName> componentNames) {
+    void removeItemsByComponentName(final HashSet<ComponentName> componentNames,
+            final UserHandleCompat user) {
         ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts();
         for (final CellLayout layoutParent: cellLayouts) {
             final ViewGroup layout = layoutParent.getShortcutsAndWidgets();
@@ -4687,7 +4691,7 @@
                 public boolean filterItem(ItemInfo parent, ItemInfo info,
                                           ComponentName cn) {
                     if (parent instanceof FolderInfo) {
-                        if (componentNames.contains(cn)) {
+                        if (componentNames.contains(cn) && info.user.equals(user)) {
                             FolderInfo folder = (FolderInfo) parent;
                             ArrayList<ShortcutInfo> appsToRemove;
                             if (folderAppsToRemove.containsKey(folder)) {
@@ -4700,7 +4704,7 @@
                             return true;
                         }
                     } else {
-                        if (componentNames.contains(cn)) {
+                        if (componentNames.contains(cn) && info.user.equals(user)) {
                             childrenToRemove.add(children.get(info));
                             return true;
                         }
diff --git a/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java b/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java
new file mode 100644
index 0000000..3ba93ea
--- /dev/null
+++ b/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 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.launcher3.compat;
+
+import android.content.ComponentName;
+import android.graphics.drawable.Drawable;
+
+public abstract class LauncherActivityInfoCompat {
+
+    LauncherActivityInfoCompat() {
+    }
+
+    public abstract ComponentName getComponentName();
+    public abstract UserHandleCompat getUser();
+    public abstract CharSequence getLabel();
+    public abstract Drawable getIcon(int density);
+    public abstract int getApplicationFlags();
+    public abstract long getFirstInstallTime();
+    public abstract Drawable getBadgedIcon(int density);
+}
diff --git a/src/com/android/launcher3/compat/LauncherActivityInfoCompatV16.java b/src/com/android/launcher3/compat/LauncherActivityInfoCompatV16.java
new file mode 100644
index 0000000..9b9384d
--- /dev/null
+++ b/src/com/android/launcher3/compat/LauncherActivityInfoCompatV16.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014 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.launcher3.compat;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+
+
+public class LauncherActivityInfoCompatV16 extends LauncherActivityInfoCompat {
+    private ActivityInfo mActivityInfo;
+    private ComponentName mComponentName;
+    private PackageManager mPm;
+
+    LauncherActivityInfoCompatV16(Context context, ResolveInfo info) {
+        super();
+        this.mActivityInfo = info.activityInfo;
+        mComponentName = new ComponentName(mActivityInfo.packageName, mActivityInfo.name);
+        mPm = context.getPackageManager();
+    }
+
+    public ComponentName getComponentName() {
+        return mComponentName;
+    }
+
+    public UserHandleCompat getUser() {
+        return UserHandleCompat.myUserHandle();
+    }
+
+    public CharSequence getLabel() {
+        return mActivityInfo.loadLabel(mPm);
+    }
+
+    public Drawable getIcon(int density) {
+        Drawable d = null;
+        if (mActivityInfo.getIconResource() != 0) {
+            Resources resources;
+            try {
+                resources = mPm.getResourcesForApplication(mActivityInfo.packageName);
+            } catch (PackageManager.NameNotFoundException e) {
+                resources = null;
+            }
+            if (resources != null) {
+                d = resources.getDrawableForDensity(mActivityInfo.getIconResource(), density);
+            }
+        }
+        if (d == null) {
+            Resources resources = Resources.getSystem();
+            d = resources.getDrawableForDensity(android.R.mipmap.sym_def_app_icon, density);
+        }
+        return d;
+    }
+
+    public int getApplicationFlags() {
+        return mActivityInfo.applicationInfo.flags;
+    }
+
+    public long getFirstInstallTime() {
+        try {
+            PackageInfo info = mPm.getPackageInfo(mActivityInfo.packageName, 0);
+            return info != null ? info.firstInstallTime : 0;
+        } catch (NameNotFoundException e) {
+            return 0;
+        }
+    }
+
+    public String getName() {
+        return mActivityInfo.name;
+    }
+
+    public Drawable getBadgedIcon(int density) {
+        return getIcon(density);
+    }
+}
diff --git a/src/com/android/launcher3/compat/LauncherActivityInfoCompatVL.java b/src/com/android/launcher3/compat/LauncherActivityInfoCompatVL.java
new file mode 100644
index 0000000..76125bd
--- /dev/null
+++ b/src/com/android/launcher3/compat/LauncherActivityInfoCompatVL.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014 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.launcher3.compat;
+
+import android.content.ComponentName;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class LauncherActivityInfoCompatVL extends LauncherActivityInfoCompat {
+    private Object mLauncherActivityInfo;
+    private Class mLauncherActivityInfoClass;
+    private Method mGetComponentName;
+    private Method mGetUser;
+    private Method mGetLabel;
+    private Method mGetIcon;
+    private Method mGetApplicationFlags;
+    private Method mGetFirstInstallTime;
+    private Method mGetBadgedIcon;
+
+    LauncherActivityInfoCompatVL(Object launcherActivityInfo) {
+        super();
+        mLauncherActivityInfo = launcherActivityInfo;
+        mLauncherActivityInfoClass = ReflectUtils.getClassForName(
+                "android.content.pm.LauncherActivityInfo");
+        mGetComponentName = ReflectUtils.getMethod(mLauncherActivityInfoClass, "getComponentName");
+        mGetUser = ReflectUtils.getMethod(mLauncherActivityInfoClass, "getUser");
+        mGetLabel = ReflectUtils.getMethod(mLauncherActivityInfoClass, "getLabel");
+        mGetIcon = ReflectUtils.getMethod(mLauncherActivityInfoClass, "getIcon", int.class);
+        mGetApplicationFlags = ReflectUtils.getMethod(mLauncherActivityInfoClass,
+                "getApplicationFlags");
+        mGetFirstInstallTime = ReflectUtils.getMethod(mLauncherActivityInfoClass,
+                "getFirstInstallTime");
+        mGetBadgedIcon = ReflectUtils.getMethod(mLauncherActivityInfoClass, "getBadgedIcon",
+                int.class);
+    }
+
+    public ComponentName getComponentName() {
+        return (ComponentName) ReflectUtils.invokeMethod(mLauncherActivityInfo, mGetComponentName);
+    }
+
+    public UserHandleCompat getUser() {
+        return UserHandleCompat.fromUser((UserHandle) ReflectUtils.invokeMethod(
+                        mLauncherActivityInfo, mGetUser));
+    }
+
+    public CharSequence getLabel() {
+        return (CharSequence) ReflectUtils.invokeMethod(mLauncherActivityInfo, mGetLabel);
+    }
+
+    public Drawable getIcon(int density) {
+        return (Drawable) ReflectUtils.invokeMethod(mLauncherActivityInfo, mGetIcon, density);
+    }
+
+    public int getApplicationFlags() {
+        return (Integer) ReflectUtils.invokeMethod(mLauncherActivityInfo, mGetApplicationFlags);
+    }
+
+    public long getFirstInstallTime() {
+        return (Long) ReflectUtils.invokeMethod(mLauncherActivityInfo, mGetFirstInstallTime);
+    }
+
+    public Drawable getBadgedIcon(int density) {
+        return (Drawable) ReflectUtils.invokeMethod(mLauncherActivityInfo, mGetBadgedIcon, density);
+    }
+}
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java
new file mode 100644
index 0000000..59ee45c
--- /dev/null
+++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014 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.launcher3.compat;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public abstract class LauncherAppsCompat {
+
+    public interface OnAppsChangedListenerCompat {
+        void onPackageRemoved(UserHandleCompat user, String packageName);
+        void onPackageAdded(UserHandleCompat user, String packageName);
+        void onPackageChanged(UserHandleCompat user, String packageName);
+        void onPackagesAvailable(UserHandleCompat user, String[] packageNames, boolean replacing);
+        void onPackagesUnavailable(UserHandleCompat user, String[] packageNames, boolean replacing);
+    }
+
+    protected LauncherAppsCompat() {
+    }
+
+    public static LauncherAppsCompat getInstance(Context context) {
+        // TODO change this to use api version once L gets an API number.
+        if ("L".equals(Build.VERSION.CODENAME)) {
+            Object launcherApps = context.getSystemService("launcherapps");
+            if (launcherApps != null) {
+                LauncherAppsCompatVL compat = LauncherAppsCompatVL.build(context, launcherApps);
+                if (compat != null) {
+                    return compat;
+                }
+            }
+        }
+        // Pre L or lunacher apps service not running, or reflection failed to find something.
+        return new LauncherAppsCompatV16(context);
+    }
+
+    public abstract List<LauncherActivityInfoCompat> getActivityList(String packageName,
+            UserHandleCompat user);
+    public abstract LauncherActivityInfoCompat resolveActivity(Intent intent,
+            UserHandleCompat user);
+    public abstract void startActivityForProfile(ComponentName component, Rect sourceBounds,
+            Bundle opts, UserHandleCompat user);
+    public abstract void addOnAppsChangedListener(OnAppsChangedListenerCompat listener);
+    public abstract void removeOnAppsChangedListener(OnAppsChangedListenerCompat listener);
+    public abstract boolean isPackageEnabledForProfile(String packageName, UserHandleCompat user);
+    public abstract boolean isActivityEnabledForProfile(ComponentName component,
+            UserHandleCompat user);
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatV16.java b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
new file mode 100644
index 0000000..c739eb9
--- /dev/null
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2014 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.launcher3.compat;
+
+import android.content.BroadcastReceiver;
+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.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageInfo;
+import android.content.pm.ResolveInfo;
+import android.graphics.Rect;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class LauncherAppsCompatV16 extends LauncherAppsCompat {
+
+    private PackageManager mPm;
+    private Context mContext;
+    private List<OnAppsChangedListenerCompat> mListeners
+            = new ArrayList<OnAppsChangedListenerCompat>();
+    private PackageMonitor mPackageMonitor;
+
+    LauncherAppsCompatV16(Context context) {
+        mPm = context.getPackageManager();
+        mContext = context;
+        mPackageMonitor = new PackageMonitor();
+   }
+
+    public List<LauncherActivityInfoCompat> getActivityList(String packageName,
+            UserHandleCompat user) {
+        final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
+        mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+        mainIntent.setPackage(packageName);
+        List<ResolveInfo> infos = mPm.queryIntentActivities(mainIntent, 0);
+        List<LauncherActivityInfoCompat> list =
+                new ArrayList<LauncherActivityInfoCompat>(infos.size());
+        for (ResolveInfo info : infos) {
+            list.add(new LauncherActivityInfoCompatV16(mContext, info));
+        }
+        return list;
+    }
+
+    public LauncherActivityInfoCompat resolveActivity(Intent intent, UserHandleCompat user) {
+        ResolveInfo info = mPm.resolveActivity(intent, 0);
+        if (info != null) {
+            return new LauncherActivityInfoCompatV16(mContext, info);
+        }
+        return null;
+    }
+
+    public void startActivityForProfile(ComponentName component, Rect sourceBounds,
+            Bundle opts, UserHandleCompat user) {
+        Intent launchIntent = new Intent(Intent.ACTION_MAIN);
+        launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+        launchIntent.setComponent(component);
+        launchIntent.setSourceBounds(sourceBounds);
+        launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(launchIntent, opts);
+    }
+
+    public synchronized void addOnAppsChangedListener(OnAppsChangedListenerCompat listener) {
+        if (listener != null && !mListeners.contains(listener)) {
+            mListeners.add(listener);
+            if (mListeners.size() == 1) {
+                registerForPackageIntents();
+            }
+        }
+    }
+
+    public synchronized void removeOnAppsChangedListener(OnAppsChangedListenerCompat listener) {
+        mListeners.remove(listener);
+        if (mListeners.size() == 0) {
+            unregisterForPackageIntents();
+        }
+    }
+
+    public boolean isPackageEnabledForProfile(String packageName, UserHandleCompat user) {
+        try {
+            PackageInfo info = mPm.getPackageInfo(packageName, 0);
+            return info != null && info.applicationInfo.enabled;
+        } catch (NameNotFoundException e) {
+            return false;
+        }
+    }
+
+    public boolean isActivityEnabledForProfile(ComponentName component, UserHandleCompat user) {
+        try {
+            ActivityInfo info = mPm.getActivityInfo(component, 0);
+            return info != null && info.isEnabled();
+        } catch (NameNotFoundException e) {
+            return false;
+        }
+    }
+
+    private void unregisterForPackageIntents() {
+        mContext.unregisterReceiver(mPackageMonitor);
+    }
+
+    private void registerForPackageIntents() {
+        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        filter.addDataScheme("package");
+        mContext.registerReceiver(mPackageMonitor, filter);
+        filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+        filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+        mContext.registerReceiver(mPackageMonitor, filter);
+    }
+
+    private synchronized List<OnAppsChangedListenerCompat> getListeners() {
+        return new ArrayList<OnAppsChangedListenerCompat>(mListeners);
+    }
+
+    private class PackageMonitor extends BroadcastReceiver {
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            final UserHandleCompat user = UserHandleCompat.myUserHandle();
+
+            if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
+                    || Intent.ACTION_PACKAGE_REMOVED.equals(action)
+                    || Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+                final String packageName = intent.getData().getSchemeSpecificPart();
+                final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+
+                if (packageName == null || packageName.length() == 0) {
+                    // they sent us a bad intent
+                    return;
+                }
+                if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
+                    for (OnAppsChangedListenerCompat listener : getListeners()) {
+                        listener.onPackageChanged(user, packageName);
+                    }
+                } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+                    if (!replacing) {
+                        for (OnAppsChangedListenerCompat listener : getListeners()) {
+                            listener.onPackageRemoved(user, packageName);
+                        }
+                    }
+                    // else, we are replacing the package, so a PACKAGE_ADDED will be sent
+                    // later, we will update the package at this time
+                } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+                    if (!replacing) {
+                        for (OnAppsChangedListenerCompat listener : getListeners()) {
+                            listener.onPackageAdded(user, packageName);
+                        }
+                    } else {
+                        for (OnAppsChangedListenerCompat listener : getListeners()) {
+                            listener.onPackageChanged(user, packageName);
+                        }
+                    }
+                }
+            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
+                final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+                String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+                for (OnAppsChangedListenerCompat listener : getListeners()) {
+                    listener.onPackagesAvailable(user, packages, replacing);
+                }
+            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
+                final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+                String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+                for (OnAppsChangedListenerCompat listener : getListeners()) {
+                    listener.onPackagesUnavailable(user, packages, replacing);
+                }
+            }
+        }
+    }
+}
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
new file mode 100644
index 0000000..c933712
--- /dev/null
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2014 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.launcher3.compat;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+import java.lang.reflect.InvocationHandler;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import java.lang.reflect.Proxy;
+import java.lang.reflect.Method;
+
+public class LauncherAppsCompatVL extends LauncherAppsCompat {
+
+    private Object mLauncherApps;
+    private Class mLauncherAppsClass;
+    private Class mListenerClass;
+    private Method mGetActivityList;
+    private Method mResolveActivity;
+    private Method mStartActivityForProfile;
+    private Method mAddOnAppsChangedListener;
+    private Method mRemoveOnAppsChangedListener;
+    private Method mIsPackageEnabledForProfile;
+    private Method mIsActivityEnabledForProfile;
+
+    private Map<OnAppsChangedListenerCompat, Object> mListeners
+            = new HashMap<OnAppsChangedListenerCompat, Object>();
+
+    static LauncherAppsCompatVL build(Context context, Object launcherApps) {
+        LauncherAppsCompatVL compat = new LauncherAppsCompatVL(context, launcherApps);
+
+        compat.mListenerClass = ReflectUtils.getClassForName(
+                "android.content.pm.LauncherApps$OnAppsChangedListener");
+        compat.mLauncherAppsClass = ReflectUtils.getClassForName("android.content.pm.LauncherApps");
+
+        compat.mGetActivityList = ReflectUtils.getMethod(compat.mLauncherAppsClass,
+                "getActivityList",
+                String.class, UserHandle.class);
+        compat.mResolveActivity = ReflectUtils.getMethod(compat.mLauncherAppsClass,
+                "resolveActivity",
+                Intent.class, UserHandle.class);
+        compat.mStartActivityForProfile = ReflectUtils.getMethod(compat.mLauncherAppsClass,
+                "startActivityForProfile",
+                ComponentName.class, Rect.class, Bundle.class, UserHandle.class);
+        compat.mAddOnAppsChangedListener = ReflectUtils.getMethod(compat.mLauncherAppsClass,
+                "addOnAppsChangedListener", compat.mListenerClass);
+        compat.mRemoveOnAppsChangedListener = ReflectUtils.getMethod(compat.mLauncherAppsClass,
+                "removeOnAppsChangedListener", compat.mListenerClass);
+        compat.mIsPackageEnabledForProfile = ReflectUtils.getMethod(compat.mLauncherAppsClass,
+                "isPackageEnabledForProfile", String.class, UserHandle.class);
+        compat.mIsActivityEnabledForProfile = ReflectUtils.getMethod(compat.mLauncherAppsClass,
+                "isActivityEnabledForProfile", ComponentName.class, UserHandle.class);
+
+        if (compat.mListenerClass != null
+                && compat.mLauncherAppsClass != null
+                && compat.mGetActivityList != null
+                && compat.mResolveActivity != null
+                && compat.mStartActivityForProfile != null
+                && compat.mAddOnAppsChangedListener != null
+                && compat.mRemoveOnAppsChangedListener != null
+                && compat.mIsPackageEnabledForProfile != null
+                && compat.mIsActivityEnabledForProfile != null) {
+            return compat;
+        }
+        return null;
+    }
+
+    private LauncherAppsCompatVL(Context context, Object launcherApps) {
+        super();
+        mLauncherApps = launcherApps;
+    }
+
+    public List<LauncherActivityInfoCompat> getActivityList(String packageName,
+            UserHandleCompat user) {
+        List<Object> list = (List<Object>) ReflectUtils.invokeMethod(mLauncherApps,
+                mGetActivityList, packageName, user.getUser());
+        if (list.size() == 0) {
+            return Collections.EMPTY_LIST;
+        }
+        ArrayList<LauncherActivityInfoCompat> compatList =
+                new ArrayList<LauncherActivityInfoCompat>(list.size());
+        for (Object info : list) {
+            compatList.add(new LauncherActivityInfoCompatVL(info));
+        }
+        return compatList;
+    }
+
+    public LauncherActivityInfoCompat resolveActivity(Intent intent, UserHandleCompat user) {
+        return new LauncherActivityInfoCompatVL(ReflectUtils.invokeMethod(mLauncherApps,
+                        mResolveActivity, intent, user.getUser()));
+    }
+
+    public void startActivityForProfile(ComponentName component, Rect sourceBounds,
+            Bundle opts, UserHandleCompat user) {
+        ReflectUtils.invokeMethod(mLauncherApps, mStartActivityForProfile,
+                component, sourceBounds, opts, user.getUser());
+    }
+
+    public void addOnAppsChangedListener(LauncherAppsCompat.OnAppsChangedListenerCompat listener) {
+        Object wrappedListener = Proxy.newProxyInstance(mListenerClass.getClassLoader(),
+                new Class[]{mListenerClass}, new WrappedListener(listener));
+        synchronized (mListeners) {
+            mListeners.put(listener, wrappedListener);
+        }
+        ReflectUtils.invokeMethod(mLauncherApps, mAddOnAppsChangedListener, wrappedListener);
+    }
+
+    public void removeOnAppsChangedListener(
+            LauncherAppsCompat.OnAppsChangedListenerCompat listener) {
+        Object wrappedListener = null;
+        synchronized (mListeners) {
+            wrappedListener = mListeners.remove(listener);
+        }
+        if (wrappedListener != null) {
+            ReflectUtils.invokeMethod(mLauncherApps, mRemoveOnAppsChangedListener, wrappedListener);
+        }
+    }
+
+    public boolean isPackageEnabledForProfile(String packageName, UserHandleCompat user) {
+        return (Boolean) ReflectUtils.invokeMethod(mLauncherApps, mIsPackageEnabledForProfile,
+                packageName, user.getUser());
+    }
+
+    public boolean isActivityEnabledForProfile(ComponentName component, UserHandleCompat user) {
+        return (Boolean) ReflectUtils.invokeMethod(mLauncherApps, mIsActivityEnabledForProfile,
+                component, user.getUser());
+    }
+
+    private static class WrappedListener implements InvocationHandler {
+        private LauncherAppsCompat.OnAppsChangedListenerCompat mListener;
+
+        public WrappedListener(LauncherAppsCompat.OnAppsChangedListenerCompat listener) {
+            mListener = listener;
+        }
+
+        public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
+            try {
+                String methodName = m.getName();
+                if ("onPackageRemoved".equals(methodName)) {
+                    onPackageRemoved((UserHandle) args[0], (String) args[1]);
+                } else if ("onPackageAdded".equals(methodName)) {
+                    onPackageAdded((UserHandle) args[0], (String) args[1]);
+                } else if ("onPackageChanged".equals(methodName)) {
+                    onPackageChanged((UserHandle) args[0], (String) args[1]);
+                } else if ("onPackagesAvailable".equals(methodName)) {
+                    onPackagesAvailable((UserHandle) args[0], (String []) args[1],
+                            (Boolean) args[2]);
+                } else if ("onPackagesUnavailable".equals(methodName)) {
+                    onPackagesUnavailable((UserHandle) args[0], (String []) args[1],
+                            (Boolean) args[2]);
+                }
+            } finally {
+                return null;
+            }
+        }
+
+        public void onPackageRemoved(UserHandle user, String packageName) {
+            mListener.onPackageRemoved(UserHandleCompat.fromUser(user), packageName);
+        }
+
+        public void onPackageAdded(UserHandle user, String packageName) {
+            mListener.onPackageAdded(UserHandleCompat.fromUser(user), packageName);
+        }
+
+        public void onPackageChanged(UserHandle user, String packageName) {
+            mListener.onPackageChanged(UserHandleCompat.fromUser(user), packageName);
+        }
+
+        public void onPackagesAvailable(UserHandle user, String[] packageNames, boolean replacing) {
+            mListener.onPackagesAvailable(UserHandleCompat.fromUser(user), packageNames, replacing);
+        }
+
+        public void onPackagesUnavailable(UserHandle user, String[] packageNames,
+                boolean replacing) {
+            mListener.onPackagesUnavailable(UserHandleCompat.fromUser(user), packageNames,
+                    replacing);
+        }
+    }
+}
+
diff --git a/src/com/android/launcher3/compat/ReflectUtils.java b/src/com/android/launcher3/compat/ReflectUtils.java
new file mode 100644
index 0000000..e1b8a1f
--- /dev/null
+++ b/src/com/android/launcher3/compat/ReflectUtils.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2014 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.launcher3.compat;
+
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class ReflectUtils {
+    private static final String TAG = "LauncherReflect";
+
+    public static Class getClassForName(String className) {
+        try {
+            return Class.forName(className);
+        } catch (ClassNotFoundException e) {
+            Log.e(TAG, "Couldn't find class " + className, e);
+            return null;
+        }
+    }
+
+    public static Method getMethod(Class clazz, String method) {
+        try {
+            return clazz.getMethod(method);
+        } catch (NoSuchMethodException e) {
+            Log.e(TAG, "Couldn't find methid " + clazz.getName() + " " + method, e);
+            return null;
+        }
+    }
+
+    public static Method getMethod(Class clazz, String method, Class param1) {
+        try {
+            return clazz.getMethod(method, param1);
+        } catch (NoSuchMethodException e) {
+            Log.e(TAG, "Couldn't find methid " + clazz.getName() + " " + method, e);
+            return null;
+        }
+    }
+
+    public static Method getMethod(Class clazz, String method, Class param1, Class param2) {
+        try {
+            return clazz.getMethod(method, param1, param2);
+        } catch (NoSuchMethodException e) {
+            Log.e(TAG, "Couldn't find methid " + clazz.getName() + " " + method, e);
+            return null;
+        }
+    }
+
+    public static Method getMethod(Class clazz, String method, Class param1, Class param2,
+            Class param3) {
+        try {
+            return clazz.getMethod(method, param1, param2, param3);
+        } catch (NoSuchMethodException e) {
+            Log.e(TAG, "Couldn't find methid " + clazz.getName() + " " + method, e);
+            return null;
+        }
+    }
+
+    public static Method getMethod(Class clazz, String method, Class param1, Class param2,
+            Class param3, Class param4) {
+        try {
+            return clazz.getMethod(method, param1, param2, param3, param4);
+        } catch (NoSuchMethodException e) {
+            Log.e(TAG, "Couldn't find methid " + clazz.getName() + " " + method, e);
+            return null;
+        }
+    }
+
+    public static Object invokeMethod(Object object, Method method) {
+        try {
+            return method.invoke(object);
+        } catch (SecurityException e) {
+            Log.e(TAG, "Couldn't invoke method " + method, e);
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "Couldn't invoke method " + method, e);
+        } catch (IllegalAccessException e) {
+            Log.e(TAG, "Couldn't invoke method " + method, e);
+        } catch (InvocationTargetException e) {
+            Log.e(TAG, "Couldn't invoke method " + method, e);
+        }
+        return null;
+    }
+
+    public static Object invokeMethod(Object object, Method method, Object param1) {
+        try {
+            return method.invoke(object, param1);
+        } catch (SecurityException e) {
+            Log.e(TAG, "Couldn't invoke method " + method, e);
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "Couldn't invoke method " + method, e);
+        } catch (IllegalAccessException e) {
+            Log.e(TAG, "Couldn't invoke method " + method, e);
+        } catch (InvocationTargetException e) {
+            Log.e(TAG, "Couldn't invoke method " + method, e);
+        }
+        return null;
+    }
+
+    public static Object invokeMethod(Object object, Method method, Object param1, Object param2) {
+        try {
+            return method.invoke(object, param1, param2);
+        } catch (SecurityException e) {
+            Log.e(TAG, "Couldn't invoke method " + method, e);
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "Couldn't invoke method " + method, e);
+        } catch (IllegalAccessException e) {
+            Log.e(TAG, "Couldn't invoke method " + method, e);
+        } catch (InvocationTargetException e) {
+            Log.e(TAG, "Couldn't invoke method " + method, e);
+        }
+        return null;
+    }
+
+    public static Object invokeMethod(Object object, Method method, Object param1, Object param2,
+            Object param3) {
+        try {
+            return method.invoke(object, param1, param2, param3);
+        } catch (SecurityException e) {
+            Log.e(TAG, "Couldn't invoke method " + method, e);
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "Couldn't invoke method " + method, e);
+        } catch (IllegalAccessException e) {
+            Log.e(TAG, "Couldn't invoke method " + method, e);
+        } catch (InvocationTargetException e) {
+            Log.e(TAG, "Couldn't invoke method " + method, e);
+        }
+        return null;
+    }
+
+    public static Object invokeMethod(Object object, Method method, Object param1, Object param2,
+            Object param3, Object param4) {
+        try {
+            return method.invoke(object, param1, param2, param3, param4);
+        } catch (SecurityException e) {
+            Log.e(TAG, "Couldn't invoke method " + method, e);
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "Couldn't invoke method " + method, e);
+        } catch (IllegalAccessException e) {
+            Log.e(TAG, "Couldn't invoke method " + method, e);
+        } catch (InvocationTargetException e) {
+            Log.e(TAG, "Couldn't invoke method " + method, e);
+        }
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/compat/UserHandleCompat.java b/src/com/android/launcher3/compat/UserHandleCompat.java
new file mode 100644
index 0000000..8f5dda2
--- /dev/null
+++ b/src/com/android/launcher3/compat/UserHandleCompat.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2014 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.launcher3.compat;
+
+import android.os.Build;
+import android.os.UserHandle;
+
+public class UserHandleCompat {
+    private UserHandle mUser;
+
+    private UserHandleCompat(UserHandle user) {
+        mUser = user;
+    }
+
+    private UserHandleCompat() {
+    }
+
+    public static UserHandleCompat myUserHandle() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            return new UserHandleCompat(android.os.Process.myUserHandle());
+        } else {
+            return new UserHandleCompat();
+        }
+    }
+
+    static UserHandleCompat fromUser(UserHandle user) {
+        if (user == null) {
+            return null;
+        } else {
+            return new UserHandleCompat(user);
+        }
+    }
+
+    UserHandle getUser() {
+        return mUser;
+    }
+
+    @Override
+    public String toString() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            return mUser.toString();
+        } else {
+            return "";
+        }
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof UserHandleCompat)) {
+            return false;
+        }
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            return mUser.equals(((UserHandleCompat) other).mUser);
+        } else {
+            return true;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            return mUser.hashCode();
+        } else {
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/compat/UserManagerCompat.java b/src/com/android/launcher3/compat/UserManagerCompat.java
new file mode 100644
index 0000000..256e04a
--- /dev/null
+++ b/src/com/android/launcher3/compat/UserManagerCompat.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 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.launcher3.compat;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+
+import java.util.List;
+
+public abstract class UserManagerCompat {
+    protected UserManagerCompat() {
+    }
+
+    public static UserManagerCompat getInstance(Context context) {
+        // TODO change this to use api version once L gets an API number.
+        if ("L".equals(Build.VERSION.CODENAME)) {
+            return new UserManagerCompatVL(context);
+        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            return new UserManagerCompatV17(context);
+        } else {
+            return new UserManagerCompatV16();
+        }
+    }
+
+    public abstract List<UserHandleCompat> getUserProfiles();
+    public abstract long getSerialNumberForUser(UserHandleCompat user);
+    public abstract UserHandleCompat getUserForSerialNumber(long serialNumber);
+    public abstract Drawable getBadgedDrawableForUser(Drawable unbadged, UserHandleCompat user);
+}
diff --git a/src/com/android/launcher3/compat/UserManagerCompatV16.java b/src/com/android/launcher3/compat/UserManagerCompatV16.java
new file mode 100644
index 0000000..2009e4e
--- /dev/null
+++ b/src/com/android/launcher3/compat/UserManagerCompatV16.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 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.launcher3.compat;
+
+import android.graphics.drawable.Drawable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class UserManagerCompatV16 extends UserManagerCompat {
+
+    UserManagerCompatV16() {
+    }
+
+    public List<UserHandleCompat> getUserProfiles() {
+        List<UserHandleCompat> profiles = new ArrayList<UserHandleCompat>(1);
+        profiles.add(UserHandleCompat.myUserHandle());
+        return profiles;
+    }
+
+    public UserHandleCompat getUserForSerialNumber(long serialNumber) {
+        return UserHandleCompat.myUserHandle();
+    }
+
+    public Drawable getBadgedDrawableForUser(Drawable unbadged,
+            UserHandleCompat user) {
+        return unbadged;
+    }
+
+    public long getSerialNumberForUser(UserHandleCompat user) {
+        return 0;
+    }
+}
diff --git a/src/com/android/launcher3/compat/UserManagerCompatV17.java b/src/com/android/launcher3/compat/UserManagerCompatV17.java
new file mode 100644
index 0000000..055359a
--- /dev/null
+++ b/src/com/android/launcher3/compat/UserManagerCompatV17.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 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.launcher3.compat;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class UserManagerCompatV17 extends UserManagerCompatV16 {
+    protected UserManager mUserManager;
+
+    UserManagerCompatV17(Context context) {
+        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+    }
+
+    public long getSerialNumberForUser(UserHandleCompat user) {
+        return mUserManager.getSerialNumberForUser(user.getUser());
+    }
+
+    public UserHandleCompat getUserForSerialNumber(long serialNumber) {
+        return UserHandleCompat.fromUser(mUserManager.getUserForSerialNumber(serialNumber));
+    }
+}
+
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVL.java b/src/com/android/launcher3/compat/UserManagerCompatVL.java
new file mode 100644
index 0000000..8d3ca85
--- /dev/null
+++ b/src/com/android/launcher3/compat/UserManagerCompatVL.java
@@ -0,0 +1,68 @@
+
+/*
+ * Copyright (C) 2014 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.launcher3.compat;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+public class UserManagerCompatVL extends UserManagerCompatV17 {
+
+    UserManagerCompatVL(Context context) {
+        super(context);
+    }
+
+    public List<UserHandleCompat> getUserProfiles() {
+        Method method = ReflectUtils.getMethod(mUserManager.getClass(), "getUserProfiles");
+        if (method != null) {
+            List<UserHandle> users = (List<UserHandle>) ReflectUtils.invokeMethod(
+                    mUserManager, method);
+            if (users != null) {
+                ArrayList<UserHandleCompat> compatUsers = new ArrayList<UserHandleCompat>(
+                        users.size());
+                for (UserHandle user : users) {
+                    compatUsers.add(UserHandleCompat.fromUser(user));
+                }
+                return compatUsers;
+            }
+        }
+        // Fall back to non L version.
+        return super.getUserProfiles();
+    }
+
+    public Drawable getBadgedDrawableForUser(Drawable unbadged, UserHandleCompat user) {
+        Method method = ReflectUtils.getMethod(mUserManager.getClass(), "getBadgedDrawableForUser",
+                Drawable.class, UserHandle.class);
+        if (method != null) {
+            Drawable d = (Drawable) ReflectUtils.invokeMethod(mUserManager, method, unbadged,
+                    user.getUser());
+            if (d != null) {
+                return d;
+            }
+        }
+        // Fall back to non L version.
+        return super.getBadgedDrawableForUser(unbadged, user);
+    }
+}
+