diff options
32 files changed, 2986 insertions, 374 deletions
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 95c5fb53a014..ffd012d9502f 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -23,6 +23,8 @@ import android.annotation.Nullable; 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 @@ final class RemoteServiceException extends AndroidRuntimeException { * * {@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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { } } - 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { 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 class ActivityThread { } } - 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { } @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 @@ public final class ActivityThread { } @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 final class ActivityThread { public void handleTrustStorageUpdate() { NetworkSecurityPolicy.getInstance().handleTrustStorageUpdate(); } + + @Override + public void scheduleTransaction(ClientTransaction transaction) throws RemoteException { + ActivityThread.this.scheduleTransaction(transaction); + } + } + + @Override + public void updatePendingConfiguration(Configuration config) { + mAppThread.updatePendingConfiguration(config); + } + + @Override + public void updateProcessState(int processState, boolean fromIpc) { + mAppThread.updateProcessState(processState, fromIpc); } - private int getLifecycleSeq() { + @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 final class ActivityThread { 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 final class ActivityThread { 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { 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 final class ActivityThread { 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 @@ public final class ActivityThread { } 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { + " 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { } } - 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 @@ public final class ActivityThread { } } - 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 @@ public final class ActivityThread { } } - 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { } } - 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 @@ public final class ActivityThread { } } - 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 @@ public final class ActivityThread { } } - 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 @@ public final class ActivityThread { } } checkAndBlockForNetworkAccess(); - deliverResults(r, res.results); + deliverResults(r, results); if (resumed) { r.activity.performResume(); r.activity.mTemporaryPause = false; @@ -4410,8 +4184,9 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { /** * 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 @@ public final class ActivityThread { && 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 @@ public final class ActivityThread { } } 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 000000000000..f7f4c716d0b7 --- /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 487a94a05f7a..b25d7782652f 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -20,6 +20,7 @@ import android.app.IInstrumentationWatcher; 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 @@ import java.util.Map; * {@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 @@ oneway interface IApplicationThread { 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 @@ oneway interface IApplicationThread { 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 @@ oneway interface IApplicationThread { 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/ProfilerInfo.java b/core/java/android/app/ProfilerInfo.java index fad4798e3a3e..d5234278da7d 100644 --- a/core/java/android/app/ProfilerInfo.java +++ b/core/java/android/app/ProfilerInfo.java @@ -22,6 +22,7 @@ import android.os.Parcelable; import android.util.Slog; import java.io.IOException; +import java.util.Objects; /** * System private API for passing profiler settings. @@ -132,4 +133,32 @@ public class ProfilerInfo implements Parcelable { streamingOutput = in.readInt() != 0; agent = in.readString(); } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final ProfilerInfo other = (ProfilerInfo) o; + // TODO: Also check #profileFd for equality. + return Objects.equals(profileFile, other.profileFile) + && autoStopProfiler == other.autoStopProfiler + && samplingInterval == other.samplingInterval + && streamingOutput == other.streamingOutput + && Objects.equals(agent, other.agent); + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + Objects.hashCode(profileFile); + result = 31 * result + samplingInterval; + result = 31 * result + (autoStopProfiler ? 1 : 0); + result = 31 * result + (streamingOutput ? 1 : 0); + result = 31 * result + Objects.hashCode(agent); + return result; + } } diff --git a/core/java/android/app/ResultInfo.java b/core/java/android/app/ResultInfo.java index 5e0867c3607e..d5af08a655d8 100644 --- a/core/java/android/app/ResultInfo.java +++ b/core/java/android/app/ResultInfo.java @@ -20,6 +20,8 @@ import android.content.Intent; import android.os.Parcel; import android.os.Parcelable; +import java.util.Objects; + /** * {@hide} */ @@ -79,4 +81,29 @@ public class ResultInfo implements Parcelable { mData = null; } } + + @Override + public boolean equals(Object obj) { + if (obj == null || !(obj instanceof ResultInfo)) { + return false; + } + final ResultInfo other = (ResultInfo) obj; + final boolean intentsEqual = mData == null ? (other.mData == null) + : mData.filterEquals(other.mData); + return intentsEqual && Objects.equals(mResultWho, other.mResultWho) + && mResultCode == other.mResultCode + && mRequestCode == other.mRequestCode; + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + mRequestCode; + result = 31 * result + mResultCode; + result = 31 * result + Objects.hashCode(mResultWho); + if (mData != null) { + result = 31 * result + mData.filterHashCode(); + } + return result; + } } diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java new file mode 100644 index 000000000000..07001e2b0ad7 --- /dev/null +++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.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.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]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final ActivityConfigurationChangeItem other = (ActivityConfigurationChangeItem) o; + return mConfiguration.equals(other.mConfiguration); + } + + @Override + public int hashCode() { + return mConfiguration.hashCode(); + } +} diff --git a/core/java/android/app/servertransaction/ActivityLifecycleItem.java b/core/java/android/app/servertransaction/ActivityLifecycleItem.java new file mode 100644 index 000000000000..a64108db7c21 --- /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 000000000000..76664d8e59e6 --- /dev/null +++ b/core/java/android/app/servertransaction/ActivityResultItem.java @@ -0,0 +1,95 @@ +/* + * 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]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final ActivityResultItem other = (ActivityResultItem) o; + return mResultInfoList.equals(other.mResultInfoList); + } + + @Override + public int hashCode() { + return mResultInfoList.hashCode(); + } +} diff --git a/core/java/android/app/servertransaction/BaseClientRequest.java b/core/java/android/app/servertransaction/BaseClientRequest.java new file mode 100644 index 000000000000..4bd01afb5061 --- /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 000000000000..ad8bcbf6eb04 --- /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 000000000000..d2289ba0d745 --- /dev/null +++ b/core/java/android/app/servertransaction/ClientTransaction.java @@ -0,0 +1,201 @@ +/* + * 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.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; +import java.util.Objects; + +/** + * 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(ClientTransactionHandler)}, 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(ClientTransactionHandler)}, 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; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final ClientTransaction other = (ClientTransaction) o; + return Objects.equals(mActivityCallbacks, other.mActivityCallbacks) + && Objects.equals(mLifecycleStateRequest, other.mLifecycleStateRequest) + && mClient == other.mClient + && mActivityToken == other.mActivityToken; + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + Objects.hashCode(mActivityCallbacks); + result = 31 * result + Objects.hashCode(mLifecycleStateRequest); + return result; + } +} diff --git a/core/java/android/app/servertransaction/ClientTransactionItem.java b/core/java/android/app/servertransaction/ClientTransactionItem.java new file mode 100644 index 000000000000..6f2cc007ac27 --- /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 000000000000..055923ec8efd --- /dev/null +++ b/core/java/android/app/servertransaction/ConfigurationChangeItem.java @@ -0,0 +1,85 @@ +/* + * 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]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final ConfigurationChangeItem other = (ConfigurationChangeItem) o; + return mConfiguration.equals(other.mConfiguration); + } + + @Override + public int hashCode() { + return mConfiguration.hashCode(); + } +} diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java new file mode 100644 index 000000000000..38fd5fb6c779 --- /dev/null +++ b/core/java/android/app/servertransaction/DestroyActivityItem.java @@ -0,0 +1,99 @@ +/* + * 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]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final DestroyActivityItem other = (DestroyActivityItem) o; + return mFinished == other.mFinished && mConfigChanges == other.mConfigChanges; + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + (mFinished ? 1 : 0); + result = 31 * result + mConfigChanges; + return result; + } +} diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java new file mode 100644 index 000000000000..417ebac8ea20 --- /dev/null +++ b/core/java/android/app/servertransaction/LaunchActivityItem.java @@ -0,0 +1,232 @@ +/* + * 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.BaseBundle; +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; +import java.util.Objects; + +/** + * 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]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final LaunchActivityItem other = (LaunchActivityItem) o; + return mIntent.filterEquals(other.mIntent) && mIdent == other.mIdent + && activityInfoEqual(other.mInfo) && Objects.equals(mCurConfig, other.mCurConfig) + && Objects.equals(mOverrideConfig, other.mOverrideConfig) + && Objects.equals(mCompatInfo, other.mCompatInfo) + && Objects.equals(mReferrer, other.mReferrer) + && mProcState == other.mProcState && areBundlesEqual(mState, other.mState) + && areBundlesEqual(mPersistentState, other.mPersistentState) + && Objects.equals(mPendingResults, other.mPendingResults) + && Objects.equals(mPendingNewIntents, other.mPendingNewIntents) + && mNotResumed == other.mNotResumed && mIsForward == other.mIsForward + && Objects.equals(mProfilerInfo, other.mProfilerInfo); + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + mIntent.filterHashCode(); + result = 31 * result + mIdent; + result = 31 * result + Objects.hashCode(mCurConfig); + result = 31 * result + Objects.hashCode(mOverrideConfig); + result = 31 * result + Objects.hashCode(mCompatInfo); + result = 31 * result + Objects.hashCode(mReferrer); + result = 31 * result + Objects.hashCode(mProcState); + result = 31 * result + (mState != null ? mState.size() : 0); + result = 31 * result + (mPersistentState != null ? mPersistentState.size() : 0); + result = 31 * result + Objects.hashCode(mPendingResults); + result = 31 * result + Objects.hashCode(mPendingNewIntents); + result = 31 * result + (mNotResumed ? 1 : 0); + result = 31 * result + (mIsForward ? 1 : 0); + result = 31 * result + Objects.hashCode(mProfilerInfo); + return result; + } + + private boolean activityInfoEqual(ActivityInfo other) { + return mInfo.flags == other.flags && mInfo.maxAspectRatio == other.maxAspectRatio + && Objects.equals(mInfo.launchToken, other.launchToken) + && Objects.equals(mInfo.getComponentName(), other.getComponentName()); + } + + private static boolean areBundlesEqual(BaseBundle extras, BaseBundle newExtras) { + if (extras == null || newExtras == null) { + return extras == newExtras; + } + + if (extras.size() != newExtras.size()) { + return false; + } + + for (String key : extras.keySet()) { + if (key != null) { + final Object value = extras.get(key); + final Object newValue = newExtras.get(key); + if (!Objects.equals(value, newValue)) { + return false; + } + } + } + return true; + } +} diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java new file mode 100644 index 000000000000..ccd80d88620c --- /dev/null +++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java @@ -0,0 +1,93 @@ +/* + * 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]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final MoveToDisplayItem other = (MoveToDisplayItem) o; + return mTargetDisplayId == other.mTargetDisplayId + && mConfiguration.equals(other.mConfiguration); + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + mTargetDisplayId; + result = 31 * result + mConfiguration.hashCode(); + return result; + } +} diff --git a/core/java/android/app/servertransaction/MultiWindowModeChangeItem.java b/core/java/android/app/servertransaction/MultiWindowModeChangeItem.java new file mode 100644 index 000000000000..a0c617fa66c3 --- /dev/null +++ b/core/java/android/app/servertransaction/MultiWindowModeChangeItem.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 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]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final MultiWindowModeChangeItem other = (MultiWindowModeChangeItem) o; + return mIsInMultiWindowMode == other.mIsInMultiWindowMode + && mOverrideConfig.equals(other.mOverrideConfig); + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + (mIsInMultiWindowMode ? 1 : 0); + result = 31 * result + mOverrideConfig.hashCode(); + return result; + } +} diff --git a/core/java/android/app/servertransaction/NewIntentItem.java b/core/java/android/app/servertransaction/NewIntentItem.java new file mode 100644 index 000000000000..61a8965aae7f --- /dev/null +++ b/core/java/android/app/servertransaction/NewIntentItem.java @@ -0,0 +1,108 @@ +/* + * 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]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final NewIntentItem other = (NewIntentItem) o; + return mPause == other.mPause && mIntents.equals(other.mIntents); + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + (mPause ? 1 : 0); + result = 31 * result + mIntents.hashCode(); + return result; + } +} diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java new file mode 100644 index 000000000000..e561a4b51427 --- /dev/null +++ b/core/java/android/app/servertransaction/PauseActivityItem.java @@ -0,0 +1,125 @@ +/* + * 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]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final PauseActivityItem other = (PauseActivityItem) o; + return mFinished == other.mFinished && mUserLeaving == other.mUserLeaving + && mConfigChanges == other.mConfigChanges && mDontReport == other.mDontReport; + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + (mFinished ? 1 : 0); + result = 31 * result + (mUserLeaving ? 1 : 0); + result = 31 * result + mConfigChanges; + result = 31 * result + (mDontReport ? 1 : 0); + return result; + } +} diff --git a/core/java/android/app/servertransaction/PipModeChangeItem.java b/core/java/android/app/servertransaction/PipModeChangeItem.java new file mode 100644 index 000000000000..923839eec15b --- /dev/null +++ b/core/java/android/app/servertransaction/PipModeChangeItem.java @@ -0,0 +1,89 @@ +/* + * 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]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final PipModeChangeItem other = (PipModeChangeItem) o; + return mIsInPipMode == other.mIsInPipMode && mOverrideConfig.equals(other.mOverrideConfig); + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + (mIsInPipMode ? 1 : 0); + result = 31 * result + mOverrideConfig.hashCode(); + return result; + } +} diff --git a/core/java/android/app/servertransaction/ResumeActivityItem.java b/core/java/android/app/servertransaction/ResumeActivityItem.java new file mode 100644 index 000000000000..ea31a461f1f3 --- /dev/null +++ b/core/java/android/app/servertransaction/ResumeActivityItem.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.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]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final ResumeActivityItem other = (ResumeActivityItem) o; + return mProcState == other.mProcState && mIsForward == other.mIsForward; + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + mProcState; + result = 31 * result + (mIsForward ? 1 : 0); + return result; + } +} diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java new file mode 100644 index 000000000000..d62c507770a2 --- /dev/null +++ b/core/java/android/app/servertransaction/StopActivityItem.java @@ -0,0 +1,112 @@ +/* + * 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]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final StopActivityItem other = (StopActivityItem) o; + return mShowWindow == other.mShowWindow && mConfigChanges == other.mConfigChanges; + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + (mShowWindow ? 1 : 0); + result = 31 * result + mConfigChanges; + return result; + } +} diff --git a/core/java/android/app/servertransaction/WindowVisibilityItem.java b/core/java/android/app/servertransaction/WindowVisibilityItem.java new file mode 100644 index 000000000000..8e88b38d50b1 --- /dev/null +++ b/core/java/android/app/servertransaction/WindowVisibilityItem.java @@ -0,0 +1,85 @@ +/* + * 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]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final WindowVisibilityItem other = (WindowVisibilityItem) o; + return mShowWindow == other.mShowWindow; + } + + @Override + public int hashCode() { + return 17 + 31 * (mShowWindow ? 1 : 0); + } +} diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 837c00a72784..f8cdce64c139 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -1156,6 +1156,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { dest.writeString(permission); dest.writeString(taskAffinity); dest.writeString(targetActivity); + dest.writeString(launchToken); dest.writeInt(flags); dest.writeInt(screenOrientation); dest.writeInt(configChanges); @@ -1282,6 +1283,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { permission = source.readString(); taskAffinity = source.readString(); targetActivity = source.readString(); + launchToken = source.readString(); flags = source.readInt(); screenOrientation = source.readInt(); configChanges = source.readInt(); diff --git a/core/java/com/android/internal/content/ReferrerIntent.java b/core/java/com/android/internal/content/ReferrerIntent.java index 8d9a1cf9eee5..76dcc9bba91c 100644 --- a/core/java/com/android/internal/content/ReferrerIntent.java +++ b/core/java/com/android/internal/content/ReferrerIntent.java @@ -19,6 +19,8 @@ package com.android.internal.content; import android.content.Intent; import android.os.Parcel; +import java.util.Objects; + /** * Subclass of Intent that also contains referrer (as a package name) information. */ @@ -48,4 +50,21 @@ public class ReferrerIntent extends Intent { return new ReferrerIntent[size]; } }; + + @Override + public boolean equals(Object obj) { + if (obj == null || !(obj instanceof ReferrerIntent)) { + return false; + } + final ReferrerIntent other = (ReferrerIntent) obj; + return filterEquals(other) && Objects.equals(mReferrer, other.mReferrer); + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + filterHashCode(); + result = 31 * result + Objects.hashCode(mReferrer); + return result; + } } diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java new file mode 100644 index 000000000000..e0fcf0833232 --- /dev/null +++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.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 org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.app.ClientTransactionHandler; +import android.os.IBinder; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +// TODO(lifecycler): Add to presubmit after checking for flakiness. +public class ClientTransactionTests { + + @Test + public void testPrepare() { + ClientTransactionItem callback1 = mock(ClientTransactionItem.class); + ClientTransactionItem callback2 = mock(ClientTransactionItem.class); + ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class); + ClientTransactionHandler clientTransactionHandler = mock(ClientTransactionHandler.class); + IBinder token = mock(IBinder.class); + + ClientTransaction transaction = new ClientTransaction(null /* client */, + token /* activityToken */); + transaction.addCallback(callback1); + transaction.addCallback(callback2); + transaction.setLifecycleStateRequest(stateRequest); + + transaction.prepare(clientTransactionHandler); + + verify(callback1, times(1)).prepare(clientTransactionHandler, token); + verify(callback2, times(1)).prepare(clientTransactionHandler, token); + verify(stateRequest, times(1)).prepare(clientTransactionHandler, token); + } + + @Test + public void testExecute() { + ClientTransactionItem callback1 = mock(ClientTransactionItem.class); + ClientTransactionItem callback2 = mock(ClientTransactionItem.class); + ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class); + IBinder token = mock(IBinder.class); + + ClientTransaction transaction = new ClientTransaction(null /* client */, + token /* activityToken */); + transaction.addCallback(callback1); + transaction.addCallback(callback2); + transaction.setLifecycleStateRequest(stateRequest); + + ClientTransactionHandler clientTransactionHandler = mock(ClientTransactionHandler.class); + transaction.prepare(clientTransactionHandler); + transaction.execute(clientTransactionHandler); + + verify(callback1, times(1)).execute(clientTransactionHandler, token); + verify(callback2, times(1)).execute(clientTransactionHandler, token); + verify(stateRequest, times(1)).execute(clientTransactionHandler, token); + } +} diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java new file mode 100644 index 000000000000..9db7550a6099 --- /dev/null +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -0,0 +1,641 @@ +/* + * 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.content.res.Configuration.ORIENTATION_LANDSCAPE; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; + +import android.app.IApplicationThread; +import android.app.IInstrumentationWatcher; +import android.app.IUiAutomationConnection; +import android.app.ProfilerInfo; +import android.app.ResultInfo; +import android.content.ComponentName; +import android.content.IIntentReceiver; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.ParceledListSlice; +import android.content.pm.ProviderInfo; +import android.content.pm.ServiceInfo; +import android.content.res.CompatibilityInfo; +import android.content.res.Configuration; +import android.net.Uri; +import android.os.Binder; +import android.os.Bundle; +import android.os.Debug; +import android.os.IBinder; +import android.os.Parcel; +import android.os.ParcelFileDescriptor; +import android.os.Parcelable; +import android.os.PersistableBundle; +import android.os.RemoteException; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import com.android.internal.app.IVoiceInteractor; +import com.android.internal.content.ReferrerIntent; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** Test parcelling and unparcelling of transactions and transaction items. */ +@RunWith(AndroidJUnit4.class) +@SmallTest +// TODO(lifecycler): Add to presubmit after checking for flakiness. +public class TransactionParcelTests { + + private Parcel mParcel; + + @Before + public void setUp() throws Exception { + mParcel = Parcel.obtain(); + } + + @Test + public void testConfigurationChange() { + // Write to parcel + ConfigurationChangeItem item = new ConfigurationChangeItem(config()); + writeAndPrepareForReading(item); + + // Read from parcel and assert + ConfigurationChangeItem result = ConfigurationChangeItem.CREATOR.createFromParcel(mParcel); + + assertEquals(item.hashCode(), result.hashCode()); + assertTrue(item.equals(result)); + } + + @Test + public void testActivityConfigChange() { + // Write to parcel + ActivityConfigurationChangeItem item = new ActivityConfigurationChangeItem(config()); + writeAndPrepareForReading(item); + + // Read from parcel and assert + ActivityConfigurationChangeItem result = + ActivityConfigurationChangeItem.CREATOR.createFromParcel(mParcel); + + assertEquals(item.hashCode(), result.hashCode()); + assertTrue(item.equals(result)); + } + + @Test + public void testMoveToDisplay() { + // Write to parcel + MoveToDisplayItem item = new MoveToDisplayItem(4 /* targetDisplayId */, config()); + writeAndPrepareForReading(item); + + // Read from parcel and assert + MoveToDisplayItem result = MoveToDisplayItem.CREATOR.createFromParcel(mParcel); + + assertEquals(item.hashCode(), result.hashCode()); + assertTrue(item.equals(result)); + } + + @Test + public void testNewIntent() { + // Write to parcel + NewIntentItem item = new NewIntentItem(referrerIntentList(), true /* pause */); + writeAndPrepareForReading(item); + + // Read from parcel and assert + NewIntentItem result = NewIntentItem.CREATOR.createFromParcel(mParcel); + + assertEquals(item.hashCode(), result.hashCode()); + assertTrue(item.equals(result)); + } + + @Test + public void testActivityResult() { + // Write to parcel + ActivityResultItem item = new ActivityResultItem(resultInfoList()); + writeAndPrepareForReading(item); + + // Read from parcel and assert + ActivityResultItem result = ActivityResultItem.CREATOR.createFromParcel(mParcel); + + assertEquals(item.hashCode(), result.hashCode()); + assertTrue(item.equals(result)); + } + + @Test + public void testPipModeChange() { + // Write to parcel + PipModeChangeItem item = new PipModeChangeItem(true /* isInPipMode */, config()); + writeAndPrepareForReading(item); + + // Read from parcel and assert + PipModeChangeItem result = PipModeChangeItem.CREATOR.createFromParcel(mParcel); + + assertEquals(item.hashCode(), result.hashCode()); + assertTrue(item.equals(result)); + } + + @Test + public void testMultiWindowModeChange() { + // Write to parcel + MultiWindowModeChangeItem item = new MultiWindowModeChangeItem( + true /* isInMultiWindowMode */, config()); + writeAndPrepareForReading(item); + + // Read from parcel and assert + MultiWindowModeChangeItem result = + MultiWindowModeChangeItem.CREATOR.createFromParcel(mParcel); + + assertEquals(item.hashCode(), result.hashCode()); + assertTrue(item.equals(result)); + } + + @Test + public void testWindowVisibilityChange() { + // Write to parcel + WindowVisibilityItem item = new WindowVisibilityItem(true /* showWindow */); + writeAndPrepareForReading(item); + + // Read from parcel and assert + WindowVisibilityItem result = WindowVisibilityItem.CREATOR.createFromParcel(mParcel); + + assertEquals(item.hashCode(), result.hashCode()); + assertTrue(item.equals(result)); + + // Check different value + item = new WindowVisibilityItem(false); + + mParcel = Parcel.obtain(); + writeAndPrepareForReading(item); + + // Read from parcel and assert + result = WindowVisibilityItem.CREATOR.createFromParcel(mParcel); + + assertEquals(item.hashCode(), result.hashCode()); + assertTrue(item.equals(result)); + } + + @Test + public void testDestroy() { + DestroyActivityItem item = new DestroyActivityItem(true /* finished */, + 135 /* configChanges */); + writeAndPrepareForReading(item); + + // Read from parcel and assert + DestroyActivityItem result = DestroyActivityItem.CREATOR.createFromParcel(mParcel); + + assertEquals(item.hashCode(), result.hashCode()); + assertTrue(item.equals(result)); + } + + @Test + public void testLaunch() { + // Write to parcel + Intent intent = new Intent("action"); + int ident = 57; + ActivityInfo activityInfo = new ActivityInfo(); + activityInfo.flags = 42; + activityInfo.maxAspectRatio = 2.4f; + activityInfo.launchToken = "token"; + activityInfo.applicationInfo = new ApplicationInfo(); + activityInfo.packageName = "packageName"; + activityInfo.name = "name"; + Configuration overrideConfig = new Configuration(); + overrideConfig.assetsSeq = 5; + CompatibilityInfo compat = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; + String referrer = "referrer"; + int procState = 4; + Bundle bundle = new Bundle(); + bundle.putString("key", "value"); + PersistableBundle persistableBundle = new PersistableBundle(); + persistableBundle.putInt("k", 4); + + LaunchActivityItem item = new LaunchActivityItem(intent, ident, activityInfo, + config(), overrideConfig, compat, referrer, null /* voiceInteractor */, + procState, bundle, persistableBundle, resultInfoList(), referrerIntentList(), + true /* notResumed */, true /* isForward */, null /* profilerInfo */); + writeAndPrepareForReading(item); + + // Read from parcel and assert + LaunchActivityItem result = LaunchActivityItem.CREATOR.createFromParcel(mParcel); + + assertEquals(item.hashCode(), result.hashCode()); + assertTrue(item.equals(result)); + } + + @Test + public void testPause() { + // Write to parcel + PauseActivityItem item = new PauseActivityItem(true /* finished */, true /* userLeaving */, + 135 /* configChanges */, true /* dontReport */); + writeAndPrepareForReading(item); + + // Read from parcel and assert + PauseActivityItem result = PauseActivityItem.CREATOR.createFromParcel(mParcel); + + assertEquals(item.hashCode(), result.hashCode()); + assertTrue(item.equals(result)); + } + + @Test + public void testResume() { + // Write to parcel + ResumeActivityItem item = new ResumeActivityItem(27 /* procState */, true /* isForward */); + writeAndPrepareForReading(item); + + // Read from parcel and assert + ResumeActivityItem result = ResumeActivityItem.CREATOR.createFromParcel(mParcel); + + assertEquals(item.hashCode(), result.hashCode()); + assertTrue(item.equals(result)); + } + + @Test + public void testStop() { + // Write to parcel + StopActivityItem item = new StopActivityItem(true /* showWindow */, 14 /* configChanges */); + writeAndPrepareForReading(item); + + // Read from parcel and assert + StopActivityItem result = StopActivityItem.CREATOR.createFromParcel(mParcel); + + assertEquals(item.hashCode(), result.hashCode()); + assertTrue(item.equals(result)); + } + + @Test + public void testClientTransaction() { + // Write to parcel + WindowVisibilityItem callback1 = new WindowVisibilityItem(true); + ActivityConfigurationChangeItem callback2 = new ActivityConfigurationChangeItem(config()); + + StopActivityItem lifecycleRequest = new StopActivityItem(true /* showWindow */, + 78 /* configChanges */); + + IApplicationThread appThread = new StubAppThread(); + Binder activityToken = new Binder(); + + ClientTransaction transaction = new ClientTransaction(appThread, activityToken); + transaction.addCallback(callback1); + transaction.addCallback(callback2); + transaction.setLifecycleStateRequest(lifecycleRequest); + + writeAndPrepareForReading(transaction); + + // Read from parcel and assert + ClientTransaction result = ClientTransaction.CREATOR.createFromParcel(mParcel); + + assertEquals(transaction.hashCode(), result.hashCode()); + assertTrue(transaction.equals(result)); + } + + @Test + public void testClientTransactionCallbacksOnly() { + // Write to parcel + WindowVisibilityItem callback1 = new WindowVisibilityItem(true); + ActivityConfigurationChangeItem callback2 = new ActivityConfigurationChangeItem(config()); + + IApplicationThread appThread = new StubAppThread(); + Binder activityToken = new Binder(); + + ClientTransaction transaction = new ClientTransaction(appThread, activityToken); + transaction.addCallback(callback1); + transaction.addCallback(callback2); + + writeAndPrepareForReading(transaction); + + // Read from parcel and assert + ClientTransaction result = ClientTransaction.CREATOR.createFromParcel(mParcel); + + assertEquals(transaction.hashCode(), result.hashCode()); + assertTrue(transaction.equals(result)); + } + + @Test + public void testClientTransactionLifecycleOnly() { + // Write to parcel + StopActivityItem lifecycleRequest = new StopActivityItem(true /* showWindow */, + 78 /* configChanges */); + + IApplicationThread appThread = new StubAppThread(); + Binder activityToken = new Binder(); + + ClientTransaction transaction = new ClientTransaction(appThread, activityToken); + transaction.setLifecycleStateRequest(lifecycleRequest); + + writeAndPrepareForReading(transaction); + + // Read from parcel and assert + ClientTransaction result = ClientTransaction.CREATOR.createFromParcel(mParcel); + + assertEquals(transaction.hashCode(), result.hashCode()); + assertTrue(transaction.equals(result)); + } + + private static List<ResultInfo> resultInfoList() { + String resultWho1 = "resultWho1"; + int requestCode1 = 7; + int resultCode1 = 4; + Intent data1 = new Intent("action1"); + ResultInfo resultInfo1 = new ResultInfo(resultWho1, requestCode1, resultCode1, data1); + + String resultWho2 = "resultWho2"; + int requestCode2 = 8; + int resultCode2 = 6; + Intent data2 = new Intent("action2"); + ResultInfo resultInfo2 = new ResultInfo(resultWho2, requestCode2, resultCode2, data2); + + List<ResultInfo> resultInfoList = new ArrayList<>(); + resultInfoList.add(resultInfo1); + resultInfoList.add(resultInfo2); + + return resultInfoList; + } + + private static List<ReferrerIntent> referrerIntentList() { + Intent intent1 = new Intent("action1"); + ReferrerIntent referrerIntent1 = new ReferrerIntent(intent1, "referrer1"); + + Intent intent2 = new Intent("action2"); + ReferrerIntent referrerIntent2 = new ReferrerIntent(intent2, "referrer2"); + + List<ReferrerIntent> referrerIntents = new ArrayList<>(); + referrerIntents.add(referrerIntent1); + referrerIntents.add(referrerIntent2); + + return referrerIntents; + } + + private static Configuration config() { + Configuration config = new Configuration(); + config.densityDpi = 10; + config.fontScale = 0.3f; + config.screenHeightDp = 15; + config.orientation = ORIENTATION_LANDSCAPE; + return config; + } + + /** Write to {@link #mParcel} and reset its position to prepare for reading from the start. */ + private void writeAndPrepareForReading(Parcelable parcelable) { + parcelable.writeToParcel(mParcel, 0 /* flags */); + mParcel.setDataPosition(0); + } + + /** Stub implementation of IApplicationThread that can be presented as {@link Binder}. */ + class StubAppThread extends android.app.IApplicationThread.Stub { + + @Override + public void scheduleTransaction(ClientTransaction transaction) throws RemoteException { + } + + @Override + public void scheduleReceiver(Intent intent, ActivityInfo activityInfo, + CompatibilityInfo compatibilityInfo, int i, String s, Bundle bundle, boolean b, + int i1, int i2) throws RemoteException { + } + + @Override + public void scheduleCreateService(IBinder iBinder, ServiceInfo serviceInfo, + CompatibilityInfo compatibilityInfo, int i) throws RemoteException { + } + + @Override + public void scheduleStopService(IBinder iBinder) throws RemoteException { + } + + @Override + public void bindApplication(String s, ApplicationInfo applicationInfo, + List<ProviderInfo> list, ComponentName componentName, ProfilerInfo profilerInfo, + Bundle bundle, IInstrumentationWatcher iInstrumentationWatcher, + IUiAutomationConnection iUiAutomationConnection, int i, boolean b, boolean b1, + boolean b2, boolean b3, Configuration configuration, + CompatibilityInfo compatibilityInfo, Map map, Bundle bundle1, String s1) + throws RemoteException { + } + + @Override + public void scheduleExit() throws RemoteException { + } + + @Override + public void scheduleServiceArgs(IBinder iBinder, ParceledListSlice parceledListSlice) + throws RemoteException { + } + + @Override + public void updateTimeZone() throws RemoteException { + } + + @Override + public void processInBackground() throws RemoteException { + } + + @Override + public void scheduleBindService(IBinder iBinder, Intent intent, boolean b, int i) + throws RemoteException { + } + + @Override + public void scheduleUnbindService(IBinder iBinder, Intent intent) throws RemoteException { + } + + @Override + public void dumpService(ParcelFileDescriptor parcelFileDescriptor, IBinder iBinder, + String[] strings) throws RemoteException { + } + + @Override + public void scheduleRegisteredReceiver(IIntentReceiver iIntentReceiver, Intent intent, + int i, String s, Bundle bundle, boolean b, boolean b1, int i1, int i2) + throws RemoteException { + } + + @Override + public void scheduleLowMemory() throws RemoteException { + } + + @Override + public void scheduleRelaunchActivity(IBinder iBinder, List<ResultInfo> list, + List<ReferrerIntent> list1, int i, boolean b, Configuration configuration, + Configuration configuration1, boolean b1) throws RemoteException { + } + + @Override + public void scheduleSleeping(IBinder iBinder, boolean b) throws RemoteException { + } + + @Override + public void profilerControl(boolean b, ProfilerInfo profilerInfo, int i) + throws RemoteException { + } + + @Override + public void setSchedulingGroup(int i) throws RemoteException { + } + + @Override + public void scheduleCreateBackupAgent(ApplicationInfo applicationInfo, + CompatibilityInfo compatibilityInfo, int i) throws RemoteException { + } + + @Override + public void scheduleDestroyBackupAgent(ApplicationInfo applicationInfo, + CompatibilityInfo compatibilityInfo) throws RemoteException { + } + + @Override + public void scheduleOnNewActivityOptions(IBinder iBinder, Bundle bundle) + throws RemoteException { + } + + @Override + public void scheduleSuicide() throws RemoteException { + } + + @Override + public void dispatchPackageBroadcast(int i, String[] strings) throws RemoteException { + } + + @Override + public void scheduleCrash(String s) throws RemoteException { + } + + @Override + public void dumpActivity(ParcelFileDescriptor parcelFileDescriptor, IBinder iBinder, + String s, String[] strings) throws RemoteException { + } + + @Override + public void clearDnsCache() throws RemoteException { + } + + @Override + public void setHttpProxy(String s, String s1, String s2, Uri uri) throws RemoteException { + } + + @Override + public void setCoreSettings(Bundle bundle) throws RemoteException { + } + + @Override + public void updatePackageCompatibilityInfo(String s, CompatibilityInfo compatibilityInfo) + throws RemoteException { + } + + @Override + public void scheduleTrimMemory(int i) throws RemoteException { + } + + @Override + public void dumpMemInfo(ParcelFileDescriptor parcelFileDescriptor, + Debug.MemoryInfo memoryInfo, boolean b, boolean b1, boolean b2, boolean b3, + boolean b4, String[] strings) throws RemoteException { + } + + @Override + public void dumpGfxInfo(ParcelFileDescriptor parcelFileDescriptor, String[] strings) + throws RemoteException { + } + + @Override + public void dumpProvider(ParcelFileDescriptor parcelFileDescriptor, IBinder iBinder, + String[] strings) throws RemoteException { + } + + @Override + public void dumpDbInfo(ParcelFileDescriptor parcelFileDescriptor, String[] strings) + throws RemoteException { + } + + @Override + public void unstableProviderDied(IBinder iBinder) throws RemoteException { + } + + @Override + public void requestAssistContextExtras(IBinder iBinder, IBinder iBinder1, int i, int i1, + int i2) throws RemoteException { + } + + @Override + public void scheduleTranslucentConversionComplete(IBinder iBinder, boolean b) + throws RemoteException { + } + + @Override + public void setProcessState(int i) throws RemoteException { + } + + @Override + public void scheduleInstallProvider(ProviderInfo providerInfo) throws RemoteException { + } + + @Override + public void updateTimePrefs(int i) throws RemoteException { + } + + @Override + public void scheduleEnterAnimationComplete(IBinder iBinder) throws RemoteException { + } + + @Override + public void notifyCleartextNetwork(byte[] bytes) throws RemoteException { + } + + @Override + public void startBinderTracking() throws RemoteException { + } + + @Override + public void stopBinderTrackingAndDump(ParcelFileDescriptor parcelFileDescriptor) + throws RemoteException { + } + + @Override + public void scheduleLocalVoiceInteractionStarted(IBinder iBinder, + IVoiceInteractor iVoiceInteractor) throws RemoteException { + } + + @Override + public void handleTrustStorageUpdate() throws RemoteException { + } + + @Override + public void attachAgent(String s) throws RemoteException { + } + + @Override + public void scheduleApplicationInfoChanged(ApplicationInfo applicationInfo) + throws RemoteException { + } + + @Override + public void setNetworkBlockSeq(long l) throws RemoteException { + } + + @Override + public void dumpHeap(boolean managed, boolean mallocInfo, boolean runGc, String path, + ParcelFileDescriptor fd) { + } + + @Override + public final void runIsolatedEntryPoint(String entryPoint, String[] entryPointArgs) { + } + } +} diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 9ac241e0dc55..3139699cd5d9 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -246,6 +246,7 @@ import android.app.admin.DevicePolicyManager; 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; @@ -631,6 +632,8 @@ public class ActivityManagerService extends IActivityManager.Stub final ActivityStarter mActivityStarter; + final ClientLifecycleManager mLifecycleManager; + final TaskChangeNotificationController mTaskChangeNotificationController; final InstrumentationReporter mInstrumentationReporter = new InstrumentationReporter(); @@ -2693,6 +2696,7 @@ public class ActivityManagerService extends IActivityManager.Stub mUserController = null; mVrController = null; mLockTaskController = null; + mLifecycleManager = null; } // Note: This method is invoked on the main thread but may need to attach various @@ -2795,6 +2799,7 @@ public class ActivityManagerService extends IActivityManager.Stub mRecentTasks = createRecentTasks(); mStackSupervisor.setRecentTasks(mRecentTasks); mLockTaskController = new LockTaskController(mContext, mStackSupervisor, mHandler); + mLifecycleManager = new ClientLifecycleManager(); mProcessCpuThread = new Thread("CpuTracker") { @Override @@ -20562,9 +20567,11 @@ public class ActivityManagerService extends IActivityManager.Stub 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 11590d6ca83b..883019ea7b10 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.ActivityOptions; 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; @@ -611,8 +617,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo "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. } @@ -629,7 +635,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo 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. } @@ -650,8 +657,9 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo 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. } @@ -677,8 +685,9 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo 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. } @@ -1365,8 +1374,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo 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); @@ -1588,7 +1597,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo 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 481699833e41..cd5524b1ef2f 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -104,6 +104,13 @@ 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; @@ -1419,8 +1426,10 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai 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); @@ -2055,7 +2064,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai 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 @@ -2575,13 +2585,15 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai 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. @@ -2598,8 +2610,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai 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); @@ -3249,7 +3262,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai 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); @@ -3377,7 +3391,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } 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); } @@ -4174,8 +4189,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai 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 5525cdb380b7..62ae621f0862 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.ResultInfo; 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; @@ -1392,7 +1393,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D 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. @@ -1400,7 +1402,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D 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 000000000000..c04d103c7ff5 --- /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; + } +} |