VoiceInteractionManagerServiceImpl.java revision 6ac42aeed905181b484f97a53db57a17134ef7a8
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.Bundle; 30import android.os.Handler; 31import android.os.IBinder; 32import android.os.RemoteException; 33import android.os.ServiceManager; 34import android.os.UserHandle; 35import android.service.voice.IVoiceInteractionService; 36import android.service.voice.IVoiceInteractionSession; 37import android.service.voice.VoiceInteractionService; 38import android.service.voice.VoiceInteractionServiceInfo; 39import android.util.PrintWriterPrinter; 40import android.util.Slog; 41import android.view.IWindowManager; 42 43import com.android.internal.app.IVoiceInteractionSessionShowCallback; 44import com.android.internal.app.IVoiceInteractor; 45 46import java.io.FileDescriptor; 47import java.io.PrintWriter; 48 49class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConnection.Callback { 50 final static String TAG = "VoiceInteractionServiceManager"; 51 52 final static String CLOSE_REASON_VOICE_INTERACTION = "voiceinteraction"; 53 54 final boolean mValid; 55 56 final Context mContext; 57 final Handler mHandler; 58 final Object mLock; 59 final int mUser; 60 final ComponentName mComponent; 61 final IActivityManager mAm; 62 final VoiceInteractionServiceInfo mInfo; 63 final ComponentName mSessionComponentName; 64 final IWindowManager mIWindowManager; 65 boolean mBound = false; 66 IVoiceInteractionService mService; 67 68 VoiceInteractionSessionConnection mActiveSession; 69 int mDisabledShowContext; 70 71 final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 72 @Override 73 public void onReceive(Context context, Intent intent) { 74 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) { 75 String reason = intent.getStringExtra("reason"); 76 if (!CLOSE_REASON_VOICE_INTERACTION.equals(reason) && !"dream".equals(reason)) { 77 synchronized (mLock) { 78 if (mActiveSession != null && mActiveSession.mSession != null) { 79 try { 80 mActiveSession.mSession.closeSystemDialogs(); 81 } catch (RemoteException e) { 82 } 83 } 84 } 85 } 86 } 87 } 88 }; 89 90 final ServiceConnection mConnection = new ServiceConnection() { 91 @Override 92 public void onServiceConnected(ComponentName name, IBinder service) { 93 synchronized (mLock) { 94 mService = IVoiceInteractionService.Stub.asInterface(service); 95 try { 96 mService.ready(); 97 } catch (RemoteException e) { 98 } 99 } 100 } 101 102 @Override 103 public void onServiceDisconnected(ComponentName name) { 104 mService = null; 105 } 106 }; 107 108 VoiceInteractionManagerServiceImpl(Context context, Handler handler, Object lock, 109 int userHandle, ComponentName service) { 110 mContext = context; 111 mHandler = handler; 112 mLock = lock; 113 mUser = userHandle; 114 mComponent = service; 115 mAm = ActivityManagerNative.getDefault(); 116 VoiceInteractionServiceInfo info; 117 try { 118 info = new VoiceInteractionServiceInfo(context.getPackageManager(), service, mUser); 119 } catch (PackageManager.NameNotFoundException e) { 120 Slog.w(TAG, "Voice interaction service not found: " + service, e); 121 mInfo = null; 122 mSessionComponentName = null; 123 mIWindowManager = null; 124 mValid = false; 125 return; 126 } 127 mInfo = info; 128 if (mInfo.getParseError() != null) { 129 Slog.w(TAG, "Bad voice interaction service: " + mInfo.getParseError()); 130 mSessionComponentName = null; 131 mIWindowManager = null; 132 mValid = false; 133 return; 134 } 135 mValid = true; 136 mSessionComponentName = new ComponentName(service.getPackageName(), 137 mInfo.getSessionService()); 138 mIWindowManager = IWindowManager.Stub.asInterface( 139 ServiceManager.getService(Context.WINDOW_SERVICE)); 140 IntentFilter filter = new IntentFilter(); 141 filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 142 mContext.registerReceiver(mBroadcastReceiver, filter, null, handler); 143 } 144 145 public boolean showSessionLocked(Bundle args, int flags, 146 IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken) { 147 if (mActiveSession == null) { 148 mActiveSession = new VoiceInteractionSessionConnection(mLock, mSessionComponentName, 149 mUser, mContext, this, mInfo.getServiceInfo().applicationInfo.uid, mHandler); 150 } 151 return mActiveSession.showLocked(args, flags, mDisabledShowContext, showCallback, 152 activityToken); 153 } 154 155 public boolean hideSessionLocked() { 156 if (mActiveSession != null) { 157 return mActiveSession.hideLocked(); 158 } 159 return false; 160 } 161 162 public boolean deliverNewSessionLocked(IBinder token, 163 IVoiceInteractionSession session, IVoiceInteractor interactor) { 164 if (mActiveSession == null || token != mActiveSession.mToken) { 165 Slog.w(TAG, "deliverNewSession does not match active session"); 166 return false; 167 } 168 mActiveSession.deliverNewSessionLocked(session, interactor); 169 return true; 170 } 171 172 public int startVoiceActivityLocked(int callingPid, int callingUid, IBinder token, 173 Intent intent, String resolvedType) { 174 try { 175 if (mActiveSession == null || token != mActiveSession.mToken) { 176 Slog.w(TAG, "startVoiceActivity does not match active session"); 177 return ActivityManager.START_CANCELED; 178 } 179 if (!mActiveSession.mShown) { 180 Slog.w(TAG, "startVoiceActivity not allowed on hidden session"); 181 return ActivityManager.START_CANCELED; 182 } 183 intent = new Intent(intent); 184 intent.addCategory(Intent.CATEGORY_VOICE); 185 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); 186 return mAm.startVoiceActivity(mComponent.getPackageName(), callingPid, callingUid, 187 intent, resolvedType, mActiveSession.mSession, mActiveSession.mInteractor, 188 0, null, null, mUser); 189 } catch (RemoteException e) { 190 throw new IllegalStateException("Unexpected remote error", e); 191 } 192 } 193 194 public void setKeepAwakeLocked(IBinder token, boolean keepAwake) { 195 try { 196 if (mActiveSession == null || token != mActiveSession.mToken) { 197 Slog.w(TAG, "setKeepAwake does not match active session"); 198 return; 199 } 200 mAm.setVoiceKeepAwake(mActiveSession.mSession, keepAwake); 201 } catch (RemoteException e) { 202 throw new IllegalStateException("Unexpected remote error", e); 203 } 204 } 205 206 public void closeSystemDialogsLocked(IBinder token) { 207 try { 208 if (mActiveSession == null || token != mActiveSession.mToken) { 209 Slog.w(TAG, "closeSystemDialogs does not match active session"); 210 return; 211 } 212 mAm.closeSystemDialogs(CLOSE_REASON_VOICE_INTERACTION); 213 } catch (RemoteException e) { 214 throw new IllegalStateException("Unexpected remote error", e); 215 } 216 } 217 218 public void finishLocked(IBinder token) { 219 if (mActiveSession == null || token != mActiveSession.mToken) { 220 Slog.w(TAG, "finish does not match active session"); 221 return; 222 } 223 mActiveSession.cancelLocked(); 224 mActiveSession = null; 225 } 226 227 public void setDisabledShowContextLocked(int callingUid, int flags) { 228 int activeUid = mInfo.getServiceInfo().applicationInfo.uid; 229 if (callingUid != activeUid) { 230 throw new SecurityException("Calling uid " + callingUid 231 + " does not match active uid " + activeUid); 232 } 233 mDisabledShowContext = flags; 234 } 235 236 public int getDisabledShowContextLocked(int callingUid) { 237 int activeUid = mInfo.getServiceInfo().applicationInfo.uid; 238 if (callingUid != activeUid) { 239 throw new SecurityException("Calling uid " + callingUid 240 + " does not match active uid " + activeUid); 241 } 242 return mDisabledShowContext; 243 } 244 245 public int getUserDisabledShowContextLocked(int callingUid) { 246 int activeUid = mInfo.getServiceInfo().applicationInfo.uid; 247 if (callingUid != activeUid) { 248 throw new SecurityException("Calling uid " + callingUid 249 + " does not match active uid " + activeUid); 250 } 251 return mActiveSession != null ? mActiveSession.getUserDisabledShowContextLocked() : 0; 252 } 253 254 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) { 255 if (!mValid) { 256 pw.print(" NOT VALID: "); 257 if (mInfo == null) { 258 pw.println("no info"); 259 } else { 260 pw.println(mInfo.getParseError()); 261 } 262 return; 263 } 264 pw.print(" mUser="); pw.println(mUser); 265 pw.print(" mComponent="); pw.println(mComponent.flattenToShortString()); 266 pw.print(" Session service="); pw.println(mInfo.getSessionService()); 267 pw.println(" Service info:"); 268 mInfo.getServiceInfo().dump(new PrintWriterPrinter(pw), " "); 269 pw.print(" Recognition service="); pw.println(mInfo.getRecognitionService()); 270 pw.print(" Settings activity="); pw.println(mInfo.getSettingsActivity()); 271 pw.print(" Supports assist="); pw.println(mInfo.getSupportsAssist()); 272 pw.print(" Supports launch from keyguard="); 273 pw.println(mInfo.getSupportsLaunchFromKeyguard()); 274 if (mDisabledShowContext != 0) { 275 pw.print(" mDisabledShowContext="); 276 pw.println(Integer.toHexString(mDisabledShowContext)); 277 } 278 pw.print(" mBound="); pw.print(mBound); pw.print(" mService="); pw.println(mService); 279 if (mActiveSession != null) { 280 pw.println(" Active session:"); 281 mActiveSession.dump(" ", pw); 282 } 283 } 284 285 void startLocked() { 286 Intent intent = new Intent(VoiceInteractionService.SERVICE_INTERFACE); 287 intent.setComponent(mComponent); 288 mBound = mContext.bindServiceAsUser(intent, mConnection, 289 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, new UserHandle(mUser)); 290 if (!mBound) { 291 Slog.w(TAG, "Failed binding to voice interaction service " + mComponent); 292 } 293 } 294 295 public void launchVoiceAssistFromKeyguard() { 296 if (mService == null) { 297 Slog.w(TAG, "Not bound to voice interaction service " + mComponent); 298 return; 299 } 300 try { 301 mService.launchVoiceAssistFromKeyguard(); 302 } catch (RemoteException e) { 303 Slog.w(TAG, "RemoteException while calling launchVoiceAssistFromKeyguard", e); 304 } 305 } 306 307 void shutdownLocked() { 308 // If there is an active session, cancel it to allow it to clean up its window and other 309 // state. 310 if (mActiveSession != null) { 311 mActiveSession.cancelLocked(); 312 mActiveSession = null; 313 } 314 try { 315 if (mService != null) { 316 mService.shutdown(); 317 } 318 } catch (RemoteException e) { 319 Slog.w(TAG, "RemoteException in shutdown", e); 320 } 321 322 if (mBound) { 323 mContext.unbindService(mConnection); 324 mBound = false; 325 } 326 if (mValid) { 327 mContext.unregisterReceiver(mBroadcastReceiver); 328 } 329 } 330 331 void notifySoundModelsChangedLocked() { 332 if (mService == null) { 333 Slog.w(TAG, "Not bound to voice interaction service " + mComponent); 334 return; 335 } 336 try { 337 mService.soundModelsChanged(); 338 } catch (RemoteException e) { 339 Slog.w(TAG, "RemoteException while calling soundModelsChanged", e); 340 } 341 } 342 343 @Override 344 public void sessionConnectionGone(VoiceInteractionSessionConnection connection) { 345 synchronized (mLock) { 346 finishLocked(connection.mToken); 347 } 348 } 349} 350