VoiceInteractionSessionConnection.java revision a83ce1dd2ad3a6b71e90ff4845afc1299fe17b9d
1/* 2 * Copyright (C) 2015 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.AssistContent; 22import android.app.IActivityManager; 23import android.content.ClipData; 24import android.content.ComponentName; 25import android.content.ContentProvider; 26import android.content.Context; 27import android.content.Intent; 28import android.content.ServiceConnection; 29import android.net.Uri; 30import android.os.Binder; 31import android.os.Bundle; 32import android.os.IBinder; 33import android.os.RemoteException; 34import android.os.ServiceManager; 35import android.os.UserHandle; 36import android.service.voice.IVoiceInteractionSession; 37import android.service.voice.IVoiceInteractionSessionService; 38import android.service.voice.VoiceInteractionService; 39import android.util.Slog; 40import android.view.IWindowManager; 41import android.view.WindowManager; 42import com.android.internal.app.IVoiceInteractor; 43import com.android.internal.os.IResultReceiver; 44 45import java.io.PrintWriter; 46 47final class VoiceInteractionSessionConnection implements ServiceConnection { 48 final static String TAG = "VoiceInteractionServiceManager"; 49 50 final IBinder mToken = new Binder(); 51 final Object mLock; 52 final ComponentName mSessionComponentName; 53 final Intent mBindIntent; 54 final int mUser; 55 final Context mContext; 56 final Callback mCallback; 57 final int mCallingPid; 58 final int mCallingUid; 59 final IActivityManager mAm; 60 final IWindowManager mIWindowManager; 61 final IBinder mPermissionOwner; 62 boolean mShown; 63 Bundle mShowArgs; 64 int mShowFlags; 65 boolean mBound; 66 boolean mFullyBound; 67 boolean mCanceled; 68 IVoiceInteractionSessionService mService; 69 IVoiceInteractionSession mSession; 70 IVoiceInteractor mInteractor; 71 boolean mHaveAssistData; 72 Bundle mAssistData; 73 74 public interface Callback { 75 public void sessionConnectionGone(VoiceInteractionSessionConnection connection); 76 } 77 78 final ServiceConnection mFullConnection = new ServiceConnection() { 79 @Override 80 public void onServiceConnected(ComponentName name, IBinder service) { 81 } 82 @Override 83 public void onServiceDisconnected(ComponentName name) { 84 } 85 }; 86 87 final IResultReceiver mAssistReceiver = new IResultReceiver.Stub() { 88 @Override 89 public void send(int resultCode, Bundle resultData) throws RemoteException { 90 synchronized (mLock) { 91 if (mShown) { 92 mHaveAssistData = true; 93 mAssistData = resultData; 94 deliverAssistData(); 95 } 96 } 97 } 98 }; 99 100 public VoiceInteractionSessionConnection(Object lock, ComponentName component, int user, 101 Context context, Callback callback, int callingPid, int callingUid) { 102 mLock = lock; 103 mSessionComponentName = component; 104 mUser = user; 105 mContext = context; 106 mCallback = callback; 107 mCallingPid = callingPid; 108 mCallingUid = callingUid; 109 mAm = ActivityManagerNative.getDefault(); 110 mIWindowManager = IWindowManager.Stub.asInterface( 111 ServiceManager.getService(Context.WINDOW_SERVICE)); 112 IBinder permOwner = null; 113 try { 114 permOwner = mAm.newUriPermissionOwner("voicesession:" 115 + component.flattenToShortString()); 116 } catch (RemoteException e) { 117 Slog.w("voicesession", "AM dead", e); 118 } 119 mPermissionOwner = permOwner; 120 mBindIntent = new Intent(VoiceInteractionService.SERVICE_INTERFACE); 121 mBindIntent.setComponent(mSessionComponentName); 122 mBound = mContext.bindServiceAsUser(mBindIntent, this, 123 Context.BIND_AUTO_CREATE|Context.BIND_ALLOW_OOM_MANAGEMENT, 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 " 133 + mSessionComponentName); 134 } 135 } 136 137 public boolean showLocked(Bundle args, int flags) { 138 if (mBound) { 139 if (!mFullyBound) { 140 mFullyBound = mContext.bindServiceAsUser(mBindIntent, mFullConnection, 141 Context.BIND_AUTO_CREATE|Context.BIND_TREAT_LIKE_ACTIVITY, 142 new UserHandle(mUser)); 143 } 144 mShown = true; 145 mShowArgs = args; 146 mShowFlags = flags; 147 if ((flags&VoiceInteractionService.START_WITH_ASSIST) != 0) { 148 try { 149 mAm.requestAssistContextExtras(ActivityManager.ASSIST_CONTEXT_FULL, 150 mAssistReceiver); 151 } catch (RemoteException e) { 152 } 153 } else { 154 mHaveAssistData = false; 155 mAssistData = null; 156 } 157 if (mSession != null) { 158 try { 159 mSession.show(mShowArgs, mShowFlags); 160 mShowArgs = null; 161 mShowFlags = 0; 162 } catch (RemoteException e) { 163 } 164 deliverAssistData(); 165 } 166 return true; 167 } 168 return false; 169 } 170 171 void grantUriPermission(Uri uri, int mode, int srcUid, int destUid, String destPkg) { 172 if (!"content".equals(uri.getScheme())) { 173 return; 174 } 175 long ident = Binder.clearCallingIdentity(); 176 try { 177 // This will throw SecurityException for us. 178 mAm.checkGrantUriPermission(srcUid, null, ContentProvider.getUriWithoutUserId(uri), 179 mode, ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(srcUid))); 180 // No security exception, do the grant. 181 int sourceUserId = ContentProvider.getUserIdFromUri(uri, mUser); 182 uri = ContentProvider.getUriWithoutUserId(uri); 183 mAm.grantUriPermissionFromOwner(mPermissionOwner, srcUid, destPkg, 184 uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, sourceUserId, mUser); 185 } catch (RemoteException e) { 186 } catch (SecurityException e) { 187 Slog.w(TAG, "Can't propagate permission", e); 188 } finally { 189 Binder.restoreCallingIdentity(ident); 190 } 191 192 } 193 194 void grantClipDataItemPermission(ClipData.Item item, int mode, int srcUid, int destUid, 195 String destPkg) { 196 if (item.getUri() != null) { 197 grantUriPermission(item.getUri(), mode, srcUid, destUid, destPkg); 198 } 199 Intent intent = item.getIntent(); 200 if (intent != null && intent.getData() != null) { 201 grantUriPermission(intent.getData(), mode, srcUid, destUid, destPkg); 202 } 203 } 204 205 void grantClipDataPermissions(ClipData data, int mode, int srcUid, int destUid, 206 String destPkg) { 207 final int N = data.getItemCount(); 208 for (int i=0; i<N; i++) { 209 grantClipDataItemPermission(data.getItemAt(i), mode, srcUid, destUid, destPkg); 210 } 211 } 212 213 void deliverAssistData() { 214 if (mSession == null || !mHaveAssistData) { 215 return; 216 } 217 if (mAssistData != null) { 218 int uid = mAssistData.getInt(Intent.EXTRA_ASSIST_UID, -1); 219 if (uid >= 0) { 220 Bundle assistContext = mAssistData.getBundle(Intent.EXTRA_ASSIST_CONTEXT); 221 if (assistContext != null) { 222 AssistContent content = AssistContent.getAssistContent(assistContext); 223 if (content != null) { 224 Intent intent = content.getIntent(); 225 if (intent != null) { 226 ClipData data = intent.getClipData(); 227 if (data != null && Intent.isAccessUriMode(intent.getFlags())) { 228 grantClipDataPermissions(data, intent.getFlags(), uid, 229 mCallingUid, mSessionComponentName.getPackageName()); 230 } 231 } 232 ClipData data = content.getClipData(); 233 if (data != null) { 234 grantClipDataPermissions(data, Intent.FLAG_GRANT_READ_URI_PERMISSION, 235 uid, mCallingUid, mSessionComponentName.getPackageName()); 236 } 237 } 238 } 239 } 240 } 241 try { 242 mSession.handleAssist(mAssistData); 243 mAssistData = null; 244 mHaveAssistData = false; 245 } catch (RemoteException e) { 246 } 247 } 248 249 public boolean hideLocked() { 250 if (mBound) { 251 if (mShown) { 252 mShown = false; 253 mShowArgs = null; 254 mShowFlags = 0; 255 mHaveAssistData = false; 256 mAssistData = null; 257 if (mSession != null) { 258 try { 259 mSession.hide(); 260 } catch (RemoteException e) { 261 } 262 } 263 try { 264 mAm.revokeUriPermissionFromOwner(mPermissionOwner, null, 265 Intent.FLAG_GRANT_READ_URI_PERMISSION 266 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION, 267 mUser); 268 } catch (RemoteException e) { 269 } 270 } 271 if (mFullyBound) { 272 mContext.unbindService(mFullConnection); 273 mFullyBound = false; 274 } 275 return true; 276 } 277 return false; 278 } 279 280 public boolean deliverNewSessionLocked(IVoiceInteractionSession session, 281 IVoiceInteractor interactor) { 282 mSession = session; 283 mInteractor = interactor; 284 if (mShown) { 285 try { 286 session.show(mShowArgs, mShowFlags); 287 mShowArgs = null; 288 mShowFlags = 0; 289 } catch (RemoteException e) { 290 } 291 if (mHaveAssistData) { 292 try { 293 session.handleAssist(mAssistData); 294 mAssistData = null; 295 mHaveAssistData = false; 296 } catch (RemoteException e) { 297 } 298 } 299 } 300 return true; 301 } 302 303 @Override 304 public void onServiceConnected(ComponentName name, IBinder service) { 305 synchronized (mLock) { 306 mService = IVoiceInteractionSessionService.Stub.asInterface(service); 307 if (!mCanceled) { 308 try { 309 mService.newSession(mToken, mShowArgs, mShowFlags); 310 } catch (RemoteException e) { 311 Slog.w(TAG, "Failed adding window token", e); 312 } 313 } 314 } 315 } 316 317 @Override 318 public void onServiceDisconnected(ComponentName name) { 319 mCallback.sessionConnectionGone(this); 320 mService = null; 321 } 322 323 public void cancel() { 324 mCanceled = true; 325 if (mBound) { 326 if (mSession != null) { 327 try { 328 mSession.destroy(); 329 } catch (RemoteException e) { 330 Slog.w(TAG, "Voice interation session already dead"); 331 } 332 } 333 if (mSession != null) { 334 try { 335 mAm.finishVoiceTask(mSession); 336 } catch (RemoteException e) { 337 } 338 } 339 mContext.unbindService(this); 340 try { 341 mIWindowManager.removeWindowToken(mToken); 342 } catch (RemoteException e) { 343 Slog.w(TAG, "Failed removing window token", e); 344 } 345 mBound = false; 346 mService = null; 347 mSession = null; 348 mInteractor = null; 349 } 350 if (mFullyBound) { 351 mContext.unbindService(mFullConnection); 352 mFullyBound = false; 353 } 354 } 355 356 public void dump(String prefix, PrintWriter pw) { 357 pw.print(prefix); pw.print("mToken="); pw.println(mToken); 358 pw.print(prefix); pw.print("mShown="); pw.println(mShown); 359 pw.print(prefix); pw.print("mShowArgs="); pw.println(mShowArgs); 360 pw.print(prefix); pw.print("mShowFlags=0x"); pw.println(Integer.toHexString(mShowFlags)); 361 pw.print(prefix); pw.print("mBound="); pw.println(mBound); 362 if (mBound) { 363 pw.print(prefix); pw.print("mService="); pw.println(mService); 364 pw.print(prefix); pw.print("mSession="); pw.println(mSession); 365 pw.print(prefix); pw.print("mInteractor="); pw.println(mInteractor); 366 } 367 pw.print(prefix); pw.print("mHaveAssistData="); pw.println(mHaveAssistData); 368 if (mHaveAssistData) { 369 pw.print(prefix); pw.print("mAssistData="); pw.println(mAssistData); 370 } 371 } 372}; 373