Preventing a shortcut which requires permissions from being added to
homescreen
A shortcut can be added by any app as INSTALL_SHORTCUT is a normal
level permission. But the intent is actually launched by the launcher
app which can have other permission as well.
> When adding a shortcut from the broadcast, verify that the intent does
not require any permission
> When adding a shortcut using the two-step drop process, verify that
the source app also has the permission to create such a shortcut
Bug: 30778130
Change-Id: I33a391bc0af81248aaff4459aaa79d1adc77926b
(cherry picked from commit fb5096d07b)
This commit is contained in:
@@ -33,6 +33,7 @@ import com.android.launcher3.compat.LauncherActivityInfoCompat;
|
|||||||
import com.android.launcher3.compat.LauncherAppsCompat;
|
import com.android.launcher3.compat.LauncherAppsCompat;
|
||||||
import com.android.launcher3.compat.UserHandleCompat;
|
import com.android.launcher3.compat.UserHandleCompat;
|
||||||
import com.android.launcher3.compat.UserManagerCompat;
|
import com.android.launcher3.compat.UserManagerCompat;
|
||||||
|
import com.android.launcher3.util.PackageManagerHelper;
|
||||||
import com.android.launcher3.util.Thunk;
|
import com.android.launcher3.util.Thunk;
|
||||||
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
@@ -146,6 +147,15 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
|
|||||||
}
|
}
|
||||||
PendingInstallShortcutInfo info = createPendingInfo(context, data);
|
PendingInstallShortcutInfo info = createPendingInfo(context, data);
|
||||||
if (info != null) {
|
if (info != null) {
|
||||||
|
if (!info.isLauncherActivity()) {
|
||||||
|
// Since its a custom shortcut, verify that it is safe to launch.
|
||||||
|
if (!PackageManagerHelper.hasPermissionForActivity(
|
||||||
|
context, info.launchIntent, null)) {
|
||||||
|
// Target cannot be launched, or requires some special permission to launch
|
||||||
|
Log.e(TAG, "Ignoring malicious intent " + info.launchIntent.toUri(0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
queuePendingShortcutInfo(info, context);
|
queuePendingShortcutInfo(info, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ import com.android.launcher3.compat.UserManagerCompat;
|
|||||||
import com.android.launcher3.model.WidgetsModel;
|
import com.android.launcher3.model.WidgetsModel;
|
||||||
import com.android.launcher3.util.ComponentKey;
|
import com.android.launcher3.util.ComponentKey;
|
||||||
import com.android.launcher3.util.LongArrayMap;
|
import com.android.launcher3.util.LongArrayMap;
|
||||||
|
import com.android.launcher3.util.PackageManagerHelper;
|
||||||
import com.android.launcher3.util.TestingUtils;
|
import com.android.launcher3.util.TestingUtils;
|
||||||
import com.android.launcher3.util.Thunk;
|
import com.android.launcher3.util.Thunk;
|
||||||
import com.android.launcher3.widget.PendingAddWidgetInfo;
|
import com.android.launcher3.widget.PendingAddWidgetInfo;
|
||||||
@@ -191,6 +192,8 @@ public class Launcher extends Activity
|
|||||||
private static final String RUNTIME_STATE_PENDING_ADD_SPAN_X = "launcher.add_span_x";
|
private static final String RUNTIME_STATE_PENDING_ADD_SPAN_X = "launcher.add_span_x";
|
||||||
// Type: int
|
// Type: int
|
||||||
private static final String RUNTIME_STATE_PENDING_ADD_SPAN_Y = "launcher.add_span_y";
|
private static final String RUNTIME_STATE_PENDING_ADD_SPAN_Y = "launcher.add_span_y";
|
||||||
|
// Type: int
|
||||||
|
private static final String RUNTIME_STATE_PENDING_ADD_COMPONENT = "launcher.add_component";
|
||||||
// Type: parcelable
|
// Type: parcelable
|
||||||
private static final String RUNTIME_STATE_PENDING_ADD_WIDGET_INFO = "launcher.add_widget_info";
|
private static final String RUNTIME_STATE_PENDING_ADD_WIDGET_INFO = "launcher.add_widget_info";
|
||||||
// Type: parcelable
|
// Type: parcelable
|
||||||
@@ -242,7 +245,7 @@ public class Launcher extends Activity
|
|||||||
private AppWidgetManagerCompat mAppWidgetManager;
|
private AppWidgetManagerCompat mAppWidgetManager;
|
||||||
private LauncherAppWidgetHost mAppWidgetHost;
|
private LauncherAppWidgetHost mAppWidgetHost;
|
||||||
|
|
||||||
@Thunk ItemInfo mPendingAddInfo = new ItemInfo();
|
@Thunk PendingAddItemInfo mPendingAddInfo = new PendingAddItemInfo();
|
||||||
private LauncherAppWidgetProviderInfo mPendingAddWidgetInfo;
|
private LauncherAppWidgetProviderInfo mPendingAddWidgetInfo;
|
||||||
private int mPendingAddWidgetId = -1;
|
private int mPendingAddWidgetId = -1;
|
||||||
|
|
||||||
@@ -1312,6 +1315,8 @@ public class Launcher extends Activity
|
|||||||
mPendingAddInfo.cellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y);
|
mPendingAddInfo.cellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y);
|
||||||
mPendingAddInfo.spanX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_X);
|
mPendingAddInfo.spanX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_X);
|
||||||
mPendingAddInfo.spanY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y);
|
mPendingAddInfo.spanY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y);
|
||||||
|
mPendingAddInfo.componentName =
|
||||||
|
savedState.getParcelable(RUNTIME_STATE_PENDING_ADD_COMPONENT);
|
||||||
AppWidgetProviderInfo info = savedState.getParcelable(
|
AppWidgetProviderInfo info = savedState.getParcelable(
|
||||||
RUNTIME_STATE_PENDING_ADD_WIDGET_INFO);
|
RUNTIME_STATE_PENDING_ADD_WIDGET_INFO);
|
||||||
mPendingAddWidgetInfo = info == null ?
|
mPendingAddWidgetInfo = info == null ?
|
||||||
@@ -1499,7 +1504,13 @@ public class Launcher extends Activity
|
|||||||
CellLayout layout = getCellLayout(container, screenId);
|
CellLayout layout = getCellLayout(container, screenId);
|
||||||
|
|
||||||
ShortcutInfo info = InstallShortcutReceiver.fromShortcutIntent(this, data);
|
ShortcutInfo info = InstallShortcutReceiver.fromShortcutIntent(this, data);
|
||||||
if (info == null) {
|
if (info == null || mPendingAddInfo.componentName == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!PackageManagerHelper.hasPermissionForActivity(
|
||||||
|
this, info.intent, mPendingAddInfo.componentName.getPackageName())) {
|
||||||
|
// The app is trying to add a shortcut without sufficient permissions
|
||||||
|
Log.e(TAG, "Ignoring malicious intent " + info.intent.toUri(0));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final View view = createShortcut(info);
|
final View view = createShortcut(info);
|
||||||
@@ -1966,6 +1977,8 @@ public class Launcher extends Activity
|
|||||||
outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_Y, mPendingAddInfo.cellY);
|
outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_Y, mPendingAddInfo.cellY);
|
||||||
outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_X, mPendingAddInfo.spanX);
|
outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_X, mPendingAddInfo.spanX);
|
||||||
outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y, mPendingAddInfo.spanY);
|
outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y, mPendingAddInfo.spanY);
|
||||||
|
outState.putParcelable(RUNTIME_STATE_PENDING_ADD_COMPONENT,
|
||||||
|
mPendingAddInfo.componentName);
|
||||||
outState.putParcelable(RUNTIME_STATE_PENDING_ADD_WIDGET_INFO, mPendingAddWidgetInfo);
|
outState.putParcelable(RUNTIME_STATE_PENDING_ADD_WIDGET_INFO, mPendingAddWidgetInfo);
|
||||||
outState.putInt(RUNTIME_STATE_PENDING_ADD_WIDGET_ID, mPendingAddWidgetId);
|
outState.putInt(RUNTIME_STATE_PENDING_ADD_WIDGET_ID, mPendingAddWidgetId);
|
||||||
}
|
}
|
||||||
@@ -2198,6 +2211,7 @@ public class Launcher extends Activity
|
|||||||
mPendingAddInfo.spanX = mPendingAddInfo.spanY = -1;
|
mPendingAddInfo.spanX = mPendingAddInfo.spanY = -1;
|
||||||
mPendingAddInfo.minSpanX = mPendingAddInfo.minSpanY = 1;
|
mPendingAddInfo.minSpanX = mPendingAddInfo.minSpanY = 1;
|
||||||
mPendingAddInfo.dropPos = null;
|
mPendingAddInfo.dropPos = null;
|
||||||
|
mPendingAddInfo.componentName = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void addAppWidgetFromDropImpl(final int appWidgetId, final ItemInfo info, final
|
void addAppWidgetFromDropImpl(final int appWidgetId, final ItemInfo info, final
|
||||||
@@ -2273,6 +2287,7 @@ public class Launcher extends Activity
|
|||||||
mPendingAddInfo.container = container;
|
mPendingAddInfo.container = container;
|
||||||
mPendingAddInfo.screenId = screenId;
|
mPendingAddInfo.screenId = screenId;
|
||||||
mPendingAddInfo.dropPos = null;
|
mPendingAddInfo.dropPos = null;
|
||||||
|
mPendingAddInfo.componentName = componentName;
|
||||||
|
|
||||||
if (cell != null) {
|
if (cell != null) {
|
||||||
mPendingAddInfo.cellX = cell[0];
|
mPendingAddInfo.cellX = cell[0];
|
||||||
|
|||||||
@@ -16,8 +16,15 @@
|
|||||||
|
|
||||||
package com.android.launcher3.util;
|
package com.android.launcher3.util;
|
||||||
|
|
||||||
|
import android.app.AppOpsManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
|
import android.content.pm.ResolveInfo;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import com.android.launcher3.Utilities;
|
import com.android.launcher3.Utilities;
|
||||||
|
|
||||||
@@ -68,4 +75,53 @@ public class PackageManagerHelper {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 static boolean hasPermissionForActivity(Context context, Intent intent,
|
||||||
|
String srcPackage) {
|
||||||
|
PackageManager pm = context.getPackageManager();
|
||||||
|
ResolveInfo target = pm.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(pm.checkPermission(target.activityInfo.permission, srcPackage) !=
|
||||||
|
PackageManager.PERMISSION_GRANTED) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Utilities.ATLEAST_MARSHMALLOW) {
|
||||||
|
// These checks are sufficient for below M devices.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 pm.getApplicationInfo(srcPackage, 0).targetSdkVersion >= Build.VERSION_CODES.M;
|
||||||
|
} catch (NameNotFoundException e) { }
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user