VoiceInteractionSessionConnection.java revision d59a5d59df920d743723521a2afed9de1da3373b
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.AppOpsManager; 22import android.app.AssistContent; 23import android.app.IActivityManager; 24import android.content.ClipData; 25import android.content.ComponentName; 26import android.content.ContentProvider; 27import android.content.Context; 28import android.content.Intent; 29import android.content.ServiceConnection; 30import android.graphics.Bitmap; 31import android.net.Uri; 32import android.os.Binder; 33import android.os.Bundle; 34import android.os.IBinder; 35import android.os.RemoteException; 36import android.os.ServiceManager; 37import android.os.UserHandle; 38import android.service.voice.IVoiceInteractionSession; 39import android.service.voice.IVoiceInteractionSessionService; 40import android.service.voice.VoiceInteractionService; 41import android.util.Slog; 42import android.view.IWindowManager; 43import android.view.WindowManager; 44import com.android.internal.app.IAssistScreenshotReceiver; 45import com.android.internal.app.IVoiceInteractionSessionShowCallback; 46import com.android.internal.app.IVoiceInteractor; 47import com.android.internal.os.IResultReceiver; 48 49import java.io.PrintWriter; 50import java.util.ArrayList; 51 52final class VoiceInteractionSessionConnection implements ServiceConnection { 53 final static String TAG = "VoiceInteractionServiceManager"; 54 55 final IBinder mToken = new Binder(); 56 final Object mLock; 57 final ComponentName mSessionComponentName; 58 final Intent mBindIntent; 59 final int mUser; 60 final Context mContext; 61 final Callback mCallback; 62 final int mCallingPid; 63 final int mCallingUid; 64 final IActivityManager mAm; 65 final IWindowManager mIWindowManager; 66 final AppOpsManager mAppOps; 67 final IBinder mPermissionOwner; 68 boolean mShown; 69 Bundle mShowArgs; 70 int mShowFlags; 71 boolean mBound; 72 boolean mFullyBound; 73 boolean mCanceled; 74 IVoiceInteractionSessionService mService; 75 IVoiceInteractionSession mSession; 76 IVoiceInteractor mInteractor; 77 boolean mHaveAssistData; 78 Bundle mAssistData; 79 boolean mHaveScreenshot; 80 Bitmap mScreenshot; 81 ArrayList<IVoiceInteractionSessionShowCallback> mPendingShowCallbacks = new ArrayList<>(); 82 83 IVoiceInteractionSessionShowCallback mShowCallback = 84 new IVoiceInteractionSessionShowCallback.Stub() { 85 @Override 86 public void onFailed() throws RemoteException { 87 synchronized (mLock) { 88 notifyPendingShowCallbacksFailedLocked(); 89 } 90 } 91 92 @Override 93 public void onShown() throws RemoteException { 94 synchronized (mLock) { 95 // TODO: Figure out whether this is good enough or whether we need to hook into 96 // Window manager to actually wait for the window to be drawn. 97 notifyPendingShowCallbacksShownLocked(); 98 } 99 } 100 }; 101 102 public interface Callback { 103 public void sessionConnectionGone(VoiceInteractionSessionConnection connection); 104 } 105 106 final ServiceConnection mFullConnection = new ServiceConnection() { 107 @Override 108 public void onServiceConnected(ComponentName name, IBinder service) { 109 } 110 @Override 111 public void onServiceDisconnected(ComponentName name) { 112 } 113 }; 114 115 final IResultReceiver mAssistReceiver = new IResultReceiver.Stub() { 116 @Override 117 public void send(int resultCode, Bundle resultData) throws RemoteException { 118 synchronized (mLock) { 119 if (mShown) { 120 mHaveAssistData = true; 121 mAssistData = resultData; 122 deliverSessionDataLocked(); 123 } 124 } 125 } 126 }; 127 128 final IAssistScreenshotReceiver mScreenshotReceiver = new IAssistScreenshotReceiver.Stub() { 129 @Override 130 public void send(Bitmap screenshot) throws RemoteException { 131 synchronized (mLock) { 132 if (mShown) { 133 mHaveScreenshot = true; 134 mScreenshot = screenshot; 135 deliverSessionDataLocked(); 136 } 137 } 138 } 139 }; 140 141 public VoiceInteractionSessionConnection(Object lock, ComponentName component, int user, 142 Context context, Callback callback, int callingPid, int callingUid) { 143 mLock = lock; 144 mSessionComponentName = component; 145 mUser = user; 146 mContext = context; 147 mCallback = callback; 148 mCallingPid = callingPid; 149 mCallingUid = callingUid; 150 mAm = ActivityManagerNative.getDefault(); 151 mIWindowManager = IWindowManager.Stub.asInterface( 152 ServiceManager.getService(Context.WINDOW_SERVICE)); 153 mAppOps = context.getSystemService(AppOpsManager.class); 154 IBinder permOwner = null; 155 try { 156 permOwner = mAm.newUriPermissionOwner("voicesession:" 157 + component.flattenToShortString()); 158 } catch (RemoteException e) { 159 Slog.w("voicesession", "AM dead", e); 160 } 161 mPermissionOwner = permOwner; 162 mBindIntent = new Intent(VoiceInteractionService.SERVICE_INTERFACE); 163 mBindIntent.setComponent(mSessionComponentName); 164 mBound = mContext.bindServiceAsUser(mBindIntent, this, 165 Context.BIND_AUTO_CREATE|Context.BIND_WAIVE_PRIORITY 166 |Context.BIND_ALLOW_OOM_MANAGEMENT, new UserHandle(mUser)); 167 if (mBound) { 168 try { 169 mIWindowManager.addWindowToken(mToken, 170 WindowManager.LayoutParams.TYPE_VOICE_INTERACTION); 171 } catch (RemoteException e) { 172 Slog.w(TAG, "Failed adding window token", e); 173 } 174 } else { 175 Slog.w(TAG, "Failed binding to voice interaction session service " 176 + mSessionComponentName); 177 } 178 } 179 180 public boolean showLocked(Bundle args, int flags, 181 IVoiceInteractionSessionShowCallback showCallback) { 182 if (mBound) { 183 if (!mFullyBound) { 184 mFullyBound = mContext.bindServiceAsUser(mBindIntent, mFullConnection, 185 Context.BIND_AUTO_CREATE|Context.BIND_TREAT_LIKE_ACTIVITY, 186 new UserHandle(mUser)); 187 } 188 mShown = true; 189 mShowArgs = args; 190 mShowFlags = flags; 191 mHaveAssistData = false; 192 if ((flags&VoiceInteractionService.START_WITH_ASSIST) != 0) { 193 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ASSIST_STRUCTURE, mCallingUid, 194 mSessionComponentName.getPackageName()) == AppOpsManager.MODE_ALLOWED) { 195 try { 196 mAm.requestAssistContextExtras(ActivityManager.ASSIST_CONTEXT_FULL, 197 mAssistReceiver); 198 } catch (RemoteException e) { 199 } 200 } else { 201 mHaveAssistData = true; 202 mAssistData = null; 203 } 204 } else { 205 mAssistData = null; 206 } 207 mHaveScreenshot = false; 208 if ((flags&VoiceInteractionService.START_WITH_SCREENSHOT) != 0) { 209 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ASSIST_SCREENSHOT, mCallingUid, 210 mSessionComponentName.getPackageName()) == AppOpsManager.MODE_ALLOWED) { 211 try { 212 mIWindowManager.requestAssistScreenshot(mScreenshotReceiver); 213 } catch (RemoteException e) { 214 } 215 } else { 216 mHaveScreenshot = true; 217 mScreenshot = null; 218 } 219 } else { 220 mScreenshot = null; 221 } 222 if (mSession != null) { 223 try { 224 mSession.show(mShowArgs, mShowFlags, showCallback); 225 mShowArgs = null; 226 mShowFlags = 0; 227 } catch (RemoteException e) { 228 } 229 deliverSessionDataLocked(); 230 } else if (showCallback != null) { 231 mPendingShowCallbacks.add(showCallback); 232 } 233 return true; 234 } 235 if (showCallback != null) { 236 try { 237 showCallback.onFailed(); 238 } catch (RemoteException e) { 239 } 240 } 241 return false; 242 } 243 244 void grantUriPermission(Uri uri, int mode, int srcUid, int destUid, String destPkg) { 245 if (!"content".equals(uri.getScheme())) { 246 return; 247 } 248 long ident = Binder.clearCallingIdentity(); 249 try { 250 // This will throw SecurityException for us. 251 mAm.checkGrantUriPermission(srcUid, null, ContentProvider.getUriWithoutUserId(uri), 252 mode, ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(srcUid))); 253 // No security exception, do the grant. 254 int sourceUserId = ContentProvider.getUserIdFromUri(uri, mUser); 255 uri = ContentProvider.getUriWithoutUserId(uri); 256 mAm.grantUriPermissionFromOwner(mPermissionOwner, srcUid, destPkg, 257 uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, sourceUserId, mUser); 258 } catch (RemoteException e) { 259 } catch (SecurityException e) { 260 Slog.w(TAG, "Can't propagate permission", e); 261 } finally { 262 Binder.restoreCallingIdentity(ident); 263 } 264 265 } 266 267 void grantClipDataItemPermission(ClipData.Item item, int mode, int srcUid, int destUid, 268 String destPkg) { 269 if (item.getUri() != null) { 270 grantUriPermission(item.getUri(), mode, srcUid, destUid, destPkg); 271 } 272 Intent intent = item.getIntent(); 273 if (intent != null && intent.getData() != null) { 274 grantUriPermission(intent.getData(), mode, srcUid, destUid, destPkg); 275 } 276 } 277 278 void grantClipDataPermissions(ClipData data, int mode, int srcUid, int destUid, 279 String destPkg) { 280 final int N = data.getItemCount(); 281 for (int i=0; i<N; i++) { 282 grantClipDataItemPermission(data.getItemAt(i), mode, srcUid, destUid, destPkg); 283 } 284 } 285 286 void deliverSessionDataLocked() { 287 if (mSession == null) { 288 return; 289 } 290 if (mHaveAssistData) { 291 if (mAssistData != null) { 292 int uid = mAssistData.getInt(Intent.EXTRA_ASSIST_UID, -1); 293 if (uid >= 0) { 294 Bundle assistContext = mAssistData.getBundle(Intent.EXTRA_ASSIST_CONTEXT); 295 if (assistContext != null) { 296 AssistContent content = AssistContent.getAssistContent(assistContext); 297 if (content != null) { 298 Intent intent = content.getIntent(); 299 if (intent != null) { 300 ClipData data = intent.getClipData(); 301 if (data != null && Intent.isAccessUriMode(intent.getFlags())) { 302 grantClipDataPermissions(data, intent.getFlags(), uid, 303 mCallingUid, mSessionComponentName.getPackageName()); 304 } 305 } 306 ClipData data = content.getClipData(); 307 if (data != null) { 308 grantClipDataPermissions(data, 309 Intent.FLAG_GRANT_READ_URI_PERMISSION, 310 uid, mCallingUid, mSessionComponentName.getPackageName()); 311 } 312 } 313 } 314 } 315 } 316 try { 317 mSession.handleAssist(mAssistData); 318 } catch (RemoteException e) { 319 } 320 mAssistData = null; 321 mHaveAssistData = false; 322 } 323 if (mHaveScreenshot) { 324 try { 325 mSession.handleScreenshot(mScreenshot); 326 } catch (RemoteException e) { 327 } 328 mScreenshot = null; 329 mHaveScreenshot = false; 330 } 331 } 332 333 public boolean hideLocked() { 334 if (mBound) { 335 if (mShown) { 336 mShown = false; 337 mShowArgs = null; 338 mShowFlags = 0; 339 mHaveAssistData = false; 340 mAssistData = null; 341 if (mSession != null) { 342 try { 343 mSession.hide(); 344 } catch (RemoteException e) { 345 } 346 } 347 try { 348 mAm.revokeUriPermissionFromOwner(mPermissionOwner, null, 349 Intent.FLAG_GRANT_READ_URI_PERMISSION 350 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION, 351 mUser); 352 } catch (RemoteException e) { 353 } 354 if (mSession != null) { 355 try { 356 mAm.finishVoiceTask(mSession); 357 } catch (RemoteException e) { 358 } 359 } 360 } 361 if (mFullyBound) { 362 mContext.unbindService(mFullConnection); 363 mFullyBound = false; 364 } 365 return true; 366 } 367 return false; 368 } 369 370 public boolean deliverNewSessionLocked(IVoiceInteractionSession session, 371 IVoiceInteractor interactor) { 372 mSession = session; 373 mInteractor = interactor; 374 if (mShown) { 375 try { 376 session.show(mShowArgs, mShowFlags, mShowCallback); 377 mShowArgs = null; 378 mShowFlags = 0; 379 } catch (RemoteException e) { 380 } 381 deliverSessionDataLocked(); 382 } 383 return true; 384 } 385 386 private void notifyPendingShowCallbacksShownLocked() { 387 for (int i = 0; i < mPendingShowCallbacks.size(); i++) { 388 try { 389 mPendingShowCallbacks.get(i).onShown(); 390 } catch (RemoteException e) { 391 } 392 } 393 mPendingShowCallbacks.clear(); 394 } 395 396 private void notifyPendingShowCallbacksFailedLocked() { 397 for (int i = 0; i < mPendingShowCallbacks.size(); i++) { 398 try { 399 mPendingShowCallbacks.get(i).onFailed(); 400 } catch (RemoteException e) { 401 } 402 } 403 mPendingShowCallbacks.clear(); 404 } 405 406 @Override 407 public void onServiceConnected(ComponentName name, IBinder service) { 408 synchronized (mLock) { 409 mService = IVoiceInteractionSessionService.Stub.asInterface(service); 410 if (!mCanceled) { 411 try { 412 mService.newSession(mToken, mShowArgs, mShowFlags); 413 } catch (RemoteException e) { 414 Slog.w(TAG, "Failed adding window token", e); 415 } 416 } 417 } 418 } 419 420 @Override 421 public void onServiceDisconnected(ComponentName name) { 422 mCallback.sessionConnectionGone(this); 423 mService = null; 424 } 425 426 public void cancel() { 427 mCanceled = true; 428 if (mBound) { 429 if (mSession != null) { 430 try { 431 mSession.destroy(); 432 } catch (RemoteException e) { 433 Slog.w(TAG, "Voice interation session already dead"); 434 } 435 } 436 if (mSession != null) { 437 try { 438 mAm.finishVoiceTask(mSession); 439 } catch (RemoteException e) { 440 } 441 } 442 mContext.unbindService(this); 443 try { 444 mIWindowManager.removeWindowToken(mToken); 445 } catch (RemoteException e) { 446 Slog.w(TAG, "Failed removing window token", e); 447 } 448 mBound = false; 449 mService = null; 450 mSession = null; 451 mInteractor = null; 452 } 453 if (mFullyBound) { 454 mContext.unbindService(mFullConnection); 455 mFullyBound = false; 456 } 457 } 458 459 public void dump(String prefix, PrintWriter pw) { 460 pw.print(prefix); pw.print("mToken="); pw.println(mToken); 461 pw.print(prefix); pw.print("mShown="); pw.println(mShown); 462 pw.print(prefix); pw.print("mShowArgs="); pw.println(mShowArgs); 463 pw.print(prefix); pw.print("mShowFlags=0x"); pw.println(Integer.toHexString(mShowFlags)); 464 pw.print(prefix); pw.print("mBound="); pw.println(mBound); 465 if (mBound) { 466 pw.print(prefix); pw.print("mService="); pw.println(mService); 467 pw.print(prefix); pw.print("mSession="); pw.println(mSession); 468 pw.print(prefix); pw.print("mInteractor="); pw.println(mInteractor); 469 } 470 pw.print(prefix); pw.print("mHaveAssistData="); pw.println(mHaveAssistData); 471 if (mHaveAssistData) { 472 pw.print(prefix); pw.print("mAssistData="); pw.println(mAssistData); 473 } 474 } 475}; 476