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