VoiceInteractionManagerServiceImpl.java revision 6daae9622672e0b38fc2efed29f68061d749cacc
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 try { 91 mService.ready(); 92 } catch (RemoteException e) { 93 } 94 } 95 } 96 97 @Override 98 public void onServiceDisconnected(ComponentName name) { 99 try { 100 if (mService != null) { 101 mService.shutdown(); 102 } 103 } catch (RemoteException e) { 104 Slog.w(TAG, "RemoteException in shutdown", e); 105 } 106 mService = null; 107 } 108 }; 109 110 final class SessionConnection implements ServiceConnection { 111 final IBinder mToken = new Binder(); 112 final Bundle mArgs; 113 boolean mBound; 114 IVoiceInteractionSessionService mService; 115 IVoiceInteractionSession mSession; 116 IVoiceInteractor mInteractor; 117 118 SessionConnection(Bundle args) { 119 mArgs = args; 120 Intent serviceIntent = new Intent(VoiceInteractionService.SERVICE_INTERFACE); 121 serviceIntent.setComponent(mSessionComponentName); 122 mBound = mContext.bindServiceAsUser(serviceIntent, this, 123 Context.BIND_AUTO_CREATE, new UserHandle(mUser)); 124 if (mBound) { 125 try { 126 mIWindowManager.addWindowToken(mToken, 127 WindowManager.LayoutParams.TYPE_VOICE_INTERACTION); 128 } catch (RemoteException e) { 129 Slog.w(TAG, "Failed adding window token", e); 130 } 131 } else { 132 Slog.w(TAG, "Failed binding to voice interaction session service " + mComponent); 133 } 134 } 135 136 @Override 137 public void onServiceConnected(ComponentName name, IBinder service) { 138 synchronized (mLock) { 139 mService = IVoiceInteractionSessionService.Stub.asInterface(service); 140 if (mActiveSession == this) { 141 try { 142 mService.newSession(mToken, mArgs); 143 } catch (RemoteException e) { 144 Slog.w(TAG, "Failed adding window token", e); 145 } 146 } 147 } 148 } 149 150 @Override 151 public void onServiceDisconnected(ComponentName name) { 152 mService = null; 153 } 154 155 public void cancel() { 156 if (mBound) { 157 if (mSession != null) { 158 try { 159 mSession.destroy(); 160 } catch (RemoteException e) { 161 Slog.w(TAG, "Voice interation session already dead"); 162 } 163 } 164 if (mSession != null) { 165 try { 166 mAm.finishVoiceTask(mSession); 167 } catch (RemoteException e) { 168 } 169 } 170 mContext.unbindService(this); 171 try { 172 mIWindowManager.removeWindowToken(mToken); 173 } catch (RemoteException e) { 174 Slog.w(TAG, "Failed removing window token", e); 175 } 176 mBound = false; 177 mService = null; 178 mSession = null; 179 mInteractor = null; 180 } 181 } 182 183 public void dump(String prefix, PrintWriter pw) { 184 pw.print(prefix); pw.print("mToken="); pw.println(mToken); 185 pw.print(prefix); pw.print("mArgs="); pw.println(mArgs); 186 pw.print(prefix); pw.print("mBound="); pw.println(mBound); 187 if (mBound) { 188 pw.print(prefix); pw.print("mService="); pw.println(mService); 189 pw.print(prefix); pw.print("mSession="); pw.println(mSession); 190 pw.print(prefix); pw.print("mInteractor="); pw.println(mInteractor); 191 } 192 } 193 }; 194 195 VoiceInteractionManagerServiceImpl(Context context, Handler handler, Object lock, 196 int userHandle, ComponentName service) { 197 mContext = context; 198 mHandler = handler; 199 mLock = lock; 200 mUser = userHandle; 201 mComponent = service; 202 mAm = ActivityManagerNative.getDefault(); 203 VoiceInteractionServiceInfo info; 204 try { 205 info = new VoiceInteractionServiceInfo(context.getPackageManager(), service); 206 } catch (PackageManager.NameNotFoundException e) { 207 Slog.w(TAG, "Voice interaction service not found: " + service); 208 mInfo = null; 209 mSessionComponentName = null; 210 mIWindowManager = null; 211 mValid = false; 212 return; 213 } 214 mInfo = info; 215 if (mInfo.getParseError() != null) { 216 Slog.w(TAG, "Bad voice interaction service: " + mInfo.getParseError()); 217 mSessionComponentName = null; 218 mIWindowManager = null; 219 mValid = false; 220 return; 221 } 222 mValid = true; 223 mSessionComponentName = new ComponentName(service.getPackageName(), 224 mInfo.getSessionService()); 225 mIWindowManager = IWindowManager.Stub.asInterface( 226 ServiceManager.getService(Context.WINDOW_SERVICE)); 227 IntentFilter filter = new IntentFilter(); 228 filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 229 mContext.registerReceiver(mBroadcastReceiver, filter, null, handler); 230 } 231 232 public void startSessionLocked(int callingPid, int callingUid, Bundle args) { 233 if (mActiveSession != null) { 234 mActiveSession.cancel(); 235 mActiveSession = null; 236 } 237 mActiveSession = new SessionConnection(args); 238 } 239 240 public boolean deliverNewSessionLocked(int callingPid, int callingUid, IBinder token, 241 IVoiceInteractionSession session, IVoiceInteractor interactor) { 242 if (mActiveSession == null || token != mActiveSession.mToken) { 243 Slog.w(TAG, "deliverNewSession does not match active session"); 244 return false; 245 } 246 mActiveSession.mSession = session; 247 mActiveSession.mInteractor = interactor; 248 return true; 249 } 250 251 public int startVoiceActivityLocked(int callingPid, int callingUid, IBinder token, 252 Intent intent, String resolvedType) { 253 try { 254 if (mActiveSession == null || token != mActiveSession.mToken) { 255 Slog.w(TAG, "startVoiceActivity does not match active session"); 256 return ActivityManager.START_CANCELED; 257 } 258 intent = new Intent(intent); 259 intent.addCategory(Intent.CATEGORY_VOICE); 260 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); 261 return mAm.startVoiceActivity(mComponent.getPackageName(), callingPid, callingUid, 262 intent, resolvedType, mActiveSession.mSession, mActiveSession.mInteractor, 263 0, null, null, null, mUser); 264 } catch (RemoteException e) { 265 throw new IllegalStateException("Unexpected remote error", e); 266 } 267 } 268 269 270 public void finishLocked(int callingPid, int callingUid, IBinder token) { 271 if (mActiveSession == null || token != mActiveSession.mToken) { 272 Slog.w(TAG, "finish does not match active session"); 273 return; 274 } 275 mActiveSession.cancel(); 276 mActiveSession = null; 277 } 278 279 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) { 280 if (!mValid) { 281 pw.print(" NOT VALID: "); 282 if (mInfo == null) { 283 pw.println("no info"); 284 } else { 285 pw.println(mInfo.getParseError()); 286 } 287 return; 288 } 289 pw.print(" mComponent="); pw.println(mComponent.flattenToShortString()); 290 pw.print(" Session service="); pw.println(mInfo.getSessionService()); 291 pw.print(" Settings activity="); pw.println(mInfo.getSettingsActivity()); 292 pw.print(" mBound="); pw.print(mBound); pw.print(" mService="); pw.println(mService); 293 if (mActiveSession != null) { 294 pw.println(" Active session:"); 295 mActiveSession.dump(" ", pw); 296 } 297 } 298 299 void startLocked() { 300 Intent intent = new Intent(VoiceInteractionService.SERVICE_INTERFACE); 301 intent.setComponent(mComponent); 302 mBound = mContext.bindServiceAsUser(intent, mConnection, 303 Context.BIND_AUTO_CREATE, new UserHandle(mUser)); 304 if (!mBound) { 305 Slog.w(TAG, "Failed binding to voice interaction service " + mComponent); 306 } 307 } 308 309 void shutdownLocked() { 310 if (mBound) { 311 mContext.unbindService(mConnection); 312 mBound = false; 313 } 314 if (mValid) { 315 mContext.unregisterReceiver(mBroadcastReceiver); 316 } 317 } 318 319 void notifySoundModelsChangedLocked() { 320 if (mService == null) { 321 Slog.w(TAG, "Not bound to voice interaction service " + mComponent); 322 } 323 try { 324 mService.soundModelsChanged(); 325 } catch (RemoteException e) { 326 Slog.w(TAG, "RemoteException while calling soundModelsChanged", e); 327 } 328 } 329} 330