Fix ActivityView lifecycle

Major changes to maintain the VirtualDisplay across repeated
attach/detach cycles of an ActivityView. This keeps the activities
and VirtualDisplays in the ActivityView from getting into bad states.

Fixes bug 14107002.

Change-Id: Idc2aaf85ac496eab0eeb436736cb10a2020040e8
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 51cb12a..a4ea17b 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -33,15 +33,18 @@
 import android.view.Surface;
 import android.view.TextureView;
 import android.view.TextureView.SurfaceTextureListener;
-import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
+import dalvik.system.CloseGuard;
+
+import java.lang.ref.WeakReference;
 
 /** @hide */
 public class ActivityView extends ViewGroup {
-    private final String TAG = "ActivityView";
-    private final boolean DEBUG = false;
+    private static final String TAG = "ActivityView";
+    private static final boolean DEBUG = false;
 
+    DisplayMetrics mMetrics;
     private final TextureView mTextureView;
     private IActivityContainer mActivityContainer;
     private Activity mActivity;
@@ -53,6 +56,8 @@
     IIntentSender mQueuedPendingIntent;
     Intent mQueuedIntent;
 
+    private final CloseGuard mGuard = CloseGuard.get();
+
     public ActivityView(Context context) {
         this(context, null);
     }
@@ -75,9 +80,24 @@
             throw new IllegalStateException("The ActivityView's Context is not an Activity.");
         }
 
+        try {
+            mActivityContainer = ActivityManagerNative.getDefault().createActivityContainer(
+                    mActivity.getActivityToken(), new ActivityContainerCallback(this));
+        } catch (RemoteException e) {
+            throw new IllegalStateException("ActivityView: Unable to create ActivityContainer. "
+                    + e);
+        }
+
         mTextureView = new TextureView(context);
         mTextureView.setSurfaceTextureListener(new ActivityViewSurfaceTextureListener());
         addView(mTextureView);
+
+        WindowManager wm = (WindowManager)mActivity.getSystemService(Context.WINDOW_SERVICE);
+        mMetrics = new DisplayMetrics();
+        wm.getDefaultDisplay().getMetrics(mMetrics);
+
+        mGuard.open("release");
+
         if (DEBUG) Log.v(TAG, "ctor()");
     }
 
@@ -86,51 +106,6 @@
         mTextureView.layout(0, 0, r - l, b - t);
     }
 
-    @Override
-    protected void onAttachedToWindow() {
-        if (DEBUG) Log.v(TAG, "onAttachedToWindow()");
-        super.onAttachedToWindow();
-        try {
-            final IBinder token = mActivity.getActivityToken();
-            mActivityContainer = ActivityManagerNative.getDefault().createActivityContainer(token,
-                      new ActivityContainerCallback());
-        } catch (RemoteException e) {
-            throw new IllegalStateException("ActivityView: Unable to create ActivityContainer. "
-                    + e);
-        }
-
-        attachToSurfaceWhenReady();
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        if (DEBUG) Log.v(TAG, "onDetachedFromWindow(): mActivityContainer=" + mActivityContainer);
-        super.onDetachedFromWindow();
-        if (mActivityContainer != null) {
-            detach();
-            try {
-                ActivityManagerNative.getDefault().deleteActivityContainer(mActivityContainer);
-            } catch (RemoteException e) {
-            }
-            mActivityContainer = null;
-        }
-    }
-
-    @Override
-    protected void onWindowVisibilityChanged(int visibility) {
-        if (DEBUG) Log.v(TAG, "onWindowVisibilityChanged(): visibility=" + visibility);
-        super.onWindowVisibilityChanged(visibility);
-        switch (visibility) {
-            case  View.VISIBLE:
-                attachToSurfaceWhenReady();
-                break;
-            case  View.INVISIBLE:
-                break;
-            case View.GONE:
-                break;
-        }
-    }
-
     private boolean injectInputEvent(InputEvent event) {
         try {
             return mActivityContainer != null && mActivityContainer.injectEvent(event);
@@ -159,6 +134,9 @@
     }
 
     public void startActivity(Intent intent) {
+        if (mActivityContainer == null) {
+            throw new IllegalStateException("Attempt to call startActivity after release");
+        }
         if (DEBUG) Log.v(TAG, "startActivity(): intent=" + intent + " " +
                 (isAttachedToDisplay() ? "" : "not") + " attached");
         if (mSurface != null) {
@@ -183,6 +161,9 @@
     }
 
     public void startActivity(IntentSender intentSender) {
+        if (mActivityContainer == null) {
+            throw new IllegalStateException("Attempt to call startActivity after release");
+        }
         if (DEBUG) Log.v(TAG, "startActivityIntentSender(): intentSender=" + intentSender + " " +
                 (isAttachedToDisplay() ? "" : "not") + " attached");
         final IIntentSender iIntentSender = intentSender.getTarget();
@@ -195,6 +176,9 @@
     }
 
     public void startActivity(PendingIntent pendingIntent) {
+        if (mActivityContainer == null) {
+            throw new IllegalStateException("Attempt to call startActivity after release");
+        }
         if (DEBUG) Log.v(TAG, "startActivityPendingIntent(): PendingIntent=" + pendingIntent + " "
                 + (isAttachedToDisplay() ? "" : "not") + " attached");
         final IIntentSender iIntentSender = pendingIntent.getTarget();
@@ -206,24 +190,54 @@
         }
     }
 
+    public void release() {
+        if (DEBUG) Log.v(TAG, "release()");
+        if (mActivityContainer == null) {
+            Log.e(TAG, "Duplicate call to release");
+            return;
+        }
+        try {
+            mActivityContainer.release();
+        } catch (RemoteException e) {
+        }
+        mActivityContainer = null;
+
+        if (mSurface != null) {
+            mSurface.release();
+            mSurface = null;
+        }
+
+        mTextureView.setSurfaceTextureListener(null);
+
+        mGuard.close();
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            if (mGuard != null) {
+                mGuard.warnIfOpen();
+                release();
+            }
+        } finally {
+            super.finalize();
+        }
+    }
+
     private void attachToSurfaceWhenReady() {
         final SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
-        if (mActivityContainer == null || surfaceTexture == null || mSurface != null) {
+        if (surfaceTexture == null || mSurface != null) {
             // Either not ready to attach, or already attached.
             return;
         }
 
-        WindowManager wm = (WindowManager)mActivity.getSystemService(Context.WINDOW_SERVICE);
-        DisplayMetrics metrics = new DisplayMetrics();
-        wm.getDefaultDisplay().getMetrics(metrics);
-
         mSurface = new Surface(surfaceTexture);
         try {
-            mActivityContainer.attachToSurface(mSurface, mWidth, mHeight, metrics.densityDpi);
+            mActivityContainer.setSurface(mSurface, mWidth, mHeight, mMetrics.densityDpi);
         } catch (RemoteException e) {
             mSurface.release();
             mSurface = null;
-            throw new IllegalStateException(
+            throw new RuntimeException(
                     "ActivityView: Unable to create ActivityContainer. " + e);
         }
 
@@ -238,41 +252,43 @@
         }
     }
 
-    private void detach() {
-        if (DEBUG) Log.d(TAG, "detach: attached=" + isAttachedToDisplay());
-        if (mSurface != null) {
-            try {
-                mActivityContainer.detachFromDisplay();
-            } catch (RemoteException e) {
-            }
-            mSurface.release();
-            mSurface = null;
-        }
-    }
-
     private class ActivityViewSurfaceTextureListener implements SurfaceTextureListener {
         @Override
         public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width,
                 int height) {
+            if (mActivityContainer == null) {
+                return;
+            }
             if (DEBUG) Log.d(TAG, "onSurfaceTextureAvailable: width=" + width + " height="
                     + height);
             mWidth = width;
             mHeight = height;
-            if (mActivityContainer != null) {
-                attachToSurfaceWhenReady();
-            }
+            attachToSurfaceWhenReady();
         }
 
         @Override
         public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width,
                 int height) {
+            if (mActivityContainer == null) {
+                return;
+            }
             if (DEBUG) Log.d(TAG, "onSurfaceTextureSizeChanged: w=" + width + " h=" + height);
         }
 
         @Override
         public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
+            if (mActivityContainer == null) {
+                return true;
+            }
             if (DEBUG) Log.d(TAG, "onSurfaceTextureDestroyed");
-            detach();
+            mSurface.release();
+            mSurface = null;
+            try {
+                mActivityContainer.setSurface(null, mWidth, mHeight, mMetrics.densityDpi);
+            } catch (RemoteException e) {
+                throw new RuntimeException(
+                        "ActivityView: Unable to set surface of ActivityContainer. " + e);
+            }
             return true;
         }
 
@@ -283,13 +299,17 @@
 
     }
 
-    private class ActivityContainerCallback extends IActivityContainerCallback.Stub {
+    private static class ActivityContainerCallback extends IActivityContainerCallback.Stub {
+        private final WeakReference<ActivityView> mActivityViewWeakReference;
+
+        ActivityContainerCallback(ActivityView activityView) {
+            mActivityViewWeakReference = new WeakReference<ActivityView>(activityView);
+        }
+
         @Override
         public void setVisible(IBinder container, boolean visible) {
-            if (DEBUG) Log.v(TAG, "setVisible(): container=" + container + " visible=" + visible);
-            if (visible) {
-            } else {
-            }
+            if (DEBUG) Log.v(TAG, "setVisible(): container=" + container + " visible=" + visible +
+                    " ActivityView=" + mActivityViewWeakReference.get());
         }
     }
 }
diff --git a/core/java/android/app/IActivityContainer.aidl b/core/java/android/app/IActivityContainer.aidl
index 5b80e06..cc3b10c 100644
--- a/core/java/android/app/IActivityContainer.aidl
+++ b/core/java/android/app/IActivityContainer.aidl
@@ -26,10 +26,10 @@
 /** @hide */
 interface IActivityContainer {
     void attachToDisplay(int displayId);
-    void attachToSurface(in Surface surface, int width, int height, int density);
-    void detachFromDisplay();
+    void setSurface(in Surface surface, int width, int height, int density);
     int startActivity(in Intent intent);
     int startActivityIntentSender(in IIntentSender intentSender);
     int getDisplayId();
     boolean injectEvent(in InputEvent event);
+    void release();
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 98cbe7d..d09ee96 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7078,6 +7078,9 @@
             if (r == null) {
                 return null;
             }
+            if (callback == null) {
+                throw new IllegalArgumentException("callback must not be null");
+            }
             return mStackSupervisor.createActivityContainer(r, callback);
         }
     }
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index a27288a..5a9e690c 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -139,7 +139,7 @@
     boolean forceNewConfig; // force re-create with new config next time
     int launchCount;        // count of launches since last state
     long lastLaunchTime;    // time of last lauch of this activity
-    ArrayList<ActivityStack> mChildContainers = new ArrayList<ActivityStack>();
+    ArrayList<ActivityContainer> mChildContainers = new ArrayList<ActivityContainer>();
 
     String stringName;      // for caching of toString().
 
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 42e1a37..f4f39e2 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -36,6 +36,8 @@
 import static com.android.server.am.ActivityStackSupervisor.DEBUG_STATES;
 import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
 
+import static com.android.server.am.ActivityStackSupervisor.ActivityContainer.CONTAINER_STATE_HAS_SURFACE;
+
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.server.Watchdog;
 import com.android.server.am.ActivityManagerService.ItemMatcher;
@@ -1028,9 +1030,9 @@
     private void setVisibile(ActivityRecord r, boolean visible) {
         r.visible = visible;
         mWindowManager.setAppVisibility(r.appToken, visible);
-        final ArrayList<ActivityStack> containers = r.mChildContainers;
+        final ArrayList<ActivityContainer> containers = r.mChildContainers;
         for (int containerNdx = containers.size() - 1; containerNdx >= 0; --containerNdx) {
-            ActivityContainer container = containers.get(containerNdx).mActivityContainer;
+            ActivityContainer container = containers.get(containerNdx);
             container.setVisible(visible);
         }
     }
@@ -1271,7 +1273,8 @@
         if (ActivityManagerService.DEBUG_LOCKSCREEN) mService.logLockScreen("");
 
         ActivityRecord parent = mActivityContainer.mParentActivity;
-        if (parent != null && parent.state != ActivityState.RESUMED) {
+        if ((parent != null && parent.state != ActivityState.RESUMED) ||
+                mActivityContainer.mContainerState != CONTAINER_STATE_HAS_SURFACE) {
             // Do not resume this stack if its parent is not resumed.
             // TODO: If in a loop, make sure that parent stack resumeTopActivity is called 1st.
             return false;
@@ -2552,6 +2555,20 @@
         return r;
     }
 
+    void finishAllActivitiesLocked() {
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = activities.get(activityNdx);
+                if (r.finishing) {
+                    continue;
+                }
+                Slog.d(TAG, "finishAllActivitiesLocked: finishing " + r);
+                finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false);
+            }
+        }
+    }
+
     final boolean navigateUpToLocked(IBinder token, Intent destIntent, int resultCode,
             Intent resultData) {
         final ActivityRecord srec = ActivityRecord.forToken(token);
@@ -2787,7 +2804,6 @@
         }
         if (activityRemoved) {
             mStackSupervisor.resumeTopActivitiesLocked();
-
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index efa3ad49..a316336 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -95,7 +95,6 @@
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -224,11 +223,11 @@
 
     // TODO: Add listener for removal of references.
     /** Mapping from (ActivityStack/TaskStack).mStackId to their current state */
-    SparseArray<WeakReference<ActivityContainer>> mActivityContainers =
-            new SparseArray<WeakReference<ActivityContainer>>();
+    SparseArray<ActivityContainer> mActivityContainers = new SparseArray<ActivityContainer>();
 
     /** Mapping from displayId to display current state */
-    private SparseArray<ActivityDisplay> mActivityDisplays = new SparseArray<ActivityDisplay>();
+    private final SparseArray<ActivityDisplay> mActivityDisplays =
+            new SparseArray<ActivityDisplay>();
 
     InputManagerInternal mInputManagerInternal;
 
@@ -260,7 +259,7 @@
                 mActivityDisplays.put(displayId, activityDisplay);
             }
 
-            createStackOnDisplay(null, HOME_STACK_ID, Display.DEFAULT_DISPLAY);
+            createStackOnDisplay(HOME_STACK_ID, Display.DEFAULT_DISPLAY);
             mHomeStack = mFocusedStack = mLastFocusedStack = getStack(HOME_STACK_ID);
 
             mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
@@ -1378,7 +1377,7 @@
             }
 
             // Need to create an app stack for this user.
-            int stackId = createStackOnDisplay(null, getNextStackId(), Display.DEFAULT_DISPLAY);
+            int stackId = createStackOnDisplay(getNextStackId(), Display.DEFAULT_DISPLAY);
             if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG, "adjustStackFocus: New stack r=" + r +
                     " stackId=" + stackId);
             mFocusedStack = getStack(stackId);
@@ -2127,14 +2126,9 @@
     }
 
     ActivityStack getStack(int stackId) {
-        WeakReference<ActivityContainer> weakReference = mActivityContainers.get(stackId);
-        if (weakReference != null) {
-            ActivityContainer activityContainer = weakReference.get();
-            if (activityContainer != null) {
-                return activityContainer.mStack;
-            } else {
-                mActivityContainers.remove(stackId);
-            }
+        ActivityContainer activityContainer = mActivityContainers.get(stackId);
+        if (activityContainer != null) {
+            return activityContainer.mStack;
         }
         return null;
     }
@@ -2164,49 +2158,26 @@
         return null;
     }
 
-    ActivityContainer createActivityContainer(ActivityRecord parentActivity, int stackId,
+    ActivityContainer createActivityContainer(ActivityRecord parentActivity,
             IActivityContainerCallback callback) {
-        ActivityContainer activityContainer = new ActivityContainer(parentActivity, stackId,
-                callback);
-        mActivityContainers.put(stackId, new WeakReference<ActivityContainer>(activityContainer));
-        if (parentActivity != null) {
-            parentActivity.mChildContainers.add(activityContainer.mStack);
-        }
+        ActivityContainer activityContainer = new VirtualActivityContainer(parentActivity, callback);
+        mActivityContainers.put(activityContainer.mStackId, activityContainer);
+        parentActivity.mChildContainers.add(activityContainer);
         return activityContainer;
     }
 
-    ActivityContainer createActivityContainer(ActivityRecord parentActivity,
-            IActivityContainerCallback callback) {
-        return createActivityContainer(parentActivity, getNextStackId(), callback);
-    }
-
     void removeChildActivityContainers(ActivityRecord parentActivity) {
-        for (int ndx = mActivityContainers.size() - 1; ndx >= 0; --ndx) {
-            final ActivityContainer container = mActivityContainers.valueAt(ndx).get();
-            if (container == null) {
-                mActivityContainers.removeAt(ndx);
-                continue;
-            }
-            if (container.mParentActivity != parentActivity) {
-                continue;
-            }
-
-            ActivityStack stack = container.mStack;
-            ActivityRecord top = stack.topRunningNonDelayedActivityLocked(null);
-            if (top != null) {
-                // TODO: Make sure the next activity doesn't start up when top is destroyed.
-                stack.destroyActivityLocked(top, true, true, "stack parent destroyed");
-            }
-            mActivityContainers.removeAt(ndx);
-            container.detachLocked();
+        final ArrayList<ActivityContainer> childStacks = parentActivity.mChildContainers;
+        for (int containerNdx = childStacks.size() - 1; containerNdx >= 0; --containerNdx) {
+            ActivityContainer container = childStacks.remove(containerNdx);
+            container.release();
         }
     }
 
     void deleteActivityContainer(IActivityContainer container) {
         ActivityContainer activityContainer = (ActivityContainer)container;
         if (activityContainer != null) {
-            activityContainer.mStack.destroyActivitiesLocked(null, true,
-                    "deleteActivityContainer");
+            activityContainer.mStack.finishAllActivitiesLocked();
             final ActivityRecord parent = activityContainer.mParentActivity;
             if (parent != null) {
                 parent.mChildContainers.remove(activityContainer);
@@ -2217,14 +2188,14 @@
         }
     }
 
-    private int createStackOnDisplay(ActivityRecord parentActivity, int stackId, int displayId) {
+    private int createStackOnDisplay(int stackId, int displayId) {
         ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
         if (activityDisplay == null) {
             return -1;
         }
 
-        ActivityContainer activityContainer =
-                createActivityContainer(parentActivity, stackId, null);
+        ActivityContainer activityContainer = new ActivityContainer(stackId);
+        mActivityContainers.put(stackId, activityContainer);
         activityContainer.attachToDisplayLocked(activityDisplay);
         return stackId;
     }
@@ -2306,9 +2277,9 @@
     }
 
     boolean shutdownLocked(int timeout) {
-        boolean timedout = false;
         goingToSleepLocked();
 
+        boolean timedout = false;
         final long endTime = System.currentTimeMillis() + timeout;
         while (true) {
             boolean cantShutdown = false;
@@ -2967,24 +2938,26 @@
 
     class ActivityContainer extends IActivityContainer.Stub {
         final int mStackId;
-        final IActivityContainerCallback mCallback;
+        IActivityContainerCallback mCallback = null;
         final ActivityStack mStack;
-        final ActivityRecord mParentActivity;
-        final String mIdString;
+        ActivityRecord mParentActivity = null;
+        String mIdString;
 
         boolean mVisible = true;
 
         /** Display this ActivityStack is currently on. Null if not attached to a Display. */
         ActivityDisplay mActivityDisplay;
 
-        ActivityContainer(ActivityRecord parentActivity, int stackId,
-                IActivityContainerCallback callback) {
+        final static int CONTAINER_STATE_HAS_SURFACE = 0;
+        final static int CONTAINER_STATE_NO_SURFACE = 1;
+        final static int CONTAINER_STATE_FINISHING = 2;
+        int mContainerState = CONTAINER_STATE_HAS_SURFACE;
+
+        ActivityContainer(int stackId) {
             synchronized (mService) {
                 mStackId = stackId;
                 mStack = new ActivityStack(this);
-                mParentActivity = parentActivity;
-                mCallback = callback;
-                mIdString = "ActivtyContainer{" + mStackId + ", parent=" + mParentActivity + "}";
+                mIdString = "ActivtyContainer{" + mStackId + "}";
                 if (DEBUG_STACK) Slog.d(TAG, "Creating " + this);
             }
         }
@@ -3034,6 +3007,14 @@
             }
         }
 
+        @Override
+        public void release() {
+            mContainerState = CONTAINER_STATE_FINISHING;
+            mStack.finishAllActivitiesLocked();
+            detachLocked();
+            mWindowManager.removeStack(mStackId);
+        }
+
         private void detachLocked() {
             if (DEBUG_STACK) Slog.d(TAG, "detachLocked: " + this + " from display="
                     + mActivityDisplay + " Callers=" + Debug.getCallers(2));
@@ -3047,13 +3028,6 @@
         }
 
         @Override
-        public void detachFromDisplay() {
-            synchronized (mService) {
-                detachLocked();
-            }
-        }
-
-        @Override
         public final int startActivity(Intent intent) {
             mService.enforceNotIsolatedCaller("ActivityContainer.startActivity");
             int userId = mService.handleIncomingUser(Binder.getCallingPid(),
@@ -3086,23 +3060,8 @@
         }
 
         @Override
-        public void attachToSurface(Surface surface, int width, int height, int density) {
+        public void setSurface(Surface surface, int width, int height, int density) {
             mService.enforceNotIsolatedCaller("ActivityContainer.attachToSurface");
-
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                synchronized (mService) {
-                    ActivityDisplay activityDisplay =
-                            new ActivityDisplay(surface, width, height, density);
-                    mActivityDisplays.put(activityDisplay.mDisplayId, activityDisplay);
-                    attachToDisplayLocked(activityDisplay);
-                    mStack.resumeTopActivityLocked(null);
-                }
-                if (DEBUG_STACK) Slog.d(TAG, "attachToSurface: " + this + " to display="
-                        + mActivityDisplay);
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
         }
 
         ActivityStackSupervisor getOuter() {
@@ -3137,46 +3096,87 @@
         }
     }
 
+    private class VirtualActivityContainer extends ActivityContainer {
+        Surface mSurface;
+
+        VirtualActivityContainer(ActivityRecord parent, IActivityContainerCallback callback) {
+            super(getNextStackId());
+            mParentActivity = parent;
+            mCallback = callback;
+            mContainerState = CONTAINER_STATE_NO_SURFACE;
+            mIdString = "VirtualActivtyContainer{" + mStackId + ", parent=" + mParentActivity + "}";
+        }
+
+        @Override
+        public void setSurface(Surface surface, int width, int height, int density) {
+            super.setSurface(surface, width, height, density);
+
+            synchronized (mService) {
+                final long origId = Binder.clearCallingIdentity();
+                try {
+                    setSurfaceLocked(surface, width, height, density);
+                } finally {
+                    Binder.restoreCallingIdentity(origId);
+                }
+            }
+        }
+
+        private void setSurfaceLocked(Surface surface, int width, int height, int density) {
+            if (mContainerState == CONTAINER_STATE_FINISHING) {
+                return;
+            }
+            VirtualActivityDisplay virtualActivityDisplay =
+                    (VirtualActivityDisplay) mActivityDisplay;
+            if (virtualActivityDisplay == null) {
+                virtualActivityDisplay =
+                        new VirtualActivityDisplay(surface, width, height, density);
+                mActivityDisplay = virtualActivityDisplay;
+                mActivityDisplays.put(virtualActivityDisplay.mDisplayId, virtualActivityDisplay);
+                attachToDisplayLocked(virtualActivityDisplay);
+            }
+
+            if (mSurface != null) {
+                mSurface.release();
+                mSurface = null;
+            }
+
+            if (surface != null) {
+                mContainerState = CONTAINER_STATE_HAS_SURFACE;
+                mSurface = surface;
+                mStack.resumeTopActivityLocked(null);
+            } else {
+                mContainerState = CONTAINER_STATE_NO_SURFACE;
+                if (mStack.mPausingActivity == null && mStack.mResumedActivity != null) {
+                    mStack.startPausingLocked(false, true);
+                }
+            }
+            if (DEBUG_STACK) Slog.d(TAG, "setSurface: " + this + " to display="
+                    + virtualActivityDisplay);
+
+            virtualActivityDisplay.setSurface(surface);
+        }
+    }
+
     /** Exactly one of these classes per Display in the system. Capable of holding zero or more
      * attached {@link ActivityStack}s */
-    final class ActivityDisplay {
+    class ActivityDisplay {
         /** Actual Display this object tracks. */
         int mDisplayId;
         Display mDisplay;
         DisplayInfo mDisplayInfo = new DisplayInfo();
-        Surface mSurface;
 
         /** All of the stacks on this display. Order matters, topmost stack is in front of all other
          * stacks, bottommost behind. Accessed directly by ActivityManager package classes */
         final ArrayList<ActivityStack> mStacks = new ArrayList<ActivityStack>();
 
-        /** If this display is for an ActivityView then the VirtualDisplay created for it is stored
-         * here. */
-        VirtualDisplay mVirtualDisplay;
+        ActivityDisplay() {
+        }
 
         ActivityDisplay(int displayId) {
             init(mDisplayManager.getDisplay(displayId));
         }
 
-        ActivityDisplay(Surface surface, int width, int height, int density) {
-            DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
-            long ident = Binder.clearCallingIdentity();
-            try {
-                mVirtualDisplay = dm.createVirtualDisplay(mService.mContext,
-                        VIRTUAL_DISPLAY_BASE_NAME, width, height, density, surface,
-                        DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC |
-                        DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-
-            init(mVirtualDisplay.getDisplay());
-            mSurface = surface;
-
-            mWindowManager.handleDisplayAdded(mDisplayId);
-        }
-
-        private void init(Display display) {
+        void init(Display display) {
             mDisplay = display;
             mDisplayId = display.getDisplayId();
             mDisplay.getDisplayInfo(mDisplayInfo);
@@ -3192,11 +3192,6 @@
             if (DEBUG_STACK) Slog.v(TAG, "detachActivitiesLocked: detaching " + stack
                     + " from displayId=" + mDisplayId);
             mStacks.remove(stack);
-            if (mStacks.isEmpty() && mVirtualDisplay != null) {
-                mVirtualDisplay.release();
-                mVirtualDisplay = null;
-            }
-            mSurface.release();
         }
 
         void getBounds(Point bounds) {
@@ -3207,8 +3202,42 @@
 
         @Override
         public String toString() {
-            return "ActivityDisplay={" + mDisplayId + (mVirtualDisplay == null ? "" : "V")
-                    + " numStacks=" + mStacks.size() + "}";
+            return "ActivityDisplay={" + mDisplayId + " numStacks=" + mStacks.size() + "}";
+        }
+    }
+
+    class VirtualActivityDisplay extends ActivityDisplay {
+        VirtualDisplay mVirtualDisplay;
+
+        VirtualActivityDisplay(Surface surface, int width, int height, int density) {
+            DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
+            mVirtualDisplay = dm.createVirtualDisplay(mService.mContext, VIRTUAL_DISPLAY_BASE_NAME,
+                    width, height, density, surface, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC |
+                    DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
+
+            init(mVirtualDisplay.getDisplay());
+
+            mWindowManager.handleDisplayAdded(mDisplayId);
+        }
+
+        void setSurface(Surface surface) {
+            if (mVirtualDisplay != null) {
+                mVirtualDisplay.setSurface(surface);
+            }
+        }
+
+        @Override
+        void detachActivitiesLocked(ActivityStack stack) {
+            super.detachActivitiesLocked(stack);
+            if (mVirtualDisplay != null) {
+                mVirtualDisplay.release();
+                mVirtualDisplay = null;
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "VirtualActivityDisplay={" + mDisplayId + "}";
         }
     }
 }