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