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 17 package com.android.server.voiceinteraction; 18 19 import static android.app.AppOpsManager.OP_ASSIST_SCREENSHOT; 20 import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE; 21 import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION; 22 import static android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION; 23 import static android.view.Display.DEFAULT_DISPLAY; 24 import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; 25 26 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_ACTIVITY_ID; 27 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT; 28 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA; 29 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE; 30 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_TASK_ID; 31 32 import android.annotation.NonNull; 33 import android.annotation.Nullable; 34 import android.app.ActivityManager; 35 import android.app.ActivityTaskManager; 36 import android.app.AppOpsManager; 37 import android.app.IActivityManager; 38 import android.app.UriGrantsManager; 39 import android.app.assist.AssistContent; 40 import android.app.assist.AssistStructure; 41 import android.content.ClipData; 42 import android.content.ComponentName; 43 import android.content.ContentProvider; 44 import android.content.Context; 45 import android.content.Intent; 46 import android.content.ServiceConnection; 47 import android.graphics.Bitmap; 48 import android.hardware.power.Boost; 49 import android.net.Uri; 50 import android.os.Binder; 51 import android.os.Bundle; 52 import android.os.Handler; 53 import android.os.IBinder; 54 import android.os.PowerManager; 55 import android.os.PowerManagerInternal; 56 import android.os.RemoteException; 57 import android.os.ServiceManager; 58 import android.os.UserHandle; 59 import android.provider.Settings; 60 import android.service.voice.IVoiceInteractionSession; 61 import android.service.voice.IVoiceInteractionSessionService; 62 import android.service.voice.VisibleActivityInfo; 63 import android.service.voice.VoiceInteractionService; 64 import android.service.voice.VoiceInteractionSession; 65 import android.util.ArrayMap; 66 import android.util.Slog; 67 import android.view.IWindowManager; 68 69 import com.android.internal.app.AssistUtils; 70 import com.android.internal.app.IVoiceInteractionSessionShowCallback; 71 import com.android.internal.app.IVoiceInteractor; 72 import com.android.server.FgThread; 73 import com.android.server.LocalServices; 74 import com.android.server.am.AssistDataRequester; 75 import com.android.server.am.AssistDataRequester.AssistDataRequesterCallbacks; 76 import com.android.server.power.LowPowerStandbyControllerInternal; 77 import com.android.server.statusbar.StatusBarManagerInternal; 78 import com.android.server.uri.UriGrantsManagerInternal; 79 import com.android.server.wm.ActivityAssistInfo; 80 import com.android.server.wm.ActivityTaskManagerInternal; 81 82 import java.io.PrintWriter; 83 import java.time.Instant; 84 import java.util.ArrayList; 85 import java.util.List; 86 import java.util.concurrent.Executors; 87 import java.util.concurrent.ScheduledExecutorService; 88 89 final class VoiceInteractionSessionConnection implements ServiceConnection, 90 AssistDataRequesterCallbacks { 91 92 static final String TAG = "VoiceInteractionServiceManager"; 93 static final boolean DEBUG = false; 94 static final int POWER_BOOST_TIMEOUT_MS = Integer.parseInt( 95 System.getProperty("vendor.powerhal.interaction.max", "200")); 96 static final int BOOST_TIMEOUT_MS = 300; 97 /** 98 * The maximum time an app can stay on the Low Power Standby allowlist when 99 * the session is shown. There to safeguard against apps that don't call hide. 100 */ 101 private static final int LOW_POWER_STANDBY_ALLOWLIST_TIMEOUT_MS = 120_000; 102 // TODO: To avoid ap doesn't call hide, only 10 secs for now, need a better way to manage it 103 // in the future. 104 static final int MAX_POWER_BOOST_TIMEOUT = 10_000; 105 106 final IBinder mToken = new Binder(); 107 final Object mLock; 108 final ComponentName mSessionComponentName; 109 final Intent mBindIntent; 110 final int mUser; 111 final Context mContext; 112 final Callback mCallback; 113 final int mCallingUid; 114 final Handler mHandler; 115 final IActivityManager mAm; 116 final UriGrantsManagerInternal mUgmInternal; 117 final IWindowManager mIWindowManager; 118 final AppOpsManager mAppOps; 119 final IBinder mPermissionOwner; 120 boolean mShown; 121 Bundle mShowArgs; 122 int mShowFlags; 123 boolean mBound; 124 boolean mFullyBound; 125 boolean mCanceled; 126 IVoiceInteractionSessionService mService; 127 IVoiceInteractionSession mSession; 128 IVoiceInteractor mInteractor; 129 ArrayList<IVoiceInteractionSessionShowCallback> mPendingShowCallbacks = new ArrayList<>(); 130 private List<ActivityAssistInfo> mPendingHandleAssistWithoutData = new ArrayList<>(); 131 AssistDataRequester mAssistDataRequester; 132 private boolean mListeningVisibleActivity; 133 private final ScheduledExecutorService mScheduledExecutorService = 134 Executors.newSingleThreadScheduledExecutor(); 135 // Records the visible activity information the system has already called onVisible, without 136 // confirming the result of callback. When activity visible state is changed, we use this to 137 // determine to call onVisible or onInvisible to assistant application. 138 private final ArrayMap<IBinder, VisibleActivityInfo> mVisibleActivityInfoForToken = 139 new ArrayMap<>(); 140 private final PowerManagerInternal mPowerManagerInternal; 141 private final LowPowerStandbyControllerInternal mLowPowerStandbyControllerInternal; 142 private final Runnable mRemoveFromLowPowerStandbyAllowlistRunnable = 143 this::removeFromLowPowerStandbyAllowlist; 144 private boolean mLowPowerStandbyAllowlisted; 145 private PowerBoostSetter mSetPowerBoostRunnable; 146 private final Handler mFgHandler; 147 148 class PowerBoostSetter implements Runnable { 149 150 private boolean mCanceled; 151 private final Instant mExpiryTime; 152 PowerBoostSetter(Instant expiryTime)153 PowerBoostSetter(Instant expiryTime) { 154 mExpiryTime = expiryTime; 155 } 156 157 @Override run()158 public void run() { 159 synchronized (mLock) { 160 if (mCanceled) { 161 return; 162 } 163 // To avoid voice interaction service does not call hide to cancel setting 164 // power boost. We will cancel set boost when reaching the max timeout. 165 if (Instant.now().isBefore(mExpiryTime)) { 166 mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, BOOST_TIMEOUT_MS); 167 if (mSetPowerBoostRunnable != null) { 168 mFgHandler.postDelayed(mSetPowerBoostRunnable, POWER_BOOST_TIMEOUT_MS); 169 } 170 } else { 171 Slog.w(TAG, "Reset power boost INTERACTION because reaching max timeout."); 172 mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, /* durationMs */ -1); 173 } 174 } 175 } 176 cancel()177 void cancel() { 178 synchronized (mLock) { 179 mCanceled = true; 180 } 181 } 182 } 183 184 IVoiceInteractionSessionShowCallback mShowCallback = 185 new IVoiceInteractionSessionShowCallback.Stub() { 186 @Override 187 public void onFailed() throws RemoteException { 188 synchronized (mLock) { 189 notifyPendingShowCallbacksFailedLocked(); 190 } 191 } 192 193 @Override 194 public void onShown() throws RemoteException { 195 synchronized (mLock) { 196 // TODO: Figure out whether this is good enough or whether we need to hook into 197 // Window manager to actually wait for the window to be drawn. 198 notifyPendingShowCallbacksShownLocked(); 199 } 200 } 201 }; 202 203 public interface Callback { sessionConnectionGone(VoiceInteractionSessionConnection connection)204 public void sessionConnectionGone(VoiceInteractionSessionConnection connection); onSessionShown(VoiceInteractionSessionConnection connection)205 public void onSessionShown(VoiceInteractionSessionConnection connection); onSessionHidden(VoiceInteractionSessionConnection connection)206 public void onSessionHidden(VoiceInteractionSessionConnection connection); 207 } 208 209 final ServiceConnection mFullConnection = new ServiceConnection() { 210 @Override 211 public void onServiceConnected(ComponentName name, IBinder service) { 212 } 213 @Override 214 public void onServiceDisconnected(ComponentName name) { 215 } 216 }; 217 VoiceInteractionSessionConnection(Object lock, ComponentName component, int user, Context context, Callback callback, int callingUid, Handler handler)218 public VoiceInteractionSessionConnection(Object lock, ComponentName component, int user, 219 Context context, Callback callback, int callingUid, Handler handler) { 220 mLock = lock; 221 mSessionComponentName = component; 222 mUser = user; 223 mContext = context; 224 mCallback = callback; 225 mCallingUid = callingUid; 226 mHandler = handler; 227 mAm = ActivityManager.getService(); 228 mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class); 229 mIWindowManager = IWindowManager.Stub.asInterface( 230 ServiceManager.getService(Context.WINDOW_SERVICE)); 231 mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); 232 mLowPowerStandbyControllerInternal = LocalServices.getService( 233 LowPowerStandbyControllerInternal.class); 234 mAppOps = context.getSystemService(AppOpsManager.class); 235 mFgHandler = FgThread.getHandler(); 236 mAssistDataRequester = new AssistDataRequester(mContext, mIWindowManager, 237 (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE), 238 this, mLock, OP_ASSIST_STRUCTURE, OP_ASSIST_SCREENSHOT); 239 final IBinder permOwner = mUgmInternal.newUriPermissionOwner("voicesession:" 240 + component.flattenToShortString()); 241 mPermissionOwner = permOwner; 242 mBindIntent = new Intent(VoiceInteractionService.SERVICE_INTERFACE); 243 mBindIntent.setComponent(mSessionComponentName); 244 mBound = mContext.bindServiceAsUser(mBindIntent, this, 245 Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY 246 | Context.BIND_ALLOW_OOM_MANAGEMENT 247 | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS, new UserHandle(mUser)); 248 if (mBound) { 249 try { 250 mIWindowManager.addWindowToken(mToken, TYPE_VOICE_INTERACTION, DEFAULT_DISPLAY, 251 null /* options */); 252 } catch (RemoteException e) { 253 Slog.w(TAG, "Failed adding window token", e); 254 } 255 } else { 256 Slog.w(TAG, "Failed binding to voice interaction session service " 257 + mSessionComponentName); 258 } 259 } 260 getUserDisabledShowContextLocked()261 public int getUserDisabledShowContextLocked() { 262 int flags = 0; 263 if (Settings.Secure.getIntForUser(mContext.getContentResolver(), 264 Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1, mUser) == 0) { 265 flags |= VoiceInteractionSession.SHOW_WITH_ASSIST; 266 } 267 if (Settings.Secure.getIntForUser(mContext.getContentResolver(), 268 Settings.Secure.ASSIST_SCREENSHOT_ENABLED, 1, mUser) == 0) { 269 flags |= VoiceInteractionSession.SHOW_WITH_SCREENSHOT; 270 } 271 return flags; 272 } 273 showLocked(@onNull Bundle args, int flags, @Nullable String attributionTag, int disabledContext, @Nullable IVoiceInteractionSessionShowCallback showCallback, @NonNull List<ActivityAssistInfo> topActivities)274 public boolean showLocked(@NonNull Bundle args, int flags, @Nullable String attributionTag, 275 int disabledContext, @Nullable IVoiceInteractionSessionShowCallback showCallback, 276 @NonNull List<ActivityAssistInfo> topActivities) { 277 if (mBound) { 278 if (!mFullyBound) { 279 mFullyBound = mContext.bindServiceAsUser(mBindIntent, mFullConnection, 280 Context.BIND_AUTO_CREATE | Context.BIND_TREAT_LIKE_ACTIVITY 281 | Context.BIND_SCHEDULE_LIKE_TOP_APP 282 | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS, 283 new UserHandle(mUser)); 284 } 285 286 mShown = true; 287 mShowArgs = args; 288 mShowFlags = flags; 289 290 disabledContext |= getUserDisabledShowContextLocked(); 291 292 boolean fetchData = (flags & VoiceInteractionSession.SHOW_WITH_ASSIST) != 0; 293 boolean fetchScreenshot = (flags & VoiceInteractionSession.SHOW_WITH_SCREENSHOT) != 0; 294 boolean assistDataRequestNeeded = fetchData || fetchScreenshot; 295 296 if (assistDataRequestNeeded) { 297 int topActivitiesCount = topActivities.size(); 298 final ArrayList<IBinder> topActivitiesToken = new ArrayList<>(topActivitiesCount); 299 for (int i = 0; i < topActivitiesCount; i++) { 300 topActivitiesToken.add(topActivities.get(i).getActivityToken()); 301 } 302 303 mAssistDataRequester.requestAssistData(topActivitiesToken, 304 fetchData, 305 fetchScreenshot, 306 (disabledContext & VoiceInteractionSession.SHOW_WITH_ASSIST) == 0, 307 (disabledContext & VoiceInteractionSession.SHOW_WITH_SCREENSHOT) == 0, 308 mCallingUid, 309 mSessionComponentName.getPackageName(), 310 attributionTag); 311 312 boolean needDisclosure = mAssistDataRequester.getPendingDataCount() > 0 313 || mAssistDataRequester.getPendingScreenshotCount() > 0; 314 if (needDisclosure && AssistUtils.shouldDisclose(mContext, mSessionComponentName)) { 315 mHandler.post(mShowAssistDisclosureRunnable); 316 } 317 } 318 if (mSession != null) { 319 try { 320 mSession.show(mShowArgs, mShowFlags, showCallback); 321 mShowArgs = null; 322 mShowFlags = 0; 323 } catch (RemoteException e) { 324 } 325 if (assistDataRequestNeeded) { 326 mAssistDataRequester.processPendingAssistData(); 327 } else { 328 doHandleAssistWithoutData(topActivities); 329 } 330 } else { 331 if (showCallback != null) { 332 mPendingShowCallbacks.add(showCallback); 333 } 334 if (!assistDataRequestNeeded) { 335 // If no data are required we are not passing trough mAssistDataRequester. As 336 // a consequence, when a new session is delivered it is needed to process those 337 // requests manually. 338 mPendingHandleAssistWithoutData = topActivities; 339 } 340 } 341 // remove if already existing one. 342 if (mSetPowerBoostRunnable != null) { 343 mSetPowerBoostRunnable.cancel(); 344 } 345 mSetPowerBoostRunnable = new PowerBoostSetter( 346 Instant.now().plusMillis(MAX_POWER_BOOST_TIMEOUT)); 347 mFgHandler.post(mSetPowerBoostRunnable); 348 349 if (mLowPowerStandbyControllerInternal != null) { 350 mLowPowerStandbyControllerInternal.addToAllowlist(mCallingUid, 351 PowerManager.LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION); 352 mLowPowerStandbyAllowlisted = true; 353 mFgHandler.removeCallbacks(mRemoveFromLowPowerStandbyAllowlistRunnable); 354 mFgHandler.postDelayed(mRemoveFromLowPowerStandbyAllowlistRunnable, 355 LOW_POWER_STANDBY_ALLOWLIST_TIMEOUT_MS); 356 } 357 358 mCallback.onSessionShown(this); 359 return true; 360 } 361 if (showCallback != null) { 362 try { 363 showCallback.onFailed(); 364 } catch (RemoteException e) { 365 } 366 } 367 return false; 368 } 369 doHandleAssistWithoutData(List<ActivityAssistInfo> topActivities)370 private void doHandleAssistWithoutData(List<ActivityAssistInfo> topActivities) { 371 final int activityCount = topActivities.size(); 372 for (int i = 0; i < activityCount; i++) { 373 final ActivityAssistInfo topActivity = topActivities.get(i); 374 final IBinder assistToken = topActivity.getAssistToken(); 375 final int taskId = topActivity.getTaskId(); 376 final int activityIndex = i; 377 try { 378 mSession.handleAssist( 379 taskId, 380 assistToken, 381 /* assistData = */ null, 382 /* assistStructure = */ null, 383 /* assistContent = */ null, 384 activityIndex, 385 activityCount); 386 } catch (RemoteException e) { 387 // Ignore 388 } 389 } 390 } 391 392 @Override canHandleReceivedAssistDataLocked()393 public boolean canHandleReceivedAssistDataLocked() { 394 return mSession != null; 395 } 396 397 @Override onAssistDataReceivedLocked(Bundle data, int activityIndex, int activityCount)398 public void onAssistDataReceivedLocked(Bundle data, int activityIndex, int activityCount) { 399 // Return early if we have no session 400 if (mSession == null) { 401 return; 402 } 403 404 if (data == null) { 405 try { 406 mSession.handleAssist(-1, null, null, null, null, 0, 0); 407 } catch (RemoteException e) { 408 // Ignore 409 } 410 } else { 411 final int taskId = data.getInt(ASSIST_TASK_ID); 412 final IBinder activityId = data.getBinder(ASSIST_ACTIVITY_ID); 413 final Bundle assistData = data.getBundle(ASSIST_KEY_DATA); 414 final AssistStructure structure = data.getParcelable(ASSIST_KEY_STRUCTURE, android.app.assist.AssistStructure.class); 415 final AssistContent content = data.getParcelable(ASSIST_KEY_CONTENT, android.app.assist.AssistContent.class); 416 int uid = -1; 417 if (assistData != null) { 418 uid = assistData.getInt(Intent.EXTRA_ASSIST_UID, -1); 419 } 420 if (uid >= 0 && content != null) { 421 Intent intent = content.getIntent(); 422 if (intent != null) { 423 ClipData clipData = intent.getClipData(); 424 if (clipData != null && Intent.isAccessUriMode(intent.getFlags())) { 425 grantClipDataPermissions(clipData, intent.getFlags(), uid, 426 mCallingUid, mSessionComponentName.getPackageName()); 427 } 428 } 429 ClipData clipData = content.getClipData(); 430 if (clipData != null) { 431 grantClipDataPermissions(clipData, FLAG_GRANT_READ_URI_PERMISSION, 432 uid, mCallingUid, mSessionComponentName.getPackageName()); 433 } 434 } 435 try { 436 mSession.handleAssist(taskId, activityId, assistData, structure, 437 content, activityIndex, activityCount); 438 } catch (RemoteException e) { 439 // Ignore 440 } 441 } 442 } 443 444 @Override onAssistScreenshotReceivedLocked(Bitmap screenshot)445 public void onAssistScreenshotReceivedLocked(Bitmap screenshot) { 446 // Return early if we have no session 447 if (mSession == null) { 448 return; 449 } 450 451 try { 452 mSession.handleScreenshot(screenshot); 453 } catch (RemoteException e) { 454 // Ignore 455 } 456 } 457 grantUriPermission(Uri uri, int mode, int srcUid, int destUid, String destPkg)458 void grantUriPermission(Uri uri, int mode, int srcUid, int destUid, String destPkg) { 459 if (!"content".equals(uri.getScheme())) { 460 return; 461 } 462 final long ident = Binder.clearCallingIdentity(); 463 try { 464 // This will throw SecurityException for us. 465 mUgmInternal.checkGrantUriPermission(srcUid, null, 466 ContentProvider.getUriWithoutUserId(uri), mode, 467 ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(srcUid))); 468 // No security exception, do the grant. 469 int sourceUserId = ContentProvider.getUserIdFromUri(uri, mUser); 470 uri = ContentProvider.getUriWithoutUserId(uri); 471 UriGrantsManager.getService().grantUriPermissionFromOwner(mPermissionOwner, srcUid, 472 destPkg, uri, FLAG_GRANT_READ_URI_PERMISSION, sourceUserId, mUser); 473 } catch (RemoteException e) { 474 } catch (SecurityException e) { 475 Slog.w(TAG, "Can't propagate permission", e); 476 } finally { 477 Binder.restoreCallingIdentity(ident); 478 } 479 480 } 481 grantClipDataItemPermission(ClipData.Item item, int mode, int srcUid, int destUid, String destPkg)482 void grantClipDataItemPermission(ClipData.Item item, int mode, int srcUid, int destUid, 483 String destPkg) { 484 if (item.getUri() != null) { 485 grantUriPermission(item.getUri(), mode, srcUid, destUid, destPkg); 486 } 487 Intent intent = item.getIntent(); 488 if (intent != null && intent.getData() != null) { 489 grantUriPermission(intent.getData(), mode, srcUid, destUid, destPkg); 490 } 491 } 492 grantClipDataPermissions(ClipData data, int mode, int srcUid, int destUid, String destPkg)493 void grantClipDataPermissions(ClipData data, int mode, int srcUid, int destUid, 494 String destPkg) { 495 final int N = data.getItemCount(); 496 for (int i=0; i<N; i++) { 497 grantClipDataItemPermission(data.getItemAt(i), mode, srcUid, destUid, destPkg); 498 } 499 } 500 hideLocked()501 public boolean hideLocked() { 502 if (mBound) { 503 if (mShown) { 504 mShown = false; 505 mShowArgs = null; 506 mShowFlags = 0; 507 mAssistDataRequester.cancel(); 508 mPendingShowCallbacks.clear(); 509 if (mSession != null) { 510 try { 511 mSession.hide(); 512 } catch (RemoteException e) { 513 } 514 } 515 mUgmInternal.revokeUriPermissionFromOwner(mPermissionOwner, null, 516 FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION, mUser); 517 if (mSession != null) { 518 try { 519 ActivityTaskManager.getService().finishVoiceTask(mSession); 520 } catch (RemoteException e) { 521 } 522 } 523 if (mSetPowerBoostRunnable != null) { 524 mSetPowerBoostRunnable.cancel(); 525 mSetPowerBoostRunnable = null; 526 } 527 // A negative value indicates canceling previous boost. 528 mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, /* durationMs */ -1); 529 if (mLowPowerStandbyControllerInternal != null) { 530 removeFromLowPowerStandbyAllowlist(); 531 } 532 mCallback.onSessionHidden(this); 533 } 534 if (mFullyBound) { 535 mContext.unbindService(mFullConnection); 536 mFullyBound = false; 537 } 538 return true; 539 } 540 return false; 541 } 542 cancelLocked(boolean finishTask)543 public void cancelLocked(boolean finishTask) { 544 mListeningVisibleActivity = false; 545 mVisibleActivityInfoForToken.clear(); 546 hideLocked(); 547 mCanceled = true; 548 if (mBound) { 549 if (mSession != null) { 550 try { 551 mSession.destroy(); 552 } catch (RemoteException e) { 553 Slog.w(TAG, "Voice interation session already dead"); 554 } 555 } 556 if (finishTask && mSession != null) { 557 try { 558 ActivityTaskManager.getService().finishVoiceTask(mSession); 559 } catch (RemoteException e) { 560 } 561 } 562 mContext.unbindService(this); 563 try { 564 mIWindowManager.removeWindowToken(mToken, DEFAULT_DISPLAY); 565 } catch (RemoteException e) { 566 Slog.w(TAG, "Failed removing window token", e); 567 } 568 mBound = false; 569 mService = null; 570 mSession = null; 571 mInteractor = null; 572 } 573 if (mFullyBound) { 574 mContext.unbindService(mFullConnection); 575 mFullyBound = false; 576 } 577 } 578 deliverNewSessionLocked(IVoiceInteractionSession session, IVoiceInteractor interactor)579 public boolean deliverNewSessionLocked(IVoiceInteractionSession session, 580 IVoiceInteractor interactor) { 581 mSession = session; 582 mInteractor = interactor; 583 if (mShown) { 584 try { 585 session.show(mShowArgs, mShowFlags, mShowCallback); 586 mShowArgs = null; 587 mShowFlags = 0; 588 } catch (RemoteException e) { 589 } 590 mAssistDataRequester.processPendingAssistData(); 591 if (!mPendingHandleAssistWithoutData.isEmpty()) { 592 doHandleAssistWithoutData(mPendingHandleAssistWithoutData); 593 mPendingHandleAssistWithoutData.clear(); 594 } 595 } 596 return true; 597 } 598 notifyPendingShowCallbacksShownLocked()599 private void notifyPendingShowCallbacksShownLocked() { 600 for (int i = 0; i < mPendingShowCallbacks.size(); i++) { 601 try { 602 mPendingShowCallbacks.get(i).onShown(); 603 } catch (RemoteException e) { 604 } 605 } 606 mPendingShowCallbacks.clear(); 607 } 608 notifyPendingShowCallbacksFailedLocked()609 private void notifyPendingShowCallbacksFailedLocked() { 610 for (int i = 0; i < mPendingShowCallbacks.size(); i++) { 611 try { 612 mPendingShowCallbacks.get(i).onFailed(); 613 } catch (RemoteException e) { 614 } 615 } 616 mPendingShowCallbacks.clear(); 617 } 618 startListeningVisibleActivityChangedLocked()619 void startListeningVisibleActivityChangedLocked() { 620 if (DEBUG) { 621 Slog.d(TAG, "startListeningVisibleActivityChangedLocked"); 622 } 623 624 if (!mShown || mCanceled || mSession == null) { 625 return; 626 } 627 628 mListeningVisibleActivity = true; 629 mVisibleActivityInfoForToken.clear(); 630 631 // It should only need to report which activities are visible 632 final ArrayMap<IBinder, VisibleActivityInfo> newVisibleActivityInfos = 633 getTopVisibleActivityInfosLocked(); 634 635 if (newVisibleActivityInfos == null || newVisibleActivityInfos.isEmpty()) { 636 return; 637 } 638 notifyVisibleActivitiesChangedLocked(newVisibleActivityInfos, 639 VisibleActivityInfo.TYPE_ACTIVITY_ADDED); 640 mVisibleActivityInfoForToken.putAll(newVisibleActivityInfos); 641 } 642 stopListeningVisibleActivityChangedLocked()643 void stopListeningVisibleActivityChangedLocked() { 644 if (DEBUG) { 645 Slog.d(TAG, "stopListeningVisibleActivityChangedLocked"); 646 } 647 mListeningVisibleActivity = false; 648 mVisibleActivityInfoForToken.clear(); 649 } 650 notifyActivityEventChangedLocked(@onNull IBinder activityToken, int type)651 void notifyActivityEventChangedLocked(@NonNull IBinder activityToken, int type) { 652 if (DEBUG) { 653 Slog.d(TAG, "notifyActivityEventChangedLocked activityToken=" + activityToken 654 + ", type=" + type); 655 } 656 if (!mListeningVisibleActivity) { 657 if (DEBUG) { 658 Slog.d(TAG, "not enable listening visible activity"); 659 } 660 return; 661 } 662 mScheduledExecutorService.execute(() -> { 663 synchronized (mLock) { 664 handleVisibleActivitiesLocked(activityToken, type); 665 } 666 }); 667 } 668 getTopVisibleActivityInfosLocked()669 private ArrayMap<IBinder, VisibleActivityInfo> getTopVisibleActivityInfosLocked() { 670 if (DEBUG) { 671 Slog.d(TAG, "getTopVisibleActivityInfosLocked"); 672 } 673 List<ActivityAssistInfo> allVisibleActivities = 674 LocalServices.getService(ActivityTaskManagerInternal.class) 675 .getTopVisibleActivities(); 676 if (DEBUG) { 677 Slog.d(TAG, "getTopVisibleActivityInfosLocked: allVisibleActivities=" 678 + allVisibleActivities); 679 } 680 if (allVisibleActivities.isEmpty()) { 681 Slog.w(TAG, "no visible activity"); 682 return null; 683 } 684 final int count = allVisibleActivities.size(); 685 final ArrayMap<IBinder, VisibleActivityInfo> visibleActivityInfoArrayMap = 686 new ArrayMap<>(count); 687 for (int i = 0; i < count; i++) { 688 ActivityAssistInfo info = allVisibleActivities.get(i); 689 if (DEBUG) { 690 Slog.d(TAG, "ActivityAssistInfo : activityToken=" + info.getActivityToken() 691 + ", assistToken=" + info.getAssistToken() 692 + ", taskId=" + info.getTaskId()); 693 } 694 visibleActivityInfoArrayMap.put(info.getActivityToken(), 695 new VisibleActivityInfo(info.getTaskId(), info.getAssistToken())); 696 } 697 return visibleActivityInfoArrayMap; 698 } 699 700 // TODO(b/242359988): Split this method up handleVisibleActivitiesLocked(@onNull IBinder activityToken, int type)701 private void handleVisibleActivitiesLocked(@NonNull IBinder activityToken, int type) { 702 if (DEBUG) { 703 Slog.d(TAG, "handleVisibleActivitiesLocked activityToken=" + activityToken 704 + ", type=" + type); 705 } 706 707 if (!mListeningVisibleActivity) { 708 if (DEBUG) { 709 Slog.d(TAG, "not enable listening visible activity"); 710 } 711 return; 712 } 713 if (!mShown || mCanceled || mSession == null) { 714 return; 715 } 716 717 // We use this local variable to determine to call onVisible or onInvisible. 718 boolean notifyOnVisible = false; 719 VisibleActivityInfo notifyVisibleActivityInfo = null; 720 721 if (type == VoiceInteractionSession.VOICE_INTERACTION_ACTIVITY_EVENT_START 722 || type == VoiceInteractionSession.VOICE_INTERACTION_ACTIVITY_EVENT_RESUME) { 723 // It seems that the onStart is unnecessary. But if we have it, the assistant 724 // application can request the directActions early. Even if we have the onStart, 725 // we still need the onResume because it is possible that the activity goes to 726 // onResume from onPause with invisible before the activity goes to onStop from 727 // onPause. 728 729 // Check if we have reported this activity as visible. If we have reported it as 730 // visible, do nothing. 731 if (mVisibleActivityInfoForToken.containsKey(activityToken)) { 732 return; 733 } 734 735 // Before reporting this activity as visible, we need to make sure the activity 736 // is really visible. 737 notifyVisibleActivityInfo = getVisibleActivityInfoFromTopVisibleActivity( 738 activityToken); 739 if (notifyVisibleActivityInfo == null) { 740 return; 741 } 742 notifyOnVisible = true; 743 } else if (type == VoiceInteractionSession.VOICE_INTERACTION_ACTIVITY_EVENT_PAUSE) { 744 // For the onPause stage, the Activity is not necessarily invisible now, so we need 745 // to check its state. 746 // Note: After syncing with Activity owner, before the onPause is called, the 747 // visibility state has been updated. 748 notifyVisibleActivityInfo = getVisibleActivityInfoFromTopVisibleActivity( 749 activityToken); 750 if (notifyVisibleActivityInfo != null) { 751 return; 752 } 753 754 // Also make sure we previously reported this Activity as visible. 755 notifyVisibleActivityInfo = mVisibleActivityInfoForToken.get(activityToken); 756 if (notifyVisibleActivityInfo == null) { 757 return; 758 } 759 } else if (type == VoiceInteractionSession.VOICE_INTERACTION_ACTIVITY_EVENT_STOP) { 760 // For the onStop stage, the activity is in invisible state. We only need to consider if 761 // we have reported this activity as visible. If we have reported it as visible, we 762 // need to report it as invisible. 763 // Why we still need onStop? Because it is possible that the activity is in a visible 764 // state during onPause stage, when the activity enters onStop from onPause, we may 765 // need to notify onInvisible. 766 // Note: After syncing with Activity owner, before the onStop is called, the 767 // visibility state has been updated. 768 notifyVisibleActivityInfo = mVisibleActivityInfoForToken.get(activityToken); 769 if (notifyVisibleActivityInfo == null) { 770 return; 771 } 772 } else { 773 Slog.w(TAG, "notifyActivityEventChangedLocked unexpected type=" + type); 774 return; 775 } 776 777 try { 778 mSession.notifyVisibleActivityInfoChanged(notifyVisibleActivityInfo, 779 notifyOnVisible ? VisibleActivityInfo.TYPE_ACTIVITY_ADDED 780 : VisibleActivityInfo.TYPE_ACTIVITY_REMOVED); 781 } catch (RemoteException e) { 782 if (DEBUG) { 783 Slog.w(TAG, "handleVisibleActivitiesLocked RemoteException : " + e); 784 } 785 } 786 787 if (notifyOnVisible) { 788 mVisibleActivityInfoForToken.put(activityToken, notifyVisibleActivityInfo); 789 } else { 790 mVisibleActivityInfoForToken.remove(activityToken); 791 } 792 } 793 notifyVisibleActivitiesChangedLocked( ArrayMap<IBinder, VisibleActivityInfo> visibleActivityInfos, int type)794 private void notifyVisibleActivitiesChangedLocked( 795 ArrayMap<IBinder, VisibleActivityInfo> visibleActivityInfos, int type) { 796 if (visibleActivityInfos == null || visibleActivityInfos.isEmpty()) { 797 return; 798 } 799 if (mSession == null) { 800 return; 801 } 802 try { 803 for (int i = 0; i < visibleActivityInfos.size(); i++) { 804 mSession.notifyVisibleActivityInfoChanged(visibleActivityInfos.valueAt(i), type); 805 } 806 } catch (RemoteException e) { 807 if (DEBUG) { 808 Slog.w(TAG, "notifyVisibleActivitiesChangedLocked RemoteException : " + e); 809 } 810 } 811 if (DEBUG) { 812 Slog.d(TAG, "notifyVisibleActivitiesChangedLocked type=" + type + ", count=" 813 + visibleActivityInfos.size()); 814 } 815 } 816 getVisibleActivityInfoFromTopVisibleActivity( @onNull IBinder activityToken)817 private VisibleActivityInfo getVisibleActivityInfoFromTopVisibleActivity( 818 @NonNull IBinder activityToken) { 819 final ArrayMap<IBinder, VisibleActivityInfo> visibleActivityInfos = 820 getTopVisibleActivityInfosLocked(); 821 if (visibleActivityInfos == null) { 822 return null; 823 } 824 return visibleActivityInfos.get(activityToken); 825 } 826 notifyActivityDestroyedLocked(@onNull IBinder activityToken)827 void notifyActivityDestroyedLocked(@NonNull IBinder activityToken) { 828 if (DEBUG) { 829 Slog.d(TAG, "notifyActivityDestroyedLocked activityToken=" + activityToken); 830 } 831 if (!mListeningVisibleActivity) { 832 if (DEBUG) { 833 Slog.d(TAG, "not enable listening visible activity"); 834 } 835 return; 836 } 837 mScheduledExecutorService.execute(() -> { 838 synchronized (mLock) { 839 if (!mListeningVisibleActivity) { 840 return; 841 } 842 if (!mShown || mCanceled || mSession == null) { 843 return; 844 } 845 846 VisibleActivityInfo visibleActivityInfo = mVisibleActivityInfoForToken.remove( 847 activityToken); 848 if (visibleActivityInfo != null) { 849 try { 850 mSession.notifyVisibleActivityInfoChanged(visibleActivityInfo, 851 VisibleActivityInfo.TYPE_ACTIVITY_REMOVED); 852 } catch (RemoteException e) { 853 if (DEBUG) { 854 Slog.w(TAG, "notifyVisibleActivityInfoChanged RemoteException : " + e); 855 } 856 } 857 } 858 } 859 }); 860 } 861 removeFromLowPowerStandbyAllowlist()862 private void removeFromLowPowerStandbyAllowlist() { 863 synchronized (mLock) { 864 if (mLowPowerStandbyAllowlisted) { 865 mFgHandler.removeCallbacks(mRemoveFromLowPowerStandbyAllowlistRunnable); 866 mLowPowerStandbyControllerInternal.removeFromAllowlist(mCallingUid, 867 PowerManager.LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION); 868 mLowPowerStandbyAllowlisted = false; 869 } 870 } 871 } 872 873 @Override onServiceConnected(ComponentName name, IBinder service)874 public void onServiceConnected(ComponentName name, IBinder service) { 875 synchronized (mLock) { 876 mService = IVoiceInteractionSessionService.Stub.asInterface(service); 877 if (!mCanceled) { 878 try { 879 mService.newSession(mToken, mShowArgs, mShowFlags); 880 } catch (RemoteException e) { 881 Slog.w(TAG, "Failed adding window token", e); 882 } 883 } 884 } 885 } 886 887 @Override onServiceDisconnected(ComponentName name)888 public void onServiceDisconnected(ComponentName name) { 889 mCallback.sessionConnectionGone(this); 890 synchronized (mLock) { 891 mService = null; 892 } 893 } 894 dump(String prefix, PrintWriter pw)895 public void dump(String prefix, PrintWriter pw) { 896 pw.print(prefix); pw.print("mToken="); pw.println(mToken); 897 pw.print(prefix); pw.print("mShown="); pw.println(mShown); 898 pw.print(prefix); pw.print("mShowArgs="); pw.println(mShowArgs); 899 pw.print(prefix); pw.print("mShowFlags=0x"); pw.println(Integer.toHexString(mShowFlags)); 900 pw.print(prefix); pw.print("mBound="); pw.println(mBound); 901 if (mBound) { 902 pw.print(prefix); pw.print("mService="); pw.println(mService); 903 pw.print(prefix); pw.print("mSession="); pw.println(mSession); 904 pw.print(prefix); pw.print("mInteractor="); pw.println(mInteractor); 905 } 906 mAssistDataRequester.dump(prefix, pw); 907 } 908 909 private Runnable mShowAssistDisclosureRunnable = new Runnable() { 910 @Override 911 public void run() { 912 StatusBarManagerInternal statusBarInternal = LocalServices.getService( 913 StatusBarManagerInternal.class); 914 if (statusBarInternal != null) { 915 statusBarInternal.showAssistDisclosure(); 916 } 917 } 918 }; 919 } 920