Removing support for adding legacy shortcuts.

All existing legacy shortcuts will be migrated one-time to deep shortcuts
This shortcuts are pinned under the Launcher package, with custom badging

Bug: 275875209
Test: Updated unit tests
Flag: N/A
Change-Id: I7da001f724776ad8d6c807517b7e4e259de626c2
diff --git a/res/raw/downgrade_schema.json b/res/raw/downgrade_schema.json
index b8d0c6f..9929916 100644
--- a/res/raw/downgrade_schema.json
+++ b/res/raw/downgrade_schema.json
@@ -2,8 +2,11 @@
   // Note: Comments are not supported in JSON schema, but android parser is lenient.
 
   // Maximum DB version supported by this schema
-  "version" : 31,
-
+  "version" : 32,
+  "downgrade_to_31" : [
+    "ALTER TABLE favorites ADD COLUMN iconPackage TEXT;",
+    "ALTER TABLE favorites ADD COLUMN iconResource TEXT;"
+  ],
   "downgrade_to_30" : [],
   "downgrade_to_29" : [],
   "downgrade_to_28" : [
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 7552b22..aebc1d0 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -192,9 +192,6 @@
     <string name="permdesc_write_settings">Allows the app to change the settings and
         shortcuts in home.</string>
 
-    <!-- Toast shown on clicking a direct call shortcut. [CHAR_LIMIT=80] -->
-    <string name="msg_no_phone_permission"><xliff:g id="app_name" example="Launcher3">%1$s</xliff:g> is not allowed to make phone calls</string>
-
     <!-- Widgets: -->
     <skip />
 
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 4f7380a..59f56ff 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -82,7 +82,6 @@
 import android.content.Intent;
 import android.content.IntentSender;
 import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.database.sqlite.SQLiteDatabase;
 import android.graphics.Color;
@@ -92,7 +91,6 @@
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.Parcelable;
-import android.os.Process;
 import android.os.StrictMode;
 import android.os.SystemClock;
 import android.os.Trace;
@@ -115,7 +113,6 @@
 import android.view.WindowManager.LayoutParams;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.OvershootInterpolator;
-import android.widget.Toast;
 import android.window.BackEvent;
 import android.window.OnBackAnimationCallback;
 
@@ -160,7 +157,6 @@
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.model.ItemInstallQueue;
-import com.android.launcher3.model.ModelUtils;
 import com.android.launcher3.model.ModelWriter;
 import com.android.launcher3.model.StringCache;
 import com.android.launcher3.model.WidgetsModel;
@@ -192,7 +188,6 @@
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.OnboardingPrefs;
-import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.PendingRequestArgs;
 import com.android.launcher3.util.RunnableList;
@@ -263,8 +258,6 @@
     public static final int REQUEST_BIND_PENDING_APPWIDGET = 12;
     public static final int REQUEST_RECONFIGURE_APPWIDGET = 13;
 
-    private static final int REQUEST_PERMISSION_CALL_PHONE = 14;
-
     private static final float BOUNCE_ANIMATION_TENSION = 1.3f;
 
     /**
@@ -975,33 +968,6 @@
         handleActivityResult(requestCode, resultCode, data);
     }
 
-    @Override
-    public void onRequestPermissionsResult(int requestCode, String[] permissions,
-            int[] grantResults) {
-        PendingRequestArgs pendingArgs = mPendingRequestArgs;
-        if (requestCode == REQUEST_PERMISSION_CALL_PHONE && pendingArgs != null
-                && pendingArgs.getRequestCode() == REQUEST_PERMISSION_CALL_PHONE) {
-            setWaitingForResult(null);
-
-            View v = null;
-            CellPos cellPos = getCellPosMapper().mapModelToPresenter(pendingArgs);
-            CellLayout layout = getCellLayout(pendingArgs.container, cellPos.screenId);
-            if (layout != null) {
-                v = layout.getChildAt(cellPos.cellX, cellPos.cellY);
-            }
-            Intent intent = pendingArgs.getPendingIntent();
-
-            if (grantResults.length > 0
-                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
-                startActivitySafely(v, intent, null);
-            } else {
-                // TODO: Show a snack bar with link to settings
-                Toast.makeText(this, getString(R.string.msg_no_phone_permission,
-                        getString(R.string.derived_app_name)), Toast.LENGTH_SHORT).show();
-            }
-        }
-    }
-
     /**
      * Check to see if a given screen id exists. If not, create it at the end, return the new id.
      *
@@ -1417,21 +1383,9 @@
 
         WorkspaceItemInfo info = PinRequestHelper.createWorkspaceItemFromPinItemRequest(
                     this, PinRequestHelper.getPinItemRequest(data), 0);
-
         if (info == null) {
-            // Legacy shortcuts are only supported for primary profile.
-            info = Process.myUserHandle().equals(args.user)
-                    ? ModelUtils.fromLegacyShortcutIntent(this, data) : null;
-
-            if (info == null) {
-                Log.e(TAG, "Unable to parse a valid custom shortcut result");
-                return;
-            } else if (!new PackageManagerHelper(this).hasPermissionForActivity(
-                    info.intent, args.getPendingIntent().getComponent().getPackageName())) {
-                // The app is trying to add a shortcut without sufficient permissions
-                Log.e(TAG, "Ignoring malicious intent " + info.intent.toUri(0));
-                return;
-            }
+            Log.e(TAG, "Unable to parse a valid shortcut result");
+            return;
         }
 
         if (container < 0) {
@@ -2151,27 +2105,6 @@
         }
     }
 
-    @TargetApi(Build.VERSION_CODES.M)
-    @Override
-    public boolean onErrorStartingShortcut(Intent intent, ItemInfo info) {
-        // Due to legacy reasons, direct call shortcuts require Launchers to have the
-        // corresponding permission. Show the appropriate permission prompt if that
-        // is the case.
-        if (intent.getComponent() == null
-                && Intent.ACTION_CALL.equals(intent.getAction())
-                && checkSelfPermission(android.Manifest.permission.CALL_PHONE) !=
-                PackageManager.PERMISSION_GRANTED) {
-
-            setWaitingForResult(PendingRequestArgs
-                    .forIntent(REQUEST_PERMISSION_CALL_PHONE, intent, info));
-            requestPermissions(new String[]{android.Manifest.permission.CALL_PHONE},
-                    REQUEST_PERMISSION_CALL_PHONE);
-            return true;
-        } else {
-            return false;
-        }
-    }
-
     @Override
     public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
         if (!hasBeenResumed()) {
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 1cd2a30..1bbb09a 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -131,18 +131,6 @@
         public static final int ITEM_TYPE_SEARCH_ACTION = 9;
 
         /**
-         * The icon package name in Intent.ShortcutIconResource
-         * <P>Type: TEXT</P>
-         */
-        public static final String ICON_PACKAGE = "iconPackage";
-
-        /**
-         * The icon resource name in Intent.ShortcutIconResource
-         * <P>Type: TEXT</P>
-         */
-        public static final String ICON_RESOURCE = "iconResource";
-
-        /**
          * The custom icon bitmap.
          * <P>Type: BLOB</P>
          */
@@ -357,8 +345,6 @@
                     "spanY INTEGER," +
                     "itemType INTEGER," +
                     "appWidgetId INTEGER NOT NULL DEFAULT -1," +
-                    "iconPackage TEXT," +
-                    "iconResource TEXT," +
                     "icon BLOB," +
                     "appWidgetProvider TEXT," +
                     "modified INTEGER NOT NULL DEFAULT 0," +
diff --git a/src/com/android/launcher3/model/DatabaseHelper.java b/src/com/android/launcher3/model/DatabaseHelper.java
index 3578b67..1840b75 100644
--- a/src/com/android/launcher3/model/DatabaseHelper.java
+++ b/src/com/android/launcher3/model/DatabaseHelper.java
@@ -72,7 +72,7 @@
      * Represents the schema of the database. Changes in scheme need not be backwards compatible.
      * When increasing the scheme version, ensure that downgrade_schema.json is updated
      */
-    public static final int SCHEMA_VERSION = 31;
+    public static final int SCHEMA_VERSION = 32;
     private static final String TAG = "DatabaseHelper";
     private static final boolean LOGD = false;
 
@@ -327,6 +327,10 @@
                 return;
             }
             case 31: {
+                LauncherDbUtils.migrateLegacyShortcuts(mContext, db);
+            }
+            // Fall through
+            case 32: {
                 // DB Upgraded successfully
                 return;
             }
diff --git a/src/com/android/launcher3/model/DbDowngradeHelper.java b/src/com/android/launcher3/model/DbDowngradeHelper.java
index e5c44d1..006a9b7 100644
--- a/src/com/android/launcher3/model/DbDowngradeHelper.java
+++ b/src/com/android/launcher3/model/DbDowngradeHelper.java
@@ -72,8 +72,18 @@
         }
     }
 
+    /**
+     * Creates a helper from the provided file
+     */
     public static DbDowngradeHelper parse(File file) throws JSONException, IOException {
-        JSONObject obj = new JSONObject(new String(IOUtils.toByteArray(file)));
+        return parse(IOUtils.toByteArray(file));
+    }
+
+    /**
+     * Creates a helper from the provided bytes
+     */
+    public static DbDowngradeHelper parse(byte[] fileData) throws JSONException {
+        JSONObject obj = new JSONObject(new String(fileData));
         DbDowngradeHelper helper = new DbDowngradeHelper(obj.getInt(KEY_VERSION));
         for (int version = helper.version - 1; version > 0; version--) {
             if (obj.has(KEY_DOWNGRADE_TO + version)) {
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 855a69d..c237f5b 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -75,8 +75,6 @@
     private final IntArray mRestoredRows = new IntArray();
     private final IntSparseArrayMap<GridOccupancy> mOccupied = new IntSparseArrayMap<>();
 
-    private final int mIconPackageIndex;
-    private final int mIconResourceIndex;
     private final int mIconIndex;
     public final int mTitleIndex;
 
@@ -122,8 +120,6 @@
 
         // Init column indices
         mIconIndex = getColumnIndexOrThrow(Favorites.ICON);
-        mIconPackageIndex = getColumnIndexOrThrow(Favorites.ICON_PACKAGE);
-        mIconResourceIndex = getColumnIndexOrThrow(Favorites.ICON_RESOURCE);
         mTitleIndex = getColumnIndexOrThrow(Favorites.TITLE);
 
         mIdIndex = getColumnIndexOrThrow(Favorites._ID);
@@ -200,23 +196,25 @@
 
     public IconRequestInfo<WorkspaceItemInfo> createIconRequestInfo(
             WorkspaceItemInfo wai, boolean useLowResIcon) {
-        String packageName = itemType == Favorites.ITEM_TYPE_SHORTCUT
-                ? getString(mIconPackageIndex) : null;
-        String resourceName = itemType == Favorites.ITEM_TYPE_SHORTCUT
-                ? getString(mIconResourceIndex) : null;
         byte[] iconBlob = itemType == Favorites.ITEM_TYPE_SHORTCUT
                 || itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT
                 || restoreFlag != 0
-                ? getBlob(mIconIndex) : null;
+                ? getIconBlob() : null;
 
-        return new IconRequestInfo<>(
-                wai, mActivityInfo, packageName, resourceName, iconBlob, useLowResIcon);
+        return new IconRequestInfo<>(wai, mActivityInfo, iconBlob, useLowResIcon);
+    }
+
+    /**
+     * Returns the icon data for at the current position
+     */
+    public byte[] getIconBlob() {
+        return getBlob(mIconIndex);
     }
 
     /**
      * Returns the title or empty string
      */
-    private String getTitle() {
+    public String getTitle() {
         return Utilities.trim(getString(mTitleIndex));
     }
 
diff --git a/src/com/android/launcher3/model/ModelUtils.java b/src/com/android/launcher3/model/ModelUtils.java
index c21fc38..48fb537 100644
--- a/src/com/android/launcher3/model/ModelUtils.java
+++ b/src/com/android/launcher3/model/ModelUtils.java
@@ -15,18 +15,10 @@
  */
 package com.android.launcher3.model;
 
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.os.Process;
 import android.util.Log;
 
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSet;
@@ -99,60 +91,4 @@
         IntStream.range(0, len).filter(i -> !seen.contains(i)).forEach(result::add);
         return result;
     }
-
-
-    /**
-     * Creates a workspace item info for the legacy shortcut intent
-     */
-    @SuppressWarnings("deprecation")
-    public static WorkspaceItemInfo fromLegacyShortcutIntent(Context context, Intent data) {
-        if (!isValidExtraType(data, Intent.EXTRA_SHORTCUT_INTENT, Intent.class)
-                || !(isValidExtraType(data, Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
-                        Intent.ShortcutIconResource.class))
-                || !(isValidExtraType(data, Intent.EXTRA_SHORTCUT_ICON, Bitmap.class))) {
-
-            Log.e(TAG, "Invalid install shortcut intent");
-            return null;
-        }
-
-        Intent launchIntent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
-        String label = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
-        if (launchIntent == null || label == null) {
-            Log.e(TAG, "Invalid install shortcut intent");
-            return null;
-        }
-
-        final WorkspaceItemInfo info = new WorkspaceItemInfo();
-        info.user = Process.myUserHandle();
-
-        BitmapInfo iconInfo = null;
-        try (LauncherIcons li = LauncherIcons.obtain(context)) {
-            Bitmap bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
-            if (bitmap != null) {
-                iconInfo = li.createIconBitmap(bitmap);
-            } else {
-                info.iconResource = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
-                if (info.iconResource != null) {
-                    iconInfo = li.createIconBitmap(info.iconResource);
-                }
-            }
-        }
-
-        if (iconInfo == null) {
-            Log.e(TAG, "Invalid icon by the app");
-            return null;
-        }
-        info.bitmap = iconInfo;
-        info.contentDescription = info.title = Utilities.trim(label);
-        info.intent = launchIntent;
-        return info;
-    }
-
-    /**
-     * @return true if the extra is either null or is of type {@param type}
-     */
-    private static boolean isValidExtraType(Intent intent, String key, Class type) {
-        Object extra = intent.getParcelableExtra(key);
-        return extra == null || type.isInstance(extra);
-    }
 }
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 3d9d81f..1ab64df 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -36,9 +36,7 @@
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -204,18 +202,6 @@
                     boolean infoUpdated = false;
                     boolean shortcutUpdated = false;
 
-                    // Update shortcuts which use iconResource.
-                    if ((si.iconResource != null)
-                            && packageSet.contains(si.iconResource.packageName)) {
-                        LauncherIcons li = LauncherIcons.obtain(context);
-                        BitmapInfo iconInfo = li.createIconBitmap(si.iconResource);
-                        li.recycle();
-                        if (iconInfo != null) {
-                            si.bitmap = iconInfo;
-                            infoUpdated = true;
-                        }
-                    }
-
                     ComponentName cn = si.getTargetComponent();
                     if (cn != null && matcher.test(si)) {
                         String packageName = cn.getPackageName();
diff --git a/src/com/android/launcher3/model/data/IconRequestInfo.java b/src/com/android/launcher3/model/data/IconRequestInfo.java
index fbf01e5..e77e527 100644
--- a/src/com/android/launcher3/model/data/IconRequestInfo.java
+++ b/src/com/android/launcher3/model/data/IconRequestInfo.java
@@ -18,16 +18,12 @@
 import static android.graphics.BitmapFactory.decodeByteArray;
 
 import android.content.Context;
-import android.content.Intent;
 import android.content.pm.LauncherActivityInfo;
-import android.text.TextUtils;
 import android.util.Log;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.LauncherIcons;
 
 /**
@@ -42,8 +38,6 @@
 
     @NonNull public final T itemInfo;
     @Nullable public final LauncherActivityInfo launcherActivityInfo;
-    @Nullable public final String packageName;
-    @Nullable public final String resourceName;
     @Nullable public final byte[] iconBlob;
     public final boolean useLowResIcon;
 
@@ -54,8 +48,6 @@
         this(
                 itemInfo,
                 launcherActivityInfo,
-                /* packageName= */ null,
-                /* resourceName= */ null,
                 /* iconBlob= */ null,
                 useLowResIcon);
     }
@@ -63,14 +55,10 @@
     public IconRequestInfo(
             @NonNull T itemInfo,
             @Nullable LauncherActivityInfo launcherActivityInfo,
-            @Nullable String packageName,
-            @Nullable String resourceName,
             @Nullable byte[] iconBlob,
             boolean useLowResIcon) {
         this.itemInfo = itemInfo;
         this.launcherActivityInfo = launcherActivityInfo;
-        this.packageName = packageName;
-        this.resourceName = resourceName;
         this.iconBlob = iconBlob;
         this.useLowResIcon = useLowResIcon;
     }
@@ -87,31 +75,16 @@
 
         try (LauncherIcons li = LauncherIcons.obtain(context)) {
             WorkspaceItemInfo info = (WorkspaceItemInfo) itemInfo;
-            if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
-                if (!TextUtils.isEmpty(packageName) || !TextUtils.isEmpty(resourceName)) {
-                    info.iconResource = new Intent.ShortcutIconResource();
-                    info.iconResource.packageName = packageName;
-                    info.iconResource.resourceName = resourceName;
-                    BitmapInfo iconInfo = li.createIconBitmap(info.iconResource);
-                    if (iconInfo != null) {
-                        info.bitmap = iconInfo;
-                        return true;
-                    }
-                }
-            }
-
             // Failed to load from resource, try loading from DB.
-            try {
-                if (iconBlob == null) {
-                    return false;
-                }
-                info.bitmap = li.createIconBitmap(decodeByteArray(
-                        iconBlob, 0, iconBlob.length));
-                return true;
-            } catch (Exception e) {
-                Log.e(TAG, "Failed to decode byte array for info " + info, e);
+            if (iconBlob == null) {
                 return false;
             }
+            info.bitmap = li.createIconBitmap(decodeByteArray(
+                    iconBlob, 0, iconBlob.length));
+            return true;
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to decode byte array for info " + itemInfo, e);
+            return false;
         }
     }
 }
diff --git a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
index 59ef320..01606d4 100644
--- a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
@@ -79,12 +79,6 @@
     public Intent intent;
 
     /**
-     * If isShortcut=true and customIcon=false, this contains a reference to the
-     * shortcut icon as an application's resource.
-     */
-    public Intent.ShortcutIconResource iconResource;
-
-    /**
      * A message to display when the user tries to start a disabled shortcut.
      * This is currently only used for deep shortcuts.
      */
@@ -109,7 +103,6 @@
         super(info);
         title = info.title;
         intent = new Intent(info.intent);
-        iconResource = info.iconResource;
         status = info.status;
         personKeys = info.personKeys.clone();
     }
@@ -141,10 +134,6 @@
         if (!usingLowResIcon()) {
             writer.putIcon(bitmap, user);
         }
-        if (iconResource != null) {
-            writer.put(Favorites.ICON_PACKAGE, iconResource.packageName)
-                    .put(Favorites.ICON_RESOURCE, iconResource.resourceName);
-        }
     }
 
     @Override
diff --git a/src/com/android/launcher3/pm/PinRequestHelper.java b/src/com/android/launcher3/pm/PinRequestHelper.java
index 179061f..667136a 100644
--- a/src/com/android/launcher3/pm/PinRequestHelper.java
+++ b/src/com/android/launcher3/pm/PinRequestHelper.java
@@ -24,8 +24,10 @@
 import android.content.pm.LauncherApps;
 import android.content.pm.LauncherApps.PinItemRequest;
 import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
 import android.os.Build;
 import android.os.Parcelable;
+import android.os.SystemClock;
 
 import androidx.annotation.Nullable;
 
@@ -63,17 +65,10 @@
                 }
             } else {
                 // Block the worker thread until the accept() is called.
-                MODEL_EXECUTOR.execute(new Runnable() {
-                    @Override
-                    public void run() {
-                        try {
-                            Thread.sleep(acceptDelay);
-                        } catch (InterruptedException e) {
-                            // Ignore
-                        }
-                        if (request.isValid()) {
-                            request.accept();
-                        }
+                MODEL_EXECUTOR.execute(() -> {
+                    SystemClock.sleep(acceptDelay);
+                    if (request.isValid()) {
+                        request.accept();
                     }
                 });
             }
@@ -95,4 +90,13 @@
         Parcelable extra = intent.getParcelableExtra(LauncherApps.EXTRA_PIN_ITEM_REQUEST);
         return extra instanceof PinItemRequest ? (PinItemRequest) extra : null;
     }
+
+    /**
+     * Returns a PinItemRequest corresponding to the provided ShortcutInfo
+     */
+    public static PinItemRequest createRequestForShortcut(Context context, ShortcutInfo info) {
+        return context.getSystemService(LauncherApps.class)
+                .getPinItemRequest(context.getSystemService(ShortcutManager.class)
+                        .createShortcutResultIntent(info));
+    }
 }
diff --git a/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java b/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
index 14e67b2..b24ee34 100644
--- a/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
+++ b/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
@@ -151,8 +151,6 @@
     public static List<ShortcutConfigActivityInfo> queryList(
             Context context, @Nullable PackageUserKey packageUser) {
         List<ShortcutConfigActivityInfo> result = new ArrayList<>();
-        UserHandle myUser = Process.myUserHandle();
-
         final List<UserHandle> users;
         final String packageName;
         if (packageUser == null) {
@@ -164,11 +162,9 @@
         }
         LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
         for (UserHandle user : users) {
-            boolean ignoreTargetSdk = myUser.equals(user);
             for (LauncherActivityInfo activityInfo :
                     launcherApps.getShortcutConfigActivityList(packageName, user)) {
-                if (ignoreTargetSdk || activityInfo.getApplicationInfo().targetSdkVersion
-                        >= Build.VERSION_CODES.O) {
+                if (activityInfo.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.O) {
                     result.add(new ShortcutConfigActivityInfoVO(activityInfo));
                 }
             }
diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.java b/src/com/android/launcher3/provider/LauncherDbUtils.java
index b510378..48969fc 100644
--- a/src/com/android/launcher3/provider/LauncherDbUtils.java
+++ b/src/com/android/launcher3/provider/LauncherDbUtils.java
@@ -16,15 +16,34 @@
 
 package com.android.launcher3.provider;
 
+import static com.android.launcher3.icons.IconCache.EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE;
+
+import android.content.ContentValues;
 import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ShortcutInfo;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.Icon;
 import android.os.Binder;
+import android.os.PersistableBundle;
 import android.os.Process;
+import android.os.UserManager;
+import android.text.TextUtils;
 
+import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.model.LoaderCursor;
+import com.android.launcher3.model.UserManagerState;
+import com.android.launcher3.pm.PinRequestHelper;
 import com.android.launcher3.pm.UserCache;
+import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.IntSet;
 
 /**
  * A set of utility methods for Launcher DB used for DB updates and migration.
@@ -73,6 +92,78 @@
     }
 
     /**
+     * Migrates the legacy shortcuts to deep shortcuts pinned under Launcher.
+     * Removes any invalid shortcut or any shortcut which requires some permission to launch
+     */
+    public static void migrateLegacyShortcuts(Context context, SQLiteDatabase db) {
+        Cursor c = db.query(
+                Favorites.TABLE_NAME, null, "itemType = 1", null, null, null, null);
+        UserManagerState ums = new UserManagerState();
+        ums.init(UserCache.INSTANCE.get(context),
+                context.getSystemService(UserManager.class));
+        LoaderCursor lc = new LoaderCursor(c, null, LauncherAppState.getInstance(context), ums);
+        IntSet deletedShortcuts = new IntSet();
+
+        while (lc.moveToNext()) {
+            if (lc.user != Process.myUserHandle()) {
+                deletedShortcuts.add(lc.id);
+                continue;
+            }
+            Intent intent = lc.parseIntent();
+            if (intent == null) {
+                deletedShortcuts.add(lc.id);
+                continue;
+            }
+
+            // Make sure the target intent can be launched without any permissions. Otherwise remove
+            // the shortcut
+            ResolveInfo ri = context.getPackageManager().resolveActivity(intent, 0);
+            if (ri == null || !TextUtils.isEmpty(ri.activityInfo.permission)) {
+                deletedShortcuts.add(lc.id);
+                continue;
+            }
+            PersistableBundle extras = new PersistableBundle();
+            extras.putString(EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE, ri.activityInfo.packageName);
+            ShortcutInfo.Builder infoBuilder = new ShortcutInfo.Builder(
+                    context, "migrated_shortcut-" + lc.id)
+                    .setIntent(intent)
+                    .setExtras(extras)
+                    .setShortLabel(lc.getTitle());
+
+            Bitmap bitmap = null;
+            byte[] iconData = lc.getIconBlob();
+            if (iconData != null) {
+                bitmap = BitmapFactory.decodeByteArray(iconData, 0, iconData.length);
+            }
+            if (bitmap != null) {
+                infoBuilder.setIcon(Icon.createWithBitmap(bitmap));
+            }
+
+            ShortcutInfo info = infoBuilder.build();
+            if (!PinRequestHelper.createRequestForShortcut(context, info).accept()) {
+                deletedShortcuts.add(lc.id);
+                continue;
+            }
+            ContentValues update = new ContentValues();
+            update.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_DEEP_SHORTCUT);
+            update.put(Favorites.INTENT,
+                    ShortcutKey.makeIntent(info.getId(), context.getPackageName()).toUri(0));
+            db.update(Favorites.TABLE_NAME, update, "_id = ?",
+                    new String[] {Integer.toString(lc.id)});
+        }
+        lc.close();
+        if (!deletedShortcuts.isEmpty()) {
+            db.delete(Favorites.TABLE_NAME,
+                    Utilities.createDbSelectionQuery(Favorites._ID, deletedShortcuts.getArray()),
+                    null);
+        }
+
+        // Drop the unused columns
+        db.execSQL("ALTER TABLE " + Favorites.TABLE_NAME + " DROP COLUMN iconPackage;");
+        db.execSQL("ALTER TABLE " + Favorites.TABLE_NAME + " DROP COLUMN iconResource;");
+    }
+
+    /**
      * Utility class to simplify managing sqlite transactions
      */
     public static class SQLiteTransaction extends Binder implements AutoCloseable {
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index a6a2751..1d6bc25 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -16,7 +16,6 @@
 
 package com.android.launcher3.util;
 
-import android.app.AppOpsManager;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
@@ -30,7 +29,6 @@
 import android.content.pm.ResolveInfo;
 import android.graphics.Rect;
 import android.net.Uri;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.text.TextUtils;
@@ -139,48 +137,6 @@
         return (info.flags & ApplicationInfo.FLAG_SUSPENDED) != 0;
     }
 
-    /**
-     * Returns true if {@param srcPackage} has the permission required to start the activity from
-     * {@param intent}. If {@param srcPackage} is null, then the activity should not need
-     * any permissions
-     */
-    public boolean hasPermissionForActivity(Intent intent, String srcPackage) {
-        ResolveInfo target = mPm.resolveActivity(intent, 0);
-        if (target == null) {
-            // Not a valid target
-            return false;
-        }
-        if (TextUtils.isEmpty(target.activityInfo.permission)) {
-            // No permission is needed
-            return true;
-        }
-        if (TextUtils.isEmpty(srcPackage)) {
-            // The activity requires some permission but there is no source.
-            return false;
-        }
-
-        // Source does not have sufficient permissions.
-        if(mPm.checkPermission(target.activityInfo.permission, srcPackage) !=
-                PackageManager.PERMISSION_GRANTED) {
-            return false;
-        }
-
-        // On M and above also check AppOpsManager for compatibility mode permissions.
-        if (TextUtils.isEmpty(AppOpsManager.permissionToOp(target.activityInfo.permission))) {
-            // There is no app-op for this permission, which could have been disabled.
-            return true;
-        }
-
-        // There is no direct way to check if the app-op is allowed for a particular app. Since
-        // app-op is only enabled for apps running in compatibility mode, simply block such apps.
-
-        try {
-            return mPm.getApplicationInfo(srcPackage, 0).targetSdkVersion >= Build.VERSION_CODES.M;
-        } catch (NameNotFoundException e) { }
-
-        return false;
-    }
-
     public Intent getMarketIntent(String packageName) {
         return new Intent(Intent.ACTION_VIEW)
                 .setData(new Uri.Builder()
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index a6744fb..79d4df0 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -437,9 +437,7 @@
                 StrictMode.setVmPolicy(oldPolicy);
             }
         } catch (SecurityException e) {
-            if (!onErrorStartingShortcut(intent, info)) {
-                throw e;
-            }
+            throw e;
         }
     }
 
@@ -459,16 +457,6 @@
         }
     }
 
-    /**
-     * Invoked when a shortcut fails to launch.
-     * @param intent Shortcut intent that failed to start.
-     * @param info Shortcut information.
-     * @return {@code true} if the error is handled by this callback.
-     */
-    default boolean onErrorStartingShortcut(Intent intent, ItemInfo info) {
-        return false;
-    }
-
     default CellPosMapper getCellPosMapper() {
         return CellPosMapper.DEFAULT;
     }
diff --git a/tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java b/tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
index 8a092bf..0a1a9ba 100644
--- a/tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
+++ b/tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
@@ -109,6 +109,21 @@
     }
 
     @Test
+    public void testDowngrade_success_v31() throws Exception {
+        setupTestDb();
+
+        try (SQLiteOpenHelper helper = new MyDatabaseHelper()) {
+            assertFalse(hasFavoritesColumn(helper.getWritableDatabase(), "iconPackage"));
+            assertFalse(hasFavoritesColumn(helper.getWritableDatabase(), "iconResource"));
+        }
+
+        try (TestOpenHelper helper = new TestOpenHelper(24)) {
+            assertTrue(hasFavoritesColumn(helper.getWritableDatabase(), "iconPackage"));
+            assertTrue(hasFavoritesColumn(helper.getWritableDatabase(), "iconResource"));
+        }
+    }
+
+    @Test
     public void testDowngrade_success_v24() throws Exception {
         setupTestDb();
 
@@ -121,34 +136,18 @@
     public void testDowngrade_success_v22() throws Exception {
         setupTestDb();
 
-        SQLiteOpenHelper helper = new TestOpenHelper(22);
-        assertEquals(22, helper.getWritableDatabase().getVersion());
-
-        // Check column does not exist
-        try (Cursor c = helper.getWritableDatabase().query(Favorites.TABLE_NAME,
-                null, null, null, null, null, null)) {
-            assertEquals(-1, c.getColumnIndex(Favorites.OPTIONS));
-
-            // Check data is present
-            assertEquals(10, c.getCount());
+        try (SQLiteOpenHelper helper = new TestOpenHelper(22)) {
+            assertEquals(22, helper.getWritableDatabase().getVersion());
+            assertFalse(hasFavoritesColumn(helper.getWritableDatabase(), Favorites.OPTIONS));
+            assertEquals(10, getFavoriteDataCount(helper.getWritableDatabase()));
         }
-        helper.close();
 
-        helper = new DatabaseHelper(mContext, DB_FILE, false) {
-            @Override
-            public void onOpen(SQLiteDatabase db) { }
-        };
-        assertEquals(DatabaseHelper.SCHEMA_VERSION, helper.getWritableDatabase().getVersion());
-
-        try (Cursor c = helper.getWritableDatabase().query(Favorites.TABLE_NAME,
-                null, null, null, null, null, null)) {
-            // Check column exists
-            assertNotSame(-1, c.getColumnIndex(Favorites.OPTIONS));
-
-            // Check data is present
-            assertEquals(10, c.getCount());
+        try (SQLiteOpenHelper helper = new MyDatabaseHelper()) {
+            assertEquals(DatabaseHelper.SCHEMA_VERSION,
+                    helper.getWritableDatabase().getVersion());
+            assertTrue(hasFavoritesColumn(helper.getWritableDatabase(), Favorites.OPTIONS));
+            assertEquals(10, getFavoriteDataCount(helper.getWritableDatabase()));
         }
-        helper.close();
     }
 
     @Test(expected = DowngradeFailException.class)
@@ -165,10 +164,7 @@
 
         DbDowngradeHelper.updateSchemaFile(mSchemaFile, DatabaseHelper.SCHEMA_VERSION, mContext);
 
-        DatabaseHelper dbHelper = new DatabaseHelper(mContext, DB_FILE, false) {
-            @Override
-            public void onOpen(SQLiteDatabase db) { }
-        };
+        DatabaseHelper dbHelper = new MyDatabaseHelper();
         // Insert mock data
         for (int i = 0; i < 10; i++) {
             ContentValues values = new ContentValues();
@@ -210,4 +206,26 @@
             super(e);
         }
     }
+
+    private static boolean hasFavoritesColumn(SQLiteDatabase db, String columnName) {
+        try (Cursor c = db.query(Favorites.TABLE_NAME, null, null, null, null, null, null)) {
+            return c.getColumnIndex(columnName) >= 0;
+        }
+    }
+
+    public static int getFavoriteDataCount(SQLiteDatabase db) {
+        try (Cursor c = db.query(Favorites.TABLE_NAME, null, null, null, null, null, null)) {
+            return c.getCount();
+        }
+    }
+
+    private class MyDatabaseHelper extends DatabaseHelper {
+
+        MyDatabaseHelper() {
+            super(mContext, DB_FILE, false);
+        }
+
+        @Override
+        public void onOpen(SQLiteDatabase db) { }
+    }
 }
diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
index 7ab86ad..d192be4 100644
--- a/tests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -27,8 +27,6 @@
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
 import static com.android.launcher3.LauncherSettings.Favorites.ICON;
-import static com.android.launcher3.LauncherSettings.Favorites.ICON_PACKAGE;
-import static com.android.launcher3.LauncherSettings.Favorites.ICON_RESOURCE;
 import static com.android.launcher3.LauncherSettings.Favorites.INTENT;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
@@ -97,11 +95,10 @@
         mApp = LauncherAppState.getInstance(mContext);
 
         mCursor = new MatrixCursor(new String[] {
-                ICON, ICON_PACKAGE, ICON_RESOURCE, TITLE,
-                _ID, CONTAINER, ITEM_TYPE, PROFILE_ID,
-                SCREEN, CELLX, CELLY, RESTORED, INTENT,
-                APPWIDGET_ID, APPWIDGET_PROVIDER, SPANX,
-                SPANY, RANK, OPTIONS, APPWIDGET_SOURCE
+                ICON, TITLE, _ID, CONTAINER, ITEM_TYPE,
+                PROFILE_ID, SCREEN, CELLX, CELLY, RESTORED,
+                INTENT, APPWIDGET_ID, APPWIDGET_PROVIDER,
+                SPANX, SPANY, RANK, OPTIONS, APPWIDGET_SOURCE
         });
 
         UserManagerState ums = new UserManagerState();
diff --git a/tests/src/com/android/launcher3/provider/LauncherDbUtilsTest.java b/tests/src/com/android/launcher3/provider/LauncherDbUtilsTest.java
new file mode 100644
index 0000000..2b6f9ff
--- /dev/null
+++ b/tests/src/com/android/launcher3/provider/LauncherDbUtilsTest.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2023 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.provider;
+
+import static android.content.pm.LauncherApps.EXTRA_PIN_ITEM_REQUEST;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.launcher3.LauncherSettings.Favorites.INTENT;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.pm.LauncherApps.PinItemRequest;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
+import android.os.Process;
+
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.R;
+import com.android.launcher3.model.DatabaseHelper;
+import com.android.launcher3.model.DbDowngradeHelper;
+import com.android.launcher3.settings.SettingsActivity;
+import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.util.IOUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.io.InputStream;
+
+/**
+ * Test for {@link LauncherDbUtils}.
+ */
+public class LauncherDbUtilsTest {
+
+    private Context mContext;
+    private ArgumentCaptor<ShortcutInfo> mInfoArgumentCaptor;
+    private boolean mPinningAllowed = true;
+
+    @Before
+    public void setup() {
+        PinItemRequest req = mock(PinItemRequest.class);
+        doAnswer(i -> mPinningAllowed).when(req).accept();
+
+        mInfoArgumentCaptor = ArgumentCaptor.forClass(ShortcutInfo.class);
+        ShortcutManager sm = mock(ShortcutManager.class);
+        when(sm.createShortcutResultIntent(mInfoArgumentCaptor.capture()))
+                .thenReturn(new Intent().putExtra(EXTRA_PIN_ITEM_REQUEST, req));
+
+        mContext = new ContextWrapper(getInstrumentation().getTargetContext()) {
+
+            @Override
+            public Object getSystemService(String name) {
+                return SHORTCUT_SERVICE.equals(name) ? sm : super.getSystemService(name);
+            }
+        };
+    }
+
+    @Test
+    public void migrateLegacyShortcuts_invalidIntent() throws Exception {
+        SQLiteDatabase db = setupLegacyShortcut(null);
+        assertEquals(1, getFavoriteDataCount(db));
+
+        LauncherDbUtils.migrateLegacyShortcuts(mContext, db);
+
+        assertEquals(0, getFavoriteDataCount(db));
+    }
+
+    @Test
+    public void migrateLegacyShortcuts_protectedIntent() throws Exception {
+        Intent intent = new Intent(Intent.ACTION_CALL)
+                .setData(Uri.parse("tel:0000000000"));
+        SQLiteDatabase db = setupLegacyShortcut(intent);
+        assertEquals(1, getFavoriteDataCount(db));
+
+        LauncherDbUtils.migrateLegacyShortcuts(mContext, db);
+
+        assertEquals(0, getFavoriteDataCount(db));
+    }
+
+    @Test
+    public void migrateLegacyShortcuts_pinningDisabled() throws Exception {
+        mPinningAllowed = false;
+        Intent intent = new Intent(mContext, SettingsActivity.class);
+        SQLiteDatabase db = setupLegacyShortcut(intent);
+        assertEquals(1, getFavoriteDataCount(db));
+
+        LauncherDbUtils.migrateLegacyShortcuts(mContext, db);
+
+        assertEquals(0, getFavoriteDataCount(db));
+    }
+
+    @Test
+    public void migrateLegacyShortcuts_success() throws Exception {
+        Intent intent = new Intent(mContext, SettingsActivity.class);
+        SQLiteDatabase db = setupLegacyShortcut(intent);
+        assertEquals(1, getFavoriteDataCount(db));
+
+        LauncherDbUtils.migrateLegacyShortcuts(mContext, db);
+
+        assertEquals(1, getFavoriteDataCount(db));
+        ShortcutInfo info = mInfoArgumentCaptor.getValue();
+        assertNotNull(info);
+        assertEquals("Hello", info.getTitle());
+        try (Cursor c = db.query(Favorites.TABLE_NAME, null, null, null, null, null, null)) {
+            c.moveToNext();
+            assertEquals(Favorites.ITEM_TYPE_DEEP_SHORTCUT, c.getInt(c.getColumnIndex(ITEM_TYPE)));
+
+            ShortcutKey key = ShortcutKey.fromIntent(
+                    Intent.parseUri(c.getString(c.getColumnIndex(INTENT)), 0),
+                    Process.myUserHandle());
+
+            assertEquals(info.getId(), key.getId());
+            assertEquals(info.getPackage(), key.getPackageName());
+        }
+    }
+
+    private SQLiteDatabase setupLegacyShortcut(Intent intent) throws Exception {
+        SQLiteDatabase db = new MyDatabaseHelper().getWritableDatabase();
+        try (InputStream in = mContext.getResources().openRawResource(R.raw.downgrade_schema)) {
+            DbDowngradeHelper.parse(IOUtils.toByteArray(in)).onDowngrade(db, db.getVersion(), 31);
+        }
+
+        ContentValues cv = new ContentValues();
+        cv.put("itemType", 1);
+        cv.put("title", "Hello");
+        cv.put("intent", intent == null ? null : intent.toUri(0));
+        db.insert(Favorites.TABLE_NAME, null, cv);
+        return db;
+    }
+
+    public static int getFavoriteDataCount(SQLiteDatabase db) {
+        try (Cursor c = db.query(Favorites.TABLE_NAME, null, null, null, null, null, null)) {
+            return c.getCount();
+        }
+    }
+
+    private class MyDatabaseHelper extends DatabaseHelper {
+
+        MyDatabaseHelper() {
+            super(mContext, null, false);
+        }
+
+        @Override
+        protected void handleOneTimeDataUpgrade(SQLiteDatabase db) { }
+
+        protected void onEmptyDbCreated() { }
+    }
+}