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