Remove dock divider surface when it's not visible.

We achieve the removal by notifying System UI about the visibility of
the dock divider. This way System UI can change visibility of the root
view, which in turn will cause the WMS to destroy or create the surface
as necessary.

Bug: 25844096
Bug: 25683717

Change-Id: Idbc33368db697a059af49106dfadb80c3d7d06c1
diff --git a/Android.mk b/Android.mk
index f5d5a11..d22273c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -261,6 +261,7 @@
 	core/java/android/view/IApplicationToken.aidl \
 	core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl \
 	core/java/android/view/IAssetAtlas.aidl \
+	core/java/android/view/IDockDividerVisibilityListener.aidl \
 	core/java/android/view/IGraphicsStats.aidl \
 	core/java/android/view/IInputFilter.aidl \
 	core/java/android/view/IInputFilterHost.aidl \
diff --git a/core/java/android/view/IDockDividerVisibilityListener.aidl b/core/java/android/view/IDockDividerVisibilityListener.aidl
new file mode 100644
index 0000000..a7d5cda
--- /dev/null
+++ b/core/java/android/view/IDockDividerVisibilityListener.aidl
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2015, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+/**
+  * Listener for showing/hiding of the dock divider. Will fire when an app is shown in side by side
+  * mode and a divider should be shown.
+  *
+  * @hide
+  */
+oneway interface IDockDividerVisibilityListener {
+    void onDockDividerVisibilityChanged(boolean visible);
+}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 64a046e..bd65532 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -29,6 +29,7 @@
 import android.os.IRemoteCallback;
 import android.view.IApplicationToken;
 import android.view.IAppTransitionAnimationSpecsFuture;
+import android.view.IDockDividerVisibilityListener;
 import android.view.IOnKeyguardExitResult;
 import android.view.IRotationWatcher;
 import android.view.IWindowSession;
@@ -290,10 +291,10 @@
     /**
      * Create a screenshot of the applications currently displayed.
      *
-     * @param frameScale the scale to apply to the frame, only used when width = -1 and 
+     * @param frameScale the scale to apply to the frame, only used when width = -1 and
      *                   height = -1
      */
-    Bitmap screenshotApplications(IBinder appToken, int displayId, int maxWidth, int maxHeight, 
+    Bitmap screenshotApplications(IBinder appToken, int displayId, int maxWidth, int maxHeight,
             float frameScale);
 
     /**
@@ -348,4 +349,9 @@
      * stack size.
      */
     void setDockedStackResizing(boolean resizing);
+
+    /**
+     * Registers a listener that will be called when the dock divider changes its visibility.
+     */
+    void registerDockDividerVisibilityListener(IDockDividerVisibilityListener listener);
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 57338be..568b601 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1877,6 +1877,11 @@
     <permission android:name="android.permission.MANAGE_APP_TOKENS"
         android:protectionLevel="signature" />
 
+    <!-- Allows System UI to register listeners for events from Window Manager.
+         @hide -->
+    <permission android:name="android.permission.REGISTER_WINDOW_MANAGER_LISTENERS"
+        android:protectionLevel="signature" />
+
     <!-- @hide Allows the application to temporarily freeze the screen for a
          full-screen transition. -->
     <permission android:name="android.permission.FREEZE_SCREEN"
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 6fda2c6..2f79adf 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -87,6 +87,7 @@
     <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
     <uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
     <uses-permission android:name="android.permission.MANAGE_APP_TOKENS" />
+    <uses-permission android:name="android.permission.REGISTER_WINDOW_MANAGER_LISTENERS" />
     <uses-permission android:name="android.permission.SET_ORIENTATION" />
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index de40a37..59fb6cf 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -56,6 +56,7 @@
 import android.util.MutableBoolean;
 import android.util.Pair;
 import android.view.Display;
+import android.view.IDockDividerVisibilityListener;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityManager;
@@ -847,4 +848,15 @@
             e.printStackTrace();
         }
     }
+
+    public void registerDockDividerVisibilityListener(IDockDividerVisibilityListener listener) {
+        if (mWm == null) return;
+
+        try {
+            WindowManagerGlobal.getWindowManagerService().registerDockDividerVisibilityListener(
+                    listener);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index 50e010f..6ff7a3e 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -17,10 +17,14 @@
 package com.android.systemui.stackdivider;
 
 import android.content.res.Configuration;
+import android.view.IDockDividerVisibilityListener;
 import android.view.LayoutInflater;
+import android.view.View;
 
 import com.android.systemui.R;
 import com.android.systemui.SystemUI;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.misc.SystemServicesProxy;
 
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
@@ -33,6 +37,8 @@
     private int mDividerWindowWidth;
     private DividerWindowManager mWindowManager;
     private DividerView mView;
+    private DockDividerVisibilityListener mDockDividerVisibilityListener;
+    private boolean mVisible = false;
 
     @Override
     public void start() {
@@ -41,6 +47,9 @@
                 com.android.internal.R.dimen.docked_stack_divider_thickness);
         update(mContext.getResources().getConfiguration());
         putComponent(Divider.class, this);
+        mDockDividerVisibilityListener = new DockDividerVisibilityListener();
+        SystemServicesProxy ssp = Recents.getSystemServices();
+        ssp.registerDockDividerVisibilityListener(mDockDividerVisibilityListener);
     }
 
     @Override
@@ -56,6 +65,7 @@
     private void addDivider(Configuration configuration) {
         mView = (DividerView)
                 LayoutInflater.from(mContext).inflate(R.layout.docked_stack_divider, null);
+        mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE);
         final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE;
         final int width = landscape ? mDividerWindowWidth : MATCH_PARENT;
         final int height = landscape ? MATCH_PARENT : mDividerWindowWidth;
@@ -71,4 +81,23 @@
         removeDivider();
         addDivider(configuration);
     }
+
+    private void updateVisibility(final boolean visible) {
+        mView.post(new Runnable() {
+            @Override
+            public void run() {
+                if (mVisible != visible) {
+                    mVisible = visible;
+                    mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+                }
+            }
+        });
+    }
+
+    class DockDividerVisibilityListener extends IDockDividerVisibilityListener.Stub {
+        @Override
+        public void onDockDividerVisibilityChanged(boolean visible) {
+            updateVisibility(visible);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index ae6874f..639753a 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2533,7 +2533,7 @@
                 }
             }
         } else if (win.getAttrs().type == TYPE_DOCK_DIVIDER) {
-            if (transit == TRANSIT_ENTER) {
+            if (transit == TRANSIT_ENTER || transit == TRANSIT_SHOW) {
                 return R.anim.fade_in;
             } else if (transit == TRANSIT_EXIT) {
                 return R.anim.fade_out;
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 6b62467..df8d5d6 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -18,7 +18,11 @@
 
 import android.content.Context;
 import android.graphics.Rect;
+import android.os.RemoteException;
 import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.view.IDockDividerVisibilityListener;
 
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.view.WindowManager.DOCKED_BOTTOM;
@@ -40,6 +44,8 @@
     private WindowState mWindow;
     private final Rect mTmpRect = new Rect();
     private final Rect mLastRect = new Rect();
+    private IDockDividerVisibilityListener mListener;
+    private boolean mLastVisibility = false;
 
     DockedStackDividerController(Context context, DisplayContent displayContent) {
         mDisplayContent = displayContent;
@@ -67,12 +73,21 @@
     }
 
     void reevaluateVisibility() {
-        if (mWindow == null) return;
+        if (mWindow == null) {
+            return;
+        }
         TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(DOCKED_STACK_ID);
-        if (stack != null && stack.isVisibleLocked()) {
-            mWindow.showLw(true /* doAnimation */);
-        } else {
-            mWindow.hideLw(true /* doAnimation */);
+        final boolean visible = stack != null && stack.isVisibleLocked();
+        if (mLastVisibility == visible) {
+            return;
+        }
+        mLastVisibility = visible;
+        if (mListener != null) {
+            try {
+                mListener.onDockDividerVisibilityChanged(visible);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "visibility call failed: " + e);
+            }
         }
     }
 
@@ -110,4 +125,11 @@
         }
         mLastRect.set(frame);
     }
+
+    public void registerDockDividerVisibilityListener(IDockDividerVisibilityListener listener) {
+        if (mListener != null && listener != null) {
+            throw new IllegalStateException("Dock divider visibility listener already set!");
+        }
+        mListener = listener;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ac90daf..395962c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -121,6 +121,7 @@
 import android.view.Gravity;
 import android.view.IApplicationToken;
 import android.view.IAppTransitionAnimationSpecsFuture;
+import android.view.IDockDividerVisibilityListener;
 import android.view.IInputFilter;
 import android.view.IOnKeyguardExitResult;
 import android.view.IRotationWatcher;
@@ -2689,8 +2690,7 @@
                     // need to see about starting one.
                     final boolean notExitingOrAnimating =
                             !win.mExiting && !win.isAnimatingWithSavedSurface();
-                    result |= notExitingOrAnimating
-                            ? RELAYOUT_RES_SURFACE_CHANGED : 0;
+                    result |= notExitingOrAnimating ? RELAYOUT_RES_SURFACE_CHANGED : 0;
                     if (notExitingOrAnimating) {
                         focusMayChange = tryStartingAnimation(win, winAnimator, isDefaultDisplay,
                                 focusMayChange);
@@ -3075,7 +3075,7 @@
         // TODO:
     }
 
-    boolean checkCallingPermission(String permission, String func) {
+    private boolean checkCallingPermission(String permission, String func) {
         // Quick check: if the calling permission is me, it's all okay.
         if (Binder.getCallingPid() == Process.myPid()) {
             return true;
@@ -10211,6 +10211,29 @@
         mDestroySurface.add(win);
     }
 
+    @Override
+    public void registerDockDividerVisibilityListener(IDockDividerVisibilityListener listener) {
+        if (!checkCallingPermission(android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS,
+                "registerDockDividerVisibilityListener()")) {
+            return;
+        }
+        // TODO(multi-display): The listener is registered on the default display only.
+        final DockedStackDividerController controller =
+                getDefaultDisplayContentLocked().getDockedDividerController();
+        controller.registerDockDividerVisibilityListener(listener);
+        try {
+            listener.asBinder().linkToDeath(new IBinder.DeathRecipient() {
+                @Override
+                public void binderDied() {
+                    getDefaultDisplayContentLocked().getDockedDividerController()
+                            .registerDockDividerVisibilityListener(null);
+                }
+            }, 0);
+        } catch (RemoteException e) {
+            controller.registerDockDividerVisibilityListener(null);
+        }
+    }
+
     private final class LocalService extends WindowManagerInternal {
         @Override
         public void requestTraversalFromDisplayManager() {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 064b412..29cadf3 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -730,7 +730,7 @@
             mVisibleFrame.set(mContentFrame);
             mStableFrame.set(mContentFrame);
         } else if (mAttrs.type == TYPE_DOCK_DIVIDER) {
-            if (isVisibleLw()) {
+            if (isVisibleLw() || mWinAnimator.isAnimating()) {
                 // We don't adjust the dock divider frame for reasons other than performance. The
                 // real reason is that if it gets adjusted before it is shown for the first time,
                 // it would get size (0, 0). This causes a problem when we finally show the dock
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 6951ede..eea254b 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -541,4 +541,8 @@
     @Override
     public void endProlongedAnimations() {
     }
+
+    @Override
+    public void registerDockDividerVisibilityListener(IDockDividerVisibilityListener listener) {
+    }
 }