blob: 9f7621f2e78e5fd010ab1a52874ec1cc22a8196b [file] [log] [blame]
Winson Chungda2818f2017-10-23 16:25:49 -07001/*
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
17package com.android.server.am;
18
19import static android.app.ActivityManager.ASSIST_CONTEXT_FULL;
Winson Chungec1ef092017-10-25 16:22:34 -070020import static android.app.ActivityManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
Winson Chungda2818f2017-10-23 16:25:49 -070021import static android.app.AppOpsManager.MODE_ALLOWED;
22import static android.app.AppOpsManager.OP_NONE;
Winson Chungda2818f2017-10-23 16:25:49 -070023
24import android.app.AppOpsManager;
25import android.app.IActivityManager;
Winson Chunge2104682017-11-08 17:31:14 -080026import android.app.IAssistDataReceiver;
Winson Chungda2818f2017-10-23 16:25:49 -070027import android.content.Context;
28import android.graphics.Bitmap;
29import android.os.Bundle;
30import android.os.IBinder;
31import android.os.RemoteException;
32import android.view.IWindowManager;
33
34import com.android.internal.annotations.GuardedBy;
Winson Chungda2818f2017-10-23 16:25:49 -070035import com.android.internal.logging.MetricsLogger;
36
37import java.io.PrintWriter;
38import java.util.ArrayList;
39import 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 */
46public 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
51 private IActivityManager mService;
52 private IWindowManager mWindowManager;
53 private Context mContext;
54 private AppOpsManager mAppOpsManager;
55
56 private AssistDataRequesterCallbacks mCallbacks;
57 private Object mCallbacksLock;
58
59 private int mRequestStructureAppOps;
60 private int mRequestScreenshotAppOps;
61 private boolean mCanceled;
62 private int mPendingDataCount;
63 private int mPendingScreenshotCount;
64 private final ArrayList<Bundle> mAssistData = new ArrayList<>();
65 private final ArrayList<Bitmap> mAssistScreenshot = new ArrayList<>();
66
67
68 /**
69 * Interface to handle the events from the fetcher.
70 */
71 public interface AssistDataRequesterCallbacks {
72 /**
73 * @return whether the currently received assist data can be handled by the callbacks.
74 */
75 @GuardedBy("mCallbacksLock")
76 boolean canHandleReceivedAssistDataLocked();
77
78 /**
79 * Called when we receive asynchronous assist data. This call is only made if the
80 * {@param fetchData} argument to requestAssistData() is true, and if the current activity
81 * allows assist data to be fetched. In addition, the callback will be made with the
82 * {@param mCallbacksLock} held, and only if {@link #canHandleReceivedAssistDataLocked()}
83 * is true.
84 */
85 @GuardedBy("mCallbacksLock")
86 void onAssistDataReceivedLocked(Bundle data, int activityIndex, int activityCount);
87
88 /**
89 * Called when we receive asynchronous assist screenshot. This call is only made if
90 * {@param fetchScreenshot} argument to requestAssistData() is true, and if the current
91 * activity allows assist data to be fetched. In addition, the callback will be made with
92 * the {@param mCallbacksLock} held, and only if
93 * {@link #canHandleReceivedAssistDataLocked()} is true.
94 */
95 @GuardedBy("mCallbacksLock")
96 void onAssistScreenshotReceivedLocked(Bitmap screenshot);
Winson Chung397967f2017-11-01 16:21:35 -070097
98 /**
99 * Called when there is no more pending assist data or screenshots for the last request.
100 * If the request was canceled, then this callback will not be made. In addition, the
101 * callback will be made with the {@param mCallbacksLock} held, and only if
102 * {@link #canHandleReceivedAssistDataLocked()} is true.
103 */
104 @GuardedBy("mCallbacksLock")
105 default void onAssistRequestCompleted() {
106 // Do nothing
107 }
Winson Chungda2818f2017-10-23 16:25:49 -0700108 }
109
110 /**
111 * @param callbacks The callbacks to handle the asynchronous reply with the assist data.
112 * @param callbacksLock The lock for the requester to hold when calling any of the
113 * {@param callbacks}. The owner should also take care in locking
114 * appropriately when calling into this requester.
115 * @param requestStructureAppOps The app ops to check before requesting the assist structure
116 * @param requestScreenshotAppOps The app ops to check before requesting the assist screenshot.
117 * This can be {@link AppOpsManager#OP_NONE} to indicate that
118 * screenshots should never be fetched.
119 */
120 public AssistDataRequester(Context context, IActivityManager service,
121 IWindowManager windowManager, AppOpsManager appOpsManager,
122 AssistDataRequesterCallbacks callbacks, Object callbacksLock,
123 int requestStructureAppOps, int requestScreenshotAppOps) {
124 mCallbacks = callbacks;
125 mCallbacksLock = callbacksLock;
126 mWindowManager = windowManager;
127 mService = service;
128 mContext = context;
129 mAppOpsManager = appOpsManager;
130 mRequestStructureAppOps = requestStructureAppOps;
131 mRequestScreenshotAppOps = requestScreenshotAppOps;
132 }
133
134 /**
135 * Request that assist data be loaded asynchronously. The resulting data will be provided
136 * through the {@link AssistDataRequesterCallbacks}.
137 *
138 * @param activityTokens the list of visible activities
139 * @param fetchData whether or not to fetch the assist data, only applies if the caller is
140 * allowed to fetch the assist data, and the current activity allows assist data to be
141 * fetched from it
142 * @param fetchScreenshot whether or not to fetch the screenshot, only applies if fetchData is
143 * true, the caller is allowed to fetch the assist data, and the current activity allows
144 * assist data to be fetched from it
Winson Chung60f493d2017-11-01 16:02:22 -0700145 * @param allowFetchData to be joined with other checks, determines whether or not the requester
146 * is allowed to fetch the assist data
147 * @param allowFetchScreenshot to be joined with other checks, determines whether or not the
148 * requester is allowed to fetch the assist screenshot
Winson Chungda2818f2017-10-23 16:25:49 -0700149 */
Winson Chung60f493d2017-11-01 16:02:22 -0700150 public void requestAssistData(List<IBinder> activityTokens, final boolean fetchData,
151 final boolean fetchScreenshot, boolean allowFetchData, boolean allowFetchScreenshot,
152 int callingUid, String callingPackage) {
Winson Chung397967f2017-11-01 16:21:35 -0700153 // TODO(b/34090158): Known issue, if the assist data is not allowed on the current activity,
154 // then no assist data is requested for any of the other activities
Winson Chungda2818f2017-10-23 16:25:49 -0700155
156 // Early exit if there are no activity to fetch for
157 if (activityTokens.isEmpty()) {
Winson Chung397967f2017-11-01 16:21:35 -0700158 // No activities, just dispatch request-complete
159 tryDispatchRequestComplete();
Winson Chungda2818f2017-10-23 16:25:49 -0700160 return;
161 }
162
163 // Ensure that the current activity supports assist data
164 boolean isAssistDataAllowed = false;
165 try {
166 isAssistDataAllowed = mService.isAssistDataAllowedOnCurrentActivity();
167 } catch (RemoteException e) {
168 // Should never happen
169 }
Winson Chung60f493d2017-11-01 16:02:22 -0700170 allowFetchData &= isAssistDataAllowed;
171 allowFetchScreenshot &= fetchData && isAssistDataAllowed
Winson Chungda2818f2017-10-23 16:25:49 -0700172 && (mRequestScreenshotAppOps != OP_NONE);
173
174 mCanceled = false;
175 mPendingDataCount = 0;
176 mPendingScreenshotCount = 0;
177 mAssistData.clear();
178 mAssistScreenshot.clear();
179
180 if (fetchData) {
181 if (mAppOpsManager.checkOpNoThrow(mRequestStructureAppOps, callingUid, callingPackage)
Winson Chung60f493d2017-11-01 16:02:22 -0700182 == MODE_ALLOWED && allowFetchData) {
Winson Chungda2818f2017-10-23 16:25:49 -0700183 final int numActivities = activityTokens.size();
184 for (int i = 0; i < numActivities; i++) {
185 IBinder topActivity = activityTokens.get(i);
186 try {
187 MetricsLogger.count(mContext, "assist_with_context", 1);
188 Bundle receiverExtras = new Bundle();
189 receiverExtras.putInt(KEY_RECEIVER_EXTRA_INDEX, i);
190 receiverExtras.putInt(KEY_RECEIVER_EXTRA_COUNT, numActivities);
191 if (mService.requestAssistContextExtras(ASSIST_CONTEXT_FULL, this,
192 receiverExtras, topActivity, /* focused= */ i == 0,
193 /* newSessionId= */ i == 0)) {
194 mPendingDataCount++;
195 } else if (i == 0) {
196 // Wasn't allowed... given that, let's not do the screenshot either.
Winson Chung60f493d2017-11-01 16:02:22 -0700197 if (mCallbacks.canHandleReceivedAssistDataLocked()) {
198 dispatchAssistDataReceived(null);
199 } else {
200 mAssistData.add(null);
201 }
202 allowFetchScreenshot = false;
Winson Chungda2818f2017-10-23 16:25:49 -0700203 break;
204 }
205 } catch (RemoteException e) {
206 // Can't happen
207 }
208 }
209 } else {
210 // Wasn't allowed... given that, let's not do the screenshot either.
Winson Chung60f493d2017-11-01 16:02:22 -0700211 if (mCallbacks.canHandleReceivedAssistDataLocked()) {
212 dispatchAssistDataReceived(null);
213 } else {
214 mAssistData.add(null);
215 }
216 allowFetchScreenshot = false;
Winson Chungda2818f2017-10-23 16:25:49 -0700217 }
218 }
219
220 if (fetchScreenshot) {
221 if (mAppOpsManager.checkOpNoThrow(mRequestScreenshotAppOps, callingUid, callingPackage)
Winson Chung60f493d2017-11-01 16:02:22 -0700222 == MODE_ALLOWED && allowFetchScreenshot) {
Winson Chungda2818f2017-10-23 16:25:49 -0700223 try {
224 MetricsLogger.count(mContext, "assist_with_screen", 1);
225 mPendingScreenshotCount++;
226 mWindowManager.requestAssistScreenshot(this);
227 } catch (RemoteException e) {
228 // Can't happen
229 }
230 } else {
Winson Chung60f493d2017-11-01 16:02:22 -0700231 if (mCallbacks.canHandleReceivedAssistDataLocked()) {
232 dispatchAssistScreenshotReceived(null);
233 } else {
234 mAssistScreenshot.add(null);
235 }
Winson Chungda2818f2017-10-23 16:25:49 -0700236 }
237 }
Winson Chung397967f2017-11-01 16:21:35 -0700238 // For the cases where we dispatch null data/screenshot due to permissions, just dispatch
239 // request-complete after those are made
240 tryDispatchRequestComplete();
Winson Chungda2818f2017-10-23 16:25:49 -0700241 }
242
243 /**
244 * This call should only be made when the callbacks are capable of handling the received assist
Winson Chung60f493d2017-11-01 16:02:22 -0700245 * data. The owner is also responsible for locking before calling this method.
Winson Chungda2818f2017-10-23 16:25:49 -0700246 */
247 public void processPendingAssistData() {
Winson Chung397967f2017-11-01 16:21:35 -0700248 flushPendingAssistData();
249 tryDispatchRequestComplete();
250 }
251
252 private void flushPendingAssistData() {
Winson Chungda2818f2017-10-23 16:25:49 -0700253 final int dataCount = mAssistData.size();
254 for (int i = 0; i < dataCount; i++) {
255 dispatchAssistDataReceived(mAssistData.get(i));
256 }
Winson Chung60f493d2017-11-01 16:02:22 -0700257 mAssistData.clear();
Winson Chungda2818f2017-10-23 16:25:49 -0700258 final int screenshotsCount = mAssistScreenshot.size();
259 for (int i = 0; i < screenshotsCount; i++) {
260 dispatchAssistScreenshotReceived(mAssistScreenshot.get(i));
261 }
Winson Chung60f493d2017-11-01 16:02:22 -0700262 mAssistScreenshot.clear();
Winson Chungda2818f2017-10-23 16:25:49 -0700263 }
264
265 public int getPendingDataCount() {
266 return mPendingDataCount;
267 }
268
269 public int getPendingScreenshotCount() {
270 return mPendingScreenshotCount;
271 }
272
273 /**
274 * Cancels the current request for the assist data.
275 */
276 public void cancel() {
277 // Reset the pending data count, if we receive new assist data after this point, it will
278 // be ignored
279 mCanceled = true;
280 mPendingDataCount = 0;
281 mPendingScreenshotCount = 0;
282 mAssistData.clear();
283 mAssistScreenshot.clear();
284 }
285
286 @Override
287 public void onHandleAssistData(Bundle data) {
288 synchronized (mCallbacksLock) {
289 if (mCanceled) {
290 return;
291 }
292 mPendingDataCount--;
293
294 if (mCallbacks.canHandleReceivedAssistDataLocked()) {
295 // Process any pending data and dispatch the new data as well
Winson Chung397967f2017-11-01 16:21:35 -0700296 flushPendingAssistData();
Winson Chungda2818f2017-10-23 16:25:49 -0700297 dispatchAssistDataReceived(data);
Winson Chung397967f2017-11-01 16:21:35 -0700298 tryDispatchRequestComplete();
Winson Chungda2818f2017-10-23 16:25:49 -0700299 } else {
300 // Queue up the data for processing later
301 mAssistData.add(data);
302 }
303 }
304 }
305
306 @Override
307 public void onHandleAssistScreenshot(Bitmap screenshot) {
308 synchronized (mCallbacksLock) {
309 if (mCanceled) {
310 return;
311 }
312 mPendingScreenshotCount--;
313
314 if (mCallbacks.canHandleReceivedAssistDataLocked()) {
315 // Process any pending data and dispatch the new data as well
Winson Chung397967f2017-11-01 16:21:35 -0700316 flushPendingAssistData();
Winson Chungda2818f2017-10-23 16:25:49 -0700317 dispatchAssistScreenshotReceived(screenshot);
Winson Chung397967f2017-11-01 16:21:35 -0700318 tryDispatchRequestComplete();
Winson Chungda2818f2017-10-23 16:25:49 -0700319 } else {
320 // Queue up the data for processing later
321 mAssistScreenshot.add(screenshot);
322 }
323 }
324 }
325
326 private void dispatchAssistDataReceived(Bundle data) {
327 int activityIndex = 0;
328 int activityCount = 0;
Winson Chungec1ef092017-10-25 16:22:34 -0700329 final Bundle receiverExtras = data != null
330 ? data.getBundle(ASSIST_KEY_RECEIVER_EXTRAS) : null;
Winson Chungda2818f2017-10-23 16:25:49 -0700331 if (receiverExtras != null) {
332 activityIndex = receiverExtras.getInt(KEY_RECEIVER_EXTRA_INDEX);
333 activityCount = receiverExtras.getInt(KEY_RECEIVER_EXTRA_COUNT);
334 }
335 mCallbacks.onAssistDataReceivedLocked(data, activityIndex, activityCount);
336 }
337
338 private void dispatchAssistScreenshotReceived(Bitmap screenshot) {
339 mCallbacks.onAssistScreenshotReceivedLocked(screenshot);
340 }
341
Winson Chung397967f2017-11-01 16:21:35 -0700342 private void tryDispatchRequestComplete() {
343 if (mPendingDataCount == 0 && mPendingScreenshotCount == 0 &&
344 mAssistData.isEmpty() && mAssistScreenshot.isEmpty()) {
345 mCallbacks.onAssistRequestCompleted();
346 }
347 }
348
Winson Chungda2818f2017-10-23 16:25:49 -0700349 public void dump(String prefix, PrintWriter pw) {
350 pw.print(prefix); pw.print("mPendingDataCount="); pw.println(mPendingDataCount);
351 pw.print(prefix); pw.print("mAssistData="); pw.println(mAssistData);
352 pw.print(prefix); pw.print("mPendingScreenshotCount="); pw.println(mPendingScreenshotCount);
353 pw.print(prefix); pw.print("mAssistScreenshot="); pw.println(mAssistScreenshot);
354 }
355}