diff options
17 files changed, 536 insertions, 211 deletions
diff --git a/api/current.txt b/api/current.txt index de5b1e6..00ee7a8 100644 --- a/api/current.txt +++ b/api/current.txt @@ -27656,7 +27656,7 @@ package android.service.voice { method public android.os.IBinder onBind(android.content.Intent); method public void onReady(); method public void onShutdown(); - method public void startSession(android.os.Bundle, int); + method public void showSession(android.os.Bundle, int); field public static final java.lang.String SERVICE_INTERFACE = "android.service.voice.VoiceInteractionService"; field public static final java.lang.String SERVICE_META_DATA = "android.voice_interaction"; field public static final int START_WITH_ASSIST = 1; // 0x1 @@ -27668,6 +27668,7 @@ package android.service.voice { method public void finish(); method public android.view.LayoutInflater getLayoutInflater(); method public android.app.Dialog getWindow(); + method public void hide(); method public void hideWindow(); method public void onAbortVoice(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.CharSequence, android.os.Bundle); method public void onBackPressed(); @@ -27682,14 +27683,17 @@ package android.service.voice { method public void onDestroy(); method public boolean[] onGetSupportedCommands(android.service.voice.VoiceInteractionSession.Caller, java.lang.String[]); method public void onHandleAssist(android.os.Bundle); + method public void onHide(); method public boolean onKeyDown(int, android.view.KeyEvent); method public boolean onKeyLongPress(int, android.view.KeyEvent); method public boolean onKeyMultiple(int, int, android.view.KeyEvent); method public boolean onKeyUp(int, android.view.KeyEvent); + method public void onShow(android.os.Bundle, int); method public void onTaskFinished(android.content.Intent, int); method public void onTaskStarted(android.content.Intent, int); method public void setContentView(android.view.View); method public void setTheme(int); + method public void show(); method public void showWindow(); method public void startVoiceActivity(android.content.Intent); } diff --git a/api/system-current.txt b/api/system-current.txt index 92f0f4d..626dfd0 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -29345,7 +29345,7 @@ package android.service.voice { method public android.os.IBinder onBind(android.content.Intent); method public void onReady(); method public void onShutdown(); - method public void startSession(android.os.Bundle, int); + method public void showSession(android.os.Bundle, int); field public static final java.lang.String SERVICE_INTERFACE = "android.service.voice.VoiceInteractionService"; field public static final java.lang.String SERVICE_META_DATA = "android.voice_interaction"; field public static final int START_WITH_ASSIST = 1; // 0x1 @@ -29357,6 +29357,7 @@ package android.service.voice { method public void finish(); method public android.view.LayoutInflater getLayoutInflater(); method public android.app.Dialog getWindow(); + method public void hide(); method public void hideWindow(); method public void onAbortVoice(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.CharSequence, android.os.Bundle); method public void onBackPressed(); @@ -29371,14 +29372,17 @@ package android.service.voice { method public void onDestroy(); method public boolean[] onGetSupportedCommands(android.service.voice.VoiceInteractionSession.Caller, java.lang.String[]); method public void onHandleAssist(android.os.Bundle); + method public void onHide(); method public boolean onKeyDown(int, android.view.KeyEvent); method public boolean onKeyLongPress(int, android.view.KeyEvent); method public boolean onKeyMultiple(int, int, android.view.KeyEvent); method public boolean onKeyUp(int, android.view.KeyEvent); + method public void onShow(android.os.Bundle, int); method public void onTaskFinished(android.content.Intent, int); method public void onTaskStarted(android.content.Intent, int); method public void setContentView(android.view.View); method public void setTheme(int); + method public void show(); method public void showWindow(); method public void startVoiceActivity(android.content.Intent); } diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java index 7f9693f..7b84cb4 100644 --- a/core/java/android/app/VoiceInteractor.java +++ b/core/java/android/app/VoiceInteractor.java @@ -156,8 +156,8 @@ public class VoiceInteractor { @Override public void deliverCancel(IVoiceInteractorRequest request) throws RemoteException { - mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO( - MSG_CANCEL_RESULT, request)); + mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOO( + MSG_CANCEL_RESULT, request, null)); } }; diff --git a/core/java/android/service/voice/IVoiceInteractionSession.aidl b/core/java/android/service/voice/IVoiceInteractionSession.aidl index a8c0c4c..797457a 100644 --- a/core/java/android/service/voice/IVoiceInteractionSession.aidl +++ b/core/java/android/service/voice/IVoiceInteractionSession.aidl @@ -23,6 +23,8 @@ import android.os.Bundle; * @hide */ oneway interface IVoiceInteractionSession { + void show(in Bundle sessionArgs, int flags); + void hide(); void handleAssist(in Bundle assistData); void taskStarted(in Intent intent, int taskId); void taskFinished(in Intent intent, int taskId); diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java index 54a3a0a..0c01b25 100644 --- a/core/java/android/service/voice/VoiceInteractionService.java +++ b/core/java/android/service/voice/VoiceInteractionService.java @@ -71,7 +71,7 @@ public class VoiceInteractionService extends Service { public static final String SERVICE_META_DATA = "android.voice_interaction"; /** - * Flag for use with {@link #startSession}: request that the session be started with + * Flag for use with {@link #showSession: request that the session be started with * assist data from the currently focused activity. */ public static final int START_WITH_ASSIST = 1<<0; @@ -139,20 +139,25 @@ public class VoiceInteractionService extends Service { } /** - * Initiate the execution of a new {@link android.service.voice.VoiceInteractionSession}. + * Request that the associated {@link android.service.voice.VoiceInteractionSession} be + * shown to the user, starting it if necessary. * @param args Arbitrary arguments that will be propagated to the session. */ - public void startSession(Bundle args, int flags) { + public void showSession(Bundle args, int flags) { if (mSystemService == null) { throw new IllegalStateException("Not available until onReady() is called"); } try { - mSystemService.startSession(mInterface, args, flags); + mSystemService.showSession(mInterface, args, flags); } catch (RemoteException e) { } } /** @hide */ + public void startSession(Bundle args, int flags) { + showSession(args, flags); + } + /** @hide */ public void startSession(Bundle args) { startSession(args, 0); } @@ -174,7 +179,7 @@ public class VoiceInteractionService extends Service { /** * Called during service initialization to tell you when the system is ready * to receive interaction from it. You should generally do initialization here - * rather than in {@link #onCreate}. Methods such as {@link #startSession} and + * rather than in {@link #onCreate}. Methods such as {@link #showSession} and * {@link #createAlwaysOnHotwordDetector} * will not be operational until this point. */ diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java index a3a2ca1..4cf0e4c 100644 --- a/core/java/android/service/voice/VoiceInteractionSession.java +++ b/core/java/android/service/voice/VoiceInteractionSession.java @@ -53,10 +53,9 @@ import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; /** * An active voice interaction session, providing a facility for the implementation - * to interact with the user in the voice interaction layer. This interface is no shown - * by default, but you can request that it be shown with {@link #showWindow()}, which - * will result in a later call to {@link #onCreateContentView()} in which the UI can be - * built + * to interact with the user in the voice interaction layer. The user interface is + * initially shown by default, and can be created be overriding {@link #onCreateContentView()} + * in which the UI can be built. * * <p>A voice interaction session can be self-contained, ultimately calling {@link #finish} * when done. It can also initiate voice interactions with applications by calling @@ -151,6 +150,17 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { final IVoiceInteractionSession mSession = new IVoiceInteractionSession.Stub() { @Override + public void show(Bundle sessionArgs, int flags) { + mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIO(MSG_SHOW, + flags, sessionArgs)); + } + + @Override + public void hide() { + mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_HIDE)); + } + + @Override public void handleAssist(Bundle assistBundle) { mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_HANDLE_ASSIST, assistBundle)); @@ -284,6 +294,8 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { static final int MSG_CLOSE_SYSTEM_DIALOGS = 102; static final int MSG_DESTROY = 103; static final int MSG_HANDLE_ASSIST = 104; + static final int MSG_SHOW = 105; + static final int MSG_HIDE = 106; class MyCallbacks implements HandlerCaller.Callback, SoftInputWindow.Callback { @Override @@ -324,9 +336,8 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { args.arg1 = onGetSupportedCommands((Caller) args.arg1, (String[]) args.arg2); break; case MSG_CANCEL: - args = (SomeArgs)msg.obj; - if (DEBUG) Log.d(TAG, "onCancel: req=" + ((Request) args.arg1).mInterface); - onCancel((Request)args.arg1); + if (DEBUG) Log.d(TAG, "onCancel: req=" + ((Request)msg.obj)); + onCancel((Request)msg.obj); break; case MSG_TASK_STARTED: if (DEBUG) Log.d(TAG, "onTaskStarted: intent=" + msg.obj @@ -350,6 +361,15 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { if (DEBUG) Log.d(TAG, "onHandleAssist: " + (Bundle)msg.obj); onHandleAssist((Bundle) msg.obj); break; + case MSG_SHOW: + if (DEBUG) Log.d(TAG, "doShow: args=" + msg.obj + + " flags=" + msg.arg1); + doShow((Bundle) msg.obj, msg.arg1); + break; + case MSG_HIDE: + if (DEBUG) Log.d(TAG, "doHide"); + doHide(); + break; } } @@ -457,6 +477,45 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { onCreate(args, startFlags); } + void doShow(Bundle args, int flags) { + if (DEBUG) Log.v(TAG, "Showing window: mWindowAdded=" + mWindowAdded + + " mWindowVisible=" + mWindowVisible); + + if (mInShowWindow) { + Log.w(TAG, "Re-entrance in to showWindow"); + return; + } + + try { + mInShowWindow = true; + if (!mWindowVisible) { + if (!mWindowAdded) { + mWindowAdded = true; + View v = onCreateContentView(); + if (v != null) { + setContentView(v); + } + } + } + onShow(args, flags); + if (!mWindowVisible) { + mWindowVisible = true; + mWindow.show(); + } + } finally { + mWindowWasVisible = true; + mInShowWindow = false; + } + } + + void doHide() { + if (mWindowVisible) { + mWindow.hide(); + mWindowVisible = false; + onHide(); + } + } + void doDestroy() { onDestroy(); if (mInitialized) { @@ -484,39 +543,26 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { mContentFrame = (FrameLayout)mRootView.findViewById(android.R.id.content); } - public void showWindow() { - if (DEBUG) Log.v(TAG, "Showing window: mWindowAdded=" + mWindowAdded - + " mWindowVisible=" + mWindowVisible); - - if (mInShowWindow) { - Log.w(TAG, "Re-entrance in to showWindow"); - return; + public void show() { + try { + mSystemService.showSessionFromSession(mToken, null, 0); + } catch (RemoteException e) { } + } + public void hide() { try { - mInShowWindow = true; - if (!mWindowVisible) { - mWindowVisible = true; - if (!mWindowAdded) { - mWindowAdded = true; - View v = onCreateContentView(); - if (v != null) { - setContentView(v); - } - } - mWindow.show(); - } - } finally { - mWindowWasVisible = true; - mInShowWindow = false; + mSystemService.hideSessionFromSession(mToken); + } catch (RemoteException e) { } } + /** TODO: remove */ + public void showWindow() { + } + + /** TODO: remove */ public void hideWindow() { - if (mWindowVisible) { - mWindow.hide(); - mWindowVisible = false; - } } /** @@ -611,15 +657,33 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { } /** - * Initiatize a new session. + * Initiatize a new session. The given args and showFlags are the initial values + * passed to {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}, + * if possible. Normally you should handle these in {@link #onShow}. + */ + public void onCreate(Bundle args, int showFlags) { + onCreate(args); + } + + /** + * Called when the session UI is going to be shown. This is called after + * {@link #onCreateContentView} (if the session's content UI needed to be created) and + * immediately prior to the window being shown. This may be called while the window + * is already shown, if a show request has come in while it is shown, to allow you to + * update the UI to match the new show arguments. * * @param args The arguments that were supplied to - * {@link VoiceInteractionService#startSession VoiceInteractionService.startSession}. - * @param startFlags The start flags originally provided to - * {@link VoiceInteractionService#startSession VoiceInteractionService.startSession}. + * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}. + * @param showFlags The show flags originally provided to + * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}. */ - public void onCreate(Bundle args, int startFlags) { - onCreate(args); + public void onShow(Bundle args, int showFlags) { + } + + /** + * Called immediately after stopping to show the session UI. + */ + public void onHide() { } /** @@ -663,7 +727,7 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { } public void onBackPressed() { - finish(); + hide(); } /** @@ -672,7 +736,7 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { * calls {@link #finish}. */ public void onCloseSystemDialogs() { - finish(); + hide(); } /** @@ -717,7 +781,7 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { * @param taskId Unique ID of the finished task. */ public void onTaskFinished(Intent intent, int taskId) { - finish(); + hide(); } /** diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl index 6d90420..8f549a6 100644 --- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl +++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl @@ -26,9 +26,11 @@ import android.service.voice.IVoiceInteractionService; import android.service.voice.IVoiceInteractionSession; interface IVoiceInteractionManagerService { - void startSession(IVoiceInteractionService service, in Bundle sessionArgs, int flags); + void showSession(IVoiceInteractionService service, in Bundle sessionArgs, int flags); boolean deliverNewSession(IBinder token, IVoiceInteractionSession session, IVoiceInteractor interactor); + boolean showSessionFromSession(IBinder token, in Bundle sessionArgs, int flags); + boolean hideSessionFromSession(IBinder token); int startVoiceActivity(IBinder token, in Intent intent, String resolvedType); void finish(IBinder token); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index fd990d7..6b8c49c 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -385,7 +385,7 @@ public class VoiceInteractionManagerService extends SystemService { } @Override - public void startSession(IVoiceInteractionService service, Bundle args, int flags) { + public void showSession(IVoiceInteractionService service, Bundle args, int flags) { synchronized (this) { if (mImpl == null || mImpl.mService == null || service.asBinder() != mImpl.mService.asBinder()) { @@ -396,7 +396,7 @@ public class VoiceInteractionManagerService extends SystemService { final int callingUid = Binder.getCallingUid(); final long caller = Binder.clearCallingIdentity(); try { - mImpl.startSessionLocked(callingPid, callingUid, args, flags); + mImpl.showSessionLocked(callingPid, callingUid, args, flags); } finally { Binder.restoreCallingIdentity(caller); } @@ -424,6 +424,42 @@ public class VoiceInteractionManagerService extends SystemService { } @Override + public boolean showSessionFromSession(IBinder token, Bundle sessionArgs, int flags) { + synchronized (this) { + if (mImpl == null) { + Slog.w(TAG, "showSessionFromSession without running voice interaction service"); + return false; + } + final int callingPid = Binder.getCallingPid(); + final int callingUid = Binder.getCallingUid(); + final long caller = Binder.clearCallingIdentity(); + try { + return mImpl.showSessionLocked(callingPid, callingUid, sessionArgs, flags); + } finally { + Binder.restoreCallingIdentity(caller); + } + } + } + + @Override + public boolean hideSessionFromSession(IBinder token) { + synchronized (this) { + if (mImpl == null) { + Slog.w(TAG, "hideSessionFromSession without running voice interaction service"); + return false; + } + final int callingPid = Binder.getCallingPid(); + final int callingUid = Binder.getCallingUid(); + final long caller = Binder.clearCallingIdentity(); + try { + return mImpl.hideSessionLocked(callingPid, callingUid); + } finally { + Binder.restoreCallingIdentity(caller); + } + } + } + + @Override public int startVoiceActivity(IBinder token, Intent intent, String resolvedType) { synchronized (this) { if (mImpl == null) { diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java index e80f702..9e92867 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java @@ -26,7 +26,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.PackageManager; -import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -35,20 +34,17 @@ import android.os.ServiceManager; import android.os.UserHandle; import android.service.voice.IVoiceInteractionService; import android.service.voice.IVoiceInteractionSession; -import android.service.voice.IVoiceInteractionSessionService; import android.service.voice.VoiceInteractionService; import android.service.voice.VoiceInteractionServiceInfo; import android.util.Slog; import android.view.IWindowManager; -import android.view.WindowManager; import com.android.internal.app.IVoiceInteractor; -import com.android.internal.os.IResultReceiver; import java.io.FileDescriptor; import java.io.PrintWriter; -class VoiceInteractionManagerServiceImpl { +class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConnection.Callback { final static String TAG = "VoiceInteractionServiceManager"; final boolean mValid; @@ -65,7 +61,7 @@ class VoiceInteractionManagerServiceImpl { boolean mBound = false; IVoiceInteractionService mService; - SessionConnection mActiveSession; + VoiceInteractionSessionConnection mActiveSession; final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override @@ -101,124 +97,6 @@ class VoiceInteractionManagerServiceImpl { } }; - final class SessionConnection implements ServiceConnection { - final IBinder mToken = new Binder(); - final Bundle mArgs; - final int mFlags; - boolean mBound; - IVoiceInteractionSessionService mService; - IVoiceInteractionSession mSession; - IVoiceInteractor mInteractor; - boolean mHaveAssistData; - Bundle mAssistData; - - final IResultReceiver mAssistReceiver = new IResultReceiver.Stub() { - @Override - public void send(int resultCode, Bundle resultData) throws RemoteException { - synchronized (mLock) { - mHaveAssistData = true; - mAssistData = resultData; - if (mSession != null) { - try { - mSession.handleAssist(resultData); - } catch (RemoteException e) { - } - } - } - } - }; - - SessionConnection(Bundle args, int flags) { - mArgs = args; - mFlags = flags; - Intent serviceIntent = new Intent(VoiceInteractionService.SERVICE_INTERFACE); - serviceIntent.setComponent(mSessionComponentName); - mBound = mContext.bindServiceAsUser(serviceIntent, this, - Context.BIND_AUTO_CREATE, new UserHandle(mUser)); - if (mBound) { - try { - mIWindowManager.addWindowToken(mToken, - WindowManager.LayoutParams.TYPE_VOICE_INTERACTION); - } catch (RemoteException e) { - Slog.w(TAG, "Failed adding window token", e); - } - if ((flags&VoiceInteractionService.START_WITH_ASSIST) != 0) { - try { - mAm.requestAssistContextExtras(0, mAssistReceiver); - } catch (RemoteException e) { - } - } else { - mHaveAssistData = true; - } - } else { - Slog.w(TAG, "Failed binding to voice interaction session service " + mComponent); - } - } - - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - synchronized (mLock) { - mService = IVoiceInteractionSessionService.Stub.asInterface(service); - if (mActiveSession == this) { - try { - mService.newSession(mToken, mArgs, mFlags); - } catch (RemoteException e) { - Slog.w(TAG, "Failed adding window token", e); - } - } - } - } - - @Override - public void onServiceDisconnected(ComponentName name) { - mService = null; - } - - public void cancel() { - if (mBound) { - if (mSession != null) { - try { - mSession.destroy(); - } catch (RemoteException e) { - Slog.w(TAG, "Voice interation session already dead"); - } - } - if (mSession != null) { - try { - mAm.finishVoiceTask(mSession); - } catch (RemoteException e) { - } - } - mContext.unbindService(this); - try { - mIWindowManager.removeWindowToken(mToken); - } catch (RemoteException e) { - Slog.w(TAG, "Failed removing window token", e); - } - mBound = false; - mService = null; - mSession = null; - mInteractor = null; - } - } - - public void dump(String prefix, PrintWriter pw) { - pw.print(prefix); pw.print("mToken="); pw.println(mToken); - pw.print(prefix); pw.print("mArgs="); pw.println(mArgs); - pw.print(prefix); pw.print("mFlags=0x"); pw.println(Integer.toHexString(mFlags)); - pw.print(prefix); pw.print("mBound="); pw.println(mBound); - if (mBound) { - pw.print(prefix); pw.print("mService="); pw.println(mService); - pw.print(prefix); pw.print("mSession="); pw.println(mSession); - pw.print(prefix); pw.print("mInteractor="); pw.println(mInteractor); - } - pw.print(prefix); pw.print("mHaveAssistData="); pw.println(mHaveAssistData); - if (mHaveAssistData) { - pw.print(prefix); pw.print("mAssistData="); pw.println(mAssistData); - } - } - }; - VoiceInteractionManagerServiceImpl(Context context, Handler handler, Object lock, int userHandle, ComponentName service) { mContext = context; @@ -256,12 +134,16 @@ class VoiceInteractionManagerServiceImpl { mContext.registerReceiver(mBroadcastReceiver, filter, null, handler); } - public void startSessionLocked(int callingPid, int callingUid, Bundle args, int flags) { - if (mActiveSession != null) { - mActiveSession.cancel(); - mActiveSession = null; + public boolean showSessionLocked(int callingPid, int callingUid, Bundle args, int flags) { + if (mActiveSession == null) { + mActiveSession = new VoiceInteractionSessionConnection(mLock, mSessionComponentName, + mUser, mContext, this, callingPid, callingUid); } - mActiveSession = new SessionConnection(args, flags); + return mActiveSession.showLocked(args, flags); + } + + public boolean hideSessionLocked(int callingPid, int callingUid) { + return mActiveSession.hideLocked(); } public boolean deliverNewSessionLocked(int callingPid, int callingUid, IBinder token, @@ -270,14 +152,7 @@ class VoiceInteractionManagerServiceImpl { Slog.w(TAG, "deliverNewSession does not match active session"); return false; } - mActiveSession.mSession = session; - mActiveSession.mInteractor = interactor; - if (mActiveSession.mHaveAssistData) { - try { - session.handleAssist(mActiveSession.mAssistData); - } catch (RemoteException e) { - } - } + mActiveSession.deliverNewSessionLocked(session, interactor); return true; } @@ -367,4 +242,11 @@ class VoiceInteractionManagerServiceImpl { Slog.w(TAG, "RemoteException while calling soundModelsChanged", e); } } + + @Override + public void sessionConnectionGone(VoiceInteractionSessionConnection connection) { + synchronized (mLock) { + finishLocked(connection.mCallingPid, connection.mCallingUid, connection.mToken); + } + } } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java new file mode 100644 index 0000000..e2b47c3 --- /dev/null +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java @@ -0,0 +1,285 @@ +/* + * 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 com.android.server.voiceinteraction; + +import android.app.ActivityManagerNative; +import android.app.IActivityManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Binder; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.service.voice.IVoiceInteractionSession; +import android.service.voice.IVoiceInteractionSessionService; +import android.service.voice.VoiceInteractionService; +import android.util.Slog; +import android.view.IWindowManager; +import android.view.WindowManager; +import com.android.internal.app.IVoiceInteractor; +import com.android.internal.os.IResultReceiver; + +import java.io.PrintWriter; + +final class VoiceInteractionSessionConnection implements ServiceConnection { + final static String TAG = "VoiceInteractionServiceManager"; + + final IBinder mToken = new Binder(); + final Object mLock; + final ComponentName mSessionComponentName; + final Intent mBindIntent; + final int mUser; + final Context mContext; + final Callback mCallback; + final int mCallingPid; + final int mCallingUid; + final IActivityManager mAm; + final IWindowManager mIWindowManager; + boolean mShown; + Bundle mShowArgs; + int mShowFlags; + boolean mBound; + boolean mFullyBound; + boolean mCanceled; + IVoiceInteractionSessionService mService; + IVoiceInteractionSession mSession; + IVoiceInteractor mInteractor; + boolean mHaveAssistData; + Bundle mAssistData; + + public interface Callback { + public void sessionConnectionGone(VoiceInteractionSessionConnection connection); + } + + final ServiceConnection mFullConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + } + @Override + public void onServiceDisconnected(ComponentName name) { + } + }; + + final IResultReceiver mAssistReceiver = new IResultReceiver.Stub() { + @Override + public void send(int resultCode, Bundle resultData) throws RemoteException { + synchronized (mLock) { + if (mShown) { + if (mSession != null) { + try { + mSession.handleAssist(resultData); + } catch (RemoteException e) { + } + } else { + mHaveAssistData = true; + mAssistData = resultData; + } + } + } + } + }; + + public VoiceInteractionSessionConnection(Object lock, ComponentName component, int user, + Context context, Callback callback, int callingPid, int callingUid) { + mLock = lock; + mSessionComponentName = component; + mUser = user; + mContext = context; + mCallback = callback; + mCallingPid = callingPid; + mCallingUid = callingUid; + mAm = ActivityManagerNative.getDefault(); + mIWindowManager = IWindowManager.Stub.asInterface( + ServiceManager.getService(Context.WINDOW_SERVICE)); + mBindIntent = new Intent(VoiceInteractionService.SERVICE_INTERFACE); + mBindIntent.setComponent(mSessionComponentName); + mBound = mContext.bindServiceAsUser(mBindIntent, this, + Context.BIND_AUTO_CREATE|Context.BIND_ALLOW_OOM_MANAGEMENT, new UserHandle(mUser)); + if (mBound) { + try { + mIWindowManager.addWindowToken(mToken, + WindowManager.LayoutParams.TYPE_VOICE_INTERACTION); + } catch (RemoteException e) { + Slog.w(TAG, "Failed adding window token", e); + } + } else { + Slog.w(TAG, "Failed binding to voice interaction session service " + + mSessionComponentName); + } + } + + public boolean showLocked(Bundle args, int flags) { + if (mBound) { + if (!mFullyBound) { + mFullyBound = mContext.bindServiceAsUser(mBindIntent, mFullConnection, + Context.BIND_AUTO_CREATE|Context.BIND_TREAT_LIKE_ACTIVITY, + new UserHandle(mUser)); + } + mShown = true; + mShowArgs = args; + mShowFlags = flags; + if ((flags&VoiceInteractionService.START_WITH_ASSIST) != 0) { + try { + mAm.requestAssistContextExtras(0, mAssistReceiver); + } catch (RemoteException e) { + } + } else { + mHaveAssistData = false; + mAssistData = null; + } + if (mSession != null) { + try { + mSession.show(mShowArgs, mShowFlags); + mShowArgs = null; + mShowFlags = 0; + } catch (RemoteException e) { + } + if (mHaveAssistData) { + try { + mSession.handleAssist(mAssistData); + mAssistData = null; + mHaveAssistData = false; + } catch (RemoteException e) { + } + } + } + return true; + } + return false; + } + + public boolean hideLocked() { + if (mBound) { + if (mShown) { + mShown = false; + mShowArgs = null; + mShowFlags = 0; + mHaveAssistData = false; + mAssistData = null; + if (mSession != null) { + try { + mSession.hide(); + } catch (RemoteException e) { + } + } + } + if (mFullyBound) { + mContext.unbindService(mFullConnection); + mFullyBound = false; + } + return true; + } + return false; + } + + public boolean deliverNewSessionLocked(IVoiceInteractionSession session, + IVoiceInteractor interactor) { + mSession = session; + mInteractor = interactor; + if (mShown) { + try { + session.show(mShowArgs, mShowFlags); + mShowArgs = null; + mShowFlags = 0; + } catch (RemoteException e) { + } + if (mHaveAssistData) { + try { + session.handleAssist(mAssistData); + mAssistData = null; + mHaveAssistData = false; + } catch (RemoteException e) { + } + } + } + return true; + } + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + synchronized (mLock) { + mService = IVoiceInteractionSessionService.Stub.asInterface(service); + if (!mCanceled) { + try { + mService.newSession(mToken, mShowArgs, mShowFlags); + } catch (RemoteException e) { + Slog.w(TAG, "Failed adding window token", e); + } + } + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + mCallback.sessionConnectionGone(this); + mService = null; + } + + public void cancel() { + mCanceled = true; + if (mBound) { + if (mSession != null) { + try { + mSession.destroy(); + } catch (RemoteException e) { + Slog.w(TAG, "Voice interation session already dead"); + } + } + if (mSession != null) { + try { + mAm.finishVoiceTask(mSession); + } catch (RemoteException e) { + } + } + mContext.unbindService(this); + try { + mIWindowManager.removeWindowToken(mToken); + } catch (RemoteException e) { + Slog.w(TAG, "Failed removing window token", e); + } + mBound = false; + mService = null; + mSession = null; + mInteractor = null; + } + if (mFullyBound) { + mContext.unbindService(mFullConnection); + mFullyBound = false; + } + } + + public void dump(String prefix, PrintWriter pw) { + pw.print(prefix); pw.print("mToken="); pw.println(mToken); + pw.print(prefix); pw.print("mShown="); pw.println(mShown); + pw.print(prefix); pw.print("mShowArgs="); pw.println(mShowArgs); + pw.print(prefix); pw.print("mShowFlags=0x"); pw.println(Integer.toHexString(mShowFlags)); + pw.print(prefix); pw.print("mBound="); pw.println(mBound); + if (mBound) { + pw.print(prefix); pw.print("mService="); pw.println(mService); + pw.print(prefix); pw.print("mSession="); pw.println(mSession); + pw.print(prefix); pw.print("mInteractor="); pw.println(mInteractor); + } + pw.print(prefix); pw.print("mHaveAssistData="); pw.println(mHaveAssistData); + if (mHaveAssistData) { + pw.print(prefix); pw.print("mAssistData="); pw.println(mAssistData); + } + } +}; diff --git a/tests/VoiceInteraction/AndroidManifest.xml b/tests/VoiceInteraction/AndroidManifest.xml index adf572c..36d5d98 100644 --- a/tests/VoiceInteraction/AndroidManifest.xml +++ b/tests/VoiceInteraction/AndroidManifest.xml @@ -16,7 +16,8 @@ android:label="Test Assist Proxy" android:theme="@android:style/Theme.NoDisplay" android:excludeFromRecents="true" - android:noHistory="true"> + android:noHistory="true" + android:taskAffinity=""> <intent-filter> <action android:name="android.intent.action.ASSIST" /> <category android:name="android.intent.category.DEFAULT" /> diff --git a/tests/VoiceInteraction/res/layout/test_interaction.xml b/tests/VoiceInteraction/res/layout/test_interaction.xml index c4e280e..f4648b5 100644 --- a/tests/VoiceInteraction/res/layout/test_interaction.xml +++ b/tests/VoiceInteraction/res/layout/test_interaction.xml @@ -48,4 +48,11 @@ android:text="@string/abortVoice" /> + <Button android:id="@+id/cancel" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="16dp" + android:text="@string/cancelVoice" + /> + </LinearLayout> diff --git a/tests/VoiceInteraction/res/values/strings.xml b/tests/VoiceInteraction/res/values/strings.xml index 7eec90c..9f99c97 100644 --- a/tests/VoiceInteraction/res/values/strings.xml +++ b/tests/VoiceInteraction/res/values/strings.xml @@ -22,6 +22,7 @@ <string name="complete">Complete</string> <string name="abortVoice">Abort Voice</string> <string name="completeVoice">Complete Voice</string> + <string name="cancelVoice">Cancel</string> </resources> diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistProxyActivity.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistProxyActivity.java index fc04ff5..6a99351 100644 --- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistProxyActivity.java +++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistProxyActivity.java @@ -29,6 +29,6 @@ public class AssistProxyActivity extends Activity { Intent intent = new Intent(this, MainInteractionService.class); intent.setAction(Intent.ACTION_ASSIST); intent.putExtras(getIntent()); - startService(new Intent(this, MainInteractionService.class)); + startService(intent); } } diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java index 5d5ae2f..d35bc5c 100644 --- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java +++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java @@ -57,6 +57,11 @@ public class AssistVisualizer extends View { } } + public void clearAssistData() { + mAssistData = null; + mTextRects.clear(); + } + void buildTextRects(AssistData.ViewNode root, int parentLeft, int parentTop) { if (root.getVisibility() != View.VISIBLE) { return; diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java index 1aeb98a..1e30aff 100644 --- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java +++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java @@ -49,6 +49,7 @@ public class MainInteractionSession extends VoiceInteractionSession static final int STATE_COMMAND = 3; static final int STATE_ABORT_VOICE = 4; static final int STATE_COMPLETE_VOICE = 5; + static final int STATE_DONE=6; int mState = STATE_IDLE; Request mPendingRequest; @@ -60,12 +61,26 @@ public class MainInteractionSession extends VoiceInteractionSession @Override public void onCreate(Bundle args, int startFlags) { super.onCreate(args); - showWindow(); + } + + @Override + public void onShow(Bundle args, int showFlags) { + super.onShow(args, showFlags); + mState = STATE_IDLE; mStartIntent = args.getParcelable("intent"); Bundle assist = args.getBundle("assist"); - if (assist != null) { - parseAssistData(assist); + parseAssistData(assist); + updateState(); + } + + @Override + public void onHide() { + super.onHide(); + if (mAssistVisualizer != null) { + mAssistVisualizer.clearAssistData(); } + mState = STATE_DONE; + updateState(); } @Override @@ -86,7 +101,6 @@ public class MainInteractionSession extends VoiceInteractionSession mCompleteButton.setOnClickListener(this); mAbortButton = (Button)mContentView.findViewById(R.id.abort); mAbortButton.setOnClickListener(this); - updateState(); return mContentView; } @@ -100,23 +114,35 @@ public class MainInteractionSession extends VoiceInteractionSession } void parseAssistData(Bundle assistBundle) { - Bundle assistContext = assistBundle.getBundle(Intent.EXTRA_ASSIST_CONTEXT); - if (assistContext != null) { - mAssistData = AssistData.getAssistData(assistContext); - mAssistData.dump(); - if (mAssistVisualizer != null) { - mAssistVisualizer.setAssistData(mAssistData); + if (assistBundle != null) { + Bundle assistContext = assistBundle.getBundle(Intent.EXTRA_ASSIST_CONTEXT); + if (assistContext != null) { + mAssistData = AssistData.getAssistData(assistContext); + mAssistData.dump(); + if (mAssistVisualizer != null) { + mAssistVisualizer.setAssistData(mAssistData); + } + return; } } + if (mAssistVisualizer != null) { + mAssistVisualizer.clearAssistData(); + } } void updateState() { if (mState == STATE_IDLE) { mTopContent.setVisibility(View.VISIBLE); mBottomContent.setVisibility(View.GONE); + mAssistVisualizer.setVisibility(View.VISIBLE); + } else if (mState == STATE_DONE) { + mTopContent.setVisibility(View.GONE); + mBottomContent.setVisibility(View.GONE); + mAssistVisualizer.setVisibility(View.GONE); } else { mTopContent.setVisibility(View.GONE); mBottomContent.setVisibility(View.VISIBLE); + mAssistVisualizer.setVisibility(View.GONE); } mStartButton.setEnabled(mState == STATE_IDLE); mConfirmButton.setEnabled(mState == STATE_CONFIRM || mState == STATE_COMMAND); @@ -136,18 +162,12 @@ public class MainInteractionSession extends VoiceInteractionSession mPendingRequest.sendCommandResult(true, null); } mPendingRequest = null; - mState = STATE_IDLE; - updateState(); } else if (v == mAbortButton) { mPendingRequest.sendAbortVoiceResult(null); mPendingRequest = null; - mState = STATE_IDLE; - updateState(); } else if (v== mCompleteButton) { mPendingRequest.sendCompleteVoiceResult(null); mPendingRequest = null; - mState = STATE_IDLE; - updateState(); } } diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java index 8522cdc..023e0ec 100644 --- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java +++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java @@ -31,8 +31,10 @@ public class TestInteractionActivity extends Activity implements View.OnClickLis static final String TAG = "TestInteractionActivity"; VoiceInteractor mInteractor; + VoiceInteractor.Request mCurrentRequest = null; Button mAbortButton; Button mCompleteButton; + Button mCancelButton; @Override public void onCreate(Bundle savedInstanceState) { @@ -56,9 +58,11 @@ public class TestInteractionActivity extends Activity implements View.OnClickLis mAbortButton.setOnClickListener(this); mCompleteButton = (Button)findViewById(R.id.complete); mCompleteButton.setOnClickListener(this); + mCancelButton = (Button)findViewById(R.id.cancel); + mCancelButton.setOnClickListener(this); mInteractor = getVoiceInteractor(); - VoiceInteractor.ConfirmationRequest req = new VoiceInteractor.ConfirmationRequest( + mCurrentRequest = new VoiceInteractor.ConfirmationRequest( "This is a confirmation", null) { @Override public void onCancel() { @@ -72,7 +76,7 @@ public class TestInteractionActivity extends Activity implements View.OnClickLis getActivity().finish(); } }; - mInteractor.submitRequest(req); + mInteractor.submitRequest(mCurrentRequest); } @Override @@ -112,6 +116,9 @@ public class TestInteractionActivity extends Activity implements View.OnClickLis } }; mInteractor.submitRequest(req); + } else if (v == mCancelButton && mCurrentRequest != null) { + Log.i(TAG, "Cancel request"); + mCurrentRequest.cancel(); } } |