Add more internal API's for assist

- When starting a voice interaction session from SysUI, a callback
can be passed to know when voice interaction has successfully started
- Add a new window type for the voice interaction starting window,
which resides behind the voice interaction layer.
- SystemUI now also inspects the voice interaction service to get the
logo asset for the starting window.
- Make VoiceInteractionSession window fullscreen, to accomodate for
the visuals.

Change-Id: If8c3c445e8b39841f48e8d153e6d1ba81e447286
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 958caea..cfea86f 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1922,6 +1922,7 @@
         case TYPE_PHONE:
             return 3;
         case TYPE_SEARCH_BAR:
+        case TYPE_VOICE_INTERACTION_STARTING:
             return 4;
         case TYPE_VOICE_INTERACTION:
             // voice interaction layer is almost immediately above apps.
@@ -2270,16 +2271,9 @@
                 if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar);
                 break;
             case TYPE_NAVIGATION_BAR_PANEL:
-                mContext.enforceCallingOrSelfPermission(
-                        android.Manifest.permission.STATUS_BAR_SERVICE,
-                        "PhoneWindowManager");
-                break;
             case TYPE_STATUS_BAR_PANEL:
-                mContext.enforceCallingOrSelfPermission(
-                        android.Manifest.permission.STATUS_BAR_SERVICE,
-                        "PhoneWindowManager");
-                break;
             case TYPE_STATUS_BAR_SUB_PANEL:
+            case TYPE_VOICE_INTERACTION_STARTING:
                 mContext.enforceCallingOrSelfPermission(
                         android.Manifest.permission.STATUS_BAR_SERVICE,
                         "PhoneWindowManager");
@@ -3657,7 +3651,7 @@
                 pf.bottom = df.bottom = of.bottom = cf.bottom
                         = mOverscanScreenTop + mOverscanScreenHeight;
             }
-        } else  if (attrs.type == TYPE_INPUT_METHOD || attrs.type == TYPE_VOICE_INTERACTION) {
+        } else if (attrs.type == TYPE_INPUT_METHOD) {
             pf.left = df.left = of.left = cf.left = vf.left = mDockLeft;
             pf.top = df.top = of.top = cf.top = vf.top = mDockTop;
             pf.right = df.right = of.right = cf.right = vf.right = mDockRight;
@@ -3668,6 +3662,15 @@
             // IM dock windows always go to the bottom of the screen.
             attrs.gravity = Gravity.BOTTOM;
             mDockLayer = win.getSurfaceLayer();
+        } else if (attrs.type == TYPE_VOICE_INTERACTION) {
+            pf.left = df.left = of.left = cf.left = vf.left = mUnrestrictedScreenLeft;
+            pf.top = df.top = of.top = mUnrestrictedScreenTop;
+            pf.right = df.right = of.right = cf.right = vf.right = mUnrestrictedScreenLeft
+                    + mUnrestrictedScreenWidth;
+            pf.bottom = df.bottom = of.bottom = cf.bottom = mUnrestrictedScreenTop
+                    + mUnrestrictedScreenHeight;
+            cf.bottom = vf.bottom = mStableBottom;
+            cf.top = vf.top = mStableTop;
         } else if (win == mStatusBar && (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
             pf.left = df.left = of.left = mUnrestrictedScreenLeft;
             pf.top = df.top = of.top = mUnrestrictedScreenTop;
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index d8b9140..1203735 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -26,6 +26,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.database.ContentObserver;
@@ -50,6 +51,7 @@
 import android.util.Slog;
 
 import com.android.internal.app.IVoiceInteractionManagerService;
+import com.android.internal.app.IVoiceInteractionSessionShowCallback;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.os.BackgroundThread;
@@ -396,7 +398,8 @@
                 final int callingUid = Binder.getCallingUid();
                 final long caller = Binder.clearCallingIdentity();
                 try {
-                    mImpl.showSessionLocked(callingPid, callingUid, args, flags);
+                    mImpl.showSessionLocked(callingPid, callingUid, args, flags,
+                            null /* showCallback */);
                 } finally {
                     Binder.restoreCallingIdentity(caller);
                 }
@@ -434,7 +437,8 @@
                 final int callingUid = Binder.getCallingUid();
                 final long caller = Binder.clearCallingIdentity();
                 try {
-                    return mImpl.showSessionLocked(callingPid, callingUid, sessionArgs, flags);
+                    return mImpl.showSessionLocked(callingPid, callingUid, sessionArgs, flags,
+                            null /* showCallback */);
                 } finally {
                     Binder.restoreCallingIdentity(caller);
                 }
@@ -720,7 +724,20 @@
         }
 
         @Override
-        public void showSessionForActiveService() {
+        public ComponentName getActiveServiceComponentName() {
+            synchronized (this) {
+                if (mContext.checkCallingPermission(
+                        Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE)
+                        != PackageManager.PERMISSION_GRANTED) {
+                    throw new SecurityException("Caller does not hold the permission "
+                            + Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE);
+                }
+                return mImpl != null ? mImpl.mComponent : null;
+            }
+        }
+
+        @Override
+        public void showSessionForActiveService(IVoiceInteractionSessionShowCallback showCallback) {
             synchronized (this) {
                 if (mContext.checkCallingPermission(
                         Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE)
@@ -740,7 +757,8 @@
                     mImpl.showSessionLocked(callingPid, callingUid, new Bundle() /* sessionArgs */,
                             VoiceInteractionService.START_SOURCE_SYSTEM
                                     | VoiceInteractionService.START_WITH_ASSIST
-                                    | VoiceInteractionService.START_WITH_SCREENSHOT);
+                                    | VoiceInteractionService.START_WITH_SCREENSHOT,
+                            showCallback);
                 } finally {
                     Binder.restoreCallingIdentity(caller);
                 }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 5a91b88..1aa0d0b 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -39,6 +39,7 @@
 import android.util.Slog;
 import android.view.IWindowManager;
 
+import com.android.internal.app.IVoiceInteractionSessionShowCallback;
 import com.android.internal.app.IVoiceInteractor;
 
 import java.io.FileDescriptor;
@@ -134,12 +135,13 @@
         mContext.registerReceiver(mBroadcastReceiver, filter, null, handler);
     }
 
-    public boolean showSessionLocked(int callingPid, int callingUid, Bundle args, int flags) {
+    public boolean showSessionLocked(int callingPid, int callingUid, Bundle args, int flags,
+            IVoiceInteractionSessionShowCallback showCallback) {
         if (mActiveSession == null) {
             mActiveSession = new VoiceInteractionSessionConnection(mLock, mSessionComponentName,
                     mUser, mContext, this, callingPid, callingUid);
         }
-        return mActiveSession.showLocked(args, flags);
+        return mActiveSession.showLocked(args, flags, showCallback);
     }
 
     public boolean hideSessionLocked(int callingPid, int callingUid) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index 7a379c2..73c7363 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -41,10 +41,12 @@
 import android.view.IWindowManager;
 import android.view.WindowManager;
 import com.android.internal.app.IAssistScreenshotReceiver;
+import com.android.internal.app.IVoiceInteractionSessionShowCallback;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.os.IResultReceiver;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
 
 final class VoiceInteractionSessionConnection implements ServiceConnection {
     final static String TAG = "VoiceInteractionServiceManager";
@@ -74,6 +76,26 @@
     Bundle mAssistData;
     boolean mHaveScreenshot;
     Bitmap mScreenshot;
+    ArrayList<IVoiceInteractionSessionShowCallback> mPendingShowCallbacks = new ArrayList<>();
+
+    IVoiceInteractionSessionShowCallback mShowCallback =
+            new IVoiceInteractionSessionShowCallback.Stub() {
+        @Override
+        public void onFailed() throws RemoteException {
+            synchronized (mLock) {
+                notifyPendingShowCallbacksFailedLocked();
+            }
+        }
+
+        @Override
+        public void onShown() throws RemoteException {
+            synchronized (mLock) {
+                // TODO: Figure out whether this is good enough or whether we need to hook into
+                // Window manager to actually wait for the window to be drawn.
+                notifyPendingShowCallbacksShownLocked();
+            }
+        }
+    };
 
     public interface Callback {
         public void sessionConnectionGone(VoiceInteractionSessionConnection connection);
@@ -151,7 +173,8 @@
         }
     }
 
-    public boolean showLocked(Bundle args, int flags) {
+    public boolean showLocked(Bundle args, int flags,
+            IVoiceInteractionSessionShowCallback showCallback) {
         if (mBound) {
             if (!mFullyBound) {
                 mFullyBound = mContext.bindServiceAsUser(mBindIntent, mFullConnection,
@@ -182,15 +205,23 @@
             }
             if (mSession != null) {
                 try {
-                    mSession.show(mShowArgs, mShowFlags);
+                    mSession.show(mShowArgs, mShowFlags, showCallback);
                     mShowArgs = null;
                     mShowFlags = 0;
                 } catch (RemoteException e) {
                 }
                 deliverSessionDataLocked();
+            } else if (showCallback != null) {
+                mPendingShowCallbacks.add(showCallback);
             }
             return true;
         }
+        if (showCallback != null) {
+            try {
+                showCallback.onFailed();
+            } catch (RemoteException e) {
+            }
+        }
         return false;
     }
 
@@ -320,7 +351,7 @@
         mInteractor = interactor;
         if (mShown) {
             try {
-                session.show(mShowArgs, mShowFlags);
+                session.show(mShowArgs, mShowFlags, mShowCallback);
                 mShowArgs = null;
                 mShowFlags = 0;
             } catch (RemoteException e) {
@@ -330,6 +361,26 @@
         return true;
     }
 
+    private void notifyPendingShowCallbacksShownLocked() {
+        for (int i = 0; i < mPendingShowCallbacks.size(); i++) {
+            try {
+                mPendingShowCallbacks.get(i).onShown();
+            } catch (RemoteException e) {
+            }
+        }
+        mPendingShowCallbacks.clear();
+    }
+
+    private void notifyPendingShowCallbacksFailedLocked() {
+        for (int i = 0; i < mPendingShowCallbacks.size(); i++) {
+            try {
+                mPendingShowCallbacks.get(i).onFailed();
+            } catch (RemoteException e) {
+            }
+        }
+        mPendingShowCallbacks.clear();
+    }
+
     @Override
     public void onServiceConnected(ComponentName name, IBinder service) {
         synchronized (mLock) {