Initial implementation of new voice interaction API.

This gives a basic working implementation of a persist
running service that can start a voice interaction when
it wants, with the target activity(s) able to go through
the protocol to interact with it.  It may even work when
the screen is off by putting the activity manager in the
correct state to act like the screen is on.

Includes a sample app that is a voice interation service
and also has an activity it can launch.

Now that I have this initial implementation, I think I
want to rework some aspects of the API.

Change-Id: I7646d0af8fb4ac768c63a18fe3de43f8091f60e9
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
new file mode 100644
index 0000000..e3c0728
--- /dev/null
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 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.internal.app;
+
+import android.content.Intent;
+
+import com.android.internal.app.IVoiceInteractor;
+import android.service.voice.IVoiceInteractionService;
+import android.service.voice.IVoiceInteractionSession;
+
+interface IVoiceInteractionManagerService {
+    int startVoiceActivity(in Intent intent, String resolvedType, IVoiceInteractionService service,
+            IVoiceInteractionSession session, IVoiceInteractor interactor);
+}
diff --git a/core/java/com/android/internal/app/IVoiceInteractor.aidl b/core/java/com/android/internal/app/IVoiceInteractor.aidl
new file mode 100644
index 0000000..737906a
--- /dev/null
+++ b/core/java/com/android/internal/app/IVoiceInteractor.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014 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.internal.app;
+
+import android.os.Bundle;
+
+import com.android.internal.app.IVoiceInteractorCallback;
+import com.android.internal.app.IVoiceInteractorRequest;
+
+/**
+ * IPC interface for an application to perform calls through a VoiceInteractor.
+ */
+interface IVoiceInteractor {
+    IVoiceInteractorRequest startConfirmation(String callingPackage,
+            IVoiceInteractorCallback callback, String prompt, in Bundle extras);
+    IVoiceInteractorRequest startCommand(String callingPackage,
+            IVoiceInteractorCallback callback, String command, in Bundle extras);
+    boolean[] supportsCommands(String callingPackage, in String[] commands);
+}
diff --git a/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl b/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl
new file mode 100644
index 0000000..f5392e9
--- /dev/null
+++ b/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014 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.internal.app;
+
+import android.os.Bundle;
+
+import com.android.internal.app.IVoiceInteractorRequest;
+
+/**
+ * IPC interface for an application to receive callbacks from the voice system.
+ */
+oneway interface IVoiceInteractorCallback {
+    void deliverConfirmationResult(IVoiceInteractorRequest request, boolean confirmed,
+            in Bundle result);
+    void deliverCommandResult(IVoiceInteractorRequest request, in Bundle result);
+    void deliverCancel(IVoiceInteractorRequest request);
+}
diff --git a/core/java/com/android/internal/app/IVoiceInteractorRequest.aidl b/core/java/com/android/internal/app/IVoiceInteractorRequest.aidl
new file mode 100644
index 0000000..ce2902d
--- /dev/null
+++ b/core/java/com/android/internal/app/IVoiceInteractorRequest.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2014 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.internal.app;
+
+/**
+ * IPC interface identifying a request from an application calling through an IVoiceInteractor.
+ */
+interface IVoiceInteractorRequest {
+    void cancel();
+}
diff --git a/core/java/com/android/internal/os/HandlerCaller.java b/core/java/com/android/internal/os/HandlerCaller.java
index d9e3ef6..40834ba 100644
--- a/core/java/com/android/internal/os/HandlerCaller.java
+++ b/core/java/com/android/internal/os/HandlerCaller.java
@@ -85,7 +85,27 @@
     public void sendMessage(Message msg) {
         mH.sendMessage(msg);
     }
-    
+
+    public SomeArgs sendMessageAndWait(Message msg) {
+        if (Looper.myLooper() == mH.getLooper()) {
+            throw new IllegalStateException("Can't wait on same thread as looper");
+        }
+        SomeArgs args = (SomeArgs)msg.obj;
+        args.mWaitState = SomeArgs.WAIT_WAITING;
+        mH.sendMessage(msg);
+        synchronized (args) {
+            while (args.mWaitState == SomeArgs.WAIT_WAITING) {
+                try {
+                    args.wait();
+                } catch (InterruptedException e) {
+                    return null;
+                }
+            }
+        }
+        args.mWaitState = SomeArgs.WAIT_NONE;
+        return args;
+    }
+
     public Message obtainMessage(int what) {
         return mH.obtainMessage(what);
     }
@@ -136,6 +156,14 @@
         return mH.obtainMessage(what, arg1, 0, args);
     }
     
+    public Message obtainMessageIOOO(int what, int arg1, Object arg2, Object arg3, Object arg4) {
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = arg2;
+        args.arg2 = arg3;
+        args.arg3 = arg4;
+        return mH.obtainMessage(what, arg1, 0, args);
+    }
+
     public Message obtainMessageOO(int what, Object arg1, Object arg2) {
         SomeArgs args = SomeArgs.obtain();
         args.arg1 = arg1;
@@ -161,6 +189,17 @@
         return mH.obtainMessage(what, 0, 0, args);
     }
     
+    public Message obtainMessageOOOOO(int what, Object arg1, Object arg2,
+            Object arg3, Object arg4, Object arg5) {
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = arg1;
+        args.arg2 = arg2;
+        args.arg3 = arg3;
+        args.arg4 = arg4;
+        args.arg5 = arg5;
+        return mH.obtainMessage(what, 0, 0, args);
+    }
+
     public Message obtainMessageIIII(int what, int arg1, int arg2,
             int arg3, int arg4) {
         SomeArgs args = SomeArgs.obtain();
diff --git a/core/java/com/android/internal/os/SomeArgs.java b/core/java/com/android/internal/os/SomeArgs.java
index 6fb72f1..7edf4cc 100644
--- a/core/java/com/android/internal/os/SomeArgs.java
+++ b/core/java/com/android/internal/os/SomeArgs.java
@@ -35,6 +35,11 @@
 
     private boolean mInPool;
 
+    static final int WAIT_NONE = 0;
+    static final int WAIT_WAITING = 1;
+    static final int WAIT_FINISHED = 2;
+    int mWaitState = WAIT_NONE;
+
     public Object arg1;
     public Object arg2;
     public Object arg3;
@@ -70,6 +75,9 @@
         if (mInPool) {
             throw new IllegalStateException("Already recycled.");
         }
+        if (mWaitState != WAIT_NONE) {
+            return;
+        }
         synchronized (sPoolLock) {
             clear();
             if (sPoolSize < MAX_POOL_SIZE) {
diff --git a/core/java/com/android/server/SystemService.java b/core/java/com/android/server/SystemService.java
index e374563..bf36bb1 100644
--- a/core/java/com/android/server/SystemService.java
+++ b/core/java/com/android/server/SystemService.java
@@ -123,6 +123,38 @@
     public void onBootPhase(int phase) {}
 
     /**
+     * Called when a new user is starting, for system services to initialize any per-user
+     * state they maintain for running users.
+     * @param userHandle The identifier of the user.
+     */
+    public void onStartUser(int userHandle) {}
+
+    /**
+     * Called when switching to a different foreground user, for system services that have
+     * special behavior for whichever user is currently in the foreground.  This is called
+     * before any application processes are aware of the new user.
+     * @param userHandle The identifier of the user.
+     */
+    public void onSwitchUser(int userHandle) {}
+
+    /**
+     * Called when an existing user is stopping, for system services to finalize any per-user
+     * state they maintain for running users.  This is called prior to sending the SHUTDOWN
+     * broadcast to the user; it is a good place to stop making use of any resources of that
+     * user (such as binding to a service running in the user).
+     * @param userHandle The identifier of the user.
+     */
+    public void onStopUser(int userHandle) {}
+
+    /**
+     * Called when an existing user is stopping, for system services to finalize any per-user
+     * state they maintain for running users.  This is called after all application process
+     * teardown of the user is complete.
+     * @param userHandle The identifier of the user.
+     */
+    public void onCleanupUser(int userHandle) {}
+
+    /**
      * Publish the service so it is accessible to other services and apps.
      */
     protected final void publishBinderService(String name, IBinder service) {
diff --git a/core/java/com/android/server/SystemServiceManager.java b/core/java/com/android/server/SystemServiceManager.java
index eb8df0e..87a50a9 100644
--- a/core/java/com/android/server/SystemServiceManager.java
+++ b/core/java/com/android/server/SystemServiceManager.java
@@ -131,6 +131,58 @@
         }
     }
 
+    public void startUser(final int userHandle) {
+        final int serviceLen = mServices.size();
+        for (int i = 0; i < serviceLen; i++) {
+            final SystemService service = mServices.get(i);
+            try {
+                service.onStartUser(userHandle);
+            } catch (Exception ex) {
+                Slog.wtf(TAG, "Failure reporting start of user " + userHandle
+                        + " to service " + service.getClass().getName(), ex);
+            }
+        }
+    }
+
+    public void switchUser(final int userHandle) {
+        final int serviceLen = mServices.size();
+        for (int i = 0; i < serviceLen; i++) {
+            final SystemService service = mServices.get(i);
+            try {
+                service.onSwitchUser(userHandle);
+            } catch (Exception ex) {
+                Slog.wtf(TAG, "Failure reporting switch of user " + userHandle
+                        + " to service " + service.getClass().getName(), ex);
+            }
+        }
+    }
+
+    public void stopUser(final int userHandle) {
+        final int serviceLen = mServices.size();
+        for (int i = 0; i < serviceLen; i++) {
+            final SystemService service = mServices.get(i);
+            try {
+                service.onStopUser(userHandle);
+            } catch (Exception ex) {
+                Slog.wtf(TAG, "Failure reporting stop of user " + userHandle
+                        + " to service " + service.getClass().getName(), ex);
+            }
+        }
+    }
+
+    public void cleanupUser(final int userHandle) {
+        final int serviceLen = mServices.size();
+        for (int i = 0; i < serviceLen; i++) {
+            final SystemService service = mServices.get(i);
+            try {
+                service.onCleanupUser(userHandle);
+            } catch (Exception ex) {
+                Slog.wtf(TAG, "Failure reporting cleanup of user " + userHandle
+                        + " to service " + service.getClass().getName(), ex);
+            }
+        }
+    }
+
     /** Sets the safe mode flag for services to query. */
     public void setSafeMode(boolean safeMode) {
         mSafeMode = safeMode;