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