VoiceInteractionManagerServiceImpl.java revision d69e4c1460017062e7c36be55801cb434ad19d97
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.Slog; 40import android.view.IWindowManager; 41 42import com.android.internal.app.IVoiceInteractionSessionShowCallback; 43import com.android.internal.app.IVoiceInteractor; 44 45import java.io.FileDescriptor; 46import java.io.PrintWriter; 47 48class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConnection.Callback { 49 final static String TAG = "VoiceInteractionServiceManager"; 50 51 final boolean mValid; 52 53 final Context mContext; 54 final Handler mHandler; 55 final Object mLock; 56 final int mUser; 57 final ComponentName mComponent; 58 final IActivityManager mAm; 59 final VoiceInteractionServiceInfo mInfo; 60 final ComponentName mSessionComponentName; 61 final IWindowManager mIWindowManager; 62 boolean mBound = false; 63 IVoiceInteractionService mService; 64 65 VoiceInteractionSessionConnection mActiveSession; 66 67 final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 68 @Override 69 public void onReceive(Context context, Intent intent) { 70 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) { 71 synchronized (mLock) { 72 if (mActiveSession != null && mActiveSession.mSession != null) { 73 try { 74 mActiveSession.mSession.closeSystemDialogs(); 75 } catch (RemoteException e) { 76 } 77 } 78 } 79 } 80 } 81 }; 82 83 final ServiceConnection mConnection = new ServiceConnection() { 84 @Override 85 public void onServiceConnected(ComponentName name, IBinder service) { 86 synchronized (mLock) { 87 mService = IVoiceInteractionService.Stub.asInterface(service); 88 try { 89 mService.ready(); 90 } catch (RemoteException e) { 91 } 92 } 93 } 94 95 @Override 96 public void onServiceDisconnected(ComponentName name) { 97 mService = null; 98 } 99 }; 100 101 VoiceInteractionManagerServiceImpl(Context context, Handler handler, Object lock, 102 int userHandle, ComponentName service) { 103 mContext = context; 104 mHandler = handler; 105 mLock = lock; 106 mUser = userHandle; 107 mComponent = service; 108 mAm = ActivityManagerNative.getDefault(); 109 VoiceInteractionServiceInfo info; 110 try { 111 info = new VoiceInteractionServiceInfo(context.getPackageManager(), service); 112 } catch (PackageManager.NameNotFoundException e) { 113 Slog.w(TAG, "Voice interaction service not found: " + service); 114 mInfo = null; 115 mSessionComponentName = null; 116 mIWindowManager = null; 117 mValid = false; 118 return; 119 } 120 mInfo = info; 121 if (mInfo.getParseError() != null) { 122 Slog.w(TAG, "Bad voice interaction service: " + mInfo.getParseError()); 123 mSessionComponentName = null; 124 mIWindowManager = null; 125 mValid = false; 126 return; 127 } 128 mValid = true; 129 mSessionComponentName = new ComponentName(service.getPackageName(), 130 mInfo.getSessionService()); 131 mIWindowManager = IWindowManager.Stub.asInterface( 132 ServiceManager.getService(Context.WINDOW_SERVICE)); 133 IntentFilter filter = new IntentFilter(); 134 filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 135 mContext.registerReceiver(mBroadcastReceiver, filter, null, handler); 136 } 137 138 public boolean showSessionLocked(Bundle args, int flags, 139 IVoiceInteractionSessionShowCallback showCallback) { 140 if (mActiveSession == null) { 141 mActiveSession = new VoiceInteractionSessionConnection(mLock, mSessionComponentName, 142 mUser, mContext, this, mInfo.getServiceInfo().applicationInfo.uid); 143 } 144 return mActiveSession.showLocked(args, flags, showCallback); 145 } 146 147 public boolean hideSessionLocked(int callingPid, int callingUid) { 148 if (mActiveSession != null) { 149 return mActiveSession.hideLocked(); 150 } 151 return false; 152 } 153 154 public boolean deliverNewSessionLocked(int callingPid, int callingUid, IBinder token, 155 IVoiceInteractionSession session, IVoiceInteractor interactor) { 156 if (mActiveSession == null || token != mActiveSession.mToken) { 157 Slog.w(TAG, "deliverNewSession does not match active session"); 158 return false; 159 } 160 mActiveSession.deliverNewSessionLocked(session, interactor); 161 return true; 162 } 163 164 public int startVoiceActivityLocked(int callingPid, int callingUid, IBinder token, 165 Intent intent, String resolvedType) { 166 try { 167 if (mActiveSession == null || token != mActiveSession.mToken) { 168 Slog.w(TAG, "startVoiceActivity does not match active session"); 169 return ActivityManager.START_CANCELED; 170 } 171 if (!mActiveSession.mShown) { 172 Slog.w(TAG, "startVoiceActivity not allowed on hidden session"); 173 return ActivityManager.START_CANCELED; 174 } 175 intent = new Intent(intent); 176 intent.addCategory(Intent.CATEGORY_VOICE); 177 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); 178 return mAm.startVoiceActivity(mComponent.getPackageName(), callingPid, callingUid, 179 intent, resolvedType, mActiveSession.mSession, mActiveSession.mInteractor, 180 0, null, null, mUser); 181 } catch (RemoteException e) { 182 throw new IllegalStateException("Unexpected remote error", e); 183 } 184 } 185 186 public void setKeepAwakeLocked(int callingPid, int callingUid, IBinder token, 187 boolean keepAwake) { 188 try { 189 if (mActiveSession == null || token != mActiveSession.mToken) { 190 Slog.w(TAG, "setKeepAwake does not match active session"); 191 return; 192 } 193 mAm.setVoiceKeepAwake(mActiveSession.mSession, keepAwake); 194 } catch (RemoteException e) { 195 throw new IllegalStateException("Unexpected remote error", e); 196 } 197 } 198 199 public void finishLocked(IBinder token) { 200 if (mActiveSession == null || token != mActiveSession.mToken) { 201 Slog.w(TAG, "finish does not match active session"); 202 return; 203 } 204 mActiveSession.cancel(); 205 mActiveSession = null; 206 } 207 208 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) { 209 if (!mValid) { 210 pw.print(" NOT VALID: "); 211 if (mInfo == null) { 212 pw.println("no info"); 213 } else { 214 pw.println(mInfo.getParseError()); 215 } 216 return; 217 } 218 pw.print(" mComponent="); pw.println(mComponent.flattenToShortString()); 219 pw.print(" Session service="); pw.println(mInfo.getSessionService()); 220 pw.print(" Settings activity="); pw.println(mInfo.getSettingsActivity()); 221 pw.print(" mBound="); pw.print(mBound); pw.print(" mService="); pw.println(mService); 222 if (mActiveSession != null) { 223 pw.println(" Active session:"); 224 mActiveSession.dump(" ", pw); 225 } 226 } 227 228 void startLocked() { 229 Intent intent = new Intent(VoiceInteractionService.SERVICE_INTERFACE); 230 intent.setComponent(mComponent); 231 mBound = mContext.bindServiceAsUser(intent, mConnection, 232 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, new UserHandle(mUser)); 233 if (!mBound) { 234 Slog.w(TAG, "Failed binding to voice interaction service " + mComponent); 235 } 236 } 237 238 void shutdownLocked() { 239 try { 240 if (mService != null) { 241 mService.shutdown(); 242 } 243 } catch (RemoteException e) { 244 Slog.w(TAG, "RemoteException in shutdown", e); 245 } 246 247 if (mBound) { 248 mContext.unbindService(mConnection); 249 mBound = false; 250 } 251 if (mValid) { 252 mContext.unregisterReceiver(mBroadcastReceiver); 253 } 254 } 255 256 void notifySoundModelsChangedLocked() { 257 if (mService == null) { 258 Slog.w(TAG, "Not bound to voice interaction service " + mComponent); 259 } 260 try { 261 mService.soundModelsChanged(); 262 } catch (RemoteException e) { 263 Slog.w(TAG, "RemoteException while calling soundModelsChanged", e); 264 } 265 } 266 267 @Override 268 public void sessionConnectionGone(VoiceInteractionSessionConnection connection) { 269 synchronized (mLock) { 270 finishLocked(connection.mToken); 271 } 272 } 273} 274