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