VoiceInteractionManagerServiceImpl.java revision c03c9167c2d9a1e22fb2b176b00a0524177fb037
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.content.pm.ServiceInfo; 30import android.os.Binder; 31import android.os.Bundle; 32import android.os.Handler; 33import android.os.IBinder; 34import android.os.RemoteException; 35import android.os.ServiceManager; 36import android.os.UserHandle; 37import android.service.voice.IVoiceInteractionService; 38import android.service.voice.IVoiceInteractionSession; 39import android.service.voice.IVoiceInteractionSessionService; 40import android.service.voice.VoiceInteractionService; 41import android.service.voice.VoiceInteractionServiceInfo; 42import android.util.Slog; 43import android.view.IWindowManager; 44import android.view.WindowManager; 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_INPUT_METHOD); 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 mContext.unbindService(this); 154 try { 155 mIWindowManager.removeWindowToken(mToken); 156 } catch (RemoteException e) { 157 Slog.w(TAG, "Failed removing window token", e); 158 } 159 mBound = false; 160 mService = null; 161 mSession = null; 162 mInteractor = null; 163 } 164 } 165 166 public void dump(String prefix, PrintWriter pw) { 167 pw.print(prefix); pw.print("mToken="); pw.println(mToken); 168 pw.print(prefix); pw.print("mArgs="); pw.println(mArgs); 169 pw.print(prefix); pw.print("mBound="); pw.println(mBound); 170 if (mBound) { 171 pw.print(prefix); pw.print("mService="); pw.println(mService); 172 pw.print(prefix); pw.print("mSession="); pw.println(mSession); 173 pw.print(prefix); pw.print("mInteractor="); pw.println(mInteractor); 174 } 175 } 176 }; 177 178 VoiceInteractionManagerServiceImpl(Context context, Handler handler, Object lock, 179 int userHandle, ComponentName service) { 180 mContext = context; 181 mHandler = handler; 182 mLock = lock; 183 mUser = userHandle; 184 mComponent = service; 185 mAm = ActivityManagerNative.getDefault(); 186 VoiceInteractionServiceInfo info; 187 try { 188 info = new VoiceInteractionServiceInfo(context.getPackageManager(), service); 189 } catch (PackageManager.NameNotFoundException e) { 190 Slog.w(TAG, "Voice interaction service not found: " + service); 191 mInfo = null; 192 mSessionComponentName = null; 193 mIWindowManager = null; 194 mValid = false; 195 return; 196 } 197 mInfo = info; 198 if (mInfo.getParseError() != null) { 199 Slog.w(TAG, "Bad voice interaction service: " + mInfo.getParseError()); 200 mSessionComponentName = null; 201 mIWindowManager = null; 202 mValid = false; 203 return; 204 } 205 mValid = true; 206 mSessionComponentName = new ComponentName(service.getPackageName(), 207 mInfo.getSessionService()); 208 mIWindowManager = IWindowManager.Stub.asInterface( 209 ServiceManager.getService(Context.WINDOW_SERVICE)); 210 IntentFilter filter = new IntentFilter(); 211 filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 212 mContext.registerReceiver(mBroadcastReceiver, filter, null, handler); 213 } 214 215 public void startSessionLocked(int callingPid, int callingUid, Bundle args) { 216 if (mActiveSession != null) { 217 mActiveSession.cancel(); 218 mActiveSession = null; 219 } 220 mActiveSession = new SessionConnection(args); 221 } 222 223 public boolean deliverNewSessionLocked(int callingPid, int callingUid, IBinder token, 224 IVoiceInteractionSession session, IVoiceInteractor interactor) { 225 if (mActiveSession == null || token != mActiveSession.mToken) { 226 Slog.w(TAG, "deliverNewSession does not match active session"); 227 return false; 228 } 229 mActiveSession.mSession = session; 230 mActiveSession.mInteractor = interactor; 231 return true; 232 } 233 234 public int startVoiceActivityLocked(int callingPid, int callingUid, IBinder token, 235 Intent intent, String resolvedType) { 236 try { 237 if (mActiveSession == null || token != mActiveSession.mToken) { 238 Slog.w(TAG, "startVoiceActivity does not match active session"); 239 return ActivityManager.START_CANCELED; 240 } 241 intent = new Intent(intent); 242 intent.addCategory(Intent.CATEGORY_VOICE); 243 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); 244 return mAm.startVoiceActivity(mComponent.getPackageName(), callingPid, callingUid, 245 intent, resolvedType, mActiveSession.mSession, mActiveSession.mInteractor, 246 0, null, null, null, mUser); 247 } catch (RemoteException e) { 248 throw new IllegalStateException("Unexpected remote error", e); 249 } 250 } 251 252 253 public void finishLocked(int callingPid, int callingUid, IBinder token) { 254 if (mActiveSession == null || token != mActiveSession.mToken) { 255 Slog.w(TAG, "finish does not match active session"); 256 return; 257 } 258 mActiveSession.cancel(); 259 mActiveSession = null; 260 } 261 262 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) { 263 if (!mValid) { 264 pw.print(" NOT VALID: "); 265 if (mInfo == null) { 266 pw.println("no info"); 267 } else { 268 pw.println(mInfo.getParseError()); 269 } 270 return; 271 } 272 pw.print(" mComponent="); pw.println(mComponent.flattenToShortString()); 273 pw.print(" Session service="); pw.println(mInfo.getSessionService()); 274 pw.print(" Settings activity="); pw.println(mInfo.getSettingsActivity()); 275 pw.print(" mBound="); pw.print(mBound); pw.print(" mService="); pw.println(mService); 276 if (mActiveSession != null) { 277 pw.println(" Active session:"); 278 mActiveSession.dump(" ", pw); 279 } 280 } 281 282 void startLocked() { 283 Intent intent = new Intent(VoiceInteractionService.SERVICE_INTERFACE); 284 intent.setComponent(mComponent); 285 mBound = mContext.bindServiceAsUser(intent, mConnection, 286 Context.BIND_AUTO_CREATE, new UserHandle(mUser)); 287 if (!mBound) { 288 Slog.w(TAG, "Failed binding to voice interaction service " + mComponent); 289 } 290 } 291 292 void shutdownLocked() { 293 if (mBound) { 294 mContext.unbindService(mConnection); 295 mBound = false; 296 } 297 if (mValid) { 298 mContext.unregisterReceiver(mBroadcastReceiver); 299 } 300 } 301} 302