Notify VrListenerService when VR activity changes.

Bug: 27536964
Bug: 22855417

Change-Id: I67e1f8e6595332b3d768a99735bbd5fd38dffdc9
diff --git a/api/current.txt b/api/current.txt
index a66aac3..5577c8b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -35022,6 +35022,7 @@
     ctor public VrListenerService();
     method public static final boolean isVrModePackageEnabled(android.content.Context, android.content.ComponentName);
     method public android.os.IBinder onBind(android.content.Intent);
+    method public void onCurrentVrActivityChanged(android.content.ComponentName);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.vr.VrListenerService";
   }
 
diff --git a/api/system-current.txt b/api/system-current.txt
index 676dd38..78d50d4 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -37565,6 +37565,7 @@
     ctor public VrListenerService();
     method public static final boolean isVrModePackageEnabled(android.content.Context, android.content.ComponentName);
     method public android.os.IBinder onBind(android.content.Intent);
+    method public void onCurrentVrActivityChanged(android.content.ComponentName);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.vr.VrListenerService";
   }
 
diff --git a/api/test-current.txt b/api/test-current.txt
index 879f467..5acbbb9 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -35093,6 +35093,7 @@
     ctor public VrListenerService();
     method public static final boolean isVrModePackageEnabled(android.content.Context, android.content.ComponentName);
     method public android.os.IBinder onBind(android.content.Intent);
+    method public void onCurrentVrActivityChanged(android.content.ComponentName);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.vr.VrListenerService";
   }
 
diff --git a/core/java/android/service/vr/IVrListener.aidl b/core/java/android/service/vr/IVrListener.aidl
index b7273ba..afb13d3 100644
--- a/core/java/android/service/vr/IVrListener.aidl
+++ b/core/java/android/service/vr/IVrListener.aidl
@@ -16,7 +16,9 @@
 
 package android.service.vr;
 
+import android.content.ComponentName;
+
 /** @hide */
 oneway interface IVrListener {
-
-}
\ No newline at end of file
+    void focusedActivityChanged(in ComponentName component);
+}
diff --git a/core/java/android/service/vr/VrListenerService.java b/core/java/android/service/vr/VrListenerService.java
index 5f1f659..9a5e95d 100644
--- a/core/java/android/service/vr/VrListenerService.java
+++ b/core/java/android/service/vr/VrListenerService.java
@@ -23,7 +23,10 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
 
 /**
  * A service that is bound from the system while running in virtual reality (VR) mode.
@@ -55,19 +58,53 @@
     @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
     public static final String SERVICE_INTERFACE = "android.service.vr.VrListenerService";
 
-    /**
-     * @hide
-     */
-    public static class VrListenerBinder extends IVrListener.Stub {
-    }
+    private final Handler mHandler;
 
-    private final VrListenerBinder mBinder = new VrListenerBinder();
+    private static final int MSG_ON_CURRENT_VR_ACTIVITY_CHANGED = 1;
+
+    private final IVrListener.Stub mBinder = new IVrListener.Stub() {
+        @Override
+        public void focusedActivityChanged(ComponentName component) {
+            mHandler.obtainMessage(MSG_ON_CURRENT_VR_ACTIVITY_CHANGED, component).sendToTarget();
+        }
+    };
+
+    private final class VrListenerHandler extends Handler {
+        public VrListenerHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_ON_CURRENT_VR_ACTIVITY_CHANGED: {
+                    VrListenerService.this.onCurrentVrActivityChanged((ComponentName) msg.obj);
+                } break;
+            }
+        }
+    }
 
     @Override
     public IBinder onBind(Intent intent) {
         return mBinder;
     }
 
+    public VrListenerService() {
+        mHandler = new VrListenerHandler(Looper.getMainLooper());
+    }
+
+    /**
+     * Called when the current activity using VR mode is changed.
+     * <p/>
+     * This will be called immediately when this service is initially bound, but is
+     * not guaranteed to be called before onUnbind.
+     *
+     * @param component the {@link ComponentName} of the new current VR activity.
+     */
+    public void onCurrentVrActivityChanged(ComponentName component) {
+        // Override to implement
+    }
+
     /**
      * Check if the given package is available to be enabled/disabled in VR mode settings.
      *
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4f0f770..fc74623 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2168,17 +2168,19 @@
                 final ActivityRecord r = (ActivityRecord) msg.obj;
                 boolean vrMode;
                 ComponentName requestedPackage;
+                ComponentName callingPackage;
                 int userId;
                 synchronized (ActivityManagerService.this) {
                     vrMode = r.requestedVrComponent != null;
                     requestedPackage = r.requestedVrComponent;
                     userId = r.userId;
+                    callingPackage = r.info.getComponentName();
                     if (mInVrMode != vrMode) {
                         mInVrMode = vrMode;
                         mShowDialogs = shouldShowDialogs(mConfiguration, mInVrMode);
                     }
                 }
-                vrService.setVrMode(vrMode, requestedPackage, userId);
+                vrService.setVrMode(vrMode, requestedPackage, userId, callingPackage);
             } break;
             }
         }
diff --git a/services/core/java/com/android/server/utils/ManagedApplicationService.java b/services/core/java/com/android/server/utils/ManagedApplicationService.java
index a645701..ad8acef0 100644
--- a/services/core/java/com/android/server/utils/ManagedApplicationService.java
+++ b/services/core/java/com/android/server/utils/ManagedApplicationService.java
@@ -60,6 +60,8 @@
     private ServiceConnection mPendingConnection;
     private ServiceConnection mConnection;
     private IInterface mBoundInterface;
+    private PendingEvent mPendingEvent;
+
 
 
     private ManagedApplicationService(final Context context, final ComponentName component,
@@ -82,6 +84,13 @@
     }
 
     /**
+     * Implement to call IInterface methods after service is connected.
+     */
+    public interface PendingEvent {
+         void runEvent(IInterface service) throws RemoteException;
+    }
+
+    /**
      * Create a new ManagedApplicationService object but do not yet bind to the user service.
      *
      * @param context a Context to use for binding the application service.
@@ -131,6 +140,30 @@
         return true;
     }
 
+
+  /**
+   * Send an event to run as soon as the binder interface is available.
+   *
+   * @param event a {@link PendingEvent} to send.
+   */
+  public void sendEvent(@NonNull PendingEvent event) {
+        IInterface iface;
+        synchronized (mLock) {
+            iface = mBoundInterface;
+            if (iface == null) {
+                mPendingEvent = event;
+            }
+        }
+
+        if (iface != null) {
+            try {
+                event.runEvent(iface);
+            } catch (RuntimeException | RemoteException ex) {
+                Slog.e(TAG, "Received exception from user service: ", ex);
+            }
+        }
+    }
+
     /**
      * Asynchronously unbind from the application service if bound.
      */
@@ -168,6 +201,8 @@
             final ServiceConnection serviceConnection = new ServiceConnection() {
                 @Override
                 public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
+                    IInterface iface = null;
+                    PendingEvent pendingEvent = null;
                     synchronized (mLock) {
                         if (mPendingConnection == this) {
                             // No longer pending, remove from pending connection
@@ -186,12 +221,22 @@
                                 mContext.unbindService(this);
                                 mBoundInterface = null;
                             }
+                            iface = mBoundInterface;
+                            pendingEvent = mPendingEvent;
+                            mPendingEvent = null;
                         } catch (RemoteException e) {
                             // DOA
                             Slog.w(TAG, "Unable to bind service: " + intent, e);
                             mBoundInterface = null;
                         }
                     }
+                    if (iface != null && pendingEvent != null) {
+                        try {
+                            pendingEvent.runEvent(iface);
+                        } catch (RuntimeException | RemoteException ex) {
+                            Slog.e(TAG, "Received exception from user service: ", ex);
+                        }
+                    }
                 }
 
                 @Override
diff --git a/services/core/java/com/android/server/vr/VrManagerInternal.java b/services/core/java/com/android/server/vr/VrManagerInternal.java
index 6b5523f..8316efa 100644
--- a/services/core/java/com/android/server/vr/VrManagerInternal.java
+++ b/services/core/java/com/android/server/vr/VrManagerInternal.java
@@ -43,9 +43,10 @@
      * @param enabled {@code true} to enable VR mode.
      * @param packageName The package name of the requested VrListenerService to bind.
      * @param userId the user requesting the VrListenerService component.
+     * @param calling the component currently using VR mode, or null to leave unchanged.
      */
     public abstract void setVrMode(boolean enabled, @NonNull ComponentName packageName,
-            int userId);
+            int userId, @NonNull ComponentName calling);
 
     /**
      * Add a listener for VR mode state changes.
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index d0ee6e0..0aa3052 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -19,14 +19,12 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.content.ComponentName;
-import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IInterface;
 import android.os.Looper;
-import android.os.UserHandle;
+import android.os.RemoteException;
 import android.provider.Settings;
 import android.service.vr.IVrListener;
 import android.service.vr.VrListenerService;
@@ -35,11 +33,13 @@
 
 import com.android.internal.R;
 import com.android.server.SystemService;
+import com.android.server.utils.ManagedApplicationService.PendingEvent;
 import com.android.server.vr.EnabledComponentsObserver.EnabledComponentChangeListener;
 import com.android.server.utils.ManagedApplicationService;
 import com.android.server.utils.ManagedApplicationService.BinderChecker;
 
 import java.util.ArrayList;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -79,6 +79,8 @@
     private EnabledComponentsObserver mComponentObserver;
     private ManagedApplicationService mCurrentVrService;
     private Context mContext;
+    private ComponentName mCurrentVrModeComponent;
+    private int mCurrentVrModeUser;
 
     private static final BinderChecker sBinderChecker = new BinderChecker() {
         @Override
@@ -105,7 +107,7 @@
 
             // There is an active service, update it if needed
             updateCurrentVrServiceLocked(mVrModeEnabled, mCurrentVrService.getComponent(),
-                    mCurrentVrService.getUserId());
+                    mCurrentVrService.getUserId(), null);
         }
     }
 
@@ -119,8 +121,9 @@
         }
 
         @Override
-        public void setVrMode(boolean enabled, ComponentName packageName, int userId) {
-            VrManagerService.this.setVrMode(enabled, packageName, userId);
+        public void setVrMode(boolean enabled, ComponentName packageName, int userId,
+                ComponentName callingPackage) {
+            VrManagerService.this.setVrMode(enabled, packageName, userId, callingPackage);
         }
 
         @Override
@@ -227,11 +230,14 @@
      * @param enabled new state for VR mode.
      * @param component new component to be bound as a VR listener.
      * @param userId user owning the component to be bound.
+     * @param calling the component currently using VR mode, or null to leave unchanged.
      *
      * @return {@code true} if the component/user combination specified is valid.
      */
     private boolean updateCurrentVrServiceLocked(boolean enabled,
-            @NonNull ComponentName component, int userId) {
+            @NonNull ComponentName component, int userId, ComponentName calling) {
+
+        boolean sendUpdatedCaller = false;
 
         boolean validUserComponent = (mComponentObserver.isValid(component, userId) ==
                 EnabledComponentsObserver.NO_ERROR);
@@ -247,31 +253,49 @@
                 mCurrentVrService.disconnect();
                 mCurrentVrService = null;
             }
-            return validUserComponent;
+        } else {
+            if (mCurrentVrService != null) {
+                // Unbind any running service that doesn't match the component/user selection
+                if (mCurrentVrService.disconnectIfNotMatching(component, userId)) {
+                    Slog.i(TAG, "Disconnecting " + mCurrentVrService.getComponent() + " for user " +
+                        mCurrentVrService.getUserId());
+                    createAndConnectService(component, userId);
+                    sendUpdatedCaller = true;
+                }
+                // The service with the correct component/user is bound
+            } else {
+                // Nothing was previously running, bind a new service
+                createAndConnectService(component, userId);
+                sendUpdatedCaller = true;
+            }
         }
 
-        if (mCurrentVrService != null) {
-            // Unbind any running service that doesn't match the component/user selection
-            if (mCurrentVrService.disconnectIfNotMatching(component, userId)) {
-                Slog.i(TAG, "Disconnecting " + mCurrentVrService.getComponent() + " for user " +
-                        mCurrentVrService.getUserId());
-                mCurrentVrService = VrManagerService.create(mContext, component, userId);
-                mCurrentVrService.connect();
-                Slog.i(TAG, "Connecting " + mCurrentVrService.getComponent() + " for user " +
-                        mCurrentVrService.getUserId());
-            }
-            // The service with the correct component/user is bound
-        } else {
-            // Nothing was previously running, bind a new service
-            mCurrentVrService = VrManagerService.create(mContext, component, userId);
-            mCurrentVrService.connect();
-            Slog.i(TAG, "Connecting " + mCurrentVrService.getComponent() + " for user " +
-                    mCurrentVrService.getUserId());
+        if (calling != null && !Objects.equals(calling, mCurrentVrModeComponent))  {
+            mCurrentVrModeComponent = calling;
+            mCurrentVrModeUser = userId;
+            sendUpdatedCaller = true;
+        }
+
+        if (mCurrentVrService != null && sendUpdatedCaller) {
+            final ComponentName c = mCurrentVrModeComponent;
+            mCurrentVrService.sendEvent(new PendingEvent() {
+                @Override
+                public void runEvent(IInterface service) throws RemoteException {
+                    IVrListener l = (IVrListener) service;
+                    l.focusedActivityChanged(c);
+                }
+            });
         }
 
         return validUserComponent;
     }
 
+    private void createAndConnectService(@NonNull ComponentName component, int userId) {
+        mCurrentVrService = VrManagerService.create(mContext, component, userId);
+        mCurrentVrService.connect();
+        Slog.i(TAG, "Connecting " + component + " for user " + userId);
+    }
+
     /**
      * Send VR mode change callbacks to HAL and system services if mode has actually changed.
      * <p/>
@@ -319,9 +343,9 @@
      */
 
     private boolean setVrMode(boolean enabled, @NonNull ComponentName targetPackageName,
-            int userId) {
+            int userId, @NonNull ComponentName callingPackage) {
         synchronized (mLock) {
-            return updateCurrentVrServiceLocked(enabled, targetPackageName, userId);
+            return updateCurrentVrServiceLocked(enabled, targetPackageName, userId, callingPackage);
         }
     }