diff options
author | Dianne Hackborn <hackbod@google.com> | 2015-02-25 11:08:11 -0800 |
---|---|---|
committer | Dianne Hackborn <hackbod@google.com> | 2015-02-25 17:36:17 -0800 |
commit | ffeecb1bfb9b71f4b62c9ef1fbf7b58a7a63f655 (patch) | |
tree | 79ce65f76cff29d67abc6b867f0f79795be7a38b /services/voiceinteraction/java | |
parent | 6e53931f49f49245deef8622eb8e7dc6ccf04536 (diff) | |
download | frameworks_base-ffeecb1bfb9b71f4b62c9ef1fbf7b58a7a63f655.zip frameworks_base-ffeecb1bfb9b71f4b62c9ef1fbf7b58a7a63f655.tar.gz frameworks_base-ffeecb1bfb9b71f4b62c9ef1fbf7b58a7a63f655.tar.bz2 |
Rework voice interaction session lifecycle.
We now have a formal concept of the session being shown and
hidden, with it being able to continue running while hidden
as long as there is enough RAM.
This changes the flow that a VoiceInteractionSession will
see: onCreate() is when it is first created, onCreateContentView()
is when its UI first needs to be built, onShow() is called each
time it needs to be shown and has the arguments given when the
show request was made (which has been renamed from startSession to
showSession), and then onHide() will be called when the UI is
no longer shown.
The methods show() and hide() now allow a VoiceInteractionSession
subclass to control when it is shown and hidden, working with the
shown state being maintained by the system.
Change-Id: Ic4a430ec7e8bf76a5441fd0425e2932806170fcc
Diffstat (limited to 'services/voiceinteraction/java')
3 files changed, 342 insertions, 139 deletions
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); + } + } +}; |