• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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