1 /* 2 * Copyright (C) 2017 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.am; 18 19 import static android.app.ActivityManager.ASSIST_CONTEXT_CONTENT; 20 import static android.app.ActivityManager.ASSIST_CONTEXT_FULL; 21 import static android.app.AppOpsManager.MODE_ALLOWED; 22 import static android.app.AppOpsManager.OP_NONE; 23 24 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS; 25 26 import android.annotation.NonNull; 27 import android.annotation.Nullable; 28 import android.app.ActivityTaskManager; 29 import android.app.AppOpsManager; 30 import android.app.IActivityTaskManager; 31 import android.app.IAssistDataReceiver; 32 import android.content.Context; 33 import android.graphics.Bitmap; 34 import android.os.Bundle; 35 import android.os.IBinder; 36 import android.os.RemoteException; 37 import android.view.IWindowManager; 38 39 import com.android.internal.annotations.GuardedBy; 40 import com.android.internal.annotations.VisibleForTesting; 41 import com.android.internal.logging.MetricsLogger; 42 43 import java.io.PrintWriter; 44 import java.util.ArrayList; 45 import java.util.List; 46 import java.util.Objects; 47 48 /** 49 * Helper class to asynchronously fetch the assist data and screenshot from the current running 50 * activities. It manages received data and calls back to the owner when the owner is ready to 51 * receive the data itself. 52 */ 53 public class AssistDataRequester extends IAssistDataReceiver.Stub { 54 55 public static final String KEY_RECEIVER_EXTRA_COUNT = "count"; 56 public static final String KEY_RECEIVER_EXTRA_INDEX = "index"; 57 58 private IWindowManager mWindowManager; 59 @VisibleForTesting 60 public IActivityTaskManager mActivityTaskManager; 61 private Context mContext; 62 private AppOpsManager mAppOpsManager; 63 64 private AssistDataRequesterCallbacks mCallbacks; 65 private Object mCallbacksLock; 66 67 private int mRequestStructureAppOps; 68 private int mRequestScreenshotAppOps; 69 private boolean mCanceled; 70 private int mPendingDataCount; 71 private int mPendingScreenshotCount; 72 private final ArrayList<Bundle> mAssistData = new ArrayList<>(); 73 private final ArrayList<Bitmap> mAssistScreenshot = new ArrayList<>(); 74 75 76 /** 77 * Interface to handle the events from the fetcher. 78 */ 79 public interface AssistDataRequesterCallbacks { 80 /** 81 * @return whether the currently received assist data can be handled by the callbacks. 82 */ 83 @GuardedBy("mCallbacksLock") canHandleReceivedAssistDataLocked()84 boolean canHandleReceivedAssistDataLocked(); 85 86 /** 87 * Called when we receive asynchronous assist data. This call is only made if the 88 * {@param fetchData} argument to requestAssistData() is true, and if the current activity 89 * allows assist data to be fetched. In addition, the callback will be made with the 90 * {@param mCallbacksLock} held, and only if {@link #canHandleReceivedAssistDataLocked()} 91 * is true. 92 */ 93 @GuardedBy("mCallbacksLock") onAssistDataReceivedLocked(Bundle data, int activityIndex, int activityCount)94 default void onAssistDataReceivedLocked(Bundle data, int activityIndex, int activityCount) { 95 // Do nothing 96 } 97 98 /** 99 * Called when we receive asynchronous assist screenshot. This call is only made if 100 * {@param fetchScreenshot} argument to requestAssistData() is true, and if the current 101 * activity allows assist data to be fetched. In addition, the callback will be made with 102 * the {@param mCallbacksLock} held, and only if 103 * {@link #canHandleReceivedAssistDataLocked()} is true. 104 */ 105 @GuardedBy("mCallbacksLock") onAssistScreenshotReceivedLocked(Bitmap screenshot)106 default void onAssistScreenshotReceivedLocked(Bitmap screenshot) { 107 // Do nothing 108 } 109 110 /** 111 * Called when there is no more pending assist data or screenshots for the last request. 112 * If the request was canceled, then this callback will not be made. In addition, the 113 * callback will be made with the {@param mCallbacksLock} held, and only if 114 * {@link #canHandleReceivedAssistDataLocked()} is true. 115 */ 116 @GuardedBy("mCallbacksLock") onAssistRequestCompleted()117 default void onAssistRequestCompleted() { 118 // Do nothing 119 } 120 } 121 122 /** 123 * @param callbacks The callbacks to handle the asynchronous reply with the assist data. 124 * @param callbacksLock The lock for the requester to hold when calling any of the 125 * {@param callbacks}. The owner should also take care in locking 126 * appropriately when calling into this requester. 127 * @param requestStructureAppOps The app ops to check before requesting the assist structure 128 * @param requestScreenshotAppOps The app ops to check before requesting the assist screenshot. 129 * This can be {@link AppOpsManager#OP_NONE} to indicate that 130 * screenshots should never be fetched. 131 */ AssistDataRequester(Context context, IWindowManager windowManager, AppOpsManager appOpsManager, AssistDataRequesterCallbacks callbacks, Object callbacksLock, int requestStructureAppOps, int requestScreenshotAppOps)132 public AssistDataRequester(Context context, 133 IWindowManager windowManager, AppOpsManager appOpsManager, 134 AssistDataRequesterCallbacks callbacks, Object callbacksLock, 135 int requestStructureAppOps, int requestScreenshotAppOps) { 136 mCallbacks = callbacks; 137 mCallbacksLock = callbacksLock; 138 mWindowManager = windowManager; 139 mActivityTaskManager = ActivityTaskManager.getService(); 140 mContext = context; 141 mAppOpsManager = appOpsManager; 142 mRequestStructureAppOps = requestStructureAppOps; 143 mRequestScreenshotAppOps = requestScreenshotAppOps; 144 } 145 146 /** 147 * Request that assist data be loaded asynchronously. The resulting data will be provided 148 * through the {@link AssistDataRequesterCallbacks}. 149 * 150 * See {@link #requestData(List, boolean, boolean, boolean, boolean, boolean, boolean, boolean, 151 * int, String, String)}. 152 */ requestAssistData(@onNull List<IBinder> activityTokens, final boolean fetchData, final boolean fetchScreenshot, boolean allowFetchData, boolean allowFetchScreenshot, int callingUid, @NonNull String callingPackage, @Nullable String callingAttributionTag)153 public void requestAssistData(@NonNull List<IBinder> activityTokens, final boolean fetchData, 154 final boolean fetchScreenshot, boolean allowFetchData, boolean allowFetchScreenshot, 155 int callingUid, @NonNull String callingPackage, 156 @Nullable String callingAttributionTag) { 157 requestAssistData(activityTokens, fetchData, fetchScreenshot, true /* fetchStructure */, 158 allowFetchData, allowFetchScreenshot, false /* ignoreTopActivityCheck */, 159 callingUid, callingPackage, callingAttributionTag); 160 } 161 162 /** 163 * Request that assist data be loaded asynchronously. The resulting data will be provided 164 * through the {@link AssistDataRequesterCallbacks}. 165 * 166 * See {@link #requestData(List, boolean, boolean, boolean, boolean, boolean, boolean, boolean, 167 * int, String, String)}. 168 */ requestAssistData(@onNull List<IBinder> activityTokens, final boolean fetchData, final boolean fetchScreenshot, final boolean fetchStructure, boolean allowFetchData, boolean allowFetchScreenshot, boolean ignoreTopActivityCheck, int callingUid, @NonNull String callingPackage, @Nullable String callingAttributionTag)169 public void requestAssistData(@NonNull List<IBinder> activityTokens, final boolean fetchData, 170 final boolean fetchScreenshot, final boolean fetchStructure, boolean allowFetchData, 171 boolean allowFetchScreenshot, boolean ignoreTopActivityCheck, int callingUid, 172 @NonNull String callingPackage, @Nullable String callingAttributionTag) { 173 requestData(activityTokens, false /* requestAutofillData */, fetchData, fetchScreenshot, 174 fetchStructure, allowFetchData, allowFetchScreenshot, ignoreTopActivityCheck, 175 callingUid, callingPackage, callingAttributionTag); 176 } 177 178 /** 179 * Request that assist data be loaded asynchronously. The resulting data will be provided 180 * through the {@link AssistDataRequesterCallbacks}. 181 * 182 * @param activityTokens the list of visible activities 183 * @param requestAutofillData if true, will fetch the autofill data, otherwise, will fetch the 184 * assist context data 185 * @param fetchData whether or not to fetch the assist data, only applies if the caller is 186 * allowed to fetch the assist data, and the current activity allows assist data to be 187 * fetched from it 188 * @param fetchScreenshot whether or not to fetch the screenshot, only applies if fetchData is 189 * true, the caller is allowed to fetch the assist data, and the current activity allows 190 * assist data to be fetched from it 191 * @param fetchStructure whether or not to fetch the AssistStructure along with the 192 * AssistContent 193 * @param allowFetchData to be joined with other checks, determines whether or not the requester 194 * is allowed to fetch the assist data 195 * @param allowFetchScreenshot to be joined with other checks, determines whether or not the 196 * requester is allowed to fetch the assist screenshot 197 * @param ignoreTopActivityCheck overrides the check for whether the activity is in focus when 198 * making the request. Used when passing an activity from Recents. 199 * @param callingUid the uid of the real caller 200 * @param callingPackage the package name of the real caller 201 * @param callingAttributionTag The {@link Context#createAttributionContext attribution tag} 202 * of the calling context or {@code null} for default attribution 203 */ requestData(@onNull List<IBinder> activityTokens, final boolean requestAutofillData, final boolean fetchData, final boolean fetchScreenshot, final boolean fetchStructure, boolean allowFetchData, boolean allowFetchScreenshot, boolean ignoreTopActivityCheck, int callingUid, @NonNull String callingPackage, @Nullable String callingAttributionTag)204 private void requestData(@NonNull List<IBinder> activityTokens, 205 final boolean requestAutofillData, final boolean fetchData, 206 final boolean fetchScreenshot, final boolean fetchStructure, boolean allowFetchData, 207 boolean allowFetchScreenshot, boolean ignoreTopActivityCheck, int callingUid, 208 @NonNull String callingPackage, @Nullable String callingAttributionTag) { 209 // TODO(b/34090158): Known issue, if the assist data is not allowed on the current activity, 210 // then no assist data is requested for any of the other activities 211 212 Objects.requireNonNull(activityTokens); 213 Objects.requireNonNull(callingPackage); 214 215 // Early exit if there are no activity to fetch for 216 if (activityTokens.isEmpty()) { 217 // No activities, just dispatch request-complete 218 tryDispatchRequestComplete(); 219 return; 220 } 221 222 // Ensure that the current activity supports assist data 223 boolean isAssistDataAllowed = false; 224 try { 225 isAssistDataAllowed = mActivityTaskManager.isAssistDataAllowed(); 226 } catch (RemoteException e) { 227 // Should never happen 228 } 229 allowFetchData &= isAssistDataAllowed; 230 allowFetchScreenshot &= fetchData && isAssistDataAllowed 231 && (mRequestScreenshotAppOps != OP_NONE); 232 233 mCanceled = false; 234 mPendingDataCount = 0; 235 mPendingScreenshotCount = 0; 236 mAssistData.clear(); 237 mAssistScreenshot.clear(); 238 239 if (fetchData) { 240 if (mAppOpsManager.noteOpNoThrow(mRequestStructureAppOps, callingUid, 241 callingPackage, callingAttributionTag, /* message */ null) == MODE_ALLOWED 242 && allowFetchData) { 243 final int numActivities = activityTokens.size(); 244 for (int i = 0; i < numActivities; i++) { 245 IBinder topActivity = activityTokens.get(i); 246 try { 247 MetricsLogger.count(mContext, "assist_with_context", 1); 248 Bundle receiverExtras = new Bundle(); 249 receiverExtras.putInt(KEY_RECEIVER_EXTRA_INDEX, i); 250 receiverExtras.putInt(KEY_RECEIVER_EXTRA_COUNT, numActivities); 251 boolean result; 252 if (requestAutofillData) { 253 result = mActivityTaskManager.requestAutofillData(this, receiverExtras, 254 topActivity, 0 /* flags */); 255 } else { 256 int requestType = fetchStructure ? ASSIST_CONTEXT_FULL : 257 ASSIST_CONTEXT_CONTENT; 258 result = mActivityTaskManager.requestAssistContextExtras( 259 requestType, this, receiverExtras, topActivity, 260 /* checkActivityIsTop= */ (i == 0) 261 && !ignoreTopActivityCheck, /* newSessionId= */ i == 0); 262 } 263 if (result) { 264 mPendingDataCount++; 265 } else if (i == 0) { 266 // Wasn't allowed... given that, let's not do the screenshot either. 267 if (mCallbacks.canHandleReceivedAssistDataLocked()) { 268 dispatchAssistDataReceived(null); 269 } else { 270 mAssistData.add(null); 271 } 272 allowFetchScreenshot = false; 273 break; 274 } 275 } catch (RemoteException e) { 276 // Can't happen 277 } 278 } 279 } else { 280 // Wasn't allowed... given that, let's not do the screenshot either. 281 if (mCallbacks.canHandleReceivedAssistDataLocked()) { 282 dispatchAssistDataReceived(null); 283 } else { 284 mAssistData.add(null); 285 } 286 allowFetchScreenshot = false; 287 } 288 } 289 290 if (fetchScreenshot) { 291 if (mAppOpsManager.noteOpNoThrow(mRequestScreenshotAppOps, callingUid, 292 callingPackage, callingAttributionTag, /* message */ null) == MODE_ALLOWED 293 && allowFetchScreenshot) { 294 try { 295 MetricsLogger.count(mContext, "assist_with_screen", 1); 296 mPendingScreenshotCount++; 297 mWindowManager.requestAssistScreenshot(this); 298 } catch (RemoteException e) { 299 // Can't happen 300 } 301 } else { 302 if (mCallbacks.canHandleReceivedAssistDataLocked()) { 303 dispatchAssistScreenshotReceived(null); 304 } else { 305 mAssistScreenshot.add(null); 306 } 307 } 308 } 309 // For the cases where we dispatch null data/screenshot due to permissions, just dispatch 310 // request-complete after those are made 311 tryDispatchRequestComplete(); 312 } 313 314 /** 315 * This call should only be made when the callbacks are capable of handling the received assist 316 * data. The owner is also responsible for locking before calling this method. 317 */ processPendingAssistData()318 public void processPendingAssistData() { 319 flushPendingAssistData(); 320 tryDispatchRequestComplete(); 321 } 322 flushPendingAssistData()323 private void flushPendingAssistData() { 324 final int dataCount = mAssistData.size(); 325 for (int i = 0; i < dataCount; i++) { 326 dispatchAssistDataReceived(mAssistData.get(i)); 327 } 328 mAssistData.clear(); 329 final int screenshotsCount = mAssistScreenshot.size(); 330 for (int i = 0; i < screenshotsCount; i++) { 331 dispatchAssistScreenshotReceived(mAssistScreenshot.get(i)); 332 } 333 mAssistScreenshot.clear(); 334 } 335 getPendingDataCount()336 public int getPendingDataCount() { 337 return mPendingDataCount; 338 } 339 getPendingScreenshotCount()340 public int getPendingScreenshotCount() { 341 return mPendingScreenshotCount; 342 } 343 344 /** 345 * Cancels the current request for the assist data. 346 */ cancel()347 public void cancel() { 348 // Reset the pending data count, if we receive new assist data after this point, it will 349 // be ignored 350 mCanceled = true; 351 mPendingDataCount = 0; 352 mPendingScreenshotCount = 0; 353 mAssistData.clear(); 354 mAssistScreenshot.clear(); 355 } 356 357 @Override onHandleAssistData(Bundle data)358 public void onHandleAssistData(Bundle data) { 359 synchronized (mCallbacksLock) { 360 if (mCanceled) { 361 return; 362 } 363 mPendingDataCount--; 364 365 if (mCallbacks.canHandleReceivedAssistDataLocked()) { 366 // Process any pending data and dispatch the new data as well 367 flushPendingAssistData(); 368 dispatchAssistDataReceived(data); 369 tryDispatchRequestComplete(); 370 } else { 371 // Queue up the data for processing later 372 mAssistData.add(data); 373 } 374 } 375 } 376 377 @Override onHandleAssistScreenshot(Bitmap screenshot)378 public void onHandleAssistScreenshot(Bitmap screenshot) { 379 synchronized (mCallbacksLock) { 380 if (mCanceled) { 381 return; 382 } 383 mPendingScreenshotCount--; 384 385 if (mCallbacks.canHandleReceivedAssistDataLocked()) { 386 // Process any pending data and dispatch the new data as well 387 flushPendingAssistData(); 388 dispatchAssistScreenshotReceived(screenshot); 389 tryDispatchRequestComplete(); 390 } else { 391 // Queue up the data for processing later 392 mAssistScreenshot.add(screenshot); 393 } 394 } 395 } 396 dispatchAssistDataReceived(Bundle data)397 private void dispatchAssistDataReceived(Bundle data) { 398 int activityIndex = 0; 399 int activityCount = 0; 400 final Bundle receiverExtras = data != null 401 ? data.getBundle(ASSIST_KEY_RECEIVER_EXTRAS) : null; 402 if (receiverExtras != null) { 403 activityIndex = receiverExtras.getInt(KEY_RECEIVER_EXTRA_INDEX); 404 activityCount = receiverExtras.getInt(KEY_RECEIVER_EXTRA_COUNT); 405 } 406 mCallbacks.onAssistDataReceivedLocked(data, activityIndex, activityCount); 407 } 408 dispatchAssistScreenshotReceived(Bitmap screenshot)409 private void dispatchAssistScreenshotReceived(Bitmap screenshot) { 410 mCallbacks.onAssistScreenshotReceivedLocked(screenshot); 411 } 412 tryDispatchRequestComplete()413 private void tryDispatchRequestComplete() { 414 if (mPendingDataCount == 0 && mPendingScreenshotCount == 0 && 415 mAssistData.isEmpty() && mAssistScreenshot.isEmpty()) { 416 mCallbacks.onAssistRequestCompleted(); 417 } 418 } 419 dump(String prefix, PrintWriter pw)420 public void dump(String prefix, PrintWriter pw) { 421 pw.print(prefix); pw.print("mPendingDataCount="); pw.println(mPendingDataCount); 422 pw.print(prefix); pw.print("mAssistData="); pw.println(mAssistData); 423 pw.print(prefix); pw.print("mPendingScreenshotCount="); pw.println(mPendingScreenshotCount); 424 pw.print(prefix); pw.print("mAssistScreenshot="); pw.println(mAssistScreenshot); 425 } 426 }