pendingNewIntents, ActivityOptions activityOptions,
boolean isForward, ProfilerInfo profilerInfo, ClientTransactionHandler client,
IBinder assistToken, FixedRotationAdjustments fixedRotationAdjustments,
- IBinder shareableActivityToken) {
+ IBinder shareableActivityToken, boolean launchedFromBubble) {
this.token = token;
this.assistToken = assistToken;
this.shareableActivityToken = shareableActivityToken;
@@ -634,6 +637,7 @@ public final class ActivityThread extends ClientTransactionHandler
compatInfo);
mActivityOptions = activityOptions;
mPendingFixedRotationAdjustments = fixedRotationAdjustments;
+ mLaunchedFromBubble = launchedFromBubble;
init();
}
@@ -3549,6 +3553,7 @@ public final class ActivityThread extends ClientTransactionHandler
activity.mPendingOptions = r.mActivityOptions;
r.mActivityOptions = null;
}
+ activity.mLaunchedFromBubble = r.mLaunchedFromBubble;
activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 0ca0b85215684..a60f1ca3cdcd8 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -10205,13 +10205,8 @@ public class Notification implements Parcelable
* The shortcut activity will be used when the bubble is expanded. This will display
* the shortcut activity in a floating window over the existing foreground activity.
*
- * When the shortcut is displayed in a bubble, there will be an intent
- * extra set on the activity, {@link Intent#EXTRA_IS_BUBBLED}
- * with {@code true}. You may check this in the onCreate of your activity via:
- *
- *
- * boolean isBubbled = getIntent().getBooleanExtra(Intent.EXTRA_IS_BUBBLED, false);
- *
+ * When the activity is launched from a bubble,
+ * {@link Activity#isLaunchedFromBubble()} will return with {@code true}.
*
*
* If the shortcut has not been published when the bubble notification is sent,
@@ -10242,13 +10237,8 @@ public class Notification implements Parcelable
* app content in a floating window over the existing foreground activity. The intent
* should point to a resizable activity.
*
- * When the activity is displayed in a bubble, there will be an intent
- * extra set on the activity, {@link Intent#EXTRA_IS_BUBBLED}
- * with {@code true}. You may check this in the onCreate of your activity via:
- *
- *
- * boolean isBubbled = getIntent().getBooleanExtra(Intent.EXTRA_IS_BUBBLED, false);
- *
+ * When the activity is launched from a bubble,
+ * {@link Activity#isLaunchedFromBubble()} will return with {@code true}.
*
*
* @throws NullPointerException if intent is null.
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 9f8fcc15b747b..e281a02681845 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -72,6 +72,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
private ProfilerInfo mProfilerInfo;
private IBinder mAssistToken;
private IBinder mShareableActivityToken;
+ private boolean mLaunchedFromBubble;
/**
* It is only non-null if the process is the first time to launch activity. It is only an
* optimization for quick look up of the interface so the field is ignored for comparison.
@@ -96,7 +97,8 @@ public class LaunchActivityItem extends ClientTransactionItem {
ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo,
- client, mAssistToken, mFixedRotationAdjustments, mShareableActivityToken);
+ client, mAssistToken, mFixedRotationAdjustments, mShareableActivityToken,
+ mLaunchedFromBubble);
client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -120,7 +122,8 @@ public class LaunchActivityItem extends ClientTransactionItem {
List pendingNewIntents, ActivityOptions activityOptions,
boolean isForward, ProfilerInfo profilerInfo, IBinder assistToken,
IActivityClientController activityClientController,
- FixedRotationAdjustments fixedRotationAdjustments, IBinder shareableActivityToken) {
+ FixedRotationAdjustments fixedRotationAdjustments, IBinder shareableActivityToken,
+ boolean launchedFromBubble) {
LaunchActivityItem instance = ObjectPool.obtain(LaunchActivityItem.class);
if (instance == null) {
instance = new LaunchActivityItem();
@@ -128,7 +131,8 @@ public class LaunchActivityItem extends ClientTransactionItem {
setValues(instance, intent, ident, info, curConfig, overrideConfig, compatInfo, referrer,
voiceInteractor, procState, state, persistentState, pendingResults,
pendingNewIntents, activityOptions, isForward, profilerInfo, assistToken,
- activityClientController, fixedRotationAdjustments, shareableActivityToken);
+ activityClientController, fixedRotationAdjustments, shareableActivityToken,
+ launchedFromBubble);
return instance;
}
@@ -136,7 +140,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
@Override
public void recycle() {
setValues(this, null, 0, null, null, null, null, null, null, 0, null, null, null, null,
- null, false, null, null, null, null, null);
+ null, false, null, null, null, null, null, false);
ObjectPool.recycle(this);
}
@@ -166,6 +170,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
dest.writeStrongInterface(mActivityClientController);
dest.writeTypedObject(mFixedRotationAdjustments, flags);
dest.writeStrongBinder(mShareableActivityToken);
+ dest.writeBoolean(mLaunchedFromBubble);
}
/** Read from Parcel. */
@@ -183,7 +188,8 @@ public class LaunchActivityItem extends ClientTransactionItem {
in.readTypedObject(ProfilerInfo.CREATOR),
in.readStrongBinder(),
IActivityClientController.Stub.asInterface(in.readStrongBinder()),
- in.readTypedObject(FixedRotationAdjustments.CREATOR), in.readStrongBinder());
+ in.readTypedObject(FixedRotationAdjustments.CREATOR), in.readStrongBinder(),
+ in.readBoolean());
}
public static final @NonNull Creator CREATOR =
@@ -293,7 +299,8 @@ public class LaunchActivityItem extends ClientTransactionItem {
List pendingResults, List pendingNewIntents,
ActivityOptions activityOptions, boolean isForward, ProfilerInfo profilerInfo,
IBinder assistToken, IActivityClientController activityClientController,
- FixedRotationAdjustments fixedRotationAdjustments, IBinder shareableActivityToken) {
+ FixedRotationAdjustments fixedRotationAdjustments, IBinder shareableActivityToken,
+ boolean launchedFromBubble) {
instance.mIntent = intent;
instance.mIdent = ident;
instance.mInfo = info;
@@ -314,5 +321,6 @@ public class LaunchActivityItem extends ClientTransactionItem {
instance.mActivityClientController = activityClientController;
instance.mFixedRotationAdjustments = fixedRotationAdjustments;
instance.mShareableActivityToken = shareableActivityToken;
+ instance.mLaunchedFromBubble = launchedFromBubble;
}
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index a482b8c189cd6..9ad017c5f9c68 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6177,16 +6177,6 @@ public class Intent implements Parcelable, Cloneable {
*/
public static final String EXTRA_UNSTARTABLE_REASON = "android.intent.extra.UNSTARTABLE_REASON";
- /**
- * A boolean extra indicating whether an activity is bubbled. Set on the shortcut or
- * pending intent provided for the bubble. If the extra is not present or false, then it is not
- * bubbled.
- *
- * @see android.app.Notification.Builder#setBubbleMetadata(Notification.BubbleMetadata)
- * @see android.app.Notification.BubbleMetadata.Builder#Builder(String)
- */
- public static final String EXTRA_IS_BUBBLED = "android.intent.extra.IS_BUBBLED";
-
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Intent flags (see mFlags variable).
diff --git a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
index 1a06789e729f9..75da0bfba5812 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
@@ -111,6 +111,7 @@ class TestUtils {
private IBinder mAssistToken;
private IBinder mShareableActivityToken;
private FixedRotationAdjustments mFixedRotationAdjustments;
+ private boolean mLaunchedFromBubble;
LaunchActivityItemBuilder setIntent(Intent intent) {
mIntent = intent;
@@ -207,13 +208,18 @@ class TestUtils {
return this;
}
+ LaunchActivityItemBuilder setLaunchedFromBubble(boolean launchedFromBubble) {
+ mLaunchedFromBubble = launchedFromBubble;
+ return this;
+ }
+
LaunchActivityItem build() {
return LaunchActivityItem.obtain(mIntent, mIdent, mInfo,
mCurConfig, mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor,
mProcState, mState, mPersistentState, mPendingResults, mPendingNewIntents,
mActivityOptions, mIsForward, mProfilerInfo, mAssistToken,
null /* activityClientController */, mFixedRotationAdjustments,
- mShareableActivityToken);
+ mShareableActivityToken, mLaunchedFromBubble);
}
}
}
diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
index 0808186b21c5f..269d8424a78f3 100644
--- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
+++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
@@ -298,7 +298,8 @@ public class ActivityThreadClientTest {
null /* pendingResults */, null /* pendingNewIntents */,
null /* activityOptions */, true /* isForward */, null /* profilerInfo */,
mThread /* client */, null /* asssitToken */,
- null /* fixedRotationAdjustments */, null /* shareableActivityToken */);
+ null /* fixedRotationAdjustments */, null /* shareableActivityToken */,
+ false /* launchedFromBubble */);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 9ef3fb54fb356..abe1f7179dda0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -154,6 +154,7 @@ public class BubbleExpandedView extends LinearLayout {
}
try {
options.setTaskAlwaysOnTop(true);
+ options.setLaunchedFromBubble(true);
if (!mIsOverflow && mBubble.hasMetadataShortcutId()) {
options.setApplyActivityFlagsForBubbles(true);
mTaskView.startShortcutActivity(mBubble.getShortcutInfo(),
@@ -163,7 +164,6 @@ public class BubbleExpandedView extends LinearLayout {
// Apply flags to make behaviour match documentLaunchMode=always.
fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
- fillInIntent.putExtra(Intent.EXTRA_IS_BUBBLED, true);
if (mBubble != null) {
mBubble.setIntentActive();
}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 27e0ffc73d9d1..91a66acf433df 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -978,7 +978,6 @@ public class LauncherAppsService extends SystemService {
// Flag for bubble to make behaviour match documentLaunchMode=always.
intents[0].addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
intents[0].addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
- intents[0].putExtra(Intent.EXTRA_IS_BUBBLED, true);
}
intents[0].addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 631d86660dcf6..e0724fd27cf4d 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -561,6 +561,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// The locusId associated with this activity, if set.
private LocusId mLocusId;
+ // Whether the activity was launched from a bubble.
+ private boolean mLaunchedFromBubble;
+
private SizeConfigurationBuckets mSizeConfigurations;
/**
@@ -4218,7 +4221,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
+ boolean getLaunchedFromBubble() {
+ return mLaunchedFromBubble;
+ }
+
private void setOptions(@NonNull ActivityOptions options) {
+ mLaunchedFromBubble = options.getLaunchedFromBubble();
mPendingOptions = options;
if (options.getAnimationType() == ANIM_REMOTE_ANIMATION) {
mPendingRemoteAnimation = options.getRemoteAnimationAdapter();
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index bfbc10aee7a72..bdde3692ef534 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -826,7 +826,8 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
r.getSavedState(), r.getPersistentSavedState(), results, newIntents,
r.takeOptions(), dc.isNextTransitionForward(),
proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController,
- r.createFixedRotationAdjustmentsIfNeeded(), r.shareableActivityToken));
+ r.createFixedRotationAdjustmentsIfNeeded(), r.shareableActivityToken,
+ r.getLaunchedFromBubble()));
// Set desired final state.
final ActivityLifecycleItem lifecycleItem;
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index 65671959e8846..c9d5fa473ea89 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -18,6 +18,7 @@ package com.android.server.wm;
import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
+import static android.Manifest.permission.STATUS_BAR_SERVICE;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -278,6 +279,19 @@ public class SafeActivityOptions {
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
+
+ // If launched from bubble is specified, then ensure that the caller is system or sysui.
+ if (options.getLaunchedFromBubble() && callingUid != Process.SYSTEM_UID) {
+ final int statusBarPerm = ActivityTaskManagerService.checkPermission(
+ STATUS_BAR_SERVICE, callingPid, callingUid);
+ if (statusBarPerm == PERMISSION_DENIED) {
+ final String msg = "Permission Denial: starting " + getIntentString(intent)
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ") with launchedFromBubble=true";
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ }
}
private String getIntentString(Intent intent) {