Merge "Sending TaskFragmentInfo and operation type in #onTaskFragmentError" into tm-qpr-dev
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 8bfb1ae..da5b88b 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3350,7 +3350,8 @@
     method @NonNull public java.util.concurrent.Executor getExecutor();
     method @NonNull public android.window.TaskFragmentOrganizerToken getOrganizerToken();
     method public void onTaskFragmentAppeared(@NonNull android.window.TaskFragmentInfo);
-    method public void onTaskFragmentError(@NonNull android.os.IBinder, @NonNull Throwable);
+    method @Deprecated public void onTaskFragmentError(@NonNull android.os.IBinder, @NonNull Throwable);
+    method public void onTaskFragmentError(@NonNull android.os.IBinder, @Nullable android.window.TaskFragmentInfo, int, @NonNull Throwable);
     method public void onTaskFragmentInfoChanged(@NonNull android.window.TaskFragmentInfo);
     method public void onTaskFragmentParentInfoChanged(@NonNull android.os.IBinder, @NonNull android.content.res.Configuration);
     method public void onTaskFragmentVanished(@NonNull android.window.TaskFragmentInfo);
diff --git a/core/java/android/window/ITaskFragmentOrganizer.aidl b/core/java/android/window/ITaskFragmentOrganizer.aidl
index 8dfda7d..3335c9c 100644
--- a/core/java/android/window/ITaskFragmentOrganizer.aidl
+++ b/core/java/android/window/ITaskFragmentOrganizer.aidl
@@ -45,10 +45,11 @@
      *
      * @param errorCallbackToken    Token set through {@link
      *                              WindowContainerTransaction#setErrorCallbackToken(IBinder)}
-     * @param exceptionBundle   Bundle containing the exception. Should be created with
-     *                          {@link TaskFragmentOrganizer#putExceptionInBundle}.
+     * @param errorBundle       Bundle containing the exception, operation type and TaskFragmentInfo
+     *                          if any. Should be created with
+     *                          {@link TaskFragmentOrganizer#putErrorInfoInBundle}.
      */
-    void onTaskFragmentError(in IBinder errorCallbackToken, in Bundle exceptionBundle);
+    void onTaskFragmentError(in IBinder errorCallbackToken, in Bundle errorBundle);
 
     /**
      * Called when an Activity is reparented to the Task with organized TaskFragment. For example,
diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java
index 2ef49c3..e4a6ad8 100644
--- a/core/java/android/window/TaskFragmentOrganizer.java
+++ b/core/java/android/window/TaskFragmentOrganizer.java
@@ -18,6 +18,7 @@
 
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.content.Intent;
 import android.content.res.Configuration;
@@ -39,16 +40,23 @@
      * Key to the exception in {@link Bundle} in {@link ITaskFragmentOrganizer#onTaskFragmentError}.
      */
     private static final String KEY_ERROR_CALLBACK_EXCEPTION = "fragment_exception";
+    private static final String KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO = "task_fragment_info";
+    private static final String KEY_ERROR_CALLBACK_OP_TYPE = "operation_type";
 
     /**
-     * Creates a {@link Bundle} with an exception that can be passed to
-     * {@link ITaskFragmentOrganizer#onTaskFragmentError}.
+     * Creates a {@link Bundle} with an exception, operation type and TaskFragmentInfo (if any)
+     * that can be passed to {@link ITaskFragmentOrganizer#onTaskFragmentError}.
      * @hide
      */
-    public static Bundle putExceptionInBundle(@NonNull Throwable exception) {
-        final Bundle exceptionBundle = new Bundle();
-        exceptionBundle.putSerializable(KEY_ERROR_CALLBACK_EXCEPTION, exception);
-        return exceptionBundle;
+    public static @NonNull Bundle putErrorInfoInBundle(@NonNull Throwable exception,
+            @Nullable TaskFragmentInfo info, int opType) {
+        final Bundle errorBundle = new Bundle();
+        errorBundle.putSerializable(KEY_ERROR_CALLBACK_EXCEPTION, exception);
+        if (info != null) {
+            errorBundle.putParcelable(KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO, info);
+        }
+        errorBundle.putInt(KEY_ERROR_CALLBACK_OP_TYPE, opType);
+        return errorBundle;
     }
 
     /**
@@ -151,11 +159,34 @@
      * @param errorCallbackToken    token set in
      *                             {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)}
      * @param exception             exception from the server side.
+     *
+     * @deprecated Use {@link #onTaskFragmentError(IBinder, TaskFragmentInfo, int, Throwable)}
+     * instead.
      */
+    @Deprecated
     public void onTaskFragmentError(
             @NonNull IBinder errorCallbackToken, @NonNull Throwable exception) {}
 
     /**
+     * Called when the {@link WindowContainerTransaction} created with
+     * {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)} failed on the server side.
+     *
+     * @param errorCallbackToken    token set in
+     *                             {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)}
+     * @param taskFragmentInfo  The {@link TaskFragmentInfo}. This could be {@code null} if no
+     *                          TaskFragment created.
+     * @param opType            The {@link WindowContainerTransaction.HierarchyOp} of the failed
+     *                          transaction operation.
+     * @param exception             exception from the server side.
+     */
+    public void onTaskFragmentError(
+            @NonNull IBinder errorCallbackToken, @Nullable TaskFragmentInfo taskFragmentInfo,
+            int opType, @NonNull Throwable exception) {
+        // Doing so to keep compatibility. This will be removed in the next release.
+        onTaskFragmentError(errorCallbackToken, exception);
+    }
+
+    /**
      * Called when an Activity is reparented to the Task with organized TaskFragment. For example,
      * when an Activity enters and then exits Picture-in-picture, it will be reparented back to its
      * orginial Task. In this case, we need to notify the organizer so that it can check if the
@@ -217,10 +248,16 @@
 
         @Override
         public void onTaskFragmentError(
-                @NonNull IBinder errorCallbackToken, @NonNull Bundle exceptionBundle) {
-            mExecutor.execute(() -> TaskFragmentOrganizer.this.onTaskFragmentError(
-                    errorCallbackToken,
-                    (Throwable) exceptionBundle.getSerializable(KEY_ERROR_CALLBACK_EXCEPTION)));
+                @NonNull IBinder errorCallbackToken, @NonNull Bundle errorBundle) {
+            mExecutor.execute(() -> {
+                final TaskFragmentInfo info = errorBundle.getParcelable(
+                        KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO, TaskFragmentInfo.class);
+                TaskFragmentOrganizer.this.onTaskFragmentError(
+                        errorCallbackToken, info,
+                        errorBundle.getInt(KEY_ERROR_CALLBACK_OP_TYPE),
+                        (Throwable) errorBundle.getSerializable(KEY_ERROR_CALLBACK_EXCEPTION,
+                                java.lang.Throwable.class));
+            });
         }
 
         @Override
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
index 3ff5315..0dba6b0 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -72,6 +72,7 @@
                 @NonNull Configuration parentConfig);
         void onActivityReparentToTask(int taskId, @NonNull Intent activityIntent,
                 @NonNull IBinder activityToken);
+        void onTaskFragmentError(@Nullable TaskFragmentInfo taskFragmentInfo, int opType);
     }
 
     /**
@@ -323,4 +324,18 @@
             mCallback.onActivityReparentToTask(taskId, activityIntent, activityToken);
         }
     }
+
+    @Override
+    public void onTaskFragmentError(@NonNull IBinder errorCallbackToken,
+            @Nullable TaskFragmentInfo taskFragmentInfo,
+            int opType, @NonNull Throwable exception) {
+        if (taskFragmentInfo != null) {
+            final IBinder fragmentToken = taskFragmentInfo.getFragmentToken();
+            mFragmentInfos.put(fragmentToken, taskFragmentInfo);
+        }
+
+        if (mCallback != null) {
+            mCallback.onTaskFragmentError(taskFragmentInfo, opType);
+        }
+    }
 }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index da9fd0c2..c688080 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -19,6 +19,8 @@
 import static android.app.ActivityManager.START_SUCCESS;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
 
 import static androidx.window.extensions.embedding.SplitContainer.getFinishPrimaryWithSecondaryBehavior;
 import static androidx.window.extensions.embedding.SplitContainer.getFinishSecondaryWithPrimaryBehavior;
@@ -296,6 +298,37 @@
         }
     }
 
+    @Override
+    public void onTaskFragmentError(@Nullable TaskFragmentInfo taskFragmentInfo, int opType) {
+        synchronized (mLock) {
+            switch (opType) {
+                case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT:
+                case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT: {
+                    final TaskFragmentContainer container;
+                    if (taskFragmentInfo != null) {
+                        container = getContainer(taskFragmentInfo.getFragmentToken());
+                    } else {
+                        container = null;
+                    }
+                    if (container == null) {
+                        break;
+                    }
+
+                    // Update the latest taskFragmentInfo and perform necessary clean-up
+                    container.setInfo(taskFragmentInfo);
+                    container.clearPendingAppearedActivities();
+                    if (container.isEmpty()) {
+                        mPresenter.cleanupContainer(container, false /* shouldFinishDependent */);
+                    }
+                    break;
+                }
+                default:
+                    Log.e(TAG, "onTaskFragmentError: taskFragmentInfo = " + taskFragmentInfo
+                            + ", opType = " + opType);
+            }
+        }
+    }
+
     /** Called on receiving {@link #onTaskFragmentVanished(TaskFragmentInfo)} for cleanup. */
     private void cleanupTaskFragment(@NonNull IBinder taskFragmentToken) {
         for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index a188e2b..37f5b6d 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -193,6 +193,11 @@
         mPendingAppearedActivities.remove(pendingAppearedActivity);
     }
 
+    void clearPendingAppearedActivities() {
+        mPendingAppearedActivities.clear();
+        mPendingAppearedIntent = null;
+    }
+
     @Nullable
     Intent getPendingAppearedIntent() {
         return mPendingAppearedIntent;
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 5b66416..550d21a 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -56,6 +56,7 @@
 import static android.os.Process.INVALID_UID;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
@@ -3015,6 +3016,7 @@
         if (taskFragment.isOrganized()) {
             mService.mWindowOrganizerController.sendTaskFragmentOperationFailure(
                     taskFragment.getTaskFragmentOrganizer(), mRequest.errorCallbackToken,
+                    taskFragment, HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT,
                     new SecurityException(errMsg));
         } else {
             // If the taskFragment is not organized, just dump error message as warning logs.
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 392d4c2..d4551be 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -16,7 +16,7 @@
 
 package com.android.server.wm;
 
-import static android.window.TaskFragmentOrganizer.putExceptionInBundle;
+import static android.window.TaskFragmentOrganizer.putErrorInfoInBundle;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
 import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
@@ -214,12 +214,15 @@
             }
         }
 
-        void onTaskFragmentError(IBinder errorCallbackToken, Throwable exception) {
+        void onTaskFragmentError(IBinder errorCallbackToken, @Nullable TaskFragment taskFragment,
+                int opType, Throwable exception) {
             ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
                     "Sending TaskFragment error exception=%s", exception.toString());
-            final Bundle exceptionBundle = putExceptionInBundle(exception);
+            final TaskFragmentInfo info =
+                    taskFragment != null ? taskFragment.getTaskFragmentInfo() : null;
+            final Bundle errorBundle = putErrorInfoInBundle(exception, info, opType);
             try {
-                mOrganizer.onTaskFragmentError(errorCallbackToken, exceptionBundle);
+                mOrganizer.onTaskFragmentError(errorCallbackToken, errorBundle);
             } catch (RemoteException e) {
                 Slog.d(TAG, "Exception sending onTaskFragmentError callback", e);
             }
@@ -462,13 +465,15 @@
     }
 
     void onTaskFragmentError(ITaskFragmentOrganizer organizer, IBinder errorCallbackToken,
-            Throwable exception) {
+            TaskFragment taskFragment, int opType, Throwable exception) {
         validateAndGetState(organizer);
         Slog.w(TAG, "onTaskFragmentError ", exception);
         final PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent.Builder(
                 PendingTaskFragmentEvent.EVENT_ERROR, organizer)
                 .setErrorCallbackToken(errorCallbackToken)
+                .setTaskFragment(taskFragment)
                 .setException(exception)
+                .setOpType(opType)
                 .build();
         mPendingTaskFragmentEvents.add(pendingEvent);
         // Make sure the error event will be dispatched if there are no other changes.
@@ -567,19 +572,22 @@
         // Set when the event is deferred due to the host task is invisible. The defer time will
         // be the last active time of the host task.
         private long mDeferTime;
+        private int mOpType;
 
         private PendingTaskFragmentEvent(@EventType int eventType,
                 ITaskFragmentOrganizer taskFragmentOrg,
                 @Nullable TaskFragment taskFragment,
                 @Nullable IBinder errorCallbackToken,
                 @Nullable Throwable exception,
-                @Nullable ActivityRecord activity) {
+                @Nullable ActivityRecord activity,
+                int opType) {
             mEventType = eventType;
             mTaskFragmentOrg = taskFragmentOrg;
             mTaskFragment = taskFragment;
             mErrorCallbackToken = errorCallbackToken;
             mException = exception;
             mActivity = activity;
+            mOpType = opType;
         }
 
         /**
@@ -610,6 +618,7 @@
             private Throwable mException;
             @Nullable
             private ActivityRecord mActivity;
+            private int mOpType;
 
             Builder(@EventType int eventType, ITaskFragmentOrganizer taskFragmentOrg) {
                 mEventType = eventType;
@@ -636,9 +645,14 @@
                 return this;
             }
 
+            Builder setOpType(int opType) {
+                mOpType = opType;
+                return this;
+            }
+
             PendingTaskFragmentEvent build() {
                 return new PendingTaskFragmentEvent(mEventType, mTaskFragmentOrg, mTaskFragment,
-                        mErrorCallbackToken, mException, mActivity);
+                        mErrorCallbackToken, mException, mActivity, mOpType);
             }
         }
     }
@@ -667,6 +681,10 @@
     }
 
     private boolean shouldSendEventWhenTaskInvisible(@NonNull PendingTaskFragmentEvent event) {
+        if (event.mEventType == PendingTaskFragmentEvent.EVENT_ERROR) {
+            return true;
+        }
+
         final TaskFragmentOrganizerState state =
                 mTaskFragmentOrganizerState.get(event.mTaskFragmentOrg.asBinder());
         final TaskFragmentInfo lastInfo = state.mLastSentTaskFragmentInfos.get(event.mTaskFragment);
@@ -757,7 +775,8 @@
                 state.onTaskFragmentParentInfoChanged(taskFragment);
                 break;
             case PendingTaskFragmentEvent.EVENT_ERROR:
-                state.onTaskFragmentError(event.mErrorCallbackToken, event.mException);
+                state.onTaskFragmentError(event.mErrorCallbackToken, taskFragment, event.mOpType,
+                        event.mException);
                 break;
             case PendingTaskFragmentEvent.EVENT_ACTIVITY_REPARENT_TO_TASK:
                 state.onActivityReparentToTask(event.mActivity);
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 2a4360d..0f5655c 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -765,7 +765,7 @@
                                     bottomActivity)) {
                         Slog.w(TAG, "Skip removing TaskFragment due in lock task mode.");
                         sendTaskFragmentOperationFailure(organizer, errorCallbackToken,
-                                new IllegalStateException(
+                                taskFragment, type, new IllegalStateException(
                                         "Not allow to delete task fragment in lock task mode."));
                         break;
                     }
@@ -779,13 +779,15 @@
                 if (tf == null) {
                     final Throwable exception = new IllegalArgumentException(
                             "Not allowed to operate with invalid fragment token");
-                    sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+                    sendTaskFragmentOperationFailure(organizer, errorCallbackToken, tf, type,
+                            exception);
                     break;
                 }
                 if (tf.isEmbeddedTaskFragmentInPip()) {
                     final Throwable exception = new IllegalArgumentException(
                             "Not allowed to start activity in PIP TaskFragment");
-                    sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+                    sendTaskFragmentOperationFailure(organizer, errorCallbackToken, tf, type,
+                            exception);
                     break;
                 }
                 final Intent activityIntent = hop.getActivityIntent();
@@ -795,7 +797,7 @@
                                 hop.getCallingActivity(), caller.mUid, caller.mPid,
                                 errorCallbackToken);
                 if (!isStartResultSuccessful(result)) {
-                    sendTaskFragmentOperationFailure(organizer, errorCallbackToken,
+                    sendTaskFragmentOperationFailure(organizer, errorCallbackToken, tf, type,
                             convertStartFailureToThrowable(result, activityIntent));
                 } else {
                     effects |= TRANSACT_EFFECTS_LIFECYCLE;
@@ -816,25 +818,29 @@
                 if (parent == null || activity == null) {
                     final Throwable exception = new IllegalArgumentException(
                             "Not allowed to operate with invalid fragment token or activity.");
-                    sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+                    sendTaskFragmentOperationFailure(organizer, errorCallbackToken, parent, type,
+                            exception);
                     break;
                 }
                 if (parent.isEmbeddedTaskFragmentInPip()) {
                     final Throwable exception = new IllegalArgumentException(
                             "Not allowed to reparent activity to PIP TaskFragment");
-                    sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+                    sendTaskFragmentOperationFailure(organizer, errorCallbackToken, parent, type,
+                            exception);
                     break;
                 }
                 if (parent.isAllowedToEmbedActivity(activity) != EMBEDDING_ALLOWED) {
                     final Throwable exception = new SecurityException(
                             "The task fragment is not allowed to embed the given activity.");
-                    sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+                    sendTaskFragmentOperationFailure(organizer, errorCallbackToken, parent, type,
+                            exception);
                     break;
                 }
                 if (parent.getTask() != activity.getTask()) {
                     final Throwable exception = new SecurityException("The reparented activity is"
                             + " not in the same Task as the target TaskFragment.");
-                    sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+                    sendTaskFragmentOperationFailure(organizer, errorCallbackToken, parent, type,
+                            exception);
                     break;
                 }
 
@@ -854,14 +860,16 @@
                 if (tf1 == null || (adjacentFragmentToken != null && tf2 == null)) {
                     final Throwable exception = new IllegalArgumentException(
                             "Not allowed to set adjacent on invalid fragment tokens");
-                    sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+                    sendTaskFragmentOperationFailure(organizer, errorCallbackToken, tf1, type,
+                            exception);
                     break;
                 }
                 if (tf1.isEmbeddedTaskFragmentInPip()
                         || (tf2 != null && tf2.isEmbeddedTaskFragmentInPip())) {
                     final Throwable exception = new IllegalArgumentException(
                             "Not allowed to set adjacent on TaskFragment in PIP Task");
-                    sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+                    sendTaskFragmentOperationFailure(organizer, errorCallbackToken, tf1, type,
+                            exception);
                     break;
                 }
                 tf1.setAdjacentTaskFragment(tf2);
@@ -1109,7 +1117,7 @@
                 + taskFragment.getBounds() + " does not satisfy minimum dimensions:"
                 + minDimensions + " " + reason);
         sendTaskFragmentOperationFailure(taskFragment.getTaskFragmentOrganizer(),
-                errorCallbackToken, exception);
+                errorCallbackToken, taskFragment, -1 /* opType */, exception);
     }
 
     /**
@@ -1662,13 +1670,15 @@
         if (ownerActivity == null || ownerActivity.getTask() == null) {
             final Throwable exception =
                     new IllegalArgumentException("Not allowed to operate with invalid ownerToken");
-            sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+            sendTaskFragmentOperationFailure(organizer, errorCallbackToken, null /* taskFragment */,
+                    HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT, exception);
             return;
         }
         if (!ownerActivity.isResizeable()) {
             final IllegalArgumentException exception = new IllegalArgumentException("Not allowed"
                     + " to operate with non-resizable owner Activity");
-            sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+            sendTaskFragmentOperationFailure(organizer, errorCallbackToken, null /* taskFragment */,
+                    HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT, exception);
             return;
         }
         // The ownerActivity has to belong to the same app as the target Task.
@@ -1678,13 +1688,15 @@
             final Throwable exception =
                     new SecurityException("Not allowed to operate with the ownerToken while "
                             + "the root activity of the target task belong to the different app");
-            sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+            sendTaskFragmentOperationFailure(organizer, errorCallbackToken, null /* taskFragment */,
+                    HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT, exception);
             return;
         }
         if (ownerTask.inPinnedWindowingMode()) {
             final Throwable exception = new IllegalArgumentException(
                     "Not allowed to create TaskFragment in PIP Task");
-            sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+            sendTaskFragmentOperationFailure(organizer, errorCallbackToken, null /* taskFragment */,
+                    HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT, exception);
             return;
         }
         final TaskFragment taskFragment = new TaskFragment(mService,
@@ -1712,7 +1724,8 @@
         if (newParentTF == null) {
             final Throwable exception =
                     new IllegalArgumentException("Not allowed to operate with invalid container");
-            sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+            sendTaskFragmentOperationFailure(organizer, errorCallbackToken, newParentTF,
+                    HIERARCHY_OP_TYPE_REPARENT_CHILDREN, exception);
             return;
         }
         if (newParentTF.getTaskFragmentOrganizer() != null) {
@@ -1723,20 +1736,23 @@
             if (isEmbeddingDisallowed) {
                 final Throwable exception = new SecurityException(
                         "The new parent is not allowed to embed the activities.");
-                sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+                sendTaskFragmentOperationFailure(organizer, errorCallbackToken, newParentTF,
+                        HIERARCHY_OP_TYPE_REPARENT_CHILDREN, exception);
                 return;
             }
         }
         if (newParentTF.isEmbeddedTaskFragmentInPip() || oldParent.isEmbeddedTaskFragmentInPip()) {
             final Throwable exception = new SecurityException(
                     "Not allow to reparent in TaskFragment in PIP Task.");
-            sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+            sendTaskFragmentOperationFailure(organizer, errorCallbackToken, newParentTF,
+                    HIERARCHY_OP_TYPE_REPARENT_CHILDREN, exception);
             return;
         }
         if (newParentTF.getTask() != oldParent.getTask()) {
             final Throwable exception = new SecurityException(
                     "The new parent is not in the same Task as the old parent.");
-            sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+            sendTaskFragmentOperationFailure(organizer, errorCallbackToken, newParentTF,
+                    HIERARCHY_OP_TYPE_REPARENT_CHILDREN, exception);
             return;
         }
         while (oldParent.hasChild()) {
@@ -1751,7 +1767,8 @@
             final Throwable exception =
                     new IllegalArgumentException("Not allowed to operate with invalid "
                             + "taskFragment");
-            sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+            sendTaskFragmentOperationFailure(organizer, errorCallbackToken, taskFragment,
+                    HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT, exception);
             return 0;
         }
         if (taskFragment.isEmbeddedTaskFragmentInPip()
@@ -1760,7 +1777,8 @@
                 && taskFragment.getTopNonFinishingActivity() != null) {
             final Throwable exception = new IllegalArgumentException(
                     "Not allowed to delete TaskFragment in PIP Task");
-            sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+            sendTaskFragmentOperationFailure(organizer, errorCallbackToken, taskFragment,
+                    HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT, exception);
             return 0;
         }
         mLaunchTaskFragments.removeAt(index);
@@ -1788,12 +1806,14 @@
     }
 
     void sendTaskFragmentOperationFailure(@NonNull ITaskFragmentOrganizer organizer,
-            @Nullable IBinder errorCallbackToken, @NonNull Throwable exception) {
+            @Nullable IBinder errorCallbackToken, @Nullable TaskFragment taskFragment, int opType,
+            @NonNull Throwable exception) {
         if (organizer == null) {
             throw new IllegalArgumentException("Not allowed to operate with invalid organizer");
         }
         mService.mTaskFragmentOrganizerController
-                .onTaskFragmentError(organizer, errorCallbackToken, exception);
+                .onTaskFragmentError(organizer, errorCallbackToken, taskFragment, opType,
+                        exception);
     }
 
     private Throwable convertStartFailureToThrowable(int result, Intent intent) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index e47bcc9..97f0918 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -18,6 +18,12 @@
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_CHILDREN;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -237,10 +243,10 @@
 
         mController.registerOrganizer(mIOrganizer);
         mController.onTaskFragmentError(mTaskFragment.getTaskFragmentOrganizer(),
-                mErrorToken, exception);
+                mErrorToken, null /* taskFragment */, -1 /* opType */, exception);
         mController.dispatchPendingEvents();
 
-        verify(mOrganizer).onTaskFragmentError(eq(mErrorToken), eq(exception));
+        verify(mOrganizer).onTaskFragmentError(eq(mErrorToken), eq(null), eq(-1), eq(exception));
     }
 
     @Test
@@ -604,7 +610,9 @@
         verify(mAtm.getActivityStartController(), never()).startActivityInTaskFragment(any(), any(),
                 any(), any(), anyInt(), anyInt(), any());
         verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer),
-                eq(mErrorToken), any(IllegalArgumentException.class));
+                eq(mErrorToken), eq(mTaskFragment),
+                eq(HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT),
+                any(IllegalArgumentException.class));
     }
 
     @Test
@@ -619,7 +627,9 @@
         mWindowOrganizerController.applyTransaction(mTransaction);
 
         verify(mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer),
-                eq(mErrorToken), any(IllegalArgumentException.class));
+                eq(mErrorToken), eq(mTaskFragment),
+                eq(HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT),
+                any(IllegalArgumentException.class));
         assertNull(activity.getOrganizedTaskFragment());
     }
 
@@ -635,7 +645,9 @@
         mWindowOrganizerController.applyTransaction(mTransaction);
 
         verify(mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer),
-                eq(mErrorToken), any(IllegalArgumentException.class));
+                eq(mErrorToken), eq(mTaskFragment),
+                eq(HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS),
+                any(IllegalArgumentException.class));
         verify(mTaskFragment, never()).setAdjacentTaskFragment(any());
     }
 
@@ -654,7 +666,8 @@
         mWindowOrganizerController.applyTransaction(mTransaction);
 
         verify(mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer),
-                eq(mErrorToken), any(IllegalArgumentException.class));
+                eq(mErrorToken), eq(null), eq(HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT),
+                any(IllegalArgumentException.class));
         assertNull(mWindowOrganizerController.getTaskFragment(fragmentToken));
     }
 
@@ -669,7 +682,8 @@
         mWindowOrganizerController.applyTransaction(mTransaction);
 
         verify(mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer),
-                eq(mErrorToken), any(IllegalArgumentException.class));
+                eq(mErrorToken), eq(mTaskFragment), eq(HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT),
+                any(IllegalArgumentException.class));
         assertNotNull(mWindowOrganizerController.getTaskFragment(mFragmentToken));
 
         // Allow organizer to delete empty TaskFragment for cleanup.
@@ -931,7 +945,9 @@
         // The pending event will be dispatched on the handler (from requestTraversal).
         waitHandlerIdle(mWm.mAnimationHandler);
 
-        verify(mOrganizer).onTaskFragmentError(eq(mErrorToken), any(SecurityException.class));
+        verify(mOrganizer).onTaskFragmentError(eq(mErrorToken), any(),
+                eq(HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT),
+                any(SecurityException.class));
     }
 
     @Test
@@ -968,7 +984,8 @@
         // The pending event will be dispatched on the handler (from requestTraversal).
         waitHandlerIdle(mWm.mAnimationHandler);
 
-        verify(mOrganizer).onTaskFragmentError(eq(mErrorToken), any(SecurityException.class));
+        verify(mOrganizer).onTaskFragmentError(eq(mErrorToken), any(),
+                eq(HIERARCHY_OP_TYPE_REPARENT_CHILDREN), any(SecurityException.class));
     }
 
     @Test