Merge "Add support for launching a shortcut in WindowContainerTransaction"
This commit is contained in:
@@ -23,6 +23,7 @@ import android.app.PendingIntent;
|
||||
import android.app.WindowConfiguration;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
@@ -434,6 +435,21 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts activity(s) from a shortcut.
|
||||
* @param callingPackage The package launching the shortcut.
|
||||
* @param shortcutInfo Information about the shortcut to start
|
||||
* @param options bundle containing ActivityOptions for the task's top activity.
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public WindowContainerTransaction startShortcut(@NonNull String callingPackage,
|
||||
@NonNull ShortcutInfo shortcutInfo, @Nullable Bundle options) {
|
||||
mHierarchyOps.add(HierarchyOp.createForStartShortcut(
|
||||
callingPackage, shortcutInfo, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new TaskFragment with the given options.
|
||||
* @param taskFragmentOptions the options used to create the TaskFragment.
|
||||
@@ -957,11 +973,16 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
public static final int HIERARCHY_OP_TYPE_REPARENT_CHILDREN = 11;
|
||||
public static final int HIERARCHY_OP_TYPE_PENDING_INTENT = 12;
|
||||
public static final int HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS = 13;
|
||||
public static final int HIERARCHY_OP_TYPE_START_SHORTCUT = 14;
|
||||
|
||||
// The following key(s) are for use with mLaunchOptions:
|
||||
// When launching a task (eg. from recents), this is the taskId to be launched.
|
||||
public static final String LAUNCH_KEY_TASK_ID = "android:transaction.hop.taskId";
|
||||
|
||||
// When starting from a shortcut, this contains the calling package.
|
||||
public static final String LAUNCH_KEY_SHORTCUT_CALLING_PACKAGE =
|
||||
"android:transaction.hop.shortcut_calling_package";
|
||||
|
||||
private final int mType;
|
||||
|
||||
// Container we are performing the operation on.
|
||||
@@ -999,6 +1020,9 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
@Nullable
|
||||
private PendingIntent mPendingIntent;
|
||||
|
||||
@Nullable
|
||||
private ShortcutInfo mShortcutInfo;
|
||||
|
||||
public static HierarchyOp createForReparent(
|
||||
@NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) {
|
||||
return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REPARENT)
|
||||
@@ -1058,6 +1082,17 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
.build();
|
||||
}
|
||||
|
||||
/** Create a hierarchy op for starting a shortcut. */
|
||||
public static HierarchyOp createForStartShortcut(@NonNull String callingPackage,
|
||||
@NonNull ShortcutInfo shortcutInfo, @Nullable Bundle options) {
|
||||
final Bundle fullOptions = options == null ? new Bundle() : options;
|
||||
fullOptions.putString(LAUNCH_KEY_SHORTCUT_CALLING_PACKAGE, callingPackage);
|
||||
return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_START_SHORTCUT)
|
||||
.setShortcutInfo(shortcutInfo)
|
||||
.setLaunchOptions(fullOptions)
|
||||
.build();
|
||||
}
|
||||
|
||||
/** Create a hierarchy op for setting launch adjacent flag root. */
|
||||
public static HierarchyOp createForSetLaunchAdjacentFlagRoot(IBinder container,
|
||||
boolean clearRoot) {
|
||||
@@ -1085,6 +1120,7 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
mActivityIntent = copy.mActivityIntent;
|
||||
mTaskFragmentCreationOptions = copy.mTaskFragmentCreationOptions;
|
||||
mPendingIntent = copy.mPendingIntent;
|
||||
mShortcutInfo = copy.mShortcutInfo;
|
||||
}
|
||||
|
||||
protected HierarchyOp(Parcel in) {
|
||||
@@ -1100,6 +1136,7 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
mActivityIntent = in.readTypedObject(Intent.CREATOR);
|
||||
mTaskFragmentCreationOptions = in.readTypedObject(TaskFragmentCreationParams.CREATOR);
|
||||
mPendingIntent = in.readTypedObject(PendingIntent.CREATOR);
|
||||
mShortcutInfo = in.readTypedObject(ShortcutInfo.CREATOR);
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
@@ -1170,6 +1207,11 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
return mPendingIntent;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ShortcutInfo getShortcutInfo() {
|
||||
return mShortcutInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
switch (mType) {
|
||||
@@ -1212,6 +1254,9 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
case HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS:
|
||||
return "{SetAdjacentTaskFragments: container=" + mContainer
|
||||
+ " adjacentContainer=" + mReparent + "}";
|
||||
case HIERARCHY_OP_TYPE_START_SHORTCUT:
|
||||
return "{StartShortcut: options=" + mLaunchOptions + " info=" + mShortcutInfo
|
||||
+ "}";
|
||||
default:
|
||||
return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent
|
||||
+ " mToTop=" + mToTop
|
||||
@@ -1234,6 +1279,7 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
dest.writeTypedObject(mActivityIntent, flags);
|
||||
dest.writeTypedObject(mTaskFragmentCreationOptions, flags);
|
||||
dest.writeTypedObject(mPendingIntent, flags);
|
||||
dest.writeTypedObject(mShortcutInfo, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1287,6 +1333,9 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
@Nullable
|
||||
private PendingIntent mPendingIntent;
|
||||
|
||||
@Nullable
|
||||
private ShortcutInfo mShortcutInfo;
|
||||
|
||||
Builder(int type) {
|
||||
mType = type;
|
||||
}
|
||||
@@ -1347,6 +1396,11 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder setShortcutInfo(@Nullable ShortcutInfo shortcutInfo) {
|
||||
mShortcutInfo = shortcutInfo;
|
||||
return this;
|
||||
}
|
||||
|
||||
HierarchyOp build() {
|
||||
final HierarchyOp hierarchyOp = new HierarchyOp(mType);
|
||||
hierarchyOp.mContainer = mContainer;
|
||||
@@ -1364,6 +1418,7 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
hierarchyOp.mActivityIntent = mActivityIntent;
|
||||
hierarchyOp.mPendingIntent = mPendingIntent;
|
||||
hierarchyOp.mTaskFragmentCreationOptions = mTaskFragmentCreationOptions;
|
||||
hierarchyOp.mShortcutInfo = mShortcutInfo;
|
||||
|
||||
return hierarchyOp;
|
||||
}
|
||||
|
||||
@@ -121,6 +121,7 @@ public class LauncherAppsService extends SystemService {
|
||||
public void onStart() {
|
||||
publishBinderService(Context.LAUNCHER_APPS_SERVICE, mLauncherAppsImpl);
|
||||
mLauncherAppsImpl.registerLoadingProgressForIncrementalApps();
|
||||
LocalServices.addService(LauncherAppsServiceInternal.class, mLauncherAppsImpl.mInternal);
|
||||
}
|
||||
|
||||
static class BroadcastCookie {
|
||||
@@ -137,6 +138,17 @@ public class LauncherAppsService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Local system service interface.
|
||||
* @hide Only for use within system server
|
||||
*/
|
||||
public abstract static class LauncherAppsServiceInternal {
|
||||
/** Same as startShortcut except supports forwarding of caller uid/pid. */
|
||||
public abstract boolean startShortcut(int callerUid, int callerPid, String callingPackage,
|
||||
String packageName, String featureId, String shortcutId, Rect sourceBounds,
|
||||
Bundle startActivityOptions, int targetUserId);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static class LauncherAppsImpl extends ILauncherApps.Stub {
|
||||
private static final boolean DEBUG = false;
|
||||
@@ -168,6 +180,8 @@ public class LauncherAppsService extends SystemService {
|
||||
|
||||
private PackageInstallerService mPackageInstallerService;
|
||||
|
||||
final LauncherAppsServiceInternal mInternal;
|
||||
|
||||
public LauncherAppsImpl(Context context) {
|
||||
mContext = context;
|
||||
mIPM = AppGlobals.getPackageManager();
|
||||
@@ -189,6 +203,7 @@ public class LauncherAppsService extends SystemService {
|
||||
mShortcutServiceInternal.addShortcutChangeCallback(mShortcutChangeHandler);
|
||||
mCallbackHandler = BackgroundThread.getHandler();
|
||||
mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
|
||||
mInternal = new LocalService();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -359,11 +374,15 @@ public class LauncherAppsService extends SystemService {
|
||||
* group.
|
||||
*/
|
||||
private boolean canAccessProfile(int targetUserId, String message) {
|
||||
final int callingUserId = injectCallingUserId();
|
||||
return canAccessProfile(injectBinderCallingUid(), injectCallingUserId(),
|
||||
injectBinderCallingPid(), targetUserId, message);
|
||||
}
|
||||
|
||||
private boolean canAccessProfile(int callingUid, int callingUserId, int callingPid,
|
||||
int targetUserId, String message) {
|
||||
|
||||
if (targetUserId == callingUserId) return true;
|
||||
if (injectHasInteractAcrossUsersFullPermission(injectBinderCallingPid(),
|
||||
injectBinderCallingUid())) {
|
||||
if (injectHasInteractAcrossUsersFullPermission(callingPid, callingUid)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -379,25 +398,29 @@ public class LauncherAppsService extends SystemService {
|
||||
injectRestoreCallingIdentity(ident);
|
||||
}
|
||||
|
||||
return mUserManagerInternal.isProfileAccessible(injectCallingUserId(), targetUserId,
|
||||
return mUserManagerInternal.isProfileAccessible(callingUserId, targetUserId,
|
||||
message, true);
|
||||
}
|
||||
|
||||
private void verifyCallingPackage(String callingPackage) {
|
||||
verifyCallingPackage(callingPackage, injectBinderCallingUid());
|
||||
}
|
||||
|
||||
@VisibleForTesting // We override it in unit tests
|
||||
void verifyCallingPackage(String callingPackage) {
|
||||
void verifyCallingPackage(String callingPackage, int callerUid) {
|
||||
int packageUid = -1;
|
||||
try {
|
||||
packageUid = mIPM.getPackageUid(callingPackage,
|
||||
PackageManager.MATCH_DIRECT_BOOT_AWARE
|
||||
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
|
||||
| PackageManager.MATCH_UNINSTALLED_PACKAGES,
|
||||
UserHandle.getUserId(getCallingUid()));
|
||||
UserHandle.getUserId(callerUid));
|
||||
} catch (RemoteException ignore) {
|
||||
}
|
||||
if (packageUid < 0) {
|
||||
Log.e(TAG, "Package not found: " + callingPackage);
|
||||
}
|
||||
if (packageUid != injectBinderCallingUid()) {
|
||||
if (packageUid != callerUid) {
|
||||
throw new SecurityException("Calling package name mismatch");
|
||||
}
|
||||
}
|
||||
@@ -797,9 +820,15 @@ public class LauncherAppsService extends SystemService {
|
||||
}
|
||||
|
||||
private void ensureShortcutPermission(@NonNull String callingPackage) {
|
||||
verifyCallingPackage(callingPackage);
|
||||
if (!mShortcutServiceInternal.hasShortcutHostPermission(getCallingUserId(),
|
||||
callingPackage, injectBinderCallingPid(), injectBinderCallingUid())) {
|
||||
ensureShortcutPermission(injectBinderCallingUid(), injectBinderCallingPid(),
|
||||
callingPackage);
|
||||
}
|
||||
|
||||
private void ensureShortcutPermission(int callerUid, int callerPid,
|
||||
@NonNull String callingPackage) {
|
||||
verifyCallingPackage(callingPackage, callerUid);
|
||||
if (!mShortcutServiceInternal.hasShortcutHostPermission(UserHandle.getUserId(callerUid),
|
||||
callingPackage, callerPid, callerUid)) {
|
||||
throw new SecurityException("Caller can't access shortcut information");
|
||||
}
|
||||
}
|
||||
@@ -989,20 +1018,28 @@ public class LauncherAppsService extends SystemService {
|
||||
public boolean startShortcut(String callingPackage, String packageName, String featureId,
|
||||
String shortcutId, Rect sourceBounds, Bundle startActivityOptions,
|
||||
int targetUserId) {
|
||||
verifyCallingPackage(callingPackage);
|
||||
return startShortcutInner(injectBinderCallingUid(), injectBinderCallingPid(),
|
||||
injectCallingUserId(), callingPackage, packageName, featureId, shortcutId,
|
||||
sourceBounds, startActivityOptions, targetUserId);
|
||||
}
|
||||
|
||||
private boolean startShortcutInner(int callerUid, int callerPid, int callingUserId,
|
||||
String callingPackage, String packageName, String featureId, String shortcutId,
|
||||
Rect sourceBounds, Bundle startActivityOptions, int targetUserId) {
|
||||
verifyCallingPackage(callingPackage, callerUid);
|
||||
if (!canAccessProfile(targetUserId, "Cannot start activity")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Even without the permission, pinned shortcuts are always launchable.
|
||||
if (!mShortcutServiceInternal.isPinnedByCaller(getCallingUserId(),
|
||||
if (!mShortcutServiceInternal.isPinnedByCaller(callingUserId,
|
||||
callingPackage, packageName, shortcutId, targetUserId)) {
|
||||
ensureShortcutPermission(callingPackage);
|
||||
ensureShortcutPermission(callerUid, callerPid, callingPackage);
|
||||
}
|
||||
|
||||
final Intent[] intents = mShortcutServiceInternal.createShortcutIntents(
|
||||
getCallingUserId(), callingPackage, packageName, shortcutId, targetUserId,
|
||||
injectBinderCallingPid(), injectBinderCallingUid());
|
||||
callingUserId, callingPackage, packageName, shortcutId, targetUserId,
|
||||
callerPid, callerUid);
|
||||
if (intents == null || intents.length == 0) {
|
||||
return false;
|
||||
}
|
||||
@@ -1023,7 +1060,7 @@ public class LauncherAppsService extends SystemService {
|
||||
|
||||
// Replace theme for splash screen
|
||||
final String splashScreenThemeResName =
|
||||
mShortcutServiceInternal.getShortcutStartingThemeResName(getCallingUserId(),
|
||||
mShortcutServiceInternal.getShortcutStartingThemeResName(callingUserId,
|
||||
callingPackage, packageName, shortcutId, targetUserId);
|
||||
if (splashScreenThemeResName != null && !splashScreenThemeResName.isEmpty()) {
|
||||
if (startActivityOptions == null) {
|
||||
@@ -1809,5 +1846,16 @@ public class LauncherAppsService extends SystemService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class LocalService extends LauncherAppsServiceInternal {
|
||||
@Override
|
||||
public boolean startShortcut(int callerUid, int callerPid, String callingPackage,
|
||||
String packageName, String featureId, String shortcutId, Rect sourceBounds,
|
||||
Bundle startActivityOptions, int targetUserId) {
|
||||
return LauncherAppsImpl.this.startShortcutInner(callerUid, callerPid,
|
||||
UserHandle.getUserId(callerUid), callingPackage, packageName, featureId,
|
||||
shortcutId, sourceBounds, startActivityOptions, targetUserId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP
|
||||
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT;
|
||||
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT;
|
||||
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
|
||||
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_SHORTCUT;
|
||||
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
|
||||
import static com.android.server.wm.ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED;
|
||||
@@ -79,6 +80,8 @@ import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
import com.android.internal.util.function.pooled.PooledConsumer;
|
||||
import com.android.internal.util.function.pooled.PooledLambda;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.pm.LauncherAppsService.LauncherAppsServiceInternal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@@ -785,6 +788,22 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
null /* requiredPermission */, options);
|
||||
break;
|
||||
}
|
||||
case HIERARCHY_OP_TYPE_START_SHORTCUT: {
|
||||
final Bundle launchOpts = hop.getLaunchOptions();
|
||||
final String callingPackage = launchOpts.getString(
|
||||
WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_SHORTCUT_CALLING_PACKAGE);
|
||||
launchOpts.remove(
|
||||
WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_SHORTCUT_CALLING_PACKAGE);
|
||||
|
||||
final LauncherAppsServiceInternal launcherApps = LocalServices.getService(
|
||||
LauncherAppsServiceInternal.class);
|
||||
|
||||
launcherApps.startShortcut(caller.mUid, caller.mPid, callingPackage,
|
||||
hop.getShortcutInfo().getPackage(), null /* default featureId */,
|
||||
hop.getShortcutInfo().getId(), null /* sourceBounds */, launchOpts,
|
||||
hop.getShortcutInfo().getUserId());
|
||||
break;
|
||||
}
|
||||
case HIERARCHY_OP_TYPE_REPARENT_CHILDREN: {
|
||||
final WindowContainer oldParent = WindowContainer.fromBinder(hop.getContainer());
|
||||
final WindowContainer newParent = hop.getNewParent() != null
|
||||
|
||||
@@ -579,7 +579,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verifyCallingPackage(String callingPackage) {
|
||||
public void verifyCallingPackage(String callingPackage, int callerUid) {
|
||||
// SKIP
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user