diff options
author | Dianne Hackborn <hackbod@google.com> | 2014-04-25 17:06:18 -0700 |
---|---|---|
committer | Dianne Hackborn <hackbod@google.com> | 2014-04-28 10:54:15 -0700 |
commit | 18f0d357f9693fe787a3e3777d8fdf01357a6e3f (patch) | |
tree | 87ffa17a98fa81355a37e25b2c7fc649ffc4e9be /services/voiceinteraction | |
parent | 01c70711d5e4f1c3405bcd169be70605e92166f2 (diff) | |
download | frameworks_base-18f0d357f9693fe787a3e3777d8fdf01357a6e3f.zip frameworks_base-18f0d357f9693fe787a3e3777d8fdf01357a6e3f.tar.gz frameworks_base-18f0d357f9693fe787a3e3777d8fdf01357a6e3f.tar.bz2 |
Rework some of the voice interaction APIs.
On the app side, requests are now composed by subclassing
from various types of Request objects.
On the service side, starting a voice interaction session
involves starting another service that will then manage the
session. This leads the service design much more to what
we want, where the long-running main service is very tiny
and all the heavy-weight transient session work is elsewhere
in another process.
Change-Id: I46c074c6fe27b6c1cf2583c6d216aed1de2f1143
Diffstat (limited to 'services/voiceinteraction')
2 files changed, 185 insertions, 34 deletions
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 9e2bcab..045c0f6 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -16,14 +16,18 @@ package com.android.server.voiceinteraction; +import android.Manifest; import android.app.ActivityManager; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.database.ContentObserver; import android.os.Binder; +import android.os.Bundle; import android.os.Handler; +import android.os.IBinder; import android.os.UserHandle; import android.provider.Settings; import android.service.voice.IVoiceInteractionService; @@ -134,9 +138,8 @@ public class VoiceInteractionManagerService extends SystemService { } @Override - public int startVoiceActivity(Intent intent, String resolvedType, - IVoiceInteractionService service, - IVoiceInteractionSession session, IVoiceInteractor interactor) { + public void startVoiceActivity(Intent intent, String resolvedType, + IVoiceInteractionService service, Bundle args) { synchronized (this) { if (mImpl == null || service.asBinder() != mImpl.mService.asBinder()) { throw new SecurityException( @@ -146,8 +149,8 @@ public class VoiceInteractionManagerService extends SystemService { final int callingUid = Binder.getCallingUid(); final long caller = Binder.clearCallingIdentity(); try { - return mImpl.startVoiceActivityLocked(callingPid, callingUid, - intent, resolvedType, session, interactor); + mImpl.startVoiceActivityLocked(callingPid, callingUid, + intent, resolvedType, args); } finally { Binder.restoreCallingIdentity(caller); } @@ -155,8 +158,43 @@ public class VoiceInteractionManagerService extends SystemService { } @Override + public int deliverNewSession(IBinder token, IVoiceInteractionSession session, + IVoiceInteractor interactor) { + synchronized (this) { + if (mImpl == null) { + Slog.w(TAG, "deliverNewSession without running voice interaction service"); + return ActivityManager.START_CANCELED; + } + final int callingPid = Binder.getCallingPid(); + final int callingUid = Binder.getCallingUid(); + final long caller = Binder.clearCallingIdentity(); + try { + return mImpl.deliverNewSessionLocked(callingPid, callingUid, token, session, + interactor); + } finally { + Binder.restoreCallingIdentity(caller); + } + } + + } + + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); + if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP) + != PackageManager.PERMISSION_GRANTED) { + pw.println("Permission Denial: can't dump PowerManager from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + return; + } + synchronized (this) { + pw.println("VOICE INTERACTION MANAGER (dumpsys voiceinteraction)\n"); + if (mImpl == null) { + pw.println(" (No active implementation)"); + return; + } + mImpl.dumpLocked(fd, pw, args); + } } class SettingsObserver extends ContentObserver { diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java index af8ae1e..6bbd1c1 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java @@ -16,6 +16,7 @@ package com.android.server.voiceinteraction; +import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.IActivityManager; import android.content.ComponentName; @@ -24,29 +25,40 @@ import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; +import android.os.Binder; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; 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 com.android.internal.app.IVoiceInteractor; +import java.io.FileDescriptor; +import java.io.PrintWriter; + class VoiceInteractionManagerServiceImpl { final static String TAG = "VoiceInteractionServiceManager"; + final boolean mValid; + final Context mContext; final Handler mHandler; final Object mLock; final int mUser; final ComponentName mComponent; final IActivityManager mAm; + final VoiceInteractionServiceInfo mInfo; + final ComponentName mSessionComponentName; boolean mBound = false; IVoiceInteractionService mService; - IVoiceInteractionSession mActiveSession; - IVoiceInteractor mActiveInteractor; + + SessionConnection mActiveSession; final ServiceConnection mConnection = new ServiceConnection() { @Override @@ -62,6 +74,72 @@ class VoiceInteractionManagerServiceImpl { } }; + final class SessionConnection implements ServiceConnection { + final IBinder mToken = new Binder(); + final Intent mIntent; + final String mResolvedType; + final Bundle mArgs; + boolean mBound; + IVoiceInteractionSessionService mService; + IVoiceInteractionSession mSession; + IVoiceInteractor mInteractor; + + SessionConnection(Intent intent, String resolvedType, Bundle args) { + mIntent = intent; + mResolvedType = resolvedType; + mArgs = args; + Intent serviceIntent = new Intent(VoiceInteractionService.SERVICE_INTERFACE); + serviceIntent.setComponent(mSessionComponentName); + mBound = mContext.bindServiceAsUser(serviceIntent, this, + Context.BIND_AUTO_CREATE, new UserHandle(mUser)); + if (!mBound) { + 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); + } catch (RemoteException e) { + Slog.w(TAG, "Failed making new session", e); + } + } + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + mService = null; + } + + public void cancel() { + if (mBound) { + mContext.unbindService(this); + 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("mIntent="); pw.println(mIntent); + pw.print(" mResolvedType="); pw.println(mResolvedType); + pw.print(prefix); pw.print("mArgs="); pw.println(mArgs); + 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); + } + } + }; + VoiceInteractionManagerServiceImpl(Context context, Handler handler, Object lock, int userHandle, ComponentName service) { mContext = context; @@ -70,50 +148,85 @@ class VoiceInteractionManagerServiceImpl { mUser = userHandle; mComponent = service; mAm = ActivityManagerNative.getDefault(); - } - - public int startVoiceActivityLocked(int callingPid, int callingUid, Intent intent, - String resolvedType, IVoiceInteractionSession session, IVoiceInteractor interactor) { - if (session == null) { - throw new NullPointerException("session is null"); + VoiceInteractionServiceInfo info; + try { + info = new VoiceInteractionServiceInfo(context.getPackageManager(), service); + } catch (PackageManager.NameNotFoundException e) { + Slog.w(TAG, "Voice interaction service not found: " + service); + mInfo = null; + mSessionComponentName = null; + mValid = false; + return; } - if (interactor == null) { - throw new NullPointerException("interactor is null"); + mInfo = info; + if (mInfo.getParseError() != null) { + Slog.w(TAG, "Bad voice interaction service: " + mInfo.getParseError()); + mSessionComponentName = null; + mValid = false; + return; } + mValid = true; + mSessionComponentName = new ComponentName(service.getPackageName(), + mInfo.getSessionService()); + } + + public void startVoiceActivityLocked(int callingPid, int callingUid, Intent intent, + String resolvedType, Bundle args) { if (mActiveSession != null) { - // XXX cancel current session. + mActiveSession.cancel(); + mActiveSession = null; } + mActiveSession = new SessionConnection(intent, resolvedType, args); intent.addCategory(Intent.CATEGORY_VOICE); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); - mActiveSession = session; - mActiveInteractor = interactor; + } + + public int deliverNewSessionLocked(int callingPid, int callingUid, IBinder token, + IVoiceInteractionSession session, IVoiceInteractor interactor) { try { + if (mActiveSession == null || token != mActiveSession.mToken) { + Slog.w(TAG, "deliverNewSession does not match active session"); + return ActivityManager.START_CANCELED; + } + mActiveSession.mSession = session; + mActiveSession.mInteractor = interactor; return mAm.startVoiceActivity(mComponent.getPackageName(), callingPid, callingUid, - intent, resolvedType, mActiveSession, mActiveInteractor, + mActiveSession.mIntent, mActiveSession.mResolvedType, + mActiveSession.mSession, mActiveSession.mInteractor, 0, null, null, null, mUser); } catch (RemoteException e) { throw new IllegalStateException("Unexpected remote error", e); } } - void startLocked() { - Intent intent = new Intent(VoiceInteractionService.SERVICE_INTERFACE); - intent.setComponent(mComponent); - try { - ServiceInfo si = mContext.getPackageManager().getServiceInfo(mComponent, 0); - if (!android.Manifest.permission.BIND_VOICE_INTERACTION.equals(si.permission)) { - Slog.w(TAG, "Not using voice interaction service " + mComponent - + ": does not require permission " - + android.Manifest.permission.BIND_VOICE_INTERACTION); - return; + public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) { + if (!mValid) { + pw.print(" NOT VALID: "); + if (mInfo == null) { + pw.println("no info"); + } else { + pw.println(mInfo.getParseError()); } - } catch (PackageManager.NameNotFoundException e) { - Slog.w(TAG, "Unable to find voice interaction service: " + mComponent, e); return; } - mContext.bindServiceAsUser(intent, mConnection, + pw.print(" mComponent="); pw.println(mComponent.flattenToShortString()); + pw.print(" Session service="); pw.println(mInfo.getSessionService()); + pw.print(" Settings activity="); pw.println(mInfo.getSettingsActivity()); + pw.print(" mBound="); pw.print(mBound); pw.print(" mService="); pw.println(mService); + if (mActiveSession != null) { + pw.println(" Active session:"); + mActiveSession.dump(" ", pw); + } + } + + void startLocked() { + Intent intent = new Intent(VoiceInteractionService.SERVICE_INTERFACE); + intent.setComponent(mComponent); + mBound = mContext.bindServiceAsUser(intent, mConnection, Context.BIND_AUTO_CREATE, new UserHandle(mUser)); - mBound = true; + if (!mBound) { + Slog.w(TAG, "Failed binding to voice interaction service " + mComponent); + } } void shutdownLocked() { |