diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl index 6deedb6f52787..5e420c085b539 100644 --- a/core/java/android/app/ITaskStackListener.aidl +++ b/core/java/android/app/ITaskStackListener.aidl @@ -31,8 +31,10 @@ oneway interface ITaskStackListener { * Called whenever IActivityManager.startActivity is called on an activity that is already * running in the pinned stack and the activity is not actually started, but the task is either * brought to the front or a new Intent is delivered to it. + * + * @param sourceComponent the component name of the activity that initiated the restart attempt */ - void onPinnedActivityRestartAttempt(); + void onPinnedActivityRestartAttempt(in ComponentName sourceComponent); /** * Called whenever the pinned stack is done animating a resize. diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java index fd766bfbff21d..35c67d30cc5a6 100644 --- a/core/java/android/app/TaskStackListener.java +++ b/core/java/android/app/TaskStackListener.java @@ -35,7 +35,7 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { } @Override - public void onPinnedActivityRestartAttempt() throws RemoteException { + public void onPinnedActivityRestartAttempt(ComponentName sourceComponent) throws RemoteException { } @Override diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index a2d7d6be04e69..3103267344a59 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -16,14 +16,20 @@ package com.android.systemui.pip.phone; +import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.view.Display.DEFAULT_DISPLAY; import android.app.ActivityManager; +import android.app.ActivityManager.StackInfo; +import android.app.ActivityOptions; import android.app.IActivityManager; +import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.content.pm.ParceledListSlice; import android.os.Handler; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; import android.view.IPinnedStackController; import android.view.IPinnedStackListener; @@ -49,6 +55,7 @@ public class PipManager { private final PinnedStackListener mPinnedStackListener = new PinnedStackListener(); private PipMenuActivityController mMenuController; + private PipMediaController mMediaController; private PipTouchHandler mTouchHandler; /** @@ -57,7 +64,11 @@ public class PipManager { TaskStackListener mTaskStackListener = new TaskStackListener() { @Override public void onActivityPinned() { + if (!checkCurrentUserId(false /* debug */)) { + return; + } mTouchHandler.onActivityPinned(); + mMediaController.onActivityPinned(); } @Override @@ -66,8 +77,24 @@ public class PipManager { } @Override - public void onPinnedActivityRestartAttempt() { - // TODO(winsonc): Hide the menu and expand the PiP + public void onPinnedActivityRestartAttempt(ComponentName sourceComponent) { + if (!checkCurrentUserId(false /* debug */)) { + return; + } + + // Expand the activity back to fullscreen only if it was attempted to be restarted from + // another package than the top activity in the stack + boolean expandPipToFullscreen = true; + if (sourceComponent != null) { + ComponentName topActivity = PipUtils.getTopPinnedActivity(mActivityManager); + expandPipToFullscreen = topActivity != null && topActivity.getPackageName().equals( + sourceComponent.getPackageName()); + } + if (expandPipToFullscreen) { + mTouchHandler.expandPinnedStackToFullscreen(); + } else { + Log.w(TAG, "Can not expand PiP to fullscreen via intent from the same package."); + } } }; @@ -91,7 +118,7 @@ public class PipManager { @Override public void onActionsChanged(ParceledListSlice actions) { mHandler.post(() -> { - mMenuController.setActions(actions); + mMenuController.setAppActions(actions); }); } @@ -127,7 +154,9 @@ public class PipManager { } SystemServicesProxy.getInstance(mContext).registerTaskStackListener(mTaskStackListener); - mMenuController = new PipMenuActivityController(context, mActivityManager, mWindowManager); + mMediaController = new PipMediaController(context, mActivityManager); + mMenuController = new PipMenuActivityController(context, mActivityManager, mWindowManager, + mMediaController); mTouchHandler = new PipTouchHandler(context, mMenuController, mActivityManager, mWindowManager); } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java new file mode 100644 index 0000000000000..22840138e1528 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2017 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.systemui.pip.phone; + +import android.app.IActivityManager; +import android.app.RemoteAction; +import android.content.ComponentName; +import android.content.Context; +import android.graphics.drawable.Icon; +import android.media.session.MediaController; +import android.media.session.MediaController.TransportControls; +import android.media.session.MediaSession; +import android.media.session.MediaSessionManager; +import android.media.session.PlaybackState; + +import com.android.systemui.R; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Interfaces with the {@link MediaSessionManager} to compose the right set of actions to show (only + * if there are no actions from the PiP activity itself). The active media controller is only set + * when there is a media session from the top PiP activity. + */ +public class PipMediaController { + + /** + * A listener interface to receive notification on changes to the media actions. + */ + public interface ActionListener { + /** + * Called when the media actions changes. + */ + void onMediaActionsChanged(List actions); + } + + private final Context mContext; + private final IActivityManager mActivityManager; + + private final MediaSessionManager mMediaSessionManager; + private MediaController mMediaController; + + private RemoteAction mPauseAction; + private RemoteAction mPlayAction; + + private MediaController.Callback mPlaybackChangedListener = new MediaController.Callback() { + @Override + public void onPlaybackStateChanged(PlaybackState state) { + if (!mListeners.isEmpty()) { + notifyActionsChanged(getMediaActions()); + } + } + }; + + private ArrayList mListeners = new ArrayList<>(); + + public PipMediaController(Context context, IActivityManager activityManager) { + mContext = context; + mActivityManager = activityManager; + + createMediaActions(); + mMediaSessionManager = + (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE); + mMediaSessionManager.addOnActiveSessionsChangedListener(controllers -> { + resolveActiveMediaController(controllers); + }, null); + } + + /** + * Handles when an activity is pinned. + */ + public void onActivityPinned() { + // Once we enter PiP, try to find the active media controller for the top most activity + resolveActiveMediaController(mMediaSessionManager.getActiveSessions(null)); + } + + /** + * Adds a new media action listener. + */ + public void addListener(ActionListener listener) { + if (!mListeners.contains(listener)) { + mListeners.add(listener); + listener.onMediaActionsChanged(getMediaActions()); + } + } + + /** + * Removes a media action listener. + */ + public void removeListener(ActionListener listener) { + listener.onMediaActionsChanged(Collections.EMPTY_LIST); + mListeners.remove(listener); + } + + /** + * Gets the set of media actions currently available. + */ + private List getMediaActions() { + if (mMediaController == null || mMediaController.getPlaybackState() == null) { + return Collections.EMPTY_LIST; + } + + ArrayList mediaActions = new ArrayList<>(); + int state = mMediaController.getPlaybackState().getState(); + boolean isPlaying = MediaSession.isActiveState(state); + long actions = mMediaController.getPlaybackState().getActions(); + if (!isPlaying && ((actions & PlaybackState.ACTION_PLAY) != 0)) { + mediaActions.add(mPauseAction); + } else if (isPlaying && ((actions & PlaybackState.ACTION_PAUSE) != 0)) { + mediaActions.add(mPlayAction); + } + return mediaActions; + } + + /** + * Creates the standard media buttons that we may show. + */ + private void createMediaActions() { + String pauseDescription = mContext.getString(R.string.pip_pause); + mPauseAction = new RemoteAction(Icon.createWithResource(mContext, + R.drawable.ic_pause_white_24dp), pauseDescription, pauseDescription, + action -> mMediaController.getTransportControls().pause()); + + String playDescription = mContext.getString(R.string.pip_play); + mPlayAction = new RemoteAction(Icon.createWithResource(mContext, + R.drawable.ic_play_arrow_white_24dp), playDescription, playDescription, + action -> mMediaController.getTransportControls().play()); + } + + /** + * Tries to find and set the active media controller for the top PiP activity. + */ + private void resolveActiveMediaController(List controllers) { + if (controllers != null) { + final ComponentName topActivity = PipUtils.getTopPinnedActivity(mActivityManager); + if (topActivity != null) { + for (int i = 0; i < controllers.size(); i++) { + final MediaController controller = controllers.get(i); + if (controller.getPackageName().equals(topActivity.getPackageName())) { + setActiveMediaController(controller); + return; + } + } + } + } + setActiveMediaController(null); + } + + /** + * Sets the active media controller for the top PiP activity. + */ + private void setActiveMediaController(MediaController controller) { + if (controller != mMediaController) { + if (mMediaController != null) { + mMediaController.unregisterCallback(mPlaybackChangedListener); + } + mMediaController = controller; + if (controller != null) { + controller.registerCallback(mPlaybackChangedListener); + } + if (!mListeners.isEmpty()) { + notifyActionsChanged(getMediaActions()); + } + + // TODO(winsonc): Consider if we want to close the PIP after a timeout (like on TV) + } + } + + /** + * Notifies all listeners that the actions have changed. + */ + private void notifyActionsChanged(List actions) { + if (!mListeners.isEmpty()) { + mListeners.forEach(l -> l.onMediaActionsChanged(actions)); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java index 0f7647dbfd8bb..6ef30c0258c63 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2016 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.systemui.pip.phone; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; @@ -5,6 +21,7 @@ import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import android.app.ActivityManager.StackInfo; import android.app.ActivityOptions; import android.app.IActivityManager; +import android.app.RemoteAction; import android.content.Context; import android.content.Intent; import android.content.pm.ParceledListSlice; @@ -16,11 +33,20 @@ import android.os.UserHandle; import android.util.Log; import android.view.IWindowManager; -import java.util.ArrayList; +import com.android.systemui.pip.phone.PipMediaController.ActionListener; +import java.util.ArrayList; +import java.util.List; + +/** + * Manages the PiP menu activity. + * + * The current media session provides actions whenever there are no valid actions provided by the + * current PiP activity. Otherwise, those actions always take precedence. + */ public class PipMenuActivityController { - private static final String TAG = "PipMenuActivityController"; + private static final String TAG = "PipMenuActController"; public static final String EXTRA_CONTROLLER_MESSENGER = "messenger"; public static final String EXTRA_ACTIONS = "actions"; @@ -59,9 +85,12 @@ public class PipMenuActivityController { private Context mContext; private IActivityManager mActivityManager; private IWindowManager mWindowManager; + private PipMediaController mMediaController; private ArrayList mListeners = new ArrayList<>(); - private ParceledListSlice mActions; + private ParceledListSlice mAppActions; + private ParceledListSlice mMediaActions; + private boolean mVisible; private Messenger mToActivityMessenger; private Messenger mMessenger = new Messenger(new Handler() { @@ -70,13 +99,13 @@ public class PipMenuActivityController { switch (msg.what) { case MESSAGE_MENU_VISIBILITY_CHANGED: { boolean visible = msg.arg1 > 0; - mListeners.forEach(l -> l.onPipMenuVisibilityChanged(visible)); + onMenuVisibilityChanged(visible); break; } case MESSAGE_EXPAND_PIP: { mListeners.forEach(l -> l.onPipExpand()); // Preemptively mark the menu as invisible once we expand the PiP - mListeners.forEach(l -> l.onPipMenuVisibilityChanged(false)); + onMenuVisibilityChanged(false); break; } case MESSAGE_MINIMIZE_PIP: { @@ -86,14 +115,14 @@ public class PipMenuActivityController { case MESSAGE_DISMISS_PIP: { mListeners.forEach(l -> l.onPipDismiss()); // Preemptively mark the menu as invisible once we dismiss the PiP - mListeners.forEach(l -> l.onPipMenuVisibilityChanged(false)); + onMenuVisibilityChanged(false); break; } case MESSAGE_UPDATE_ACTIVITY_CALLBACK: { mToActivityMessenger = msg.replyTo; // Mark the menu as invisible once the activity finishes as well if (mToActivityMessenger == null) { - mListeners.forEach(l -> l.onPipMenuVisibilityChanged(false)); + onMenuVisibilityChanged(false); } break; } @@ -101,11 +130,20 @@ public class PipMenuActivityController { } }); + private ActionListener mMediaActionListener = new ActionListener() { + @Override + public void onMediaActionsChanged(List mediaActions) { + mMediaActions = new ParceledListSlice<>(mediaActions); + updateMenuActions(); + } + }; + public PipMenuActivityController(Context context, IActivityManager activityManager, - IWindowManager windowManager) { + IWindowManager windowManager, PipMediaController mediaController) { mContext = context; mActivityManager = activityManager; mWindowManager = windowManager; + mMediaController = mediaController; } /** @@ -137,7 +175,7 @@ public class PipMenuActivityController { pinnedStackInfo.taskIds.length > 0) { Intent intent = new Intent(mContext, PipMenuActivity.class); intent.putExtra(EXTRA_CONTROLLER_MESSENGER, mMessenger); - intent.putExtra(EXTRA_ACTIONS, mActions); + intent.putExtra(EXTRA_ACTIONS, resolveMenuActions()); ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0); options.setLaunchTaskId( pinnedStackInfo.taskIds[pinnedStackInfo.taskIds.length - 1]); @@ -168,15 +206,31 @@ public class PipMenuActivityController { } /** - * Sets the {@param actions} associated with the PiP. + * Sets the menu actions to the actions provided by the current PiP activity. */ - public void setActions(ParceledListSlice actions) { - mActions = actions; + public void setAppActions(ParceledListSlice appActions) { + mAppActions = appActions; + updateMenuActions(); + } + /** + * @return the best set of actions to show in the PiP menu. + */ + private ParceledListSlice resolveMenuActions() { + if (isValidActions(mAppActions)) { + return mAppActions; + } + return mMediaActions; + } + + /** + * Updates the PiP menu activity with the best set of actions provided. + */ + private void updateMenuActions() { if (mToActivityMessenger != null) { Message m = Message.obtain(); m.what = PipMenuActivity.MESSAGE_UPDATE_ACTIONS; - m.obj = actions; + m.obj = resolveMenuActions(); try { mToActivityMessenger.send(m); } catch (RemoteException e) { @@ -184,4 +238,30 @@ public class PipMenuActivityController { } } } + + /** + * Returns whether the set of actions are valid. + */ + private boolean isValidActions(ParceledListSlice actions) { + return actions != null && actions.getList().size() > 0; + } + + /** + * Handles changes in menu visibility. + */ + private void onMenuVisibilityChanged(boolean visible) { + mListeners.forEach(l -> l.onPipMenuVisibilityChanged(visible)); + if (visible != mVisible) { + if (visible) { + // Once visible, start listening for media action changes. This call will trigger + // the menu actions to be updated again. + mMediaController.addListener(mMediaActionListener); + } else { + // Once hidden, stop listening for media action changes. This call will trigger + // the menu actions to be updated again. + mMediaController.removeListener(mMediaActionListener); + } + } + mVisible = visible; + } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index 1476110384fbb..12fda14b3ce2d 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -487,7 +487,7 @@ public class PipTouchHandler implements TunerService.Tunable { /** * Resizes the pinned stack back to fullscreen. */ - private void expandPinnedStackToFullscreen() { + void expandPinnedStackToFullscreen() { BackgroundThread.getHandler().post(() -> { try { mActivityManager.resizeStack(PINNED_STACK_ID, null /* bounds */, diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java new file mode 100644 index 0000000000000..9c03830810aab --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2017 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.systemui.pip.phone; + +import static android.app.ActivityManager.StackId.PINNED_STACK_ID; + +import android.app.ActivityManager.StackInfo; +import android.app.IActivityManager; +import android.content.ComponentName; +import android.os.RemoteException; +import android.util.Log; + +public class PipUtils { + + private static final String TAG = "PipUtils"; + + /** + * @return the ComponentName of the top activity in the pinned stack, or null if none exists. + */ + public static ComponentName getTopPinnedActivity(IActivityManager activityManager) { + try { + StackInfo pinnedStackInfo = activityManager.getStackInfo(PINNED_STACK_ID); + if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null && + pinnedStackInfo.taskIds.length > 0) { + return pinnedStackInfo.topActivity; + } + } catch (RemoteException e) { + Log.w(TAG, "Unable to get pinned stack."); + } + return null; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java index a6226565c4f97..56947e5b6231b 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java @@ -590,7 +590,7 @@ public class PipManager { @Override public void onTaskStackChanged() { if (DEBUG) Log.d(TAG, "onTaskStackChanged()"); - if (!checkCurrentUserId()) { + if (!checkCurrentUserId(DEBUG)) { return; } if (mState != STATE_NO_PIP) { @@ -627,7 +627,7 @@ public class PipManager { @Override public void onActivityPinned() { if (DEBUG) Log.d(TAG, "onActivityPinned()"); - if (!checkCurrentUserId()) { + if (!checkCurrentUserId(DEBUG)) { return; } StackInfo stackInfo = getPinnedStackInfo(); @@ -658,9 +658,9 @@ public class PipManager { } @Override - public void onPinnedActivityRestartAttempt() { + public void onPinnedActivityRestartAttempt(ComponentName sourceComponent) { if (DEBUG) Log.d(TAG, "onPinnedActivityRestartAttempt()"); - if (!checkCurrentUserId()) { + if (!checkCurrentUserId(DEBUG)) { return; } // If PIPed activity is launched again by Launcher or intent, make it fullscreen. @@ -670,7 +670,7 @@ public class PipManager { @Override public void onPinnedStackAnimationEnded() { if (DEBUG) Log.d(TAG, "onPinnedStackAnimationEnded()"); - if (!checkCurrentUserId()) { + if (!checkCurrentUserId(DEBUG)) { return; } switch (mState) { @@ -693,26 +693,6 @@ public class PipManager { break; } } - - // {@link android.app.ITaskStackListener} isn't multi-user aware. - // Check the current uid and current SystemUI's running uid - // so we can handle the PIP status change only once. - private boolean checkCurrentUserId() { - try { - int processUserId = UserHandle.myUserId(); - int currentUserId = mActivityManager.getCurrentUser().id; - if (processUserId != currentUserId) { - if (DEBUG) { - Log.d(TAG, "UID mismatch. SystemUI is running uid=" + processUserId - + " and the current user is uid=" + currentUserId); - } - return false; - } - } catch (RemoteException e) { - Log.w(TAG, "Unable to get current user."); - } - return true; - } }; /** diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index a2b86d1f42f5f..49074a6f535de 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -79,6 +79,7 @@ import android.view.accessibility.AccessibilityManager; import com.android.internal.app.AssistUtils; import com.android.internal.os.BackgroundThread; +import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.R; import com.android.systemui.pip.tv.PipMenuActivity; import com.android.systemui.pip.tv.PipOnboardingActivity; @@ -152,11 +153,30 @@ public class SystemServicesProxy { public void onTaskStackChanged() { } public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) { } public void onActivityPinned() { } - public void onPinnedActivityRestartAttempt() { } + public void onPinnedActivityRestartAttempt(ComponentName sourceComponent) { } public void onPinnedStackAnimationEnded() { } public void onActivityForcedResizable(String packageName, int taskId) { } public void onActivityDismissingDockedStack() { } public void onTaskProfileLocked(int taskId, int userId) { } + + /** + * Checks that the current user matches the user's SystemUI process. Since + * {@link android.app.ITaskStackListener} is not multi-user aware, handlers of + * TaskStackListener should make this call to verify that we don't act on events from other + * user's processes. + */ + protected final boolean checkCurrentUserId(boolean debug) { + int processUserId = UserHandle.myUserId(); + int currentUserId = KeyguardUpdateMonitor.getCurrentUser(); + if (processUserId != currentUserId) { + if (debug) { + Log.d(TAG, "UID mismatch. SystemUI is running uid=" + processUserId + + " and the current user is uid=" + currentUserId); + } + return false; + } + return true; + } } /** @@ -178,9 +198,11 @@ public class SystemServicesProxy { } @Override - public void onPinnedActivityRestartAttempt() throws RemoteException{ + public void onPinnedActivityRestartAttempt(ComponentName sourceComponent) + throws RemoteException{ mHandler.removeMessages(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT); - mHandler.sendEmptyMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT); + mHandler.obtainMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT, sourceComponent) + .sendToTarget(); } @Override @@ -1214,7 +1236,8 @@ public class SystemServicesProxy { } case ON_PINNED_ACTIVITY_RESTART_ATTEMPT: { for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { - mTaskStackListeners.get(i).onPinnedActivityRestartAttempt(); + mTaskStackListeners.get(i).onPinnedActivityRestartAttempt( + (ComponentName) msg.obj); } break; } diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 2634385e49dcf..3f71d123aff0a 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -582,7 +582,10 @@ class ActivityStarter { // The activity was already running in the pinned stack so it wasn't started, but either // brought to the front or the new intent was delivered to it since it was already in // front. Notify anyone interested in this piece of information. - mService.mTaskChangeNotificationController.notifyPinnedActivityRestartAttempt(); + final ComponentName sourceComponent = sourceRecord == null ? null : + sourceRecord.realActivity; + mService.mTaskChangeNotificationController.notifyPinnedActivityRestartAttempt( + sourceComponent); return; } } diff --git a/services/core/java/com/android/server/am/TaskChangeNotificationController.java b/services/core/java/com/android/server/am/TaskChangeNotificationController.java index d035fa9d94c96..2990dffd8e5ca 100644 --- a/services/core/java/com/android/server/am/TaskChangeNotificationController.java +++ b/services/core/java/com/android/server/am/TaskChangeNotificationController.java @@ -97,7 +97,7 @@ class TaskChangeNotificationController { }; private final TaskStackConsumer mNotifyPinnedActivityRestartAttempt = (l, m) -> { - l.onPinnedActivityRestartAttempt(); + l.onPinnedActivityRestartAttempt((ComponentName) m.obj); }; private final TaskStackConsumer mNotifyPinnedStackAnimationEnded = (l, m) -> { @@ -267,10 +267,11 @@ class TaskChangeNotificationController { * running in the pinned stack and the activity was not actually started, but the task is * either brought to the front or a new Intent is delivered to it. */ - void notifyPinnedActivityRestartAttempt() { + void notifyPinnedActivityRestartAttempt(ComponentName sourceComponent) { mHandler.removeMessages(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG); final Message msg = - mHandler.obtainMessage(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG); + mHandler.obtainMessage(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG, + sourceComponent); forAllLocalListeners(mNotifyPinnedActivityRestartAttempt, msg); msg.sendToTarget(); }