Merge "Add -c option to meminfo to dump a compact form."
diff --git a/api/current.txt b/api/current.txt
index 5dee15e..e5156e8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2697,7 +2697,8 @@
     method public void addContentView(android.view.View, android.view.ViewGroup.LayoutParams);
     method public void closeContextMenu();
     method public void closeOptionsMenu();
-    method public void convertToOpaque();
+    method public void convertFromTranslucent();
+    method public void convertToTranslucent(android.app.Activity.TranslucentConversionListener);
     method public android.app.PendingIntent createPendingResult(int, android.content.Intent, int);
     method public final deprecated void dismissDialog(int);
     method public boolean dispatchGenericMotionEvent(android.view.MotionEvent);
@@ -2883,6 +2884,10 @@
     field public static final int RESULT_OK = -1; // 0xffffffff
   }
 
+  public static abstract interface Activity.TranslucentConversionListener {
+    method public abstract void onTranslucentConversionComplete(boolean);
+  }
+
   public deprecated class ActivityGroup extends android.app.Activity {
     ctor public ActivityGroup();
     ctor public ActivityGroup(boolean);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index d1efd0d1..fa746ba 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -745,6 +745,7 @@
     // protected by synchronized (this) 
     int mResultCode = RESULT_CANCELED;
     Intent mResultData = null;
+    private TranslucentConversionListener mTranslucentCallback;
 
     private boolean mTitleReady = false;
 
@@ -1382,6 +1383,7 @@
         if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStop " + this);
         if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false);
         getApplication().dispatchActivityStopped(this);
+        mTranslucentCallback = null;
         mCalled = true;
     }
 
@@ -4886,23 +4888,62 @@
     /**
      * Convert a translucent themed Activity {@link android.R.attr#windowIsTranslucent} to a
      * fullscreen opaque Activity.
-     *
+     * <p>
      * Call this whenever the background of a translucent Activity has changed to become opaque.
-     * Doing so will allow the previously visible Activity behind this one to be stopped. Stopped
-     * apps consume no CPU cycles and are eligible for removal when reclaiming memory.
-     *
+     * Doing so will allow the {@link android.view.Surface} of the Activity behind to be released.
+     * <p>
      * This call has no effect on non-translucent activities or on activities with the
      * {@link android.R.attr#windowIsFloating} attribute.
+     *
+     * @see #convertToTranslucent(TranslucentConversionListener)
+     * @see TranslucentConversionListener
      */
-    public void convertToOpaque() {
+    public void convertFromTranslucent() {
         try {
-            ActivityManagerNative.getDefault().convertToOpaque(mToken);
+            mTranslucentCallback = null;
+            ActivityManagerNative.getDefault().convertFromTranslucent(mToken);
         } catch (RemoteException e) {
             // pass
         }
     }
 
     /**
+     * Convert a translucent themed Activity {@link android.R.attr#windowIsTranslucent} back from
+     * opaque to translucent following a call to {@link #convertFromTranslucent()}.
+     * <p>
+     * Calling this allows the Activity behind this one to be seen again. Once all such Activities
+     * have been redrawn {@link TranslucentConversionListener#onTranslucentConversionComplete} will
+     * be called indicating that it is safe to make this activity translucent again. Until
+     * {@link TranslucentConversionListener#onTranslucentConversionComplete} is called the image
+     * behind the frontmost Activity will be indeterminate.
+     * <p>
+     * This call has no effect on non-translucent activities or on activities with the
+     * {@link android.R.attr#windowIsFloating} attribute.
+     *
+     * @param callback the method to call when all visible Activities behind this one have been
+     * drawn and it is safe to make this Activity translucent again.
+     *
+     * @see #convertFromTranslucent()
+     * @see TranslucentConversionListener
+     */
+    public void convertToTranslucent(TranslucentConversionListener callback) {
+        try {
+            mTranslucentCallback = callback;
+            ActivityManagerNative.getDefault().convertToTranslucent(mToken);
+        } catch (RemoteException e) {
+            // pass
+        }
+    }
+
+    /** @hide */
+    void onTranslucentConversionComplete(boolean drawComplete) {
+        if (mTranslucentCallback != null) {
+            mTranslucentCallback.onTranslucentConversionComplete(drawComplete);
+            mTranslucentCallback = null;
+        }
+    }
+
+    /**
      * Adjust the current immersive mode setting.
      *
      * Note that changing this value will have no effect on the activity's
@@ -4947,6 +4988,7 @@
      * @return The new action mode, or <code>null</code> if the activity does not want to
      *         provide special handling for this action mode. (It will be handled by the system.)
      */
+    @Override
     public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) {
         initActionBar();
         if (mActionBar != null) {
@@ -4961,6 +5003,7 @@
      *
      * @param mode The new action mode.
      */
+    @Override
     public void onActionModeStarted(ActionMode mode) {
     }
 
@@ -4970,6 +5013,7 @@
      *
      * @param mode The action mode that just finished.
      */
+    @Override
     public void onActionModeFinished(ActionMode mode) {
     }
 
@@ -5373,4 +5417,26 @@
             }
         }
     }
+
+    /**
+     * Interface for informing a translucent {@link Activity} once all visible activities below it
+     * have completed drawing. This is necessary only after an {@link Activity} has been made
+     * opaque using {@link Activity#convertFromTranslucent()} and before it has been drawn
+     * translucent again following a call to {@link
+     * Activity#convertToTranslucent(TranslucentConversionListener)}.
+     */
+    public interface TranslucentConversionListener {
+        /**
+         * Callback made following {@link Activity#convertToTranslucent} once all visible Activities
+         * below the top one have been redrawn. Following this callback it is safe to make the top
+         * Activity translucent because the underlying Activity has been drawn.
+         *
+         * @param drawComplete True if the background Activity has drawn itself. False if a timeout
+         * occurred waiting for the Activity to complete drawing.
+         *
+         * @see Activity#convertFromTranslucent()
+         * @see Activity#convertToTranslucent(TranslucentConversionListener)
+         */
+        public void onTranslucentConversionComplete(boolean drawComplete);
+    }
 }
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index a23611ec..acfcb40 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1499,10 +1499,18 @@
             return true;
         }
 
-        case CONVERT_TO_OPAQUE_TRANSACTION: {
+        case CONVERT_FROM_TRANSLUCENT_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             IBinder token = data.readStrongBinder();
-            convertToOpaque(token);
+            convertFromTranslucent(token);
+            reply.writeNoException();
+            return true;
+        }
+
+        case CONVERT_TO_TRANSLUCENT_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder token = data.readStrongBinder();
+            convertToTranslucent(token);
             reply.writeNoException();
             return true;
         }
@@ -1957,6 +1965,13 @@
             return true;
         }
 
+        case NOTIFY_ACTIVITY_DRAWN_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder token = data.readStrongBinder();
+            notifyActivityDrawn(token);
+            reply.writeNoException();
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -3840,13 +3855,25 @@
         reply.recycle();
     }
 
-    public void convertToOpaque(IBinder token)
+    public void convertFromTranslucent(IBinder token)
             throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(token);
-        mRemote.transact(CONVERT_TO_OPAQUE_TRANSACTION, data, reply, 0);
+        mRemote.transact(CONVERT_FROM_TRANSLUCENT_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
+    public void convertToTranslucent(IBinder token)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(token);
+        mRemote.transact(CONVERT_TO_TRANSLUCENT_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
         reply.recycle();
@@ -4482,5 +4509,16 @@
         reply.recycle();
     }
 
+    public void notifyActivityDrawn(IBinder token) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(token);
+        mRemote.transact(NOTIFY_ACTIVITY_DRAWN_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index f1b5d46..b24aeb0 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1215,6 +1215,9 @@
             queueOrSendMessage(H.TRIM_MEMORY, null, level);
         }
 
+        public void scheduleTranslucentConversionComplete(IBinder token, boolean drawComplete) {
+            queueOrSendMessage(H.TRANSLUCENT_CONVERSION_COMPLETE, token, drawComplete ? 1 : 0);
+        }
     }
 
     private class H extends Handler {
@@ -1262,6 +1265,7 @@
         public static final int DUMP_PROVIDER           = 141;
         public static final int UNSTABLE_PROVIDER_DIED  = 142;
         public static final int REQUEST_ACTIVITY_EXTRAS = 143;
+        public static final int TRANSLUCENT_CONVERSION_COMPLETE = 144;
         String codeToString(int code) {
             if (DEBUG_MESSAGES) {
                 switch (code) {
@@ -1309,6 +1313,7 @@
                     case DUMP_PROVIDER: return "DUMP_PROVIDER";
                     case UNSTABLE_PROVIDER_DIED: return "UNSTABLE_PROVIDER_DIED";
                     case REQUEST_ACTIVITY_EXTRAS: return "REQUEST_ACTIVITY_EXTRAS";
+                    case TRANSLUCENT_CONVERSION_COMPLETE: return "TRANSLUCENT_CONVERSION_COMPLETE";
                 }
             }
             return Integer.toString(code);
@@ -1523,6 +1528,9 @@
                 case REQUEST_ACTIVITY_EXTRAS:
                     handleRequestActivityExtras((RequestActivityExtras)msg.obj);
                     break;
+                case TRANSLUCENT_CONVERSION_COMPLETE:
+                    handleTranslucentConversionComplete((IBinder)msg.obj, msg.arg1 == 1);
+                    break;
             }
             if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
         }
@@ -2255,7 +2263,14 @@
         } catch (RemoteException e) {
         }
     }
-    
+
+    public void handleTranslucentConversionComplete(IBinder token, boolean drawComplete) {
+        ActivityClientRecord r = mActivities.get(token);
+        if (r != null) {
+            r.activity.onTranslucentConversionComplete(drawComplete);
+        }
+    }
+
     private static final ThreadLocal<Intent> sCurrentBroadcastIntent = new ThreadLocal<Intent>();
 
     /**
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index e903447..cc495aa 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -603,6 +603,16 @@
             reply.writeNoException();
             return true;
         }
+
+        case SCHEDULE_TRANSLUCENT_CONVERSION_COMPLETE_TRANSACTION:
+        {
+            data.enforceInterface(IApplicationThread.descriptor);
+            IBinder token = data.readStrongBinder();
+            boolean timeout = data.readInt() == 1;
+            scheduleTranslucentConversionComplete(token, timeout);
+            reply.writeNoException();
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -1197,6 +1207,7 @@
         data.recycle();
     }
 
+    @Override
     public void unstableProviderDied(IBinder provider) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
@@ -1205,6 +1216,7 @@
         data.recycle();
     }
 
+    @Override
     public void requestActivityExtras(IBinder activityToken, IBinder requestToken, int requestType)
             throws RemoteException {
         Parcel data = Parcel.obtain();
@@ -1215,4 +1227,15 @@
         mRemote.transact(REQUEST_ACTIVITY_EXTRAS_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
         data.recycle();
     }
+
+    @Override
+    public void scheduleTranslucentConversionComplete(IBinder token, boolean timeout)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        data.writeInterfaceToken(IApplicationThread.descriptor);
+        data.writeStrongBinder(token);
+        data.writeInt(timeout ? 1 : 0);
+        mRemote.transact(SCHEDULE_TRANSLUCENT_CONVERSION_COMPLETE_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
+        data.recycle();
+    }
 }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 3793c73..19858dc 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -258,7 +258,7 @@
             StrictMode.ViolationInfo crashInfo) throws RemoteException;
 
     /*
-     * This will deliver the specified signal to all the persistent processes. Currently only 
+     * This will deliver the specified signal to all the persistent processes. Currently only
      * SIGUSR1 is delivered. All others are ignored.
      */
     public void signalPersistentProcesses(int signal) throws RemoteException;
@@ -301,7 +301,9 @@
 
     public void finishHeavyWeightApp() throws RemoteException;
 
-    public void convertToOpaque(IBinder token) throws RemoteException;
+    public void convertFromTranslucent(IBinder token) throws RemoteException;
+    public void convertToTranslucent(IBinder token) throws RemoteException;
+    public void notifyActivityDrawn(IBinder token) throws RemoteException;
 
     public void setImmersive(IBinder token, boolean immersive) throws RemoteException;
     public boolean isImmersive(IBinder token) throws RemoteException;
@@ -494,7 +496,7 @@
             thisTime = source.readLong();
             totalTime = source.readLong();
         }
-    };
+    }
 
     String descriptor = "android.app.IActivityManager";
 
@@ -670,6 +672,8 @@
     int GET_STACK_BOXES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+170;
     int SET_FOCUSED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+171;
     int GET_STACK_BOX_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+172;
-    int CONVERT_TO_OPAQUE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+173;
-    int REPORT_ACTIVITY_FULLY_DRAWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+174;
+    int CONVERT_FROM_TRANSLUCENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+173;
+    int CONVERT_TO_TRANSLUCENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+174;
+    int NOTIFY_ACTIVITY_DRAWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+175;
+    int REPORT_ACTIVITY_FULLY_DRAWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+176;
 }
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index a009bd3..286566d 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -133,6 +133,8 @@
     void unstableProviderDied(IBinder provider) throws RemoteException;
     void requestActivityExtras(IBinder activityToken, IBinder requestToken, int requestType)
             throws RemoteException;
+    void scheduleTranslucentConversionComplete(IBinder token, boolean timeout)
+            throws RemoteException;
 
     String descriptor = "android.app.IApplicationThread";
 
@@ -183,4 +185,5 @@
     int DUMP_DB_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+45;
     int UNSTABLE_PROVIDER_DIED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+46;
     int REQUEST_ACTIVITY_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+47;
+    int SCHEDULE_TRANSLUCENT_CONVERSION_COMPLETE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+48;
 }
diff --git a/core/java/android/view/transition/Fade.java b/core/java/android/view/transition/Fade.java
index 3c5b6fa..c2aa90b 100644
--- a/core/java/android/view/transition/Fade.java
+++ b/core/java/android/view/transition/Fade.java
@@ -96,12 +96,19 @@
     protected Animator appear(ViewGroup sceneRoot,
             TransitionValues startValues, int startVisibility,
             TransitionValues endValues, int endVisibility) {
-        View endView = (endValues != null) ? endValues.view : null;
-        if ((mFadingMode & IN) != IN) {
+        if ((mFadingMode & IN) != IN || endValues == null) {
             return null;
         }
+        final View endView = endValues.view;
         endView.setAlpha(0);
-        return runAnimation(endView, 0, 1, null);
+        final Animator.AnimatorListener endListener = new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                // Always end animation with full alpha, in case it's canceled mid-stream
+                endView.setAlpha(1);
+            }
+        };
+        return runAnimation(endView, 0, 1, endListener);
     }
 
     @Override
diff --git a/core/java/android/view/transition/TransitionGroup.java b/core/java/android/view/transition/TransitionGroup.java
index d0e61ea..313e33e 100644
--- a/core/java/android/view/transition/TransitionGroup.java
+++ b/core/java/android/view/transition/TransitionGroup.java
@@ -104,7 +104,7 @@
                 mTransitions.add(transitions[i]);
                 transitions[i].mParent = this;
                 if (mDuration >= 0) {
-                    transitions[0].setDuration(mDuration);
+                    transitions[i].setDuration(mDuration);
                 }
             }
         }
diff --git a/core/java/com/android/internal/view/menu/ListMenuItemView.java b/core/java/com/android/internal/view/menu/ListMenuItemView.java
index 464ae2f..35b76dd 100644
--- a/core/java/com/android/internal/view/menu/ListMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/ListMenuItemView.java
@@ -242,17 +242,20 @@
 
     private void insertIconView() {
         mIconView = (ImageView) getInflater()
-                .inflate(com.android.internal.R.layout.list_menu_item_icon, this, true);
+                .inflate(com.android.internal.R.layout.list_menu_item_icon, this, false);
+        addView(mIconView);
     }
 
     private void insertRadioButton() {
         mRadioButton = (RadioButton) getInflater()
-                .inflate(com.android.internal.R.layout.list_menu_item_radio, this, true);
+                .inflate(com.android.internal.R.layout.list_menu_item_radio, this, false);
+        addView(mRadioButton);
     }
 
     private void insertCheckBox() {
         mCheckBox = (CheckBox) getInflater()
-                .inflate(com.android.internal.R.layout.list_menu_item_checkbox, this, true);
+                .inflate(com.android.internal.R.layout.list_menu_item_checkbox, this, false);
+        addView(mCheckBox);
     }
 
     private void alignTextToStartOf(View v) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 1057335..65006e5 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1000,7 +1000,6 @@
     static final int USER_SWITCH_TIMEOUT_MSG = 36;
     static final int IMMERSIVE_MODE_LOCK_MSG = 37;
     static final int PERSIST_URI_GRANTS = 38;
-    static final int SET_FOCUSED_STACK = 39;
 
     static final int FIRST_ACTIVITY_STACK_MSG = 100;
     static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1493,18 +1492,6 @@
                 writeGrantedUriPermissions();
                 break;
             }
-            case SET_FOCUSED_STACK: {
-                synchronized (ActivityManagerService.this) {
-                    ActivityStack stack = mStackSupervisor.getStack(msg.arg1);
-                    if (stack != null) {
-                        ActivityRecord r = stack.topRunningActivityLocked(null);
-                        if (r != null) {
-                            setFocusedActivityLocked(r);
-                        }
-                    }
-                }
-                break;
-            }
             }
         }
     };
@@ -2081,7 +2068,26 @@
     @Override
     public void setFocusedStack(int stackId) {
         if (DEBUG_FOCUS) Slog.d(TAG, "setFocusedStack: stackId=" + stackId);
-        mHandler.obtainMessage(SET_FOCUSED_STACK, stackId, 0).sendToTarget();
+        synchronized (ActivityManagerService.this) {
+            ActivityStack stack = mStackSupervisor.getStack(stackId);
+            if (stack != null) {
+                ActivityRecord r = stack.topRunningActivityLocked(null);
+                if (r != null) {
+                    setFocusedActivityLocked(r);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void notifyActivityDrawn(IBinder token) {
+        if (DEBUG_VISBILITY) Slog.d(TAG, "notifyActivityDrawn: token=" + token);
+        synchronized (this) {
+            ActivityRecord r= mStackSupervisor.isInAnyStackLocked(token);
+            if (r != null) {
+                r.task.stack.notifyActivityDrawnLocked(r);
+            }
+        }
     }
 
     final void applyUpdateLockStateLocked(ActivityRecord r) {
@@ -8048,7 +8054,7 @@
     }
 
     @Override
-    public void convertToOpaque(IBinder token) {
+    public void convertFromTranslucent(IBinder token) {
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
@@ -8056,8 +8062,28 @@
                 if (r == null) {
                     return;
                 }
-                if (r.convertToOpaque()) {
-                    mWindowManager.setAppFullscreen(token);
+                if (r.changeWindowTranslucency(true)) {
+                    mWindowManager.setAppFullscreen(token, true);
+                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public void convertToTranslucent(IBinder token) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (this) {
+                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+                if (r == null) {
+                    return;
+                }
+                if (r.changeWindowTranslucency(false)) {
+                    r.task.stack.convertToTranslucent(r);
+                    mWindowManager.setAppFullscreen(token, false);
                     mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
                 }
             }
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 4f09407..2ae9ed6 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import android.os.Trace;
+import com.android.internal.R.styleable;
 import com.android.internal.app.ResolverActivity;
 import com.android.server.AttributeCache;
 import com.android.server.am.ActivityStack.ActivityState;
@@ -505,19 +506,23 @@
         }
     }
 
-    boolean convertToOpaque() {
-        if (fullscreen) {
+    boolean changeWindowTranslucency(boolean toOpaque) {
+        if (fullscreen == toOpaque) {
+            return false;
+        }
+        AttributeCache.Entry ent =
+                AttributeCache.instance().get(packageName, realTheme, styleable.Window);
+        if (ent == null
+                || !ent.array.getBoolean(styleable.Window_windowIsTranslucent, false)
+                || ent.array.getBoolean(styleable.Window_windowIsFloating, false)) {
             return false;
         }
 
-        AttributeCache.Entry ent = AttributeCache.instance().get(packageName,
-                realTheme, com.android.internal.R.styleable.Window);
-        if (ent != null && !ent.array.getBoolean(
-                com.android.internal.R.styleable.Window_windowIsFloating, false)) {
-            fullscreen = true;
-            ++task.numFullscreen;
-        }
-        return fullscreen;
+        // Keep track of the number of fullscreen activities in this task.
+        task.numFullscreen += toOpaque ? +1 : -1;
+
+        fullscreen = toOpaque;
+        return true;
     }
 
     void putInHistory() {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 98b3ce9..be03ee3 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -118,6 +118,10 @@
     // is being started.
     static final boolean SHOW_APP_STARTING_PREVIEW = true;
 
+    // How long to wait for all background Activities to redraw following a call to
+    // convertToTranslucent().
+    static final long TRANSLUCENT_CONVERSION_TIMEOUT = 2000;
+
     enum ActivityState {
         INITIALIZING,
         RESUMED,
@@ -184,6 +188,16 @@
      */
     ActivityRecord mLastStartedActivity = null;
 
+    // The topmost Activity passed to convertToTranslucent(). When non-null it means we are
+    // waiting for all Activities in mUndrawnActivitiesBelowTopTranslucent to be removed as they
+    // are drawn. When the last member of mUndrawnActivitiesBelowTopTranslucent is removed the
+    // Activity in mTranslucentActivityWaiting is notified via
+    // Activity.onTranslucentConversionComplete(false). If a timeout occurs prior to the last
+    // background activity being drawn then the same call will be made with a true value.
+    ActivityRecord mTranslucentActivityWaiting = null;
+    ArrayList<ActivityRecord> mUndrawnActivitiesBelowTopTranslucent =
+            new ArrayList<ActivityRecord>();
+
     /**
      * Set when we know we are going to be calling updateConfiguration()
      * soon, so want to skip intermediate config checks.
@@ -215,6 +229,7 @@
     static final int LAUNCH_TICK_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 3;
     static final int STOP_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 4;
     static final int DESTROY_ACTIVITIES_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 5;
+    static final int TRANSLUCENT_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 6;
 
     static class ScheduleDestroyArgs {
         final ProcessRecord mOwner;
@@ -285,7 +300,12 @@
                     synchronized (mService) {
                         destroyActivitiesLocked(args.mOwner, args.mOomAdj, args.mReason);
                     }
-                }
+                } break;
+                case TRANSLUCENT_TIMEOUT_MSG: {
+                    synchronized (mService) {
+                        notifyActivityDrawnLocked(null);
+                    }
+                } break;
             }
         }
     }
@@ -952,6 +972,16 @@
                 TAG, "ensureActivitiesVisible behind " + top
                 + " configChanges=0x" + Integer.toHexString(configChanges));
 
+        if (mTranslucentActivityWaiting != top) {
+            mUndrawnActivitiesBelowTopTranslucent.clear();
+            if (mTranslucentActivityWaiting != null) {
+                // Call the callback with a timeout indication.
+                notifyActivityDrawnLocked(null);
+                mTranslucentActivityWaiting = null;
+            }
+            mHandler.removeMessages(TRANSLUCENT_TIMEOUT_MSG);
+        }
+
         // If the top activity is not fullscreen, then we need to
         // make sure any activities under it are now visible.
         boolean aboveTop = true;
@@ -1018,6 +1048,9 @@
                             if (DEBUG_VISBILITY) Slog.v(
                                     TAG, "Making visible and scheduling visibility: " + r);
                             try {
+                                if (mTranslucentActivityWaiting != null) {
+                                    mUndrawnActivitiesBelowTopTranslucent.add(r);
+                                }
                                 mWindowManager.setAppVisibility(r.appToken, true);
                                 r.sleeping = false;
                                 r.app.pendingUiClean = true;
@@ -1091,6 +1124,42 @@
         return showHomeBehindStack;
     }
 
+    void convertToTranslucent(ActivityRecord r) {
+        mTranslucentActivityWaiting = r;
+        mUndrawnActivitiesBelowTopTranslucent.clear();
+        mHandler.sendEmptyMessageDelayed(TRANSLUCENT_TIMEOUT_MSG, TRANSLUCENT_CONVERSION_TIMEOUT);
+    }
+
+    /**
+     * Called as activities below the top translucent activity are redrawn. When the last one is
+     * redrawn notify the top activity by calling
+     * {@link Activity#onTranslucentConversionComplete}.
+     *
+     * @param r The most recent background activity to be drawn. Or, if r is null then a timeout
+     * occurred and the activity will be notified immediately.
+     */
+    void notifyActivityDrawnLocked(ActivityRecord r) {
+        if ((r == null)
+                || (mUndrawnActivitiesBelowTopTranslucent.remove(r) &&
+                        mUndrawnActivitiesBelowTopTranslucent.isEmpty())) {
+            // The last undrawn activity below the top has just been drawn. If there is an
+            // opaque activity at the top, notify it that it can become translucent safely now.
+            final ActivityRecord waitingActivity = mTranslucentActivityWaiting;
+            mTranslucentActivityWaiting = null;
+            mUndrawnActivitiesBelowTopTranslucent.clear();
+            mHandler.removeMessages(TRANSLUCENT_TIMEOUT_MSG);
+
+            if (waitingActivity != null && waitingActivity.app != null &&
+                    waitingActivity.app.thread != null) {
+                try {
+                    waitingActivity.app.thread.scheduleTranslucentConversionComplete(
+                            waitingActivity.appToken, r != null);
+                } catch (RemoteException e) {
+                }
+            }
+        }
+    }
+
     /**
      * Ensure that the top activity in the stack is resumed.
      *
diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java
index 63f91ac..d39798d 100644
--- a/services/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2475,8 +2475,7 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case IDLE_TIMEOUT_MSG: {
-                    if (DEBUG_IDLE) Slog.d(TAG, "handleMessage: IDLE_TIMEOUT_MSG: Callers=" +
-                            Debug.getCallers(4));
+                    if (DEBUG_IDLE) Slog.d(TAG, "handleMessage: IDLE_TIMEOUT_MSG: r=" + msg.obj);
                     if (mService.mDidDexOpt) {
                         mService.mDidDexOpt = false;
                         Message nmsg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG);
@@ -2489,6 +2488,7 @@
                     activityIdleInternal((ActivityRecord)msg.obj);
                 } break;
                 case IDLE_NOW_MSG: {
+                    if (DEBUG_IDLE) Slog.d(TAG, "handleMessage: IDLE_NOW_MSG: r=" + msg.obj);
                     activityIdleInternal((ActivityRecord)msg.obj);
                 } break;
                 case RESUME_TOP_ACTIVITY_MSG: {
diff --git a/services/java/com/android/server/pm/KeySetManager.java b/services/java/com/android/server/pm/KeySetManager.java
index 3480b19..93992c2 100644
--- a/services/java/com/android/server/pm/KeySetManager.java
+++ b/services/java/com/android/server/pm/KeySetManager.java
@@ -69,7 +69,7 @@
         mPackages = packages;
     }
 
-    /*
+    /**
      * Determine if a package is signed by the given KeySet.
      *
      * Returns false if the package was not signed by all the
@@ -94,7 +94,7 @@
         }
     }
 
-    /*
+    /**
      * This informs the system that the given package has defined a KeySet
      * in its manifest that a) contains the given keys and b) is named
      * alias by that package.
@@ -116,7 +116,7 @@
         }
     }
 
-    /*
+    /**
      * Similar to the above, this informs the system that the given package
      * was signed by the provided KeySet.
      */
@@ -153,10 +153,9 @@
         }
     }
 
-    /*
-     * Fetches the stable identifier associated with the given KeySet.
-     *
-     * Returns KEYSET_NOT_FOUND if the KeySet... wasn't found.
+    /**
+     * Fetches the stable identifier associated with the given KeySet. Returns
+     * {@link #KEYSET_NOT_FOUND} if the KeySet... wasn't found.
      */
     public long getIdByKeySet(KeySet ks) {
         synchronized (mLockObject) {
@@ -174,10 +173,11 @@
         return KEYSET_NOT_FOUND;
     }
 
-    /*
+    /**
      * Fetches the KeySet corresponding to the given stable identifier.
      *
-     * Returns KEYSET_NOT_FOUND if the identifier doesn't identify a KeySet.
+     * Returns {@link #KEYSET_NOT_FOUND} if the identifier doesn't
+     * identify a {@link KeySet}.
      */
     public KeySet getKeySetById(long id) {
         synchronized (mLockObject) {
@@ -185,7 +185,7 @@
         }
     }
 
-    /*
+    /**
      * Fetches the KeySet that a given package refers to by the provided alias.
      *
      * If the package isn't known to us, throws an IllegalArgumentException.
@@ -205,10 +205,9 @@
         }
     }
 
-    /*
-     * Fetches all the known KeySets that signed the given package.
-     *
-     * If the package is unknown to us, throws an IllegalArgumentException.
+    /**
+     * Fetches all the known {@link KeySet KeySets} that signed the given
+     * package. Returns {@code null} if package is unknown.
      */
     public Set<KeySet> getSigningKeySetsByPackageName(String packageName) {
         synchronized (mLockObject) {
@@ -227,16 +226,16 @@
         }
     }
 
-    /*
+    /**
      * Creates a new KeySet corresponding to the given keys.
      *
-     * If the PublicKeys aren't known to the system, this adds them. Otherwise,
-     * they're deduped.
+     * If the {@link PublicKey PublicKeys} aren't known to the system, this
+     * adds them. Otherwise, they're deduped.
      *
      * If the KeySet isn't known to the system, this adds that and creates the
      * mapping to the PublicKeys. If it is known, then it's deduped.
      *
-     * Throws if the provided set is null.
+     * Throws if the provided set is {@code null}.
      */
     private KeySet addKeySetLocked(Set<PublicKey> keys) {
         if (keys == null) {
@@ -267,7 +266,7 @@
         return ks;
     }
 
-    /*
+    /**
      * Adds the given PublicKey to the system, deduping as it goes.
      */
     private long addPublicKeyLocked(PublicKey key) {
@@ -284,7 +283,7 @@
         return id;
     }
 
-    /*
+    /**
      * Finds the stable identifier for a KeySet based on a set of PublicKey stable IDs.
      *
      * Returns KEYSET_NOT_FOUND if there isn't one.
@@ -299,7 +298,7 @@
         return KEYSET_NOT_FOUND;
     }
 
-    /*
+    /**
      * Finds the stable identifier for a PublicKey or PUBLIC_KEY_NOT_FOUND.
      */
     private long getIdForPublicKeyLocked(PublicKey k) {
@@ -314,7 +313,7 @@
         return PUBLIC_KEY_NOT_FOUND;
     }
 
-    /*
+    /**
      * Gets an unused stable identifier for a KeySet.
      */
     private long getFreeKeySetIDLocked() {
@@ -322,7 +321,7 @@
         return lastIssuedKeySetId;
     }
 
-    /*
+    /**
      * Same as above, but for public keys.
      */
     private long getFreePublicKeyIdLocked() {
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index ee2ef37..50d267f 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -25,6 +25,7 @@
 import android.app.AppOpsManager;
 import android.util.TimeUtils;
 import android.view.IWindowId;
+
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.policy.PolicyManager;
 import com.android.internal.policy.impl.PhoneWindowManager;
@@ -4117,10 +4118,11 @@
         }
     }
 
-    public void setAppFullscreen(IBinder token) {
+    public void setAppFullscreen(IBinder token, boolean toOpaque) {
         AppWindowToken atoken = findAppWindowToken(token);
         if (atoken != null) {
-            atoken.appFullscreen = true;
+            atoken.appFullscreen = toOpaque;
+            requestTraversal();
         }
     }
 
@@ -7020,6 +7022,7 @@
 
         public static final int CLIENT_FREEZE_TIMEOUT = 30;
         public static final int TAP_OUTSIDE_STACK = 31;
+        public static final int NOTIFY_ACTIVITY_DRAWN = 32;
 
         @Override
         public void handleMessage(Message msg) {
@@ -7452,6 +7455,13 @@
                         }
                     }
                 }
+                break;
+                case NOTIFY_ACTIVITY_DRAWN:
+                    try {
+                        mActivityManager.notifyActivityDrawn((IBinder) msg.obj);
+                    } catch (RemoteException e) {
+                    }
+                    break;
             }
             if (DEBUG_WINDOW_TRACE) {
                 Slog.v(TAG, "handleMessage: exit");
@@ -8759,6 +8769,7 @@
                                 + " interesting=" + numInteresting
                                 + " drawn=" + wtoken.numDrawnWindows);
                         wtoken.allDrawn = true;
+                        mH.obtainMessage(H.NOTIFY_ACTIVITY_DRAWN, wtoken.token).sendToTarget();
                     }
                 }
             }
diff --git a/tests/CanvasCompare/res/layout/manual_layout.xml b/tests/CanvasCompare/res/layout/manual_layout.xml
index d7838eb..1a9288c 100644
--- a/tests/CanvasCompare/res/layout/manual_layout.xml
+++ b/tests/CanvasCompare/res/layout/manual_layout.xml
@@ -64,7 +64,7 @@
         </LinearLayout>
     </LinearLayout>
 
-    <com.android.test.hwuicompare.NearestImageView
+    <ImageView
         android:id="@+id/compare_image_view"
         android:layout_width="@dimen/layer_width_double"
         android:layout_height="@dimen/layer_height_double"
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java
index 400dff0..405ff65 100644
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java
+++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java
@@ -82,6 +82,7 @@
                         mCompareImageView.setImageBitmap(mCompareBitmap);
                         break;
                 }
+                mCompareImageView.getDrawable().setFilterBitmap(false);
                 mCompareImageView.invalidate();
             }
 
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/NearestImageView.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/NearestImageView.java
deleted file mode 100644
index 542b55a..0000000
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/NearestImageView.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2013 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.test.hwuicompare;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.PaintFlagsDrawFilter;
-import android.util.AttributeSet;
-import android.widget.ImageView;
-
-public class NearestImageView extends ImageView {
-    public NearestImageView(Context context) {
-        super(context);
-    }
-
-    public NearestImageView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public NearestImageView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-    final PaintFlagsDrawFilter mFilter = new PaintFlagsDrawFilter(Paint.FILTER_BITMAP_FLAG, 0);
-
-    @Override
-    public void onDraw(Canvas canvas) {
-        canvas.setDrawFilter(mFilter);
-        super.onDraw(canvas);
-        canvas.setDrawFilter(null);
-    }
-}
\ No newline at end of file
diff --git a/tests/TransitionTests/AndroidManifest.xml b/tests/TransitionTests/AndroidManifest.xml
index 5483f64..9a399d0 100644
--- a/tests/TransitionTests/AndroidManifest.xml
+++ b/tests/TransitionTests/AndroidManifest.xml
@@ -233,6 +233,13 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+        <activity android:label="CrossfadeImage"
+                  android:name=".CrossfadeImage">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
 
     </application>
 
diff --git a/tests/TransitionTests/res/layout/crossfade_image.xml b/tests/TransitionTests/res/layout/crossfade_image.xml
new file mode 100644
index 0000000..c46327a
--- /dev/null
+++ b/tests/TransitionTests/res/layout/crossfade_image.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:id="@+id/container"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+    <ImageView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/self_portrait_square_100"
+            android:onClick="sendMessage"
+            android:id="@+id/contact_picture"/>
+    <ImageView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/self_portrait_square_100"
+            android:onClick="sendMessage"
+            android:id="@+id/contact_picture1"/>
+    <ImageView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/self_portrait_square_100"
+            android:onClick="sendMessage"
+            android:id="@+id/contact_picture2"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/src/com/android/transitiontests/CrossfadeImage.java b/tests/TransitionTests/src/com/android/transitiontests/CrossfadeImage.java
new file mode 100644
index 0000000..28e055f
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/CrossfadeImage.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2013 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.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Crossfade;
+import android.view.transition.Move;
+import android.view.transition.Scene;
+import android.view.transition.Transition;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import android.widget.ImageView;
+
+public class CrossfadeImage extends Activity {
+    ViewGroup mSceneRoot;
+    static int mCurrentScene;
+    Scene mScene1, mScene2;
+    TransitionManager mTransitionManager;
+    boolean mExpanded = false;
+    Transition mTransition;
+    ImageView mImageView;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.crossfade_image);
+
+        ViewGroup container = (ViewGroup) findViewById(R.id.container);
+        mSceneRoot = container;
+
+        mImageView = (ImageView) findViewById(R.id.contact_picture);
+        mImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
+
+        Crossfade mCrossfade = new Crossfade();
+        mCrossfade.setTargetIds(R.id.contact_picture);
+
+        TransitionGroup group = new TransitionGroup();
+        group.setDuration(1500);
+        group.addTransitions(mCrossfade, new Move());
+        mTransition = group;
+    }
+
+    public void sendMessage(View view) {
+        TransitionManager.beginDelayedTransition(mSceneRoot, mTransition);
+        if (mExpanded) {
+            mImageView.setImageResource(R.drawable.self_portrait_square_100);
+        } else {
+            mImageView.setImageResource(R.drawable.self_portrait_square_200);
+        }
+        mExpanded = !mExpanded;
+    }
+}