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;
+ }
+}