VoiceInteractionManagerServiceImpl.java revision d7018200312e4e4dc3f67cf33dc90bf7ce585844
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.voiceinteraction; 18 19import android.app.ActivityManager; 20import android.app.ActivityManagerNative; 21import android.app.IActivityManager; 22import android.content.BroadcastReceiver; 23import android.content.ComponentName; 24import android.content.Context; 25import android.content.Intent; 26import android.content.IntentFilter; 27import android.content.ServiceConnection; 28import android.content.pm.PackageManager; 29import android.os.Binder; 30import android.os.Bundle; 31import android.os.Handler; 32import android.os.IBinder; 33import android.os.RemoteException; 34import android.os.ServiceManager; 35import android.os.UserHandle; 36import android.service.voice.IVoiceInteractionService; 37import android.service.voice.IVoiceInteractionSession; 38import android.service.voice.IVoiceInteractionSessionService; 39import android.service.voice.VoiceInteractionService; 40import android.service.voice.VoiceInteractionServiceInfo; 41import android.util.Slog; 42import android.view.IWindowManager; 43import android.view.WindowManager; 44 45import com.android.internal.app.IVoiceInteractor; 46 47import java.io.FileDescriptor; 48import java.io.PrintWriter; 49 50class VoiceInteractionManagerServiceImpl { 51 final static String TAG = "VoiceInteractionServiceManager"; 52 53 final boolean mValid; 54 55 final Context mContext; 56 final Handler mHandler; 57 final Object mLock; 58 final int mUser; 59 final ComponentName mComponent; 60 final IActivityManager mAm; 61 final VoiceInteractionServiceInfo mInfo; 62 final ComponentName mSessionComponentName; 63 final IWindowManager mIWindowManager; 64 boolean mBound = false; 65 IVoiceInteractionService mService; 66 67 SessionConnection mActiveSession; 68 69 final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 70 @Override 71 public void onReceive(Context context, Intent intent) { 72 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) { 73 synchronized (mLock) { 74 if (mActiveSession != null && mActiveSession.mSession != null) { 75 try { 76 mActiveSession.mSession.closeSystemDialogs(); 77 } catch (RemoteException e) { 78 } 79 } 80 } 81 } 82 } 83 }; 84 85 final ServiceConnection mConnection = new ServiceConnection() { 86 @Override 87 public void onServiceConnected(ComponentName name, IBinder service) { 88 synchronized (mLock) { 89 mService = IVoiceInteractionService.Stub.asInterface(service); 90 } 91 } 92 93 @Override 94 public void onServiceDisconnected(ComponentName name) { 95 mService = null; 96 } 97 }; 98 99 final class SessionConnection implements ServiceConnection { 100 final IBinder mToken = new Binder(); 101 final Bundle mArgs; 102 boolean mBound; 103 IVoiceInteractionSessionService mService; 104 IVoiceInteractionSession mSession; 105 IVoiceInteractor mInteractor; 106 107 SessionConnection(Bundle args) { 108 mArgs = args; 109 Intent serviceIntent = new Intent(VoiceInteractionService.SERVICE_INTERFACE); 110 serviceIntent.setComponent(mSessionComponentName); 111 mBound = mContext.bindServiceAsUser(serviceIntent, this, 112 Context.BIND_AUTO_CREATE, new UserHandle(mUser)); 113 if (mBound) { 114 try { 115 mIWindowManager.addWindowToken(mToken, 116 WindowManager.LayoutParams.TYPE_VOICE_INTERACTION); 117 } catch (RemoteException e) { 118 Slog.w(TAG, "Failed adding window token", e); 119 } 120 } else { 121 Slog.w(TAG, "Failed binding to voice interaction session service " + mComponent); 122 } 123 } 124 125 @Override 126 public void onServiceConnected(ComponentName name, IBinder service) { 127 synchronized (mLock) { 128 mService = IVoiceInteractionSessionService.Stub.asInterface(service); 129 if (mActiveSession == this) { 130 try { 131 mService.newSession(mToken, mArgs); 132 } catch (RemoteException e) { 133 Slog.w(TAG, "Failed adding window token", e); 134 } 135 } 136 } 137 } 138 139 @Override 140 public void onServiceDisconnected(ComponentName name) { 141 mService = null; 142 } 143 144 public void cancel() { 145 if (mBound) { 146 if (mSession != null) { 147 try { 148 mSession.destroy(); 149 } catch (RemoteException e) { 150 Slog.w(TAG, "Voice interation session already dead"); 151 } 152 } 153 if (mSession != null) { 154 try { 155 mAm.finishVoiceTask(mSession); 156 } catch (RemoteException e) { 157 } 158 } 159 mContext.unbindService(this); 160 try { 161 mIWindowManager.removeWindowToken(mToken); 162 } catch (RemoteException e) { 163 Slog.w(TAG, "Failed removing window token", e); 164 } 165 mBound = false; 166 mService = null; 167 mSession = null; 168 mInteractor = null; 169 } 170 } 171 172 public void dump(String prefix, PrintWriter pw) { 173 pw.print(prefix); pw.print("mToken="); pw.println(mToken); 174 pw.print(prefix); pw.print("mArgs="); pw.println(mArgs); 175 pw.print(prefix); pw.print("mBound="); pw.println(mBound); 176 if (mBound) { 177 pw.print(prefix); pw.print("mService="); pw.println(mService); 178 pw.print(prefix); pw.print("mSession="); pw.println(mSession); 179 pw.print(prefix); pw.print("mInteractor="); pw.println(mInteractor); 180 } 181 } 182 }; 183 184 VoiceInteractionManagerServiceImpl(Context context, Handler handler, Object lock, 185 int userHandle, ComponentName service) { 186 mContext = context; 187 mHandler = handler; 188 mLock = lock; 189 mUser = userHandle; 190 mComponent = service; 191 mAm = ActivityManagerNative.getDefault(); 192 VoiceInteractionServiceInfo info; 193 try { 194 info = new VoiceInteractionServiceInfo(context.getPackageManager(), service); 195 } catch (PackageManager.NameNotFoundException e) { 196 Slog.w(TAG, "Voice interaction service not found: " + service); 197 mInfo = null; 198 mSessionComponentName = null; 199 mIWindowManager = null; 200 mValid = false; 201 return; 202 } 203 mInfo = info; 204 if (mInfo.getParseError() != null) { 205 Slog.w(TAG, "Bad voice interaction service: " + mInfo.getParseError()); 206 mSessionComponentName = null; 207 mIWindowManager = null; 208 mValid = false; 209 return; 210 } 211 mValid = true; 212 mSessionComponentName = new ComponentName(service.getPackageName(), 213 mInfo.getSessionService()); 214 mIWindowManager = IWindowManager.Stub.asInterface( 215 ServiceManager.getService(Context.WINDOW_SERVICE)); 216 IntentFilter filter = new IntentFilter(); 217 filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 218 mContext.registerReceiver(mBroadcastReceiver, filter, null, handler); 219 } 220 221 public void startSessionLocked(int callingPid, int callingUid, Bundle args) { 222 if (mActiveSession != null) { 223 mActiveSession.cancel(); 224 mActiveSession = null; 225 } 226 mActiveSession = new SessionConnection(args); 227 } 228 229 public boolean deliverNewSessionLocked(int callingPid, int callingUid, IBinder token, 230 IVoiceInteractionSession session, IVoiceInteractor interactor) { 231 if (mActiveSession == null || token != mActiveSession.mToken) { 232 Slog.w(TAG, "deliverNewSession does not match active session"); 233 return false; 234 } 235 mActiveSession.mSession = session; 236 mActiveSession.mInteractor = interactor; 237 return true; 238 } 239 240 public int startVoiceActivityLocked(int callingPid, int callingUid, IBinder token, 241 Intent intent, String resolvedType) { 242 try { 243 if (mActiveSession == null || token != mActiveSession.mToken) { 244 Slog.w(TAG, "startVoiceActivity does not match active session"); 245 return ActivityManager.START_CANCELED; 246 } 247 intent = new Intent(intent); 248 intent.addCategory(Intent.CATEGORY_VOICE); 249 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); 250 return mAm.startVoiceActivity(mComponent.getPackageName(), callingPid, callingUid, 251 intent, resolvedType, mActiveSession.mSession, mActiveSession.mInteractor, 252 0, null, null, null, mUser); 253 } catch (RemoteException e) { 254 throw new IllegalStateException("Unexpected remote error", e); 255 } 256 } 257 258 259 public void finishLocked(int callingPid, int callingUid, IBinder token) { 260 if (mActiveSession == null || token != mActiveSession.mToken) { 261 Slog.w(TAG, "finish does not match active session"); 262 return; 263 } 264 mActiveSession.cancel(); 265 mActiveSession = null; 266 } 267 268 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) { 269 if (!mValid) { 270 pw.print(" NOT VALID: "); 271 if (mInfo == null) { 272 pw.println("no info"); 273 } else { 274 pw.println(mInfo.getParseError()); 275 } 276 return; 277 } 278 pw.print(" mComponent="); pw.println(mComponent.flattenToShortString()); 279 pw.print(" Session service="); pw.println(mInfo.getSessionService()); 280 pw.print(" Settings activity="); pw.println(mInfo.getSettingsActivity()); 281 pw.print(" mBound="); pw.print(mBound); pw.print(" mService="); pw.println(mService); 282 if (mActiveSession != null) { 283 pw.println(" Active session:"); 284 mActiveSession.dump(" ", pw); 285 } 286 } 287 288 void startLocked() { 289 Intent intent = new Intent(VoiceInteractionService.SERVICE_INTERFACE); 290 intent.setComponent(mComponent); 291 mBound = mContext.bindServiceAsUser(intent, mConnection, 292 Context.BIND_AUTO_CREATE, new UserHandle(mUser)); 293 if (!mBound) { 294 Slog.w(TAG, "Failed binding to voice interaction service " + mComponent); 295 } 296 } 297 298 void shutdownLocked() { 299 if (mBound) { 300 mContext.unbindService(mConnection); 301 mBound = false; 302 } 303 if (mValid) { 304 mContext.unregisterReceiver(mBroadcastReceiver); 305 } 306 } 307} 308