1 /* 2 * Copyright (C) 2024 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.contextualsearch; 18 19 import static android.Manifest.permission.ACCESS_CONTEXTUAL_SEARCH; 20 import static android.app.AppOpsManager.OP_ASSIST_SCREENSHOT; 21 import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE; 22 import static android.content.Context.CONTEXTUAL_SEARCH_SERVICE; 23 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK; 24 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; 25 import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION; 26 import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION; 27 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; 28 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; 29 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 30 31 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT; 32 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE; 33 34 import android.annotation.NonNull; 35 import android.annotation.Nullable; 36 import android.annotation.RequiresPermission; 37 import android.app.ActivityManager; 38 import android.app.ActivityManagerInternal; 39 import android.app.ActivityOptions; 40 import android.app.AppOpsManager; 41 import android.app.assist.AssistContent; 42 import android.app.assist.AssistStructure; 43 import android.app.contextualsearch.CallbackToken; 44 import android.app.contextualsearch.ContextualSearchManager; 45 import android.app.contextualsearch.ContextualSearchState; 46 import android.app.contextualsearch.IContextualSearchCallback; 47 import android.app.contextualsearch.IContextualSearchManager; 48 import android.app.contextualsearch.flags.Flags; 49 import android.content.ComponentName; 50 import android.content.Context; 51 import android.content.Intent; 52 import android.content.pm.PackageManagerInternal; 53 import android.content.pm.ResolveInfo; 54 import android.graphics.Bitmap; 55 import android.media.AudioManager; 56 import android.os.Binder; 57 import android.os.Bundle; 58 import android.os.Handler; 59 import android.os.IBinder; 60 import android.os.Looper; 61 import android.os.Message; 62 import android.os.ParcelableException; 63 import android.os.Process; 64 import android.os.RemoteException; 65 import android.os.ResultReceiver; 66 import android.os.ServiceManager; 67 import android.os.ShellCallback; 68 import android.os.SystemClock; 69 import android.os.UserManager; 70 import android.provider.Settings; 71 import android.util.Log; 72 import android.util.Slog; 73 import android.view.IWindowManager; 74 import android.window.ScreenCapture.ScreenshotHardwareBuffer; 75 76 import com.android.internal.R; 77 import com.android.internal.annotations.GuardedBy; 78 import com.android.server.LocalServices; 79 import com.android.server.SystemService; 80 import com.android.server.am.AssistDataRequester; 81 import com.android.server.am.AssistDataRequester.AssistDataRequesterCallbacks; 82 import com.android.server.wm.ActivityAssistInfo; 83 import com.android.server.wm.ActivityTaskManagerInternal; 84 import com.android.server.wm.WindowManagerInternal; 85 86 import java.io.FileDescriptor; 87 import java.util.ArrayList; 88 import java.util.List; 89 import java.util.Objects; 90 91 public class ContextualSearchManagerService extends SystemService { 92 private static final String TAG = ContextualSearchManagerService.class.getSimpleName(); 93 private static final int MSG_RESET_TEMPORARY_PACKAGE = 0; 94 private static final int MAX_TEMP_PACKAGE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes 95 private static final int MSG_INVALIDATE_TOKEN = 1; 96 private static final int MAX_TOKEN_VALID_DURATION_MS = 1_000 * 60 * 10; // 10 minutes 97 98 /** 99 * Below are internal entrypoints not supported by the 100 * {@link ContextualSearchManager#startContextualSearch(int entrypoint)} method. 101 * 102 * <p>These values should be negative to avoid conflicting with the system entrypoints. 103 */ 104 105 /** Entrypoint to be used when a foreground app invokes Contextual Search. */ 106 private static final int INTERNAL_ENTRYPOINT_APP = -1; 107 108 private static final boolean DEBUG = false; 109 110 private final Context mContext; 111 private final ActivityManagerInternal mActivityManagerInternal; 112 private final ActivityTaskManagerInternal mAtmInternal; 113 private final PackageManagerInternal mPackageManager; 114 private final WindowManagerInternal mWmInternal; 115 private final AudioManager mAudioManager; 116 private final UserManager mUserManager; 117 private final Object mLock = new Object(); 118 private final AssistDataRequester mAssistDataRequester; 119 120 private final AssistDataRequesterCallbacks mAssistDataCallbacks = 121 new AssistDataRequesterCallbacks() { 122 @Override 123 public boolean canHandleReceivedAssistDataLocked() { 124 synchronized (mLock) { 125 return mStateCallback != null; 126 } 127 } 128 129 @Override 130 public void onAssistDataReceivedLocked( 131 final Bundle data, 132 final int activityIndex, 133 final int activityCount) { 134 135 final IContextualSearchCallback callback; 136 synchronized (mLock) { 137 callback = mStateCallback; 138 } 139 140 if (callback != null) { 141 try { 142 callback.onResult(new ContextualSearchState( 143 data.getParcelable(ASSIST_KEY_STRUCTURE, AssistStructure.class), 144 data.getParcelable(ASSIST_KEY_CONTENT, AssistContent.class), 145 data)); 146 } catch (RemoteException e) { 147 Log.e(TAG, "Error invoking ContextualSearchCallback", e); 148 } 149 } else { 150 Log.w(TAG, "Callback went away!"); 151 } 152 } 153 154 @Override 155 public void onAssistRequestCompleted() { 156 synchronized (mLock) { 157 mStateCallback = null; 158 } 159 } 160 }; 161 162 @GuardedBy("this") 163 private Handler mTemporaryHandler; 164 @GuardedBy("this") 165 private String mTemporaryPackage = null; 166 @GuardedBy("this") 167 private long mTokenValidDurationMs = MAX_TOKEN_VALID_DURATION_MS; 168 169 @GuardedBy("mLock") 170 private IContextualSearchCallback mStateCallback; 171 ContextualSearchManagerService(@onNull Context context)172 public ContextualSearchManagerService(@NonNull Context context) { 173 super(context); 174 if (DEBUG) Log.d(TAG, "ContextualSearchManagerService created"); 175 mContext = context; 176 mActivityManagerInternal = Objects.requireNonNull( 177 LocalServices.getService(ActivityManagerInternal.class)); 178 mAtmInternal = Objects.requireNonNull( 179 LocalServices.getService(ActivityTaskManagerInternal.class)); 180 mPackageManager = LocalServices.getService(PackageManagerInternal.class); 181 mAudioManager = context.getSystemService(AudioManager.class); 182 mUserManager = context.getSystemService(UserManager.class); 183 184 mWmInternal = Objects.requireNonNull(LocalServices.getService(WindowManagerInternal.class)); 185 mAssistDataRequester = new AssistDataRequester( 186 mContext, 187 IWindowManager.Stub.asInterface(ServiceManager.getService(Context.WINDOW_SERVICE)), 188 mContext.getSystemService(AppOpsManager.class), 189 mAssistDataCallbacks, mLock, OP_ASSIST_STRUCTURE, OP_ASSIST_SCREENSHOT); 190 191 updateSecureSetting(); 192 } 193 194 @Override onStart()195 public void onStart() { 196 publishBinderService(CONTEXTUAL_SEARCH_SERVICE, new ContextualSearchManagerStub()); 197 } 198 updateSecureSetting()199 private void updateSecureSetting() { 200 // Write default package to secure setting every time there is a change. If OEM didn't 201 // supply a new value in their config, then we would write empty string. 202 Settings.Secure.putString( 203 mContext.getContentResolver(), 204 Settings.Secure.CONTEXTUAL_SEARCH_PACKAGE, 205 getContextualSearchPackageName()); 206 } 207 getContextualSearchPackageName()208 private String getContextualSearchPackageName() { 209 synchronized (this) { 210 return mTemporaryPackage != null ? mTemporaryPackage : mContext 211 .getResources().getString(R.string.config_defaultContextualSearchPackageName); 212 } 213 } 214 resetTemporaryPackage()215 void resetTemporaryPackage() { 216 synchronized (this) { 217 enforceOverridingPermission("resetTemporaryPackage"); 218 if (mTemporaryHandler != null) { 219 mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_PACKAGE); 220 mTemporaryHandler = null; 221 } 222 if (DEBUG) Log.d(TAG, "mTemporaryPackage reset."); 223 mTemporaryPackage = null; 224 updateSecureSetting(); 225 } 226 } 227 setTemporaryPackage(@onNull String temporaryPackage, int durationMs)228 void setTemporaryPackage(@NonNull String temporaryPackage, int durationMs) { 229 synchronized (this) { 230 enforceOverridingPermission("setTemporaryPackage"); 231 final int maxDurationMs = MAX_TEMP_PACKAGE_DURATION_MS; 232 if (durationMs > maxDurationMs) { 233 throw new IllegalArgumentException( 234 "Max duration is " + maxDurationMs + " (called with " + durationMs + ")"); 235 } 236 if (mTemporaryHandler == null) { 237 mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) { 238 @Override 239 public void handleMessage(Message msg) { 240 if (msg.what == MSG_RESET_TEMPORARY_PACKAGE) { 241 synchronized (this) { 242 resetTemporaryPackage(); 243 } 244 } else { 245 Slog.wtf(TAG, "invalid handler msg: " + msg); 246 } 247 } 248 }; 249 } else { 250 mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_PACKAGE); 251 } 252 mTemporaryPackage = temporaryPackage; 253 updateSecureSetting(); 254 mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_PACKAGE, durationMs); 255 if (DEBUG) Log.d(TAG, "mTemporaryPackage set to " + mTemporaryPackage); 256 } 257 } 258 resetTokenValidDurationMs()259 void resetTokenValidDurationMs() { 260 setTokenValidDurationMs(MAX_TOKEN_VALID_DURATION_MS); 261 } 262 setTokenValidDurationMs(int durationMs)263 void setTokenValidDurationMs(int durationMs) { 264 synchronized (this) { 265 enforceOverridingPermission("setTokenValidDurationMs"); 266 if (durationMs > MAX_TOKEN_VALID_DURATION_MS) { 267 throw new IllegalArgumentException( 268 "Token max duration is " + MAX_TOKEN_VALID_DURATION_MS + " (called with " 269 + durationMs + ")"); 270 } 271 mTokenValidDurationMs = durationMs; 272 if (DEBUG) Log.d(TAG, "mTokenValidDurationMs set to " + durationMs); 273 } 274 } 275 getTokenValidDurationMs()276 private long getTokenValidDurationMs() { 277 synchronized (this) { 278 return mTokenValidDurationMs; 279 } 280 } 281 getResolvedLaunchIntent(int userId)282 private Intent getResolvedLaunchIntent(int userId) { 283 synchronized (this) { 284 if(DEBUG) Log.d(TAG, "Attempting to getResolvedLaunchIntent"); 285 // If mTemporaryPackage is not null, use it to get the ContextualSearch intent. 286 String csPkgName = getContextualSearchPackageName(); 287 if (csPkgName.isEmpty()) { 288 // Return null if csPackageName is not specified. 289 if (DEBUG) Log.w(TAG, "getContextualSearchPackageName is empty"); 290 return null; 291 } 292 Intent launchIntent = new Intent( 293 ContextualSearchManager.ACTION_LAUNCH_CONTEXTUAL_SEARCH); 294 launchIntent.setPackage(csPkgName); 295 ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivityAsUser( 296 launchIntent, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId); 297 if (resolveInfo == null) { 298 if (DEBUG) Log.w(TAG, "resolveInfo is null"); 299 return null; 300 } 301 ComponentName componentName = resolveInfo.getComponentInfo().getComponentName(); 302 if (componentName == null) { 303 if (DEBUG) Log.w(TAG, "componentName is null"); 304 return null; 305 } 306 launchIntent.setComponent(componentName); 307 return launchIntent; 308 } 309 } 310 311 @RequiresPermission(anyOf = { 312 android.Manifest.permission.MANAGE_USERS, 313 android.Manifest.permission.CREATE_USERS, 314 android.Manifest.permission.QUERY_USERS 315 }) getContextualSearchIntent(int entrypoint, int userId, String callingPackage, CallbackToken mToken)316 private Intent getContextualSearchIntent(int entrypoint, int userId, String callingPackage, 317 CallbackToken mToken) { 318 final Intent launchIntent = getResolvedLaunchIntent(userId); 319 if (launchIntent == null) { 320 if (DEBUG) Log.w(TAG, "Failed getContextualSearchIntent: launchIntent is null"); 321 return null; 322 } 323 324 if (DEBUG) Log.d(TAG, "Launch component: " + launchIntent.getComponent()); 325 launchIntent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NO_ANIMATION 326 | FLAG_ACTIVITY_NO_USER_ACTION | FLAG_ACTIVITY_CLEAR_TASK); 327 launchIntent.putExtra( 328 ContextualSearchManager.EXTRA_INVOCATION_TIME_MS, 329 SystemClock.uptimeMillis()); 330 launchIntent.putExtra(ContextualSearchManager.EXTRA_ENTRYPOINT, entrypoint); 331 launchIntent.putExtra(ContextualSearchManager.EXTRA_TOKEN, mToken); 332 if (Flags.includeAudioPlayingStatus()) { 333 launchIntent.putExtra(ContextualSearchManager.EXTRA_IS_AUDIO_PLAYING, 334 mAudioManager.isMusicActive()); 335 } 336 if (Flags.selfInvocation()) { 337 launchIntent.putExtra(Intent.EXTRA_CALLING_PACKAGE, callingPackage); 338 } 339 boolean isAssistDataAllowed = mAtmInternal.isAssistDataAllowed(); 340 final List<ActivityAssistInfo> records = mAtmInternal.getTopVisibleActivities(); 341 final List<IBinder> activityTokens = new ArrayList<>(records.size()); 342 ArrayList<String> visiblePackageNames = new ArrayList<>(); 343 boolean isManagedProfileVisible = false; 344 for (ActivityAssistInfo record : records) { 345 // Add the package name to the list only if assist data is allowed. 346 if (isAssistDataAllowed) { 347 visiblePackageNames.add(record.getComponentName().getPackageName()); 348 activityTokens.add(record.getActivityToken()); 349 } 350 if (mUserManager.isManagedProfile(record.getUserId())) { 351 isManagedProfileVisible = true; 352 } 353 } 354 final String csPackage = Objects.requireNonNull(launchIntent.getPackage()); 355 final int csUid = mPackageManager.getPackageUid(csPackage, /* flags */ 0L, userId); 356 if (isAssistDataAllowed) { 357 try { 358 mAssistDataRequester.requestAssistData( 359 activityTokens, 360 /* fetchData */ true, 361 /* fetchScreenshot */ false, 362 /* allowFetchData */ true, 363 /* allowFetchScreenshot */ false, 364 csUid, 365 csPackage, 366 null); 367 } catch (Exception e) { 368 Log.e(TAG, "Could not request assist data", e); 369 } 370 } 371 final ScreenshotHardwareBuffer shb = mWmInternal.takeContextualSearchScreenshot( 372 (Flags.contextualSearchPreventSelfCapture() ? csUid : -1)); 373 final Bitmap bm = shb != null ? shb.asBitmap() : null; 374 // Now that everything is fetched, putting it in the launchIntent. 375 if (bm != null) { 376 launchIntent.putExtra(ContextualSearchManager.EXTRA_FLAG_SECURE_FOUND, 377 shb.containsSecureLayers()); 378 // Only put the screenshot if assist data is allowed 379 if (isAssistDataAllowed) { 380 launchIntent.putExtra(ContextualSearchManager.EXTRA_SCREENSHOT, bm.asShared()); 381 } 382 } 383 launchIntent.putExtra(ContextualSearchManager.EXTRA_IS_MANAGED_PROFILE_VISIBLE, 384 isManagedProfileVisible); 385 // Only put the list of visible package names if assist data is allowed 386 if (isAssistDataAllowed) { 387 launchIntent.putExtra(ContextualSearchManager.EXTRA_VISIBLE_PACKAGE_NAMES, 388 visiblePackageNames); 389 } 390 return launchIntent; 391 } 392 393 @RequiresPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS) invokeContextualSearchIntent(Intent launchIntent, final int userId)394 private int invokeContextualSearchIntent(Intent launchIntent, final int userId) { 395 // Contextual search starts with a frozen screen - so we launch without 396 // any system animations or starting window. 397 final ActivityOptions opts = ActivityOptions.makeCustomTaskAnimation(mContext, 398 /* enterResId= */ 0, /* exitResId= */ 0, null, null, null); 399 opts.setDisableStartingWindow(true); 400 return mAtmInternal.startActivityWithScreenshot(launchIntent, 401 mContext.getPackageName(), Binder.getCallingUid(), Binder.getCallingPid(), null, 402 opts.toBundle(), userId); 403 } 404 enforcePermission(@onNull final String func)405 private void enforcePermission(@NonNull final String func) { 406 Context ctx = getContext(); 407 if (!(ctx.checkCallingPermission(ACCESS_CONTEXTUAL_SEARCH) == PERMISSION_GRANTED 408 || isCallerTemporary())) { 409 String msg = "Permission Denial: Cannot call " + func + " from pid=" 410 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid(); 411 throw new SecurityException(msg); 412 } 413 } 414 enforceForegroundApp(@onNull final String func)415 private void enforceForegroundApp(@NonNull final String func) { 416 final int callingUid = Binder.getCallingUid(); 417 final String callingPackage = mPackageManager.getNameForUid(Binder.getCallingUid()); 418 if (mActivityManagerInternal.getUidProcessState(callingUid) 419 > ActivityManager.PROCESS_STATE_TOP) { 420 // The calling process must be displaying an activity in foreground to 421 // trigger contextual search. 422 String msg = "Permission Denial: Cannot call " + func + " from pid=" 423 + Binder.getCallingPid() + ", uid=" + callingUid 424 + ", package=" + callingPackage + " without a foreground activity."; 425 throw new SecurityException(msg); 426 } 427 } 428 enforceOverridingPermission(@onNull final String func)429 private void enforceOverridingPermission(@NonNull final String func) { 430 if (!(Binder.getCallingUid() == Process.SHELL_UID 431 || Binder.getCallingUid() == Process.ROOT_UID 432 || Binder.getCallingUid() == Process.SYSTEM_UID)) { 433 String msg = "Permission Denial: Cannot override Contextual Search. Called " + func 434 + " from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid(); 435 throw new SecurityException(msg); 436 } 437 } 438 isCallerTemporary()439 private boolean isCallerTemporary() { 440 synchronized (this) { 441 return mTemporaryPackage != null 442 && mTemporaryPackage.equals( 443 getContext().getPackageManager().getNameForUid(Binder.getCallingUid())); 444 } 445 } 446 447 private class ContextualSearchManagerStub extends IContextualSearchManager.Stub { 448 @GuardedBy("this") 449 private Handler mTokenHandler; 450 private @Nullable CallbackToken mToken; 451 invalidateToken()452 private void invalidateToken() { 453 synchronized (this) { 454 if (mTokenHandler != null) { 455 mTokenHandler.removeMessages(MSG_INVALIDATE_TOKEN); 456 mTokenHandler = null; 457 } 458 if (DEBUG) Log.d(TAG, "mToken invalidated."); 459 mToken = null; 460 } 461 } 462 issueToken()463 private void issueToken() { 464 synchronized (this) { 465 mToken = new CallbackToken(); 466 if (mTokenHandler == null) { 467 mTokenHandler = new Handler(Looper.getMainLooper(), null, true) { 468 @Override 469 public void handleMessage(Message msg) { 470 if (msg.what == MSG_INVALIDATE_TOKEN) { 471 invalidateToken(); 472 } else { 473 Slog.wtf(TAG, "invalid token handler msg: " + msg); 474 } 475 } 476 }; 477 } else { 478 mTokenHandler.removeMessages(MSG_INVALIDATE_TOKEN); 479 } 480 mTokenHandler.sendEmptyMessageDelayed( 481 MSG_INVALIDATE_TOKEN, getTokenValidDurationMs()); 482 } 483 } 484 485 @Override startContextualSearchForForegroundApp()486 public void startContextualSearchForForegroundApp() { 487 synchronized (this) { 488 if (DEBUG) { 489 Log.d(TAG, "Starting contextual search from: " 490 + mPackageManager.getNameForUid(Binder.getCallingUid())); 491 } 492 enforceForegroundApp("startContextualSearchForForegroundApp"); 493 startContextualSearchInternal(INTERNAL_ENTRYPOINT_APP); 494 } 495 } 496 497 @Override startContextualSearch(int entrypoint)498 public void startContextualSearch(int entrypoint) { 499 synchronized (this) { 500 if (DEBUG) Log.d(TAG, "startContextualSearch entrypoint: " + entrypoint); 501 enforcePermission("startContextualSearch"); 502 startContextualSearchInternal(entrypoint); 503 } 504 } 505 startContextualSearchInternal(int entrypoint)506 private void startContextualSearchInternal(int entrypoint) { 507 final String callingPackage = mPackageManager.getNameForUid(Binder.getCallingUid()); 508 final int callingUserId = Binder.getCallingUserHandle().getIdentifier(); 509 mAssistDataRequester.cancel(); 510 // Creates a new CallbackToken at mToken and an expiration handler. 511 issueToken(); 512 // We get the launch intent with the system server's identity because the system 513 // server has READ_FRAME_BUFFER permission to get the screenshot and because only 514 // the system server can invoke non-exported activities. 515 Binder.withCleanCallingIdentity(() -> { 516 Intent launchIntent = getContextualSearchIntent(entrypoint, callingUserId, 517 callingPackage, mToken); 518 if (launchIntent != null) { 519 int result = invokeContextualSearchIntent(launchIntent, callingUserId); 520 if (DEBUG) { 521 Log.d(TAG, "Launch intent: " + launchIntent); 522 Log.d(TAG, "Launch result: " + result); 523 } 524 } 525 }); 526 } 527 528 @Override getContextualSearchState( @onNull IBinder token, @NonNull IContextualSearchCallback callback)529 public void getContextualSearchState( 530 @NonNull IBinder token, 531 @NonNull IContextualSearchCallback callback) { 532 if (DEBUG) { 533 Log.i(TAG, "getContextualSearchState token: " + token + ", callback: " + callback); 534 } 535 if (mToken == null || !mToken.getToken().equals(token)) { 536 if (DEBUG) { 537 Log.e(TAG, "getContextualSearchState: invalid token, returning error"); 538 } 539 try { 540 callback.onError( 541 new ParcelableException(new IllegalArgumentException("Invalid token"))); 542 } catch (RemoteException e) { 543 Log.e(TAG, "Could not invoke onError callback", e); 544 } 545 return; 546 } 547 invalidateToken(); 548 if (Flags.enableTokenRefresh()) { 549 issueToken(); 550 Bundle bundle = new Bundle(); 551 bundle.putParcelable(ContextualSearchManager.EXTRA_TOKEN, mToken); 552 // We get take the screenshot with the system server's identity because the system 553 // server has READ_FRAME_BUFFER permission to get the screenshot. 554 final int callingUid = Binder.getCallingUid(); 555 Binder.withCleanCallingIdentity(() -> { 556 final ScreenshotHardwareBuffer shb = 557 mWmInternal.takeContextualSearchScreenshot( 558 (Flags.contextualSearchPreventSelfCapture() ? callingUid : -1)); 559 final Bitmap bm = shb != null ? shb.asBitmap() : null; 560 if (bm != null) { 561 bundle.putParcelable(ContextualSearchManager.EXTRA_SCREENSHOT, 562 bm.asShared()); 563 bundle.putBoolean(ContextualSearchManager.EXTRA_FLAG_SECURE_FOUND, 564 shb.containsSecureLayers()); 565 } 566 try { 567 callback.onResult( 568 new ContextualSearchState(null, null, bundle)); 569 } catch (RemoteException e) { 570 Log.e(TAG, "Error invoking ContextualSearchCallback", e); 571 } 572 }); 573 } 574 synchronized (mLock) { 575 mStateCallback = callback; 576 } 577 mAssistDataRequester.processPendingAssistData(); 578 } 579 onShellCommand( @ullable FileDescriptor in, @Nullable FileDescriptor out, @Nullable FileDescriptor err, @NonNull String[] args, @Nullable ShellCallback callback, @NonNull ResultReceiver resultReceiver)580 public void onShellCommand( 581 @Nullable FileDescriptor in, 582 @Nullable FileDescriptor out, 583 @Nullable FileDescriptor err, 584 @NonNull String[] args, 585 @Nullable ShellCallback callback, 586 @NonNull ResultReceiver resultReceiver) { 587 new ContextualSearchManagerShellCommand(ContextualSearchManagerService.this) 588 .exec(this, in, out, err, args, callback, resultReceiver); 589 } 590 } 591 } 592