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