From b916836e8dcd4aa3564479be508d7a9d73dcbce8 Mon Sep 17 00:00:00 2001 From: Craig Mautner Date: Thu, 26 Feb 2015 20:40:19 -0800 Subject: [PATCH] Change ActivityView startActivity state sequence Problems arise if an activity is started in an ActivityView when the parent activity is not resumed. In particular the ActivityView can be brought to the front in front of other activities that have been started by the parent. This change checks the state of the parent when the ActivityView is starting and if it is not resumed, throws an Exception. This change also removes the queueing up of Intents if the surface does not exist when startActivity is called. Now, the owner of the ActivityView is notified when the surface becomes available. If startActivity is called before that notification an Exception will be thrown. Fixes bug 19147472. Change-Id: I6712cf1929fe65c4238ce7f3feb4e8511ed97244 --- core/java/android/app/ActivityView.java | 96 +++++++++---------- core/java/android/app/IActivityContainer.aidl | 2 - .../server/am/ActivityStackSupervisor.java | 64 ++++++------- .../server/am/PendingIntentRecord.java | 9 ++ 4 files changed, 80 insertions(+), 91 deletions(-) diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java index 94ea2c5b4d907..fecaf6fa0ffe4 100644 --- a/core/java/android/app/ActivityView.java +++ b/core/java/android/app/ActivityView.java @@ -16,6 +16,8 @@ package android.app; +import static android.app.ActivityManager.START_CANCELED; + import android.content.Context; import android.content.ContextWrapper; import android.content.IIntentSender; @@ -23,6 +25,7 @@ import android.content.Intent; import android.content.IntentSender; import android.graphics.SurfaceTexture; import android.os.IBinder; +import android.os.OperationCanceledException; import android.os.RemoteException; import android.util.AttributeSet; import android.util.DisplayMetrics; @@ -55,10 +58,6 @@ public class ActivityView extends ViewGroup { private int mLastVisibility; private ActivityViewCallback mActivityViewCallback; - // Only one IIntentSender or Intent may be queued at a time. Most recent one wins. - IIntentSender mQueuedPendingIntent; - Intent mQueuedIntent; - public ActivityView(Context context) { this(context, null); } @@ -167,14 +166,13 @@ public class ActivityView extends ViewGroup { if (mActivityContainer == null) { throw new IllegalStateException("Attempt to call startActivity after release"); } + if (mSurface == null) { + throw new IllegalStateException("Surface not yet created."); + } if (DEBUG) Log.v(TAG, "startActivity(): intent=" + intent + " " + (isAttachedToDisplay() ? "" : "not") + " attached"); - if (mSurface != null) { - mActivityContainer.startActivity(intent); - } else { - mActivityContainer.checkEmbeddedAllowed(intent); - mQueuedIntent = intent; - mQueuedPendingIntent = null; + if (mActivityContainer.startActivity(intent) == START_CANCELED) { + throw new OperationCanceledException(); } } @@ -182,15 +180,14 @@ public class ActivityView extends ViewGroup { if (mActivityContainer == null) { throw new IllegalStateException("Attempt to call startActivity after release"); } + if (mSurface == null) { + throw new IllegalStateException("Surface not yet created."); + } if (DEBUG) Log.v(TAG, "startActivityIntentSender(): intentSender=" + intentSender + " " + (isAttachedToDisplay() ? "" : "not") + " attached"); final IIntentSender iIntentSender = intentSender.getTarget(); - if (mSurface != null) { - mActivityContainer.startActivityIntentSender(iIntentSender); - } else { - mActivityContainer.checkEmbeddedAllowedIntentSender(iIntentSender); - mQueuedPendingIntent = iIntentSender; - mQueuedIntent = null; + if (mActivityContainer.startActivityIntentSender(iIntentSender) == START_CANCELED) { + throw new OperationCanceledException(); } } @@ -198,15 +195,14 @@ public class ActivityView extends ViewGroup { if (mActivityContainer == null) { throw new IllegalStateException("Attempt to call startActivity after release"); } + if (mSurface == null) { + throw new IllegalStateException("Surface not yet created."); + } if (DEBUG) Log.v(TAG, "startActivityPendingIntent(): PendingIntent=" + pendingIntent + " " + (isAttachedToDisplay() ? "" : "not") + " attached"); final IIntentSender iIntentSender = pendingIntent.getTarget(); - if (mSurface != null) { - mActivityContainer.startActivityIntentSender(iIntentSender); - } else { - mActivityContainer.checkEmbeddedAllowedIntentSender(iIntentSender); - mQueuedPendingIntent = iIntentSender; - mQueuedIntent = null; + if (mActivityContainer.startActivityIntentSender(iIntentSender) == START_CANCELED) { + throw new OperationCanceledException(); } } @@ -243,26 +239,24 @@ public class ActivityView extends ViewGroup { mSurface = null; throw new RuntimeException("ActivityView: Unable to create ActivityContainer. " + e); } - - if (DEBUG) Log.v(TAG, "attachToSurfaceWhenReady: " + (mQueuedIntent != null || - mQueuedPendingIntent != null ? "" : "no") + " queued intent"); - if (mQueuedIntent != null) { - mActivityContainer.startActivity(mQueuedIntent); - mQueuedIntent = null; - } else if (mQueuedPendingIntent != null) { - mActivityContainer.startActivityIntentSender(mQueuedPendingIntent); - mQueuedPendingIntent = null; - } } /** * Set the callback to use to report certain state changes. - * @param callback The callback to report events to. + * + * Note: If the surface has been created prior to this call being made, then + * ActivityViewCallback.onSurfaceAvailable will be called from within setCallback. + * + * @param callback The callback to report events to. * * @see ActivityViewCallback */ public void setCallback(ActivityViewCallback callback) { mActivityViewCallback = callback; + + if (mSurface != null) { + mActivityViewCallback.onSurfaceAvailable(this); + } } public static abstract class ActivityViewCallback { @@ -272,6 +266,16 @@ public class ActivityView extends ViewGroup { * have at most one callback registered. */ public abstract void onAllActivitiesComplete(ActivityView view); + /** + * Called when the surface is ready to be drawn to. Calling startActivity prior to this + * callback will result in an IllegalStateException. + */ + public abstract void onSurfaceAvailable(ActivityView view); + /** + * Called when the surface has been removed. Calling startActivity after this callback + * will result in an IllegalStateException. + */ + public abstract void onSurfaceDestroyed(ActivityView view); } private class ActivityViewSurfaceTextureListener implements SurfaceTextureListener { @@ -286,6 +290,9 @@ public class ActivityView extends ViewGroup { mWidth = width; mHeight = height; attachToSurfaceWhenReady(); + if (mActivityViewCallback != null) { + mActivityViewCallback.onSurfaceAvailable(ActivityView.this); + } } @Override @@ -311,6 +318,9 @@ public class ActivityView extends ViewGroup { throw new RuntimeException( "ActivityView: Unable to set surface of ActivityContainer. " + e); } + if (mActivityViewCallback != null) { + mActivityViewCallback.onSurfaceDestroyed(ActivityView.this); + } return true; } @@ -325,7 +335,7 @@ public class ActivityView extends ViewGroup { private final WeakReference mActivityViewWeakReference; ActivityContainerCallback(ActivityView activityView) { - mActivityViewWeakReference = new WeakReference(activityView); + mActivityViewWeakReference = new WeakReference<>(activityView); } @Override @@ -391,24 +401,6 @@ public class ActivityView extends ViewGroup { } } - void checkEmbeddedAllowed(Intent intent) { - try { - mIActivityContainer.checkEmbeddedAllowed(intent); - } catch (RemoteException e) { - throw new RuntimeException( - "ActivityView: Unable to startActivity from Intent. " + e); - } - } - - void checkEmbeddedAllowedIntentSender(IIntentSender intentSender) { - try { - mIActivityContainer.checkEmbeddedAllowedIntentSender(intentSender); - } catch (RemoteException e) { - throw new RuntimeException( - "ActivityView: Unable to startActivity from IntentSender. " + e); - } - } - int getDisplayId() { try { return mIActivityContainer.getDisplayId(); diff --git a/core/java/android/app/IActivityContainer.aidl b/core/java/android/app/IActivityContainer.aidl index 52884f744b73b..cc3b10cfa88fc 100644 --- a/core/java/android/app/IActivityContainer.aidl +++ b/core/java/android/app/IActivityContainer.aidl @@ -29,8 +29,6 @@ interface IActivityContainer { void setSurface(in Surface surface, int width, int height, int density); int startActivity(in Intent intent); int startActivityIntentSender(in IIntentSender intentSender); - void checkEmbeddedAllowed(in Intent intent); - void checkEmbeddedAllowedIntentSender(in IIntentSender intentSender); int getDisplayId(); boolean injectEvent(in InputEvent event); void release(); diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 32787d8e9aed2..0630cad95c328 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -69,6 +69,7 @@ import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.VirtualDisplay; import android.hardware.input.InputManager; import android.hardware.input.InputManagerInternal; +import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.Debug; @@ -855,6 +856,11 @@ public final class ActivityStackSupervisor implements DisplayListener { ActivityContainer container = (ActivityContainer)iContainer; synchronized (mService) { + if (container != null && container.mParentActivity != null && + container.mParentActivity.state != ActivityState.RESUMED) { + // Cannot start a child activity if the parent is not resumed. + return ActivityManager.START_CANCELED; + } final int realCallingPid = Binder.getCallingPid(); final int realCallingUid = Binder.getCallingUid(); int callingPid; @@ -3788,18 +3794,21 @@ public final class ActivityStackSupervisor implements DisplayListener { @Override public final int startActivity(Intent intent) { mService.enforceNotIsolatedCaller("ActivityContainer.startActivity"); - int userId = mService.handleIncomingUser(Binder.getCallingPid(), + final int userId = mService.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), mCurrentUser, false, ActivityManagerService.ALLOW_FULL_ONLY, "ActivityContainer", null); + // TODO: Switch to user app stacks here. - intent.addFlags(FORCE_NEW_TASK_FLAGS); String mimeType = intent.getType(); - if (mimeType == null && intent.getData() != null - && "content".equals(intent.getData().getScheme())) { - mimeType = mService.getProviderMimeType(intent.getData(), userId); + final Uri data = intent.getData(); + if (mimeType == null && data != null && "content".equals(data.getScheme())) { + mimeType = mService.getProviderMimeType(data, userId); } - return startActivityMayWait(null, -1, null, intent, mimeType, null, null, null, null, 0, - 0, null, null, null, null, userId, this, null); + checkEmbeddedAllowedInner(userId, intent, mimeType); + + intent.addFlags(FORCE_NEW_TASK_FLAGS); + return startActivityMayWait(null, -1, null, intent, mimeType, null, null, null, null, + 0, 0, null, null, null, null, userId, this, null); } @Override @@ -3810,21 +3819,19 @@ public final class ActivityStackSupervisor implements DisplayListener { throw new IllegalArgumentException("Bad PendingIntent object"); } - return ((PendingIntentRecord)intentSender).sendInner(0, null, null, null, null, null, - null, 0, FORCE_NEW_TASK_FLAGS, FORCE_NEW_TASK_FLAGS, null, this); - } - - private void checkEmbeddedAllowedInner(Intent intent, String resolvedType) { - int userId = mService.handleIncomingUser(Binder.getCallingPid(), + final int userId = mService.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), mCurrentUser, false, ActivityManagerService.ALLOW_FULL_ONLY, "ActivityContainer", null); - if (resolvedType == null) { - resolvedType = intent.getType(); - if (resolvedType == null && intent.getData() != null - && "content".equals(intent.getData().getScheme())) { - resolvedType = mService.getProviderMimeType(intent.getData(), userId); - } - } + + final PendingIntentRecord pendingIntent = (PendingIntentRecord) intentSender; + checkEmbeddedAllowedInner(userId, pendingIntent.key.requestIntent, + pendingIntent.key.requestResolvedType); + + return pendingIntent.sendInner(0, null, null, null, null, null, null, 0, + FORCE_NEW_TASK_FLAGS, FORCE_NEW_TASK_FLAGS, null, this); + } + + private void checkEmbeddedAllowedInner(int userId, Intent intent, String resolvedType) { ActivityInfo aInfo = resolveActivity(intent, resolvedType, 0, null, userId); if (aInfo != null && (aInfo.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) == 0) { throw new SecurityException( @@ -3832,23 +3839,6 @@ public final class ActivityStackSupervisor implements DisplayListener { } } - /** Throw a SecurityException if allowEmbedded is not true */ - @Override - public final void checkEmbeddedAllowed(Intent intent) { - checkEmbeddedAllowedInner(intent, null); - } - - /** Throw a SecurityException if allowEmbedded is not true */ - @Override - public final void checkEmbeddedAllowedIntentSender(IIntentSender intentSender) { - if (!(intentSender instanceof PendingIntentRecord)) { - throw new IllegalArgumentException("Bad PendingIntent object"); - } - PendingIntentRecord pendingIntent = (PendingIntentRecord) intentSender; - checkEmbeddedAllowedInner(pendingIntent.key.requestIntent, - pendingIntent.key.requestResolvedType); - } - @Override public IBinder asBinder() { return this; diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index ffaa03d625339..7bdfcedda26fd 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -29,6 +29,8 @@ import android.os.RemoteException; import android.os.UserHandle; import android.util.Slog; +import com.android.server.am.ActivityStackSupervisor.ActivityContainer; + import java.io.PrintWriter; import java.lang.ref.WeakReference; @@ -201,6 +203,13 @@ final class PendingIntentRecord extends IIntentSender.Stub { IBinder resultTo, String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle options, IActivityContainer container) { synchronized(owner) { + final ActivityContainer activityContainer = (ActivityContainer)container; + if (activityContainer != null && activityContainer.mParentActivity != null && + activityContainer.mParentActivity.state + != ActivityStack.ActivityState.RESUMED) { + // Cannot start a child activity if the parent is not resumed. + return ActivityManager.START_CANCELED; + } if (!canceled) { sent = true; if ((key.flags&PendingIntent.FLAG_ONE_SHOT) != 0) {