Add basic lifecycle transaction containers

This adds basic containers for holding some messages to a client,
that are related to activity lifecycle. Each transaction can hold
a list of callbacks and a final lifecycle state.

Some requests from ActivityManager to client that target activities
are now switched to use transactions. Scheduling, preparing and
executing a request is moved outside of ActivityThread class to
corresponding transaction items.

Bug: 64797980
Test: Existing AM CTS tests pass
Change-Id: I96df20787b3d792f655c9500e8a71032264d02cd
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 21e454f..1d0765d 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -23,6 +23,8 @@
 import android.app.assist.AssistContent;
 import android.app.assist.AssistStructure;
 import android.app.backup.BackupAgent;
+import android.app.servertransaction.ActivityResultItem;
+import android.app.servertransaction.ClientTransaction;
 import android.content.BroadcastReceiver;
 import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
@@ -174,7 +176,7 @@
  *
  * {@hide}
  */
-public final class ActivityThread {
+public final class ActivityThread extends ClientTransactionHandler {
     /** @hide */
     public static final String TAG = "ActivityThread";
     private static final android.graphics.Bitmap.Config THUMBNAIL_FORMAT = Bitmap.Config.RGB_565;
@@ -401,8 +403,8 @@
                     throw new IllegalStateException(
                             "Received config update for non-existing activity");
                 }
-                activity.mMainThread.handleActivityConfigurationChanged(
-                        new ActivityConfigChangeData(token, overrideConfig), newDisplayId);
+                activity.mMainThread.handleActivityConfigurationChanged(token, overrideConfig,
+                        newDisplayId);
             };
         }
 
@@ -469,16 +471,6 @@
         }
     }
 
-    static final class NewIntentData {
-        List<ReferrerIntent> intents;
-        IBinder token;
-        boolean andPause;
-        public String toString() {
-            return "NewIntentData{intents=" + intents + " token=" + token
-                    + " andPause=" + andPause +"}";
-        }
-    }
-
     static final class ReceiverData extends BroadcastReceiver.PendingResult {
         public ReceiverData(Intent intent, int resultCode, String resultData, Bundle resultExtras,
                 boolean ordered, boolean sticky, IBinder token, int sendingUser) {
@@ -644,14 +636,6 @@
         String[] args;
     }
 
-    static final class ResultData {
-        IBinder token;
-        List<ResultInfo> results;
-        public String toString() {
-            return "ResultData{token=" + token + " results" + results + "}";
-        }
-    }
-
     static final class ContextCleanupInfo {
         ContextImpl context;
         String what;
@@ -679,15 +663,6 @@
         int flags;
     }
 
-    static final class ActivityConfigChangeData {
-        final IBinder activityToken;
-        final Configuration overrideConfig;
-        public ActivityConfigChangeData(IBinder token, Configuration config) {
-            activityToken = token;
-            overrideConfig = config;
-        }
-    }
-
     private class ApplicationThread extends IApplicationThread.Stub {
         private static final String DB_INFO_FORMAT = "  %8s %8s %14s %14s  %s";
 
@@ -702,93 +677,10 @@
             }
         }
 
-        public final void schedulePauseActivity(IBinder token, boolean finished,
-                boolean userLeaving, int configChanges, boolean dontReport) {
-            int seq = getLifecycleSeq();
-            if (DEBUG_ORDER) Slog.d(TAG, "pauseActivity " + ActivityThread.this
-                    + " operation received seq: " + seq);
-            sendMessage(
-                    finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
-                    token,
-                    (userLeaving ? USER_LEAVING : 0) | (dontReport ? DONT_REPORT : 0),
-                    configChanges,
-                    seq);
-        }
-
-        public final void scheduleStopActivity(IBinder token, boolean showWindow,
-                int configChanges) {
-            int seq = getLifecycleSeq();
-            if (DEBUG_ORDER) Slog.d(TAG, "stopActivity " + ActivityThread.this
-                    + " operation received seq: " + seq);
-            sendMessage(
-                showWindow ? H.STOP_ACTIVITY_SHOW : H.STOP_ACTIVITY_HIDE,
-                token, 0, configChanges, seq);
-        }
-
-        public final void scheduleWindowVisibility(IBinder token, boolean showWindow) {
-            sendMessage(
-                showWindow ? H.SHOW_WINDOW : H.HIDE_WINDOW,
-                token);
-        }
-
         public final void scheduleSleeping(IBinder token, boolean sleeping) {
             sendMessage(H.SLEEPING, token, sleeping ? 1 : 0);
         }
 
-        public final void scheduleResumeActivity(IBinder token, int processState,
-                boolean isForward, Bundle resumeArgs) {
-            int seq = getLifecycleSeq();
-            if (DEBUG_ORDER) Slog.d(TAG, "resumeActivity " + ActivityThread.this
-                    + " operation received seq: " + seq);
-            updateProcessState(processState, false);
-            sendMessage(H.RESUME_ACTIVITY, token, isForward ? 1 : 0, 0, seq);
-        }
-
-        public final void scheduleSendResult(IBinder token, List<ResultInfo> results) {
-            ResultData res = new ResultData();
-            res.token = token;
-            res.results = results;
-            sendMessage(H.SEND_RESULT, res);
-        }
-
-        // we use token to identify this activity without having to send the
-        // activity itself back to the activity manager. (matters more with ipc)
-        @Override
-        public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
-                ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
-                CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
-                int procState, Bundle state, PersistableBundle persistentState,
-                List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
-                boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
-
-            updateProcessState(procState, false);
-
-            ActivityClientRecord r = new ActivityClientRecord();
-
-            r.token = token;
-            r.ident = ident;
-            r.intent = intent;
-            r.referrer = referrer;
-            r.voiceInteractor = voiceInteractor;
-            r.activityInfo = info;
-            r.compatInfo = compatInfo;
-            r.state = state;
-            r.persistentState = persistentState;
-
-            r.pendingResults = pendingResults;
-            r.pendingIntents = pendingNewIntents;
-
-            r.startsNotResumed = notResumed;
-            r.isForward = isForward;
-
-            r.profilerInfo = profilerInfo;
-
-            r.overrideConfig = overrideConfig;
-            updatePendingConfiguration(curConfig);
-
-            sendMessage(H.LAUNCH_ACTIVITY, r);
-        }
-
         @Override
         public final void scheduleRelaunchActivity(IBinder token,
                 List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
@@ -798,22 +690,6 @@
                     configChanges, notResumed, config, overrideConfig, true, preserveWindow);
         }
 
-        public final void scheduleNewIntent(
-                List<ReferrerIntent> intents, IBinder token, boolean andPause) {
-            NewIntentData data = new NewIntentData();
-            data.intents = intents;
-            data.token = token;
-            data.andPause = andPause;
-
-            sendMessage(H.NEW_INTENT, data);
-        }
-
-        public final void scheduleDestroyActivity(IBinder token, boolean finishing,
-                int configChanges) {
-            sendMessage(H.DESTROY_ACTIVITY, token, finishing ? 1 : 0,
-                    configChanges);
-        }
-
         public final void scheduleReceiver(Intent intent, ActivityInfo info,
                 CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
                 boolean sync, int sendingUser, int processState) {
@@ -949,11 +825,6 @@
             sendMessage(H.SUICIDE, null);
         }
 
-        public void scheduleConfigurationChanged(Configuration config) {
-            updatePendingConfiguration(config);
-            sendMessage(H.CONFIGURATION_CHANGED, config);
-        }
-
         public void scheduleApplicationInfoChanged(ApplicationInfo ai) {
             sendMessage(H.APPLICATION_INFO_CHANGED, ai);
         }
@@ -1016,20 +887,6 @@
         }
 
         @Override
-        public void scheduleActivityConfigurationChanged(
-                IBinder token, Configuration overrideConfig) {
-            sendMessage(H.ACTIVITY_CONFIGURATION_CHANGED,
-                    new ActivityConfigChangeData(token, overrideConfig));
-        }
-
-        @Override
-        public void scheduleActivityMovedToDisplay(IBinder token, int displayId,
-                Configuration overrideConfig) {
-            sendMessage(H.ACTIVITY_MOVED_TO_DISPLAY,
-                    new ActivityConfigChangeData(token, overrideConfig), displayId);
-        }
-
-        @Override
         public void profilerControl(boolean start, ProfilerInfo profilerInfo, int profileType) {
             sendMessage(H.PROFILER_CONTROL, profilerInfo, start ? 1 : 0, profileType);
         }
@@ -1427,26 +1284,6 @@
         }
 
         @Override
-        public void scheduleMultiWindowModeChanged(IBinder token, boolean isInMultiWindowMode,
-                Configuration overrideConfig) throws RemoteException {
-            SomeArgs args = SomeArgs.obtain();
-            args.arg1 = token;
-            args.arg2 = overrideConfig;
-            args.argi1 = isInMultiWindowMode ? 1 : 0;
-            sendMessage(H.MULTI_WINDOW_MODE_CHANGED, args);
-        }
-
-        @Override
-        public void schedulePictureInPictureModeChanged(IBinder token, boolean isInPipMode,
-                Configuration overrideConfig) throws RemoteException {
-            SomeArgs args = SomeArgs.obtain();
-            args.arg1 = token;
-            args.arg2 = overrideConfig;
-            args.argi1 = isInPipMode ? 1 : 0;
-            sendMessage(H.PICTURE_IN_PICTURE_MODE_CHANGED, args);
-        }
-
-        @Override
         public void scheduleLocalVoiceInteractionStarted(IBinder token,
                 IVoiceInteractor voiceInteractor) throws RemoteException {
             SomeArgs args = SomeArgs.obtain();
@@ -1459,28 +1296,33 @@
         public void handleTrustStorageUpdate() {
             NetworkSecurityPolicy.getInstance().handleTrustStorageUpdate();
         }
+
+        @Override
+        public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
+            ActivityThread.this.scheduleTransaction(transaction);
+        }
     }
 
-    private int getLifecycleSeq() {
+    @Override
+    public void updatePendingConfiguration(Configuration config) {
+        mAppThread.updatePendingConfiguration(config);
+    }
+
+    @Override
+    public void updateProcessState(int processState, boolean fromIpc) {
+        mAppThread.updateProcessState(processState, fromIpc);
+    }
+
+    @Override
+    public int getLifecycleSeq() {
         synchronized (mResourcesManager) {
             return mLifecycleSeq++;
         }
     }
 
-    private class H extends Handler {
-        public static final int LAUNCH_ACTIVITY         = 100;
-        public static final int PAUSE_ACTIVITY          = 101;
-        public static final int PAUSE_ACTIVITY_FINISHING= 102;
-        public static final int STOP_ACTIVITY_SHOW      = 103;
-        public static final int STOP_ACTIVITY_HIDE      = 104;
-        public static final int SHOW_WINDOW             = 105;
-        public static final int HIDE_WINDOW             = 106;
-        public static final int RESUME_ACTIVITY         = 107;
-        public static final int SEND_RESULT             = 108;
-        public static final int DESTROY_ACTIVITY        = 109;
+    class H extends Handler {
         public static final int BIND_APPLICATION        = 110;
         public static final int EXIT_APPLICATION        = 111;
-        public static final int NEW_INTENT              = 112;
         public static final int RECEIVER                = 113;
         public static final int CREATE_SERVICE          = 114;
         public static final int SERVICE_ARGS            = 115;
@@ -1493,7 +1335,6 @@
         public static final int UNBIND_SERVICE          = 122;
         public static final int DUMP_SERVICE            = 123;
         public static final int LOW_MEMORY              = 124;
-        public static final int ACTIVITY_CONFIGURATION_CHANGED = 125;
         public static final int RELAUNCH_ACTIVITY       = 126;
         public static final int PROFILER_CONTROL        = 127;
         public static final int CREATE_BACKUP_AGENT     = 128;
@@ -1518,30 +1359,17 @@
         public static final int ENTER_ANIMATION_COMPLETE = 149;
         public static final int START_BINDER_TRACKING = 150;
         public static final int STOP_BINDER_TRACKING_AND_DUMP = 151;
-        public static final int MULTI_WINDOW_MODE_CHANGED = 152;
-        public static final int PICTURE_IN_PICTURE_MODE_CHANGED = 153;
         public static final int LOCAL_VOICE_INTERACTION_STARTED = 154;
         public static final int ATTACH_AGENT = 155;
         public static final int APPLICATION_INFO_CHANGED = 156;
-        public static final int ACTIVITY_MOVED_TO_DISPLAY = 157;
         public static final int RUN_ISOLATED_ENTRY_POINT = 158;
+        public static final int EXECUTE_TRANSACTION = 159;
 
         String codeToString(int code) {
             if (DEBUG_MESSAGES) {
                 switch (code) {
-                    case LAUNCH_ACTIVITY: return "LAUNCH_ACTIVITY";
-                    case PAUSE_ACTIVITY: return "PAUSE_ACTIVITY";
-                    case PAUSE_ACTIVITY_FINISHING: return "PAUSE_ACTIVITY_FINISHING";
-                    case STOP_ACTIVITY_SHOW: return "STOP_ACTIVITY_SHOW";
-                    case STOP_ACTIVITY_HIDE: return "STOP_ACTIVITY_HIDE";
-                    case SHOW_WINDOW: return "SHOW_WINDOW";
-                    case HIDE_WINDOW: return "HIDE_WINDOW";
-                    case RESUME_ACTIVITY: return "RESUME_ACTIVITY";
-                    case SEND_RESULT: return "SEND_RESULT";
-                    case DESTROY_ACTIVITY: return "DESTROY_ACTIVITY";
                     case BIND_APPLICATION: return "BIND_APPLICATION";
                     case EXIT_APPLICATION: return "EXIT_APPLICATION";
-                    case NEW_INTENT: return "NEW_INTENT";
                     case RECEIVER: return "RECEIVER";
                     case CREATE_SERVICE: return "CREATE_SERVICE";
                     case SERVICE_ARGS: return "SERVICE_ARGS";
@@ -1553,8 +1381,6 @@
                     case UNBIND_SERVICE: return "UNBIND_SERVICE";
                     case DUMP_SERVICE: return "DUMP_SERVICE";
                     case LOW_MEMORY: return "LOW_MEMORY";
-                    case ACTIVITY_CONFIGURATION_CHANGED: return "ACTIVITY_CONFIGURATION_CHANGED";
-                    case ACTIVITY_MOVED_TO_DISPLAY: return "ACTIVITY_MOVED_TO_DISPLAY";
                     case RELAUNCH_ACTIVITY: return "RELAUNCH_ACTIVITY";
                     case PROFILER_CONTROL: return "PROFILER_CONTROL";
                     case CREATE_BACKUP_AGENT: return "CREATE_BACKUP_AGENT";
@@ -1577,12 +1403,11 @@
                     case INSTALL_PROVIDER: return "INSTALL_PROVIDER";
                     case ON_NEW_ACTIVITY_OPTIONS: return "ON_NEW_ACTIVITY_OPTIONS";
                     case ENTER_ANIMATION_COMPLETE: return "ENTER_ANIMATION_COMPLETE";
-                    case MULTI_WINDOW_MODE_CHANGED: return "MULTI_WINDOW_MODE_CHANGED";
-                    case PICTURE_IN_PICTURE_MODE_CHANGED: return "PICTURE_IN_PICTURE_MODE_CHANGED";
                     case LOCAL_VOICE_INTERACTION_STARTED: return "LOCAL_VOICE_INTERACTION_STARTED";
                     case ATTACH_AGENT: return "ATTACH_AGENT";
                     case APPLICATION_INFO_CHANGED: return "APPLICATION_INFO_CHANGED";
                     case RUN_ISOLATED_ENTRY_POINT: return "RUN_ISOLATED_ENTRY_POINT";
+                    case EXECUTE_TRANSACTION: return "EXECUTE_TRANSACTION";
                 }
             }
             return Integer.toString(code);
@@ -1590,76 +1415,12 @@
         public void handleMessage(Message msg) {
             if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
             switch (msg.what) {
-                case LAUNCH_ACTIVITY: {
-                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
-                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
-
-                    r.packageInfo = getPackageInfoNoCheck(
-                            r.activityInfo.applicationInfo, r.compatInfo);
-                    handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
-                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-                } break;
                 case RELAUNCH_ACTIVITY: {
                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
                     ActivityClientRecord r = (ActivityClientRecord)msg.obj;
                     handleRelaunchActivity(r);
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                 } break;
-                case PAUSE_ACTIVITY: {
-                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    handlePauseActivity((IBinder) args.arg1, false,
-                            (args.argi1 & USER_LEAVING) != 0, args.argi2,
-                            (args.argi1 & DONT_REPORT) != 0, args.argi3);
-                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-                } break;
-                case PAUSE_ACTIVITY_FINISHING: {
-                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    handlePauseActivity((IBinder) args.arg1, true, (args.argi1 & USER_LEAVING) != 0,
-                            args.argi2, (args.argi1 & DONT_REPORT) != 0, args.argi3);
-                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-                } break;
-                case STOP_ACTIVITY_SHOW: {
-                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    handleStopActivity((IBinder) args.arg1, true, args.argi2, args.argi3);
-                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-                } break;
-                case STOP_ACTIVITY_HIDE: {
-                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    handleStopActivity((IBinder) args.arg1, false, args.argi2, args.argi3);
-                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-                } break;
-                case SHOW_WINDOW:
-                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityShowWindow");
-                    handleWindowVisibility((IBinder)msg.obj, true);
-                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-                    break;
-                case HIDE_WINDOW:
-                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityHideWindow");
-                    handleWindowVisibility((IBinder)msg.obj, false);
-                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-                    break;
-                case RESUME_ACTIVITY:
-                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    handleResumeActivity((IBinder) args.arg1, true, args.argi1 != 0, true,
-                            args.argi3, "RESUME_ACTIVITY");
-                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-                    break;
-                case SEND_RESULT:
-                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityDeliverResult");
-                    handleSendResult((ResultData)msg.obj);
-                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-                    break;
-                case DESTROY_ACTIVITY:
-                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityDestroy");
-                    handleDestroyActivity((IBinder)msg.obj, msg.arg1 != 0,
-                            msg.arg2, false);
-                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-                    break;
                 case BIND_APPLICATION:
                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
                     AppBindData data = (AppBindData)msg.obj;
@@ -1672,11 +1433,6 @@
                     }
                     Looper.myLooper().quit();
                     break;
-                case NEW_INTENT:
-                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityNewIntent");
-                    handleNewIntent((NewIntentData)msg.obj);
-                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-                    break;
                 case RECEIVER:
                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastReceiveComp");
                     handleReceiver((ReceiverData)msg.obj);
@@ -1708,15 +1464,7 @@
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                     break;
                 case CONFIGURATION_CHANGED:
-                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "configChanged");
-                    mCurDefaultDisplayDpi = ((Configuration)msg.obj).densityDpi;
-                    mUpdatingSystemConfig = true;
-                    try {
-                        handleConfigurationChanged((Configuration) msg.obj, null);
-                    } finally {
-                        mUpdatingSystemConfig = false;
-                    }
-                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+                    handleConfigurationChanged((Configuration) msg.obj);
                     break;
                 case CLEAN_UP_CONTEXT:
                     ContextCleanupInfo cci = (ContextCleanupInfo)msg.obj;
@@ -1733,18 +1481,6 @@
                     handleLowMemory();
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                     break;
-                case ACTIVITY_CONFIGURATION_CHANGED:
-                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityConfigChanged");
-                    handleActivityConfigurationChanged((ActivityConfigChangeData) msg.obj,
-                            INVALID_DISPLAY);
-                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-                    break;
-                case ACTIVITY_MOVED_TO_DISPLAY:
-                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityMovedToDisplay");
-                    handleActivityConfigurationChanged((ActivityConfigChangeData) msg.obj,
-                            msg.arg1 /* displayId */);
-                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-                    break;
                 case PROFILER_CONTROL:
                     handleProfilerControl(msg.arg1 != 0, (ProfilerInfo)msg.obj, msg.arg2);
                     break;
@@ -1828,16 +1564,6 @@
                 case STOP_BINDER_TRACKING_AND_DUMP:
                     handleStopBinderTrackingAndDump((ParcelFileDescriptor) msg.obj);
                     break;
-                case MULTI_WINDOW_MODE_CHANGED:
-                    handleMultiWindowModeChanged((IBinder) ((SomeArgs) msg.obj).arg1,
-                            ((SomeArgs) msg.obj).argi1 == 1,
-                            (Configuration) ((SomeArgs) msg.obj).arg2);
-                    break;
-                case PICTURE_IN_PICTURE_MODE_CHANGED:
-                    handlePictureInPictureModeChanged((IBinder) ((SomeArgs) msg.obj).arg1,
-                            ((SomeArgs) msg.obj).argi1 == 1,
-                            (Configuration) ((SomeArgs) msg.obj).arg2);
-                    break;
                 case LOCAL_VOICE_INTERACTION_STARTED:
                     handleLocalVoiceInteractionStarted((IBinder) ((SomeArgs) msg.obj).arg1,
                             (IVoiceInteractor) ((SomeArgs) msg.obj).arg2);
@@ -1857,6 +1583,9 @@
                     handleRunIsolatedEntryPoint((String) ((SomeArgs) msg.obj).arg1,
                             (String[]) ((SomeArgs) msg.obj).arg2);
                     break;
+                case EXECUTE_TRANSACTION:
+                    ((ClientTransaction) msg.obj).execute(ActivityThread.this);
+                    break;
             }
             Object obj = msg.obj;
             if (obj instanceof SomeArgs) {
@@ -2601,10 +2330,16 @@
                 + " req=" + requestCode + " res=" + resultCode + " data=" + data);
         ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
         list.add(new ResultInfo(id, requestCode, resultCode, data));
-        mAppThread.scheduleSendResult(token, list);
+        final ClientTransaction clientTransaction = new ClientTransaction(mAppThread, token);
+        clientTransaction.addCallback(new ActivityResultItem(list));
+        try {
+            mAppThread.scheduleTransaction(clientTransaction);
+        } catch (RemoteException e) {
+            // Local scheduling
+        }
     }
 
-    private void sendMessage(int what, Object obj) {
+    void sendMessage(int what, Object obj) {
         sendMessage(what, obj, 0, 0, false);
     }
 
@@ -2844,6 +2579,37 @@
         return appContext;
     }
 
+    @Override
+    public void handleLaunchActivity(IBinder token, Intent intent, int ident, ActivityInfo info,
+            Configuration overrideConfig, CompatibilityInfo compatInfo, String referrer,
+            IVoiceInteractor voiceInteractor, Bundle state, PersistableBundle persistentState,
+            List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
+            boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
+        ActivityClientRecord r = new ActivityClientRecord();
+
+        r.token = token;
+        r.ident = ident;
+        r.intent = intent;
+        r.referrer = referrer;
+        r.voiceInteractor = voiceInteractor;
+        r.activityInfo = info;
+        r.compatInfo = compatInfo;
+        r.state = state;
+        r.persistentState = persistentState;
+
+        r.pendingResults = pendingResults;
+        r.pendingIntents = pendingNewIntents;
+
+        r.startsNotResumed = notResumed;
+        r.isForward = isForward;
+
+        r.profilerInfo = profilerInfo;
+
+        r.overrideConfig = overrideConfig;
+        r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo);
+        handleLaunchActivity(r, null /* customIntent */, "LAUNCH_ACTIVITY");
+    }
+
     private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
         // If we are getting ready to gc after going to the background, well
         // we are back active so skip it.
@@ -2974,8 +2740,9 @@
         }
     }
 
-    private void handleNewIntent(NewIntentData data) {
-        performNewIntents(data.token, data.intents, data.andPause);
+    @Override
+    public void handleNewIntent(IBinder token, List<ReferrerIntent> intents, boolean andPause) {
+        performNewIntents(token, intents, andPause);
     }
 
     public void handleRequestAssistContextExtras(RequestAssistContextExtras cmd) {
@@ -3096,7 +2863,8 @@
         }
     }
 
-    private void handleMultiWindowModeChanged(IBinder token, boolean isInMultiWindowMode,
+    @Override
+    public void handleMultiWindowModeChanged(IBinder token, boolean isInMultiWindowMode,
             Configuration overrideConfig) {
         final ActivityClientRecord r = mActivities.get(token);
         if (r != null) {
@@ -3108,7 +2876,8 @@
         }
     }
 
-    private void handlePictureInPictureModeChanged(IBinder token, boolean isInPipMode,
+    @Override
+    public void handlePictureInPictureModeChanged(IBinder token, boolean isInPipMode,
             Configuration overrideConfig) {
         final ActivityClientRecord r = mActivities.get(token);
         if (r != null) {
@@ -3619,8 +3388,9 @@
         r.mPendingRemoveWindowManager = null;
     }
 
-    final void handleResumeActivity(IBinder token,
-            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
+    @Override
+    public void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
+            boolean reallyResume, int seq, String reason) {
         ActivityClientRecord r = mActivities.get(token);
         if (!checkAndUpdateLifecycleSeq(seq, r, "resumeActivity")) {
             return;
@@ -3823,7 +3593,8 @@
         return thumbnail;
     }
 
-    private void handlePauseActivity(IBinder token, boolean finished,
+    @Override
+    public void handlePauseActivity(IBinder token, boolean finished,
             boolean userLeaving, int configChanges, boolean dontReport, int seq) {
         ActivityClientRecord r = mActivities.get(token);
         if (DEBUG_ORDER) Slog.d(TAG, "handlePauseActivity " + r + ", seq: " + seq);
@@ -4087,7 +3858,8 @@
         }
     }
 
-    private void handleStopActivity(IBinder token, boolean show, int configChanges, int seq) {
+    @Override
+    public void handleStopActivity(IBinder token, boolean show, int configChanges, int seq) {
         ActivityClientRecord r = mActivities.get(token);
         if (!checkAndUpdateLifecycleSeq(seq, r, "stopActivity")) {
             return;
@@ -4142,7 +3914,8 @@
         }
     }
 
-    private void handleWindowVisibility(IBinder token, boolean show) {
+    @Override
+    public void handleWindowVisibility(IBinder token, boolean show) {
         ActivityClientRecord r = mActivities.get(token);
 
         if (r == null) {
@@ -4288,8 +4061,9 @@
         }
     }
 
-    private void handleSendResult(ResultData res) {
-        ActivityClientRecord r = mActivities.get(res.token);
+    @Override
+    public void handleSendResult(IBinder token, List<ResultInfo> results) {
+        ActivityClientRecord r = mActivities.get(token);
         if (DEBUG_RESULTS) Slog.v(TAG, "Handling send result to " + r);
         if (r != null) {
             final boolean resumed = !r.paused;
@@ -4323,7 +4097,7 @@
                 }
             }
             checkAndBlockForNetworkAccess();
-            deliverResults(r, res.results);
+            deliverResults(r, results);
             if (resumed) {
                 r.activity.performResume();
                 r.activity.mTemporaryPause = false;
@@ -4410,8 +4184,9 @@
         return component == null ? "[Unknown]" : component.toShortString();
     }
 
-    private void handleDestroyActivity(IBinder token, boolean finishing,
-            int configChanges, boolean getNonConfigInstance) {
+    @Override
+    public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
+            boolean getNonConfigInstance) {
         ActivityClientRecord r = performDestroyActivity(token, finishing,
                 configChanges, getNonConfigInstance);
         if (r != null) {
@@ -4982,7 +4757,20 @@
         return config;
     }
 
-    final void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) {
+    @Override
+    public void handleConfigurationChanged(Configuration config) {
+        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "configChanged");
+        mCurDefaultDisplayDpi = config.densityDpi;
+        mUpdatingSystemConfig = true;
+        try {
+            handleConfigurationChanged(config, null /* compat */);
+        } finally {
+            mUpdatingSystemConfig = false;
+        }
+        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+    }
+
+    private void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) {
 
         int configDiff = 0;
 
@@ -5113,12 +4901,15 @@
 
     /**
      * Handle new activity configuration and/or move to a different display.
-     * @param data Configuration update data.
+     * @param activityToken Target activity token.
+     * @param overrideConfig Activity override config.
      * @param displayId Id of the display where activity was moved to, -1 if there was no move and
      *                  value didn't change.
      */
-    void handleActivityConfigurationChanged(ActivityConfigChangeData data, int displayId) {
-        ActivityClientRecord r = mActivities.get(data.activityToken);
+    @Override
+    public void handleActivityConfigurationChanged(IBinder activityToken,
+            Configuration overrideConfig, int displayId) {
+        ActivityClientRecord r = mActivities.get(activityToken);
         // Check input params.
         if (r == null || r.activity == null) {
             if (DEBUG_CONFIGURATION) Slog.w(TAG, "Not found target activity to report to: " + r);
@@ -5128,14 +4919,14 @@
                 && displayId != r.activity.getDisplay().getDisplayId();
 
         // Perform updates.
-        r.overrideConfig = data.overrideConfig;
+        r.overrideConfig = overrideConfig;
         final ViewRootImpl viewRoot = r.activity.mDecor != null
             ? r.activity.mDecor.getViewRootImpl() : null;
 
         if (movedToDifferentDisplay) {
             if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle activity moved to display, activity:"
                     + r.activityInfo.name + ", displayId=" + displayId
-                    + ", config=" + data.overrideConfig);
+                    + ", config=" + overrideConfig);
 
             final Configuration reportedConfig = performConfigurationChangedForActivity(r,
                     mCompatConfiguration, displayId, true /* movedToDifferentDisplay */);
@@ -5144,7 +4935,7 @@
             }
         } else {
             if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle activity config changed: "
-                    + r.activityInfo.name + ", config=" + data.overrideConfig);
+                    + r.activityInfo.name + ", config=" + overrideConfig);
             performConfigurationChangedForActivity(r, mCompatConfiguration);
         }
         // Notify the ViewRootImpl instance about configuration changes. It may have initiated this
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
new file mode 100644
index 0000000..f7f4c71
--- /dev/null
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 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 android.app;
+
+import android.app.servertransaction.ClientTransaction;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.PersistableBundle;
+
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.content.ReferrerIntent;
+
+import java.util.List;
+
+/**
+ * Defines operations that a {@link android.app.servertransaction.ClientTransaction} or its items
+ * can perform on client.
+ * @hide
+ */
+public abstract class ClientTransactionHandler {
+
+    // Schedule phase related logic and handlers.
+
+    /** Prepare and schedule transaction for execution. */
+    void scheduleTransaction(ClientTransaction transaction) {
+        transaction.prepare(this);
+        sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
+    }
+
+    abstract void sendMessage(int what, Object obj);
+
+
+    // Prepare phase related logic and handlers. Methods that inform about about pending changes or
+    // do other internal bookkeeping.
+
+    /** Get current lifecycle request number to maintain correct ordering. */
+    public abstract int getLifecycleSeq();
+
+    /** Set pending config in case it will be updated by other transaction item. */
+    public abstract void updatePendingConfiguration(Configuration config);
+
+    /** Set current process state. */
+    public abstract void updateProcessState(int processState, boolean fromIpc);
+
+
+    // Execute phase related logic and handlers. Methods here execute actual lifecycle transactions
+    // and deliver callbacks.
+
+    /** Destroy the activity. */
+    public abstract void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
+            boolean getNonConfigInstance);
+
+    /** Pause the activity. */
+    public abstract void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving,
+            int configChanges, boolean dontReport, int seq);
+
+    /** Resume the activity. */
+    public abstract void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
+            boolean reallyResume, int seq, String reason);
+
+    /** Stop the activity. */
+    public abstract void handleStopActivity(IBinder token, boolean show, int configChanges,
+            int seq);
+
+    /** Deliver activity (override) configuration change. */
+    public abstract void handleActivityConfigurationChanged(IBinder activityToken,
+            Configuration overrideConfig, int displayId);
+
+    /** Deliver result from another activity. */
+    public abstract void handleSendResult(IBinder token, List<ResultInfo> results);
+
+    /** Deliver multi-window mode change notification. */
+    public abstract void handleMultiWindowModeChanged(IBinder token, boolean isInMultiWindowMode,
+            Configuration overrideConfig);
+
+    /** Deliver new intent. */
+    public abstract void handleNewIntent(IBinder token, List<ReferrerIntent> intents,
+            boolean andPause);
+
+    /** Deliver picture-in-picture mode change notification. */
+    public abstract void handlePictureInPictureModeChanged(IBinder token, boolean isInPipMode,
+            Configuration overrideConfig);
+
+    /** Update window visibility. */
+    public abstract void handleWindowVisibility(IBinder token, boolean show);
+
+    /** Perform activity launch. */
+    public abstract void handleLaunchActivity(IBinder token, Intent intent, int ident,
+            ActivityInfo info, Configuration overrideConfig, CompatibilityInfo compatInfo,
+            String referrer, IVoiceInteractor voiceInteractor, Bundle state,
+            PersistableBundle persistentState, List<ResultInfo> pendingResults,
+            List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward,
+            ProfilerInfo profilerInfo);
+
+    /** Deliver app configuration change notification. */
+    public abstract void handleConfigurationChanged(Configuration config);
+}
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 487a94a..b25d778 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -20,6 +20,7 @@
 import android.app.IUiAutomationConnection;
 import android.app.ProfilerInfo;
 import android.app.ResultInfo;
+import android.app.servertransaction.ClientTransaction;
 import android.content.ComponentName;
 import android.content.IIntentReceiver;
 import android.content.Intent;
@@ -52,24 +53,6 @@
  * {@hide}
  */
 oneway interface IApplicationThread {
-    void schedulePauseActivity(IBinder token, boolean finished, boolean userLeaving,
-            int configChanges, boolean dontReport);
-    void scheduleStopActivity(IBinder token, boolean showWindow,
-            int configChanges);
-    void scheduleWindowVisibility(IBinder token, boolean showWindow);
-    void scheduleResumeActivity(IBinder token, int procState, boolean isForward,
-            in Bundle resumeArgs);
-    void scheduleSendResult(IBinder token, in List<ResultInfo> results);
-    void scheduleLaunchActivity(in Intent intent, IBinder token, int ident,
-            in ActivityInfo info, in Configuration curConfig, in Configuration overrideConfig,
-            in CompatibilityInfo compatInfo, in String referrer, IVoiceInteractor voiceInteractor,
-            int procState, in Bundle state, in PersistableBundle persistentState,
-            in List<ResultInfo> pendingResults, in List<ReferrerIntent> pendingNewIntents,
-            boolean notResumed, boolean isForward, in ProfilerInfo profilerInfo);
-    void scheduleNewIntent(
-            in List<ReferrerIntent> intent, IBinder token, boolean andPause);
-    void scheduleDestroyActivity(IBinder token, boolean finished,
-            int configChanges);
     void scheduleReceiver(in Intent intent, in ActivityInfo info,
             in CompatibilityInfo compatInfo,
             int resultCode, in String data, in Bundle extras, boolean sync,
@@ -87,7 +70,6 @@
             in Bundle coreSettings, in String buildSerial);
     void runIsolatedEntryPoint(in String entryPoint, in String[] entryPointArgs);
     void scheduleExit();
-    void scheduleConfigurationChanged(in Configuration config);
     void scheduleServiceArgs(IBinder token, in ParceledListSlice args);
     void updateTimeZone();
     void processInBackground();
@@ -101,9 +83,6 @@
             int resultCode, in String data, in Bundle extras, boolean ordered,
             boolean sticky, int sendingUser, int processState);
     void scheduleLowMemory();
-    void scheduleActivityConfigurationChanged(IBinder token, in Configuration overrideConfig);
-    void scheduleActivityMovedToDisplay(IBinder token, int displayId,
-            in Configuration overrideConfig);
     void scheduleRelaunchActivity(IBinder token, in List<ResultInfo> pendingResults,
             in List<ReferrerIntent> pendingNewIntents, int configChanges, boolean notResumed,
             in Configuration config, in Configuration overrideConfig, boolean preserveWindow);
@@ -146,14 +125,11 @@
     void notifyCleartextNetwork(in byte[] firstPacket);
     void startBinderTracking();
     void stopBinderTrackingAndDump(in ParcelFileDescriptor fd);
-    void scheduleMultiWindowModeChanged(IBinder token, boolean isInMultiWindowMode,
-            in Configuration newConfig);
-    void schedulePictureInPictureModeChanged(IBinder token, boolean isInPictureInPictureMode,
-            in Configuration newConfig);
     void scheduleLocalVoiceInteractionStarted(IBinder token,
             IVoiceInteractor voiceInteractor);
     void handleTrustStorageUpdate();
     void attachAgent(String path);
     void scheduleApplicationInfoChanged(in ApplicationInfo ai);
     void setNetworkBlockSeq(long procStateSeq);
+    void scheduleTransaction(in ClientTransaction transaction);
 }
diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
new file mode 100644
index 0000000..fc4f431
--- /dev/null
+++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 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 android.app.servertransaction;
+
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.view.Display.INVALID_DISPLAY;
+
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Trace;
+
+/**
+ * Activity configuration changed callback.
+ * @hide
+ */
+public class ActivityConfigurationChangeItem extends ClientTransactionItem {
+
+    private final Configuration mConfiguration;
+
+    public ActivityConfigurationChangeItem(Configuration configuration) {
+        mConfiguration = configuration;
+    }
+
+    @Override
+    public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+        // TODO(lifecycler): detect if PIP or multi-window mode changed and report it here.
+        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityConfigChanged");
+        client.handleActivityConfigurationChanged(token, mConfiguration, INVALID_DISPLAY);
+        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+    }
+
+
+    // Parcelable implementation
+
+    /** Write to Parcel. */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeTypedObject(mConfiguration, flags);
+    }
+
+    /** Read from Parcel. */
+    private ActivityConfigurationChangeItem(Parcel in) {
+        mConfiguration = in.readTypedObject(Configuration.CREATOR);
+    }
+
+    public static final Creator<ActivityConfigurationChangeItem> CREATOR =
+            new Creator<ActivityConfigurationChangeItem>() {
+        public ActivityConfigurationChangeItem createFromParcel(Parcel in) {
+            return new ActivityConfigurationChangeItem(in);
+        }
+
+        public ActivityConfigurationChangeItem[] newArray(int size) {
+            return new ActivityConfigurationChangeItem[size];
+        }
+    };
+}
diff --git a/core/java/android/app/servertransaction/ActivityLifecycleItem.java b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
new file mode 100644
index 0000000..a64108d
--- /dev/null
+++ b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 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 android.app.servertransaction;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Request for lifecycle state that an activity should reach.
+ * @hide
+ */
+public abstract class ActivityLifecycleItem extends ClientTransactionItem {
+
+    static final boolean DEBUG_ORDER = false;
+
+    @IntDef({UNDEFINED, RESUMED, PAUSED, STOPPED, DESTROYED})
+    @Retention(RetentionPolicy.SOURCE)
+    @interface LifecycleState{}
+    public static final int UNDEFINED = -1;
+    public static final int RESUMED = 0;
+    public static final int PAUSED = 1;
+    public static final int STOPPED = 2;
+    public static final int DESTROYED = 3;
+
+    /** A final lifecycle state that an activity should reach. */
+    @LifecycleState
+    public abstract int getTargetState();
+}
diff --git a/core/java/android/app/servertransaction/ActivityResultItem.java b/core/java/android/app/servertransaction/ActivityResultItem.java
new file mode 100644
index 0000000..d77f767
--- /dev/null
+++ b/core/java/android/app/servertransaction/ActivityResultItem.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 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 android.app.servertransaction;
+
+import static android.app.servertransaction.ActivityLifecycleItem.PAUSED;
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+
+import android.app.ResultInfo;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.Trace;
+
+import java.util.List;
+
+/**
+ * Activity result delivery callback.
+ * @hide
+ */
+public class ActivityResultItem extends ClientTransactionItem {
+
+    private final List<ResultInfo> mResultInfoList;
+
+    public ActivityResultItem(List<ResultInfo> resultInfos) {
+        mResultInfoList = resultInfos;
+    }
+
+    @Override
+    public int getPreExecutionState() {
+        return PAUSED;
+    }
+
+    @Override
+    public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityDeliverResult");
+        client.handleSendResult(token, mResultInfoList);
+        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+    }
+
+
+    // Parcelable implementation
+
+    /** Write to Parcel. */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeTypedList(mResultInfoList, flags);
+    }
+
+    /** Read from Parcel. */
+    private ActivityResultItem(Parcel in) {
+        mResultInfoList = in.createTypedArrayList(ResultInfo.CREATOR);
+    }
+
+    public static final Parcelable.Creator<ActivityResultItem> CREATOR =
+            new Parcelable.Creator<ActivityResultItem>() {
+        public ActivityResultItem createFromParcel(Parcel in) {
+            return new ActivityResultItem(in);
+        }
+
+        public ActivityResultItem[] newArray(int size) {
+            return new ActivityResultItem[size];
+        }
+    };
+}
diff --git a/core/java/android/app/servertransaction/BaseClientRequest.java b/core/java/android/app/servertransaction/BaseClientRequest.java
new file mode 100644
index 0000000..4bd01af
--- /dev/null
+++ b/core/java/android/app/servertransaction/BaseClientRequest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 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 android.app.servertransaction;
+
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+
+/**
+ * Base interface for individual requests from server to client.
+ * Each of them can be prepared before scheduling and, eventually, executed.
+ * @hide
+ */
+public interface BaseClientRequest {
+
+    /**
+     * Prepare the client request before scheduling.
+     * An example of this might be informing about pending updates for some values.
+     *
+     * @param client Target client handler.
+     * @param token  Target activity token.
+     */
+    default void prepare(ClientTransactionHandler client, IBinder token) {
+    }
+
+    /**
+     * Execute the request.
+     * @param client Target client handler.
+     * @param token Target activity token.
+     */
+    void execute(ClientTransactionHandler client, IBinder token);
+}
diff --git a/core/java/android/app/servertransaction/ClientTransaction.aidl b/core/java/android/app/servertransaction/ClientTransaction.aidl
new file mode 100644
index 0000000..ad8bcbf
--- /dev/null
+++ b/core/java/android/app/servertransaction/ClientTransaction.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright 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 android.app.servertransaction;
+
+/** @hide */
+parcelable ClientTransaction;
\ No newline at end of file
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
new file mode 100644
index 0000000..211a3fd
--- /dev/null
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright 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 android.app.servertransaction;
+
+import android.app.IApplicationThread;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A container that holds a sequence of messages, which may be sent to a client.
+ * This includes a list of callbacks and a final lifecycle state.
+ *
+ * @see com.android.server.am.ClientLifecycleManager
+ * @see ClientTransactionItem
+ * @see ActivityLifecycleItem
+ * @hide
+ */
+public class ClientTransaction implements Parcelable {
+
+    /** A list of individual callbacks to a client. */
+    private List<ClientTransactionItem> mActivityCallbacks;
+
+    /**
+     * Final lifecycle state in which the client activity should be after the transaction is
+     * executed.
+     */
+    private ActivityLifecycleItem mLifecycleStateRequest;
+
+    /** Target client. */
+    private IApplicationThread mClient;
+
+    /** Target client activity. Might be null if the entire transaction is targeting an app. */
+    private IBinder mActivityToken;
+
+    public ClientTransaction(IApplicationThread client, IBinder activityToken) {
+        mClient = client;
+        mActivityToken = activityToken;
+    }
+
+    /**
+     * Add a message to the end of the sequence of callbacks.
+     * @param activityCallback A single message that can contain a lifecycle request/callback.
+     */
+    public void addCallback(ClientTransactionItem activityCallback) {
+        if (mActivityCallbacks == null) {
+            mActivityCallbacks = new ArrayList<>();
+        }
+        mActivityCallbacks.add(activityCallback);
+    }
+
+    /**
+     * Set the lifecycle state in which the client should be after executing the transaction.
+     * @param stateRequest A lifecycle request initialized with right parameters.
+     */
+    public void setLifecycleStateRequest(ActivityLifecycleItem stateRequest) {
+        mLifecycleStateRequest = stateRequest;
+    }
+
+    /**
+     * Do what needs to be done while the transaction is being scheduled on the client side.
+     * @param clientTransactionHandler Handler on the client side that will executed all operations
+     *                                 requested by transaction items.
+     */
+    public void prepare(android.app.ClientTransactionHandler clientTransactionHandler) {
+        if (mActivityCallbacks != null) {
+            final int size = mActivityCallbacks.size();
+            for (int i = 0; i < size; ++i) {
+                mActivityCallbacks.get(i).prepare(clientTransactionHandler, mActivityToken);
+            }
+        }
+        if (mLifecycleStateRequest != null) {
+            mLifecycleStateRequest.prepare(clientTransactionHandler, mActivityToken);
+        }
+    }
+
+    /**
+     * Execute the transaction.
+     * @param clientTransactionHandler Handler on the client side that will execute all operations
+     *                                 requested by transaction items.
+     */
+    public void execute(android.app.ClientTransactionHandler clientTransactionHandler) {
+        if (mActivityCallbacks != null) {
+            final int size = mActivityCallbacks.size();
+            for (int i = 0; i < size; ++i) {
+                mActivityCallbacks.get(i).execute(clientTransactionHandler, mActivityToken);
+            }
+        }
+        if (mLifecycleStateRequest != null) {
+            mLifecycleStateRequest.execute(clientTransactionHandler, mActivityToken);
+        }
+    }
+
+    /**
+     * Schedule the transaction after it was initialized. It will be send to client and all its
+     * individual parts will be applied in the following sequence:
+     * 1. The client calls {@link #prepare()}, which triggers all work that needs to be done before
+     *    actually scheduling the transaction for callbacks and lifecycle state request.
+     * 2. The transaction message is scheduled.
+     * 3. The client calls {@link #execute()}, which executes all callbacks and necessary lifecycle
+     *    transitions.
+     */
+    public void schedule() throws RemoteException {
+        mClient.scheduleTransaction(this);
+    }
+
+
+    // Parcelable implementation
+
+    /** Write to Parcel. */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeStrongBinder(mClient.asBinder());
+        final boolean writeActivityToken = mActivityToken != null;
+        dest.writeBoolean(writeActivityToken);
+        if (writeActivityToken) {
+            dest.writeStrongBinder(mActivityToken);
+        }
+        dest.writeParcelable(mLifecycleStateRequest, flags);
+        final boolean writeActivityCallbacks = mActivityCallbacks != null;
+        dest.writeBoolean(writeActivityCallbacks);
+        if (writeActivityCallbacks) {
+            dest.writeParcelableList(mActivityCallbacks, flags);
+        }
+    }
+
+    /** Read from Parcel. */
+    private ClientTransaction(Parcel in) {
+        mClient = (IApplicationThread) in.readStrongBinder();
+        final boolean readActivityToken = in.readBoolean();
+        if (readActivityToken) {
+            mActivityToken = in.readStrongBinder();
+        }
+        mLifecycleStateRequest = in.readParcelable(getClass().getClassLoader());
+        final boolean readActivityCallbacks = in.readBoolean();
+        if (readActivityCallbacks) {
+            mActivityCallbacks = new ArrayList<>();
+            in.readParcelableList(mActivityCallbacks, getClass().getClassLoader());
+        }
+    }
+
+    public static final Creator<ClientTransaction> CREATOR =
+            new Creator<ClientTransaction>() {
+        public ClientTransaction createFromParcel(Parcel in) {
+            return new ClientTransaction(in);
+        }
+
+        public ClientTransaction[] newArray(int size) {
+            return new ClientTransaction[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/app/servertransaction/ClientTransactionItem.java b/core/java/android/app/servertransaction/ClientTransactionItem.java
new file mode 100644
index 0000000..6f2cc00
--- /dev/null
+++ b/core/java/android/app/servertransaction/ClientTransactionItem.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 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 android.app.servertransaction;
+
+import static android.app.servertransaction.ActivityLifecycleItem.LifecycleState;
+import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
+
+import android.os.Parcelable;
+
+/**
+ * A callback message to a client that can be scheduled and executed.
+ * Examples of these might be activity configuration change, multi-window mode change, activity
+ * result delivery etc.
+ *
+ * @see ClientTransaction
+ * @see com.android.server.am.ClientLifecycleManager
+ * @hide
+ */
+public abstract class ClientTransactionItem implements BaseClientRequest, Parcelable {
+
+    /** Get the state in which this callback can be executed. */
+    @LifecycleState
+    public int getPreExecutionState() {
+        return UNDEFINED;
+    }
+
+    /** Get the state that must follow this callback. */
+    @LifecycleState
+    public int getPostExecutionState() {
+        return UNDEFINED;
+    }
+
+
+    // Parcelable
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/app/servertransaction/ConfigurationChangeItem.java b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
new file mode 100644
index 0000000..2f128e5
--- /dev/null
+++ b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 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 android.app.servertransaction;
+
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.os.Parcel;
+
+/**
+ * App configuration change message.
+ * @hide
+ */
+public class ConfigurationChangeItem extends ClientTransactionItem {
+
+    private final Configuration mConfiguration;
+
+    public ConfigurationChangeItem(Configuration configuration) {
+        mConfiguration = new Configuration(configuration);
+    }
+
+    @Override
+    public void prepare(android.app.ClientTransactionHandler client, IBinder token) {
+        client.updatePendingConfiguration(mConfiguration);
+    }
+
+    @Override
+    public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+        client.handleConfigurationChanged(mConfiguration);
+    }
+
+    // Parcelable implementation
+
+    /** Write to Parcel. */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeTypedObject(mConfiguration, flags);
+    }
+
+    /** Read from Parcel. */
+    private ConfigurationChangeItem(Parcel in) {
+        mConfiguration = in.readTypedObject(Configuration.CREATOR);
+    }
+
+    public static final Creator<ConfigurationChangeItem> CREATOR =
+            new Creator<ConfigurationChangeItem>() {
+        public ConfigurationChangeItem createFromParcel(Parcel in) {
+            return new ConfigurationChangeItem(in);
+        }
+
+        public ConfigurationChangeItem[] newArray(int size) {
+            return new ConfigurationChangeItem[size];
+        }
+    };
+}
diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java
new file mode 100644
index 0000000..3a61dfe
--- /dev/null
+++ b/core/java/android/app/servertransaction/DestroyActivityItem.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 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 android.app.servertransaction;
+
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Trace;
+
+/**
+ * Request to destroy an activity.
+ * @hide
+ */
+public class DestroyActivityItem extends ActivityLifecycleItem {
+
+    private final boolean mFinished;
+    private final int mConfigChanges;
+
+    public DestroyActivityItem(boolean finished, int configChanges) {
+        mFinished = finished;
+        mConfigChanges = configChanges;
+    }
+
+    @Override
+    public void execute(ClientTransactionHandler client, IBinder token) {
+        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityDestroy");
+        client.handleDestroyActivity(token, mFinished, mConfigChanges,
+                false /* getNonConfigInstance */);
+        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+    }
+
+    @Override
+    public int getTargetState() {
+        return DESTROYED;
+    }
+
+
+    // Parcelable implementation
+
+    /** Write to Parcel. */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeBoolean(mFinished);
+        dest.writeInt(mConfigChanges);
+    }
+
+    /** Read from Parcel. */
+    private DestroyActivityItem(Parcel in) {
+        mFinished = in.readBoolean();
+        mConfigChanges = in.readInt();
+    }
+
+    public static final Creator<DestroyActivityItem> CREATOR =
+            new Creator<DestroyActivityItem>() {
+        public DestroyActivityItem createFromParcel(Parcel in) {
+            return new DestroyActivityItem(in);
+        }
+
+        public DestroyActivityItem[] newArray(int size) {
+            return new DestroyActivityItem[size];
+        }
+    };
+}
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
new file mode 100644
index 0000000..d945a5f
--- /dev/null
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 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 android.app.servertransaction;
+
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+
+import android.app.ClientTransactionHandler;
+import android.app.ProfilerInfo;
+import android.app.ResultInfo;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.PersistableBundle;
+import android.os.Trace;
+
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.content.ReferrerIntent;
+
+import java.util.List;
+
+/**
+ * Request to launch an activity.
+ * @hide
+ */
+public class LaunchActivityItem extends ActivityLifecycleItem {
+
+    private final Intent mIntent;
+    private final int mIdent;
+    private final ActivityInfo mInfo;
+    private final Configuration mCurConfig;
+    private final Configuration mOverrideConfig;
+    private final CompatibilityInfo mCompatInfo;
+    private final String mReferrer;
+    private final IVoiceInteractor mVoiceInteractor;
+    private final int mProcState;
+    private final Bundle mState;
+    private final PersistableBundle mPersistentState;
+    private final List<ResultInfo> mPendingResults;
+    private final List<ReferrerIntent> mPendingNewIntents;
+    // TODO(lifecycler): use lifecycle request instead of this param.
+    private final boolean mNotResumed;
+    private final boolean mIsForward;
+    private final ProfilerInfo mProfilerInfo;
+
+    public LaunchActivityItem(Intent intent, int ident, ActivityInfo info,
+            Configuration curConfig, Configuration overrideConfig, CompatibilityInfo compatInfo,
+            String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state,
+            PersistableBundle persistentState, List<ResultInfo> pendingResults,
+            List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward,
+            ProfilerInfo profilerInfo) {
+        mIntent = intent;
+        mIdent = ident;
+        mInfo = info;
+        mCurConfig = curConfig;
+        mOverrideConfig = overrideConfig;
+        mCompatInfo = compatInfo;
+        mReferrer = referrer;
+        mVoiceInteractor = voiceInteractor;
+        mProcState = procState;
+        mState = state;
+        mPersistentState = persistentState;
+        mPendingResults = pendingResults;
+        mPendingNewIntents = pendingNewIntents;
+        mNotResumed = notResumed;
+        mIsForward = isForward;
+        mProfilerInfo = profilerInfo;
+    }
+
+    @Override
+    public void prepare(ClientTransactionHandler client, IBinder token) {
+        client.updateProcessState(mProcState, false);
+        client.updatePendingConfiguration(mCurConfig);
+    }
+
+    @Override
+    public void execute(ClientTransactionHandler client, IBinder token) {
+        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
+        client.handleLaunchActivity(token, mIntent, mIdent, mInfo, mOverrideConfig, mCompatInfo,
+                mReferrer, mVoiceInteractor, mState, mPersistentState, mPendingResults,
+                mPendingNewIntents, mNotResumed, mIsForward, mProfilerInfo);
+        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+    }
+
+    @Override
+    public int getTargetState() {
+        return mNotResumed ? PAUSED : RESUMED;
+    }
+
+
+    // Parcelable implementation
+
+    /** Write from Parcel. */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeTypedObject(mIntent, flags);
+        dest.writeInt(mIdent);
+        dest.writeTypedObject(mInfo, flags);
+        dest.writeTypedObject(mCurConfig, flags);
+        dest.writeTypedObject(mOverrideConfig, flags);
+        dest.writeTypedObject(mCompatInfo, flags);
+        dest.writeString(mReferrer);
+        dest.writeStrongBinder(mVoiceInteractor != null ? mVoiceInteractor.asBinder() : null);
+        dest.writeInt(mProcState);
+        dest.writeBundle(mState);
+        dest.writePersistableBundle(mPersistentState);
+        dest.writeTypedList(mPendingResults, flags);
+        dest.writeTypedList(mPendingNewIntents, flags);
+        dest.writeBoolean(mNotResumed);
+        dest.writeBoolean(mIsForward);
+        dest.writeTypedObject(mProfilerInfo, flags);
+    }
+
+    /** Read from Parcel. */
+    private LaunchActivityItem(Parcel in) {
+        mIntent = in.readTypedObject(Intent.CREATOR);
+        mIdent = in.readInt();
+        mInfo = in.readTypedObject(ActivityInfo.CREATOR);
+        mCurConfig = in.readTypedObject(Configuration.CREATOR);
+        mOverrideConfig = in.readTypedObject(Configuration.CREATOR);
+        mCompatInfo = in.readTypedObject(CompatibilityInfo.CREATOR);
+        mReferrer = in.readString();
+        mVoiceInteractor = (IVoiceInteractor) in.readStrongBinder();
+        mProcState = in.readInt();
+        mState = in.readBundle(getClass().getClassLoader());
+        mPersistentState = in.readPersistableBundle(getClass().getClassLoader());
+        mPendingResults = in.createTypedArrayList(ResultInfo.CREATOR);
+        mPendingNewIntents = in.createTypedArrayList(ReferrerIntent.CREATOR);
+        mNotResumed = in.readBoolean();
+        mIsForward = in.readBoolean();
+        mProfilerInfo = in.readTypedObject(ProfilerInfo.CREATOR);
+    }
+
+    public static final Creator<LaunchActivityItem> CREATOR =
+            new Creator<LaunchActivityItem>() {
+        public LaunchActivityItem createFromParcel(Parcel in) {
+            return new LaunchActivityItem(in);
+        }
+
+        public LaunchActivityItem[] newArray(int size) {
+            return new LaunchActivityItem[size];
+        }
+    };
+}
diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java
new file mode 100644
index 0000000..93ce78d
--- /dev/null
+++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 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 android.app.servertransaction;
+
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Trace;
+
+/**
+ * Activity move to a different display message.
+ * @hide
+ */
+public class MoveToDisplayItem extends ClientTransactionItem {
+
+    private final int mTargetDisplayId;
+    private final Configuration mConfiguration;
+
+    public MoveToDisplayItem(int targetDisplayId, Configuration configuration) {
+        mTargetDisplayId = targetDisplayId;
+        mConfiguration = configuration;
+    }
+
+    @Override
+    public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityMovedToDisplay");
+        client.handleActivityConfigurationChanged(token, mConfiguration, mTargetDisplayId);
+        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+    }
+
+
+    // Parcelable implementation
+
+    /** Write to Parcel. */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mTargetDisplayId);
+        dest.writeTypedObject(mConfiguration, flags);
+    }
+
+    /** Read from Parcel. */
+    private MoveToDisplayItem(Parcel in) {
+        mTargetDisplayId = in.readInt();
+        mConfiguration = in.readTypedObject(Configuration.CREATOR);
+    }
+
+    public static final Creator<MoveToDisplayItem> CREATOR =
+            new Creator<MoveToDisplayItem>() {
+        public MoveToDisplayItem createFromParcel(Parcel in) {
+            return new MoveToDisplayItem(in);
+        }
+
+        public MoveToDisplayItem[] newArray(int size) {
+            return new MoveToDisplayItem[size];
+        }
+    };
+}
diff --git a/core/java/android/app/servertransaction/MultiWindowModeChangeItem.java b/core/java/android/app/servertransaction/MultiWindowModeChangeItem.java
new file mode 100644
index 0000000..7f83d47
--- /dev/null
+++ b/core/java/android/app/servertransaction/MultiWindowModeChangeItem.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 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 android.app.servertransaction;
+
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.os.Parcel;
+
+/**
+ * Multi-window mode change message.
+ * @hide
+ */
+// TODO(lifecycler): Remove the use of this and just use the configuration change message to
+// communicate multi-window mode change with WindowConfiguration.
+public class MultiWindowModeChangeItem extends ClientTransactionItem {
+
+    private final boolean mIsInMultiWindowMode;
+    private final Configuration mOverrideConfig;
+
+    public MultiWindowModeChangeItem(boolean isInMultiWindowMode,
+            Configuration overrideConfig) {
+        mIsInMultiWindowMode = isInMultiWindowMode;
+        mOverrideConfig = overrideConfig;
+    }
+
+    @Override
+    public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+        client.handleMultiWindowModeChanged(token, mIsInMultiWindowMode, mOverrideConfig);
+    }
+
+
+    // Parcelable implementation
+
+    /** Write to Parcel. */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeBoolean(mIsInMultiWindowMode);
+        dest.writeTypedObject(mOverrideConfig, flags);
+    }
+
+    /** Read from Parcel. */
+    private MultiWindowModeChangeItem(Parcel in) {
+        mIsInMultiWindowMode = in.readBoolean();
+        mOverrideConfig = in.readTypedObject(Configuration.CREATOR);
+    }
+
+    public static final Creator<MultiWindowModeChangeItem> CREATOR =
+            new Creator<MultiWindowModeChangeItem>() {
+        public MultiWindowModeChangeItem createFromParcel(Parcel in) {
+            return new MultiWindowModeChangeItem(in);
+        }
+
+        public MultiWindowModeChangeItem[] newArray(int size) {
+            return new MultiWindowModeChangeItem[size];
+        }
+    };
+}
diff --git a/core/java/android/app/servertransaction/NewIntentItem.java b/core/java/android/app/servertransaction/NewIntentItem.java
new file mode 100644
index 0000000..32b11f7
--- /dev/null
+++ b/core/java/android/app/servertransaction/NewIntentItem.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 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 android.app.servertransaction;
+
+import static android.app.servertransaction.ActivityLifecycleItem.PAUSED;
+import static android.app.servertransaction.ActivityLifecycleItem.RESUMED;
+
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.Trace;
+
+import com.android.internal.content.ReferrerIntent;
+
+import java.util.List;
+
+/**
+ * New intent message.
+ * @hide
+ */
+public class NewIntentItem extends ClientTransactionItem {
+
+    private final List<ReferrerIntent> mIntents;
+    private final boolean mPause;
+
+    public NewIntentItem(List<ReferrerIntent> intents, boolean pause) {
+        mIntents = intents;
+        mPause = pause;
+    }
+
+    @Override
+    public int getPreExecutionState() {
+        return PAUSED;
+    }
+
+    @Override
+    public int getPostExecutionState() {
+        return RESUMED;
+    }
+
+    @Override
+    public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityNewIntent");
+        client.handleNewIntent(token, mIntents, mPause);
+        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+    }
+
+
+    // Parcelable implementation
+
+    /** Write to Parcel. */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeBoolean(mPause);
+        dest.writeTypedList(mIntents, flags);
+    }
+
+    /** Read from Parcel. */
+    private NewIntentItem(Parcel in) {
+        mPause = in.readBoolean();
+        mIntents = in.createTypedArrayList(ReferrerIntent.CREATOR);
+    }
+
+    public static final Parcelable.Creator<NewIntentItem> CREATOR =
+            new Parcelable.Creator<NewIntentItem>() {
+        public NewIntentItem createFromParcel(Parcel in) {
+            return new NewIntentItem(in);
+        }
+
+        public NewIntentItem[] newArray(int size) {
+            return new NewIntentItem[size];
+        }
+    };
+}
diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java
new file mode 100644
index 0000000..6f2c60d
--- /dev/null
+++ b/core/java/android/app/servertransaction/PauseActivityItem.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 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 android.app.servertransaction;
+
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Trace;
+import android.util.Slog;
+
+/**
+ * Request to move an activity to paused state.
+ * @hide
+ */
+public class PauseActivityItem extends ActivityLifecycleItem {
+
+    private static final String TAG = "PauseActivityItem";
+
+    private final boolean mFinished;
+    private final boolean mUserLeaving;
+    private final int mConfigChanges;
+    private final boolean mDontReport;
+
+    private int mLifecycleSeq;
+
+    public PauseActivityItem(boolean finished, boolean userLeaving, int configChanges,
+            boolean dontReport) {
+        mFinished = finished;
+        mUserLeaving = userLeaving;
+        mConfigChanges = configChanges;
+        mDontReport = dontReport;
+    }
+
+    @Override
+    public void prepare(ClientTransactionHandler client, IBinder token) {
+        mLifecycleSeq = client.getLifecycleSeq();
+        if (DEBUG_ORDER) {
+            Slog.d(TAG, "Pause transaction for " + client + " received seq: "
+                    + mLifecycleSeq);
+        }
+    }
+
+    @Override
+    public void execute(ClientTransactionHandler client, IBinder token) {
+        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
+        client.handlePauseActivity(token, mFinished, mUserLeaving, mConfigChanges, mDontReport,
+                mLifecycleSeq);
+        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+    }
+
+    @Override
+    public int getTargetState() {
+        return PAUSED;
+    }
+
+
+    // Parcelable implementation
+
+    /** Write to Parcel. */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeBoolean(mFinished);
+        dest.writeBoolean(mUserLeaving);
+        dest.writeInt(mConfigChanges);
+        dest.writeBoolean(mDontReport);
+    }
+
+    /** Read from Parcel. */
+    private PauseActivityItem(Parcel in) {
+        mFinished = in.readBoolean();
+        mUserLeaving = in.readBoolean();
+        mConfigChanges = in.readInt();
+        mDontReport = in.readBoolean();
+    }
+
+    public static final Creator<PauseActivityItem> CREATOR =
+            new Creator<PauseActivityItem>() {
+        public PauseActivityItem createFromParcel(Parcel in) {
+            return new PauseActivityItem(in);
+        }
+
+        public PauseActivityItem[] newArray(int size) {
+            return new PauseActivityItem[size];
+        }
+    };
+}
diff --git a/core/java/android/app/servertransaction/PipModeChangeItem.java b/core/java/android/app/servertransaction/PipModeChangeItem.java
new file mode 100644
index 0000000..1e713b6
--- /dev/null
+++ b/core/java/android/app/servertransaction/PipModeChangeItem.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 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 android.app.servertransaction;
+
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.os.Parcel;
+
+/**
+ * Picture in picture mode change message.
+ * @hide
+ */
+// TODO(lifecycler): Remove the use of this and just use the configuration change message to
+// communicate multi-window mode change with WindowConfiguration.
+public class PipModeChangeItem extends ClientTransactionItem {
+
+    private final boolean mIsInPipMode;
+    private final Configuration mOverrideConfig;
+
+    public PipModeChangeItem(boolean isInPipMode, Configuration overrideConfig) {
+        mIsInPipMode = isInPipMode;
+        mOverrideConfig = overrideConfig;
+    }
+
+    @Override
+    public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+        client.handlePictureInPictureModeChanged(token, mIsInPipMode, mOverrideConfig);
+    }
+
+
+    // Parcelable implementation
+
+    /** Write to Parcel. */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeBoolean(mIsInPipMode);
+        dest.writeTypedObject(mOverrideConfig, flags);
+    }
+
+    /** Read from Parcel. */
+    private PipModeChangeItem(Parcel in) {
+        mIsInPipMode = in.readBoolean();
+        mOverrideConfig = in.readTypedObject(Configuration.CREATOR);
+    }
+
+    public static final Creator<PipModeChangeItem> CREATOR =
+            new Creator<PipModeChangeItem>() {
+        public PipModeChangeItem createFromParcel(Parcel in) {
+            return new PipModeChangeItem(in);
+        }
+
+        public PipModeChangeItem[] newArray(int size) {
+            return new PipModeChangeItem[size];
+        }
+    };
+}
diff --git a/core/java/android/app/servertransaction/ResumeActivityItem.java b/core/java/android/app/servertransaction/ResumeActivityItem.java
new file mode 100644
index 0000000..bf6b6e2
--- /dev/null
+++ b/core/java/android/app/servertransaction/ResumeActivityItem.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 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 android.app.servertransaction;
+
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Trace;
+import android.util.Slog;
+
+/**
+ * Request to move an activity to resumed state.
+ * @hide
+ */
+public class ResumeActivityItem extends ActivityLifecycleItem {
+
+    private static final String TAG = "ResumeActivityItem";
+
+    private final int mProcState;
+    private final boolean mIsForward;
+
+    private int mLifecycleSeq;
+
+    public ResumeActivityItem(int procState, boolean isForward) {
+        mProcState = procState;
+        mIsForward = isForward;
+    }
+
+    @Override
+    public void prepare(ClientTransactionHandler client, IBinder token) {
+        mLifecycleSeq = client.getLifecycleSeq();
+        if (DEBUG_ORDER) {
+            Slog.d(TAG, "Resume transaction for " + client + " received seq: "
+                    + mLifecycleSeq);
+        }
+        client.updateProcessState(mProcState, false);
+    }
+
+    @Override
+    public void execute(ClientTransactionHandler client, IBinder token) {
+        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
+        client.handleResumeActivity(token, true /* clearHide */, mIsForward,
+                true /* reallyResume */, mLifecycleSeq, "RESUME_ACTIVITY");
+        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+    }
+
+    @Override
+    public int getTargetState() {
+        return RESUMED;
+    }
+
+
+    // Parcelable implementation
+
+    /** Write to Parcel. */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mProcState);
+        dest.writeBoolean(mIsForward);
+    }
+
+    /** Read from Parcel. */
+    private ResumeActivityItem(Parcel in) {
+        mProcState = in.readInt();
+        mIsForward = in.readBoolean();
+    }
+
+    public static final Creator<ResumeActivityItem> CREATOR =
+            new Creator<ResumeActivityItem>() {
+        public ResumeActivityItem createFromParcel(Parcel in) {
+            return new ResumeActivityItem(in);
+        }
+
+        public ResumeActivityItem[] newArray(int size) {
+            return new ResumeActivityItem[size];
+        }
+    };
+}
diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java
new file mode 100644
index 0000000..36123c6
--- /dev/null
+++ b/core/java/android/app/servertransaction/StopActivityItem.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 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 android.app.servertransaction;
+
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Trace;
+import android.util.Slog;
+
+/**
+ * Request to move an activity to stopped state.
+ * @hide
+ */
+public class StopActivityItem extends ActivityLifecycleItem {
+
+    private static final String TAG = "StopActivityItem";
+
+    private final boolean mShowWindow;
+    private final int mConfigChanges;
+
+    private int mLifecycleSeq;
+
+    public StopActivityItem(boolean showWindow, int configChanges) {
+        mShowWindow = showWindow;
+        mConfigChanges = configChanges;
+    }
+
+    @Override
+    public void prepare(ClientTransactionHandler client, IBinder token) {
+        mLifecycleSeq = client.getLifecycleSeq();
+        if (DEBUG_ORDER) {
+            Slog.d(TAG, "Stop transaction for " + client + " received seq: "
+                    + mLifecycleSeq);
+        }
+    }
+
+    @Override
+    public void execute(ClientTransactionHandler client, IBinder token) {
+        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
+        client.handleStopActivity(token, mShowWindow, mConfigChanges, mLifecycleSeq);
+        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+    }
+
+    @Override
+    public int getTargetState() {
+        return STOPPED;
+    }
+
+
+    // Parcelable implementation
+
+    /** Write to Parcel. */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeBoolean(mShowWindow);
+        dest.writeInt(mConfigChanges);
+    }
+
+    /** Read from Parcel. */
+    private StopActivityItem(Parcel in) {
+        mShowWindow = in.readBoolean();
+        mConfigChanges = in.readInt();
+    }
+
+    public static final Creator<StopActivityItem> CREATOR =
+            new Creator<StopActivityItem>() {
+        public StopActivityItem createFromParcel(Parcel in) {
+            return new StopActivityItem(in);
+        }
+
+        public StopActivityItem[] newArray(int size) {
+            return new StopActivityItem[size];
+        }
+    };
+}
diff --git a/core/java/android/app/servertransaction/WindowVisibilityItem.java b/core/java/android/app/servertransaction/WindowVisibilityItem.java
new file mode 100644
index 0000000..28308c3
--- /dev/null
+++ b/core/java/android/app/servertransaction/WindowVisibilityItem.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 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 android.app.servertransaction;
+
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Trace;
+
+/**
+ * Window visibility change message.
+ * @hide
+ */
+public class WindowVisibilityItem extends ClientTransactionItem {
+
+    private final boolean mShowWindow;
+
+    public WindowVisibilityItem(boolean showWindow) {
+        mShowWindow = showWindow;
+    }
+
+    @Override
+    public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityShowWindow");
+        client.handleWindowVisibility(token, mShowWindow);
+        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+    }
+
+
+    // Parcelable implementation
+
+    /** Write to Parcel. */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeBoolean(mShowWindow);
+    }
+
+    /** Read from Parcel. */
+    private WindowVisibilityItem(Parcel in) {
+        mShowWindow = in.readBoolean();
+    }
+
+    public static final Creator<WindowVisibilityItem> CREATOR =
+            new Creator<WindowVisibilityItem>() {
+        public WindowVisibilityItem createFromParcel(Parcel in) {
+            return new WindowVisibilityItem(in);
+        }
+
+        public WindowVisibilityItem[] newArray(int size) {
+            return new WindowVisibilityItem[size];
+        }
+    };
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 86ef857..bbd04d6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -245,6 +245,7 @@
 import android.app.assist.AssistContent;
 import android.app.assist.AssistStructure;
 import android.app.backup.IBackupManager;
+import android.app.servertransaction.ConfigurationChangeItem;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStatsManagerInternal;
 import android.appwidget.AppWidgetManager;
@@ -630,6 +631,8 @@
 
     final ActivityStarter mActivityStarter;
 
+    final ClientLifecycleManager mLifecycleManager;
+
     final TaskChangeNotificationController mTaskChangeNotificationController;
 
     final InstrumentationReporter mInstrumentationReporter = new InstrumentationReporter();
@@ -2689,6 +2692,7 @@
         mUserController = null;
         mVrController = null;
         mLockTaskController = null;
+        mLifecycleManager = null;
     }
 
     // Note: This method is invoked on the main thread but may need to attach various
@@ -2788,6 +2792,7 @@
         mRecentTasks = createRecentTasks();
         mStackSupervisor.setRecentTasks(mRecentTasks);
         mLockTaskController = new LockTaskController(mContext, mStackSupervisor, mHandler);
+        mLifecycleManager = new ClientLifecycleManager();
 
         mProcessCpuThread = new Thread("CpuTracker") {
             @Override
@@ -20450,9 +20455,11 @@
                 if (app.thread != null) {
                     if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Sending to proc "
                             + app.processName + " new config " + configCopy);
-                    app.thread.scheduleConfigurationChanged(configCopy);
+                    mLifecycleManager.scheduleTransaction(app.thread,
+                            new ConfigurationChangeItem(configCopy));
                 }
             } catch (Exception e) {
+                Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change", e);
             }
         }
 
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index b2308d5..4de1a96 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -131,6 +131,12 @@
 import android.app.PendingIntent;
 import android.app.PictureInPictureParams;
 import android.app.ResultInfo;
+import android.app.servertransaction.MoveToDisplayItem;
+import android.app.servertransaction.MultiWindowModeChangeItem;
+import android.app.servertransaction.NewIntentItem;
+import android.app.servertransaction.PipModeChangeItem;
+import android.app.servertransaction.WindowVisibilityItem;
+import android.app.servertransaction.ActivityConfigurationChangeItem;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -618,8 +624,8 @@
                     "Reporting activity moved to display" + ", activityRecord=" + this
                             + ", displayId=" + displayId + ", config=" + config);
 
-            app.thread.scheduleActivityMovedToDisplay(appToken, displayId,
-                    new Configuration(config));
+            service.mLifecycleManager.scheduleTransaction(app.thread, appToken,
+                    new MoveToDisplayItem(displayId, config));
         } catch (RemoteException e) {
             // If process died, whatever.
         }
@@ -636,7 +642,8 @@
             if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + this + ", config: "
                     + config);
 
-            app.thread.scheduleActivityConfigurationChanged(appToken, new Configuration(config));
+            service.mLifecycleManager.scheduleTransaction(app.thread, appToken,
+                    new ActivityConfigurationChangeItem(config));
         } catch (RemoteException e) {
             // If process died, whatever.
         }
@@ -657,8 +664,9 @@
 
     private void scheduleMultiWindowModeChanged(Configuration overrideConfig) {
         try {
-            app.thread.scheduleMultiWindowModeChanged(appToken, mLastReportedMultiWindowMode,
-                    overrideConfig);
+            service.mLifecycleManager.scheduleTransaction(app.thread, appToken,
+                    new MultiWindowModeChangeItem(mLastReportedMultiWindowMode,
+                            overrideConfig));
         } catch (Exception e) {
             // If process died, I don't care.
         }
@@ -684,8 +692,9 @@
 
     private void schedulePictureInPictureModeChanged(Configuration overrideConfig) {
         try {
-            app.thread.schedulePictureInPictureModeChanged(appToken,
-                    mLastReportedPictureInPictureMode, overrideConfig);
+            service.mLifecycleManager.scheduleTransaction(app.thread, appToken,
+                    new PipModeChangeItem(mLastReportedPictureInPictureMode,
+                            overrideConfig));
         } catch (Exception e) {
             // If process died, no one cares.
         }
@@ -1364,8 +1373,8 @@
             try {
                 ArrayList<ReferrerIntent> ar = new ArrayList<>(1);
                 ar.add(rintent);
-                app.thread.scheduleNewIntent(
-                        ar, appToken, state == PAUSED /* andPause */);
+                service.mLifecycleManager.scheduleTransaction(app.thread, appToken,
+                        new NewIntentItem(ar, state == PAUSED));
                 unsent = false;
             } catch (RemoteException e) {
                 Slog.w(TAG, "Exception thrown sending new intent to " + this, e);
@@ -1587,7 +1596,8 @@
             setVisible(true);
             sleeping = false;
             app.pendingUiClean = true;
-            app.thread.scheduleWindowVisibility(appToken, true /* showWindow */);
+            service.mLifecycleManager.scheduleTransaction(app.thread, appToken,
+                    new WindowVisibilityItem(true /* showWindow */));
             // The activity may be waiting for stop, but that is no longer appropriate for it.
             mStackSupervisor.mStoppingActivities.remove(this);
             mStackSupervisor.mGoingToSleepActivities.remove(this);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 8cc584e..76f9870 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -95,13 +95,19 @@
 
 import android.app.Activity;
 import android.app.ActivityManager;
-import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityOptions;
 import android.app.AppGlobals;
 import android.app.IActivityController;
 import android.app.ResultInfo;
 import android.app.WindowConfiguration.ActivityType;
 import android.app.WindowConfiguration.WindowingMode;
+import android.app.servertransaction.ActivityResultItem;
+import android.app.servertransaction.NewIntentItem;
+import android.app.servertransaction.WindowVisibilityItem;
+import android.app.servertransaction.DestroyActivityItem;
+import android.app.servertransaction.PauseActivityItem;
+import android.app.servertransaction.ResumeActivityItem;
+import android.app.servertransaction.StopActivityItem;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -1315,8 +1321,10 @@
                         prev.userId, System.identityHashCode(prev),
                         prev.shortComponentName);
                 mService.updateUsageStats(prev, false);
-                prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
-                        userLeaving, prev.configChangeFlags, pauseImmediately);
+
+                mService.mLifecycleManager.scheduleTransaction(prev.app.thread, prev.appToken,
+                        new PauseActivityItem(prev.finishing, userLeaving,
+                                prev.configChangeFlags, pauseImmediately));
             } catch (Exception e) {
                 // Ignore exception, if process died other code will cleanup.
                 Slog.w(TAG, "Exception thrown during pause", e);
@@ -1946,7 +1954,8 @@
                     if (r.app != null && r.app.thread != null) {
                         if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
                                 "Scheduling invisibility: " + r);
-                        r.app.thread.scheduleWindowVisibility(r.appToken, false);
+                        mService.mLifecycleManager.scheduleTransaction(r.app.thread, r.appToken,
+                                new WindowVisibilityItem(false /* showWindow */));
                     }
 
                     // Reset the flag indicating that an app can enter picture-in-picture once the
@@ -2466,13 +2475,15 @@
                         if (!next.finishing && N > 0) {
                             if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
                                     "Delivering results to " + next + ": " + a);
-                            next.app.thread.scheduleSendResult(next.appToken, a);
+                            mService.mLifecycleManager.scheduleTransaction(next.app.thread,
+                                    next.appToken, new ActivityResultItem(a));
                         }
                     }
 
                     if (next.newIntents != null) {
-                        next.app.thread.scheduleNewIntent(
-                                next.newIntents, next.appToken, false /* andPause */);
+                        mService.mLifecycleManager.scheduleTransaction(next.app.thread,
+                                next.appToken, new NewIntentItem(next.newIntents,
+                                        false /* andPause */));
                     }
 
                     // Well the app will no longer be stopped.
@@ -2489,8 +2500,9 @@
                     next.app.pendingUiClean = true;
                     next.app.forceProcessStateUpTo(mService.mTopProcessState);
                     next.clearOptionsLocked();
-                    next.app.thread.scheduleResumeActivity(next.appToken, next.app.repProcState,
-                            mService.isNextTransitionForward(), resumeAnimOptions);
+                    mService.mLifecycleManager.scheduleTransaction(next.app.thread, next.appToken,
+                            new ResumeActivityItem(next.app.repProcState,
+                                    mService.isNextTransitionForward()));
 
                     if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed "
                             + next);
@@ -3141,7 +3153,8 @@
                 ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
                 list.add(new ResultInfo(resultWho, requestCode,
                         resultCode, data));
-                r.app.thread.scheduleSendResult(r.appToken, list);
+                mService.mLifecycleManager.scheduleTransaction(r.app.thread, r.appToken,
+                        new ActivityResultItem(list));
                 return;
             } catch (Exception e) {
                 Slog.w(TAG, "Exception thrown sending result to " + r, e);
@@ -3269,7 +3282,8 @@
                 }
                 EventLogTags.writeAmStopActivity(
                         r.userId, System.identityHashCode(r), r.shortComponentName);
-                r.app.thread.scheduleStopActivity(r.appToken, r.visible, r.configChangeFlags);
+                mService.mLifecycleManager.scheduleTransaction(r.app.thread, r.appToken,
+                        new StopActivityItem(r.visible, r.configChangeFlags));
                 if (shouldSleepOrShutDownActivities()) {
                     r.setSleeping(true);
                 }
@@ -4066,8 +4080,8 @@
 
             try {
                 if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "Destroying: " + r);
-                r.app.thread.scheduleDestroyActivity(r.appToken, r.finishing,
-                        r.configChangeFlags);
+                mService.mLifecycleManager.scheduleTransaction(r.app.thread, r.appToken,
+                        new DestroyActivityItem(r.finishing, r.configChangeFlags));
             } catch (Exception e) {
                 // We can just ignore exceptions here...  if the process
                 // has crashed, our death notification will clean things
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 062083c..1dfe435 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -114,6 +114,7 @@
 import android.app.WaitResult;
 import android.app.WindowConfiguration.ActivityType;
 import android.app.WindowConfiguration.WindowingMode;
+import android.app.servertransaction.LaunchActivityItem;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -1375,7 +1376,8 @@
                 r.setLastReportedConfiguration(mergedConfiguration);
 
                 logIfTransactionTooLarge(r.intent, r.icicle);
-                app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
+                mService.mLifecycleManager.scheduleTransaction(app.thread, r.appToken,
+                        new LaunchActivityItem(new Intent(r.intent),
                         System.identityHashCode(r), r.info,
                         // TODO: Have this take the merged configuration instead of separate global
                         // and override configs.
@@ -1383,7 +1385,7 @@
                         mergedConfiguration.getOverrideConfiguration(), r.compat,
                         r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
                         r.persistentState, results, newIntents, !andResume,
-                        mService.isNextTransitionForward(), profilerInfo);
+                        mService.isNextTransitionForward(), profilerInfo));
 
                 if ((app.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {
                     // This may be a heavy-weight process!  Note that the package
diff --git a/services/core/java/com/android/server/am/ClientLifecycleManager.java b/services/core/java/com/android/server/am/ClientLifecycleManager.java
new file mode 100644
index 0000000..c04d103
--- /dev/null
+++ b/services/core/java/com/android/server/am/ClientLifecycleManager.java
@@ -0,0 +1,120 @@
+/*
+ * 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.server.am;
+
+import android.annotation.NonNull;
+import android.app.IApplicationThread;
+import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.ClientTransactionItem;
+import android.app.servertransaction.ActivityLifecycleItem;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+/**
+ * Class that is able to combine multiple client lifecycle transition requests and/or callbacks,
+ * and execute them as a single transaction.
+ *
+ * @see ClientTransaction
+ */
+class ClientLifecycleManager {
+    // TODO(lifecycler): Implement building transactions or global transaction.
+    // TODO(lifecycler): Use object pools for transactions and transaction items.
+
+    /**
+     * Schedule a transaction, which may consist of multiple callbacks and a lifecycle request.
+     * @param transaction A sequence of client transaction items.
+     * @throws RemoteException
+     *
+     * @see ClientTransaction
+     */
+    void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
+        transaction.schedule();
+    }
+
+    /**
+     * Schedule a single lifecycle request or callback to client activity.
+     * @param client Target client.
+     * @param activityToken Target activity token.
+     * @param stateRequest A request to move target activity to a desired lifecycle state.
+     * @throws RemoteException
+     *
+     * @see ClientTransactionItem
+     */
+    void scheduleTransaction(@NonNull IApplicationThread client, @NonNull IBinder activityToken,
+            @NonNull ActivityLifecycleItem stateRequest) throws RemoteException {
+        final ClientTransaction clientTransaction = transactionWithState(client, activityToken,
+                stateRequest);
+        scheduleTransaction(clientTransaction);
+    }
+
+    /**
+     * Schedule a single callback delivery to client activity.
+     * @param client Target client.
+     * @param activityToken Target activity token.
+     * @param callback A request to deliver a callback.
+     * @throws RemoteException
+     *
+     * @see ClientTransactionItem
+     */
+    void scheduleTransaction(@NonNull IApplicationThread client, @NonNull IBinder activityToken,
+            @NonNull ClientTransactionItem callback) throws RemoteException {
+        final ClientTransaction clientTransaction = transactionWithCallback(client, activityToken,
+                callback);
+        scheduleTransaction(clientTransaction);
+    }
+
+    /**
+     * Schedule a single callback delivery to client application.
+     * @param client Target client.
+     * @param callback A request to deliver a callback.
+     * @throws RemoteException
+     *
+     * @see ClientTransactionItem
+     */
+    void scheduleTransaction(@NonNull IApplicationThread client,
+            @NonNull ClientTransactionItem callback) throws RemoteException {
+        final ClientTransaction clientTransaction = transactionWithCallback(client,
+                null /* activityToken */, callback);
+        scheduleTransaction(clientTransaction);
+    }
+
+    /**
+     * @return A new instance of {@link ClientTransaction} with a single lifecycle state request.
+     *
+     * @see ClientTransaction
+     * @see ClientTransactionItem
+     */
+    private static ClientTransaction transactionWithState(@NonNull IApplicationThread client,
+            @NonNull IBinder activityToken, @NonNull ActivityLifecycleItem stateRequest) {
+        final ClientTransaction clientTransaction = new ClientTransaction(client, activityToken);
+        clientTransaction.setLifecycleStateRequest(stateRequest);
+        return clientTransaction;
+    }
+
+    /**
+     * @return A new instance of {@link ClientTransaction} with a single callback invocation.
+     *
+     * @see ClientTransaction
+     * @see ClientTransactionItem
+     */
+    private static ClientTransaction transactionWithCallback(@NonNull IApplicationThread client,
+            IBinder activityToken, @NonNull ClientTransactionItem callback) {
+        final ClientTransaction clientTransaction = new ClientTransaction(client, activityToken);
+        clientTransaction.addCallback(callback);
+        return clientTransaction;
+    }
+}