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 17package com.android.server.am; 18 19import static android.app.AppOpsManager.MODE_ALLOWED; 20import static android.app.AppOpsManager.MODE_ERRORED; 21import static android.app.AppOpsManager.OP_ASSIST_SCREENSHOT; 22import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE; 23import static android.graphics.Bitmap.Config.ARGB_8888; 24 25import static org.junit.Assert.assertFalse; 26import static org.junit.Assert.assertTrue; 27import static org.mockito.ArgumentMatchers.any; 28import static org.mockito.ArgumentMatchers.anyBoolean; 29import static org.mockito.ArgumentMatchers.anyInt; 30import static org.mockito.ArgumentMatchers.anyString; 31import static org.mockito.ArgumentMatchers.eq; 32import static org.mockito.Mockito.doAnswer; 33import static org.mockito.Mockito.doReturn; 34import static org.mockito.Mockito.mock; 35 36import android.app.AppOpsManager; 37import android.app.IActivityManager; 38import android.content.Context; 39import android.graphics.Bitmap; 40import android.os.Bundle; 41import android.os.Handler; 42import android.os.IBinder; 43import android.os.Looper; 44import android.support.test.InstrumentationRegistry; 45import android.support.test.filters.MediumTest; 46import android.support.test.runner.AndroidJUnit4; 47import android.util.Log; 48import android.view.IWindowManager; 49 50import com.android.server.am.AssistDataRequester.AssistDataRequesterCallbacks; 51 52import org.junit.Before; 53import org.junit.Test; 54import org.junit.runner.RunWith; 55 56import java.util.ArrayList; 57import java.util.List; 58import java.util.concurrent.CountDownLatch; 59import java.util.concurrent.TimeUnit; 60 61/** 62 * Note: Currently, we only support fetching the screenshot for the current application, so the 63 * screenshot checks are hardcoded accordingly. 64 * 65 * runtest --path frameworks/base/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java 66 */ 67@MediumTest 68@RunWith(AndroidJUnit4.class) 69public class AssistDataRequesterTest extends ActivityTestsBase { 70 71 private static final String TAG = AssistDataRequesterTest.class.getSimpleName(); 72 73 private static final boolean CURRENT_ACTIVITY_ASSIST_ALLOWED = true; 74 private static final boolean CALLER_ASSIST_STRUCTURE_ALLOWED = true; 75 private static final boolean CALLER_ASSIST_SCREENSHOT_ALLOWED = true; 76 private static final boolean FETCH_DATA = true; 77 private static final boolean FETCH_SCREENSHOTS = true; 78 private static final boolean ALLOW_FETCH_DATA = true; 79 private static final boolean ALLOW_FETCH_SCREENSHOTS = true; 80 81 private static final int TEST_UID = 0; 82 private static final String TEST_PACKAGE = ""; 83 84 private Context mContext; 85 private AssistDataRequester mDataRequester; 86 private Callbacks mCallbacks; 87 private Object mCallbacksLock; 88 private Handler mHandler; 89 private IActivityManager mAm; 90 private IWindowManager mWm; 91 private AppOpsManager mAppOpsManager; 92 93 /** 94 * The requests to fetch assist data are done incrementally from the text thread, and we 95 * immediately post onto the main thread handler below, which would immediately make the 96 * callback and decrement the pending counts. In order to assert the pending counts, we defer 97 * the callbacks on the test-side until after we flip the gate, after which we can drain the 98 * main thread handler and make assertions on the actual callbacks 99 */ 100 private CountDownLatch mGate; 101 102 @Before 103 @Override 104 public void setUp() throws Exception { 105 super.setUp(); 106 mAm = mock(IActivityManager.class); 107 mWm = mock(IWindowManager.class); 108 mAppOpsManager = mock(AppOpsManager.class); 109 mContext = InstrumentationRegistry.getContext(); 110 mHandler = new Handler(Looper.getMainLooper()); 111 mCallbacksLock = new Object(); 112 mCallbacks = new Callbacks(); 113 mDataRequester = new AssistDataRequester(mContext, mAm, mWm, mAppOpsManager, mCallbacks, 114 mCallbacksLock, OP_ASSIST_STRUCTURE, OP_ASSIST_SCREENSHOT); 115 116 // Gate the continuation of the assist data callbacks until we are ready within the tests 117 mGate = new CountDownLatch(1); 118 doAnswer(invocation -> { 119 mHandler.post(() -> { 120 try { 121 mGate.await(10, TimeUnit.SECONDS); 122 mDataRequester.onHandleAssistData(new Bundle()); 123 } catch (InterruptedException e) { 124 Log.e(TAG, "Failed to wait", e); 125 } 126 }); 127 return true; 128 }).when(mAm).requestAssistContextExtras(anyInt(), any(), any(), any(), anyBoolean(), 129 anyBoolean()); 130 doAnswer(invocation -> { 131 mHandler.post(() -> { 132 try { 133 mGate.await(10, TimeUnit.SECONDS); 134 mDataRequester.onHandleAssistScreenshot(Bitmap.createBitmap(1, 1, ARGB_8888)); 135 } catch (InterruptedException e) { 136 Log.e(TAG, "Failed to wait", e); 137 } 138 }); 139 return true; 140 }).when(mWm).requestAssistScreenshot(any()); 141 } 142 143 private void setupMocks(boolean currentActivityAssistAllowed, boolean assistStructureAllowed, 144 boolean assistScreenshotAllowed) throws Exception { 145 doReturn(currentActivityAssistAllowed).when(mAm).isAssistDataAllowedOnCurrentActivity(); 146 doReturn(assistStructureAllowed ? MODE_ALLOWED : MODE_ERRORED).when(mAppOpsManager) 147 .checkOpNoThrow(eq(OP_ASSIST_STRUCTURE), anyInt(), anyString()); 148 doReturn(assistScreenshotAllowed ? MODE_ALLOWED : MODE_ERRORED).when(mAppOpsManager) 149 .checkOpNoThrow(eq(OP_ASSIST_SCREENSHOT), anyInt(), anyString()); 150 } 151 152 @Test 153 public void testRequestData() throws Exception { 154 setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED, 155 CALLER_ASSIST_SCREENSHOT_ALLOWED); 156 157 mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS, 158 ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE); 159 assertReceivedDataCount(5, 5, 1, 1); 160 } 161 162 @Test 163 public void testEmptyActivities_expectNoCallbacks() throws Exception { 164 setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED, 165 CALLER_ASSIST_SCREENSHOT_ALLOWED); 166 167 mDataRequester.requestAssistData(createActivityList(0), FETCH_DATA, FETCH_SCREENSHOTS, 168 ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE); 169 assertReceivedDataCount(0, 0, 0, 0); 170 } 171 172 @Test 173 public void testCurrentAppDisallow_expectNullCallbacks() throws Exception { 174 setupMocks(!CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED, 175 CALLER_ASSIST_SCREENSHOT_ALLOWED); 176 177 mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS, 178 ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE); 179 assertReceivedDataCount(0, 1, 0, 1); 180 } 181 182 @Test 183 public void testProcessPendingData() throws Exception { 184 setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED, 185 CALLER_ASSIST_SCREENSHOT_ALLOWED); 186 187 mCallbacks.canHandleReceivedData = false; 188 mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS, 189 ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE); 190 assertTrue(mDataRequester.getPendingDataCount() == 5); 191 assertTrue(mDataRequester.getPendingScreenshotCount() == 1); 192 mGate.countDown(); 193 waitForIdle(mHandler); 194 195 // Callbacks still not ready to receive, but all pending data is received 196 assertTrue(mDataRequester.getPendingDataCount() == 0); 197 assertTrue(mDataRequester.getPendingScreenshotCount() == 0); 198 assertTrue(mCallbacks.receivedData.isEmpty()); 199 assertTrue(mCallbacks.receivedScreenshots.isEmpty()); 200 assertFalse(mCallbacks.requestCompleted); 201 202 mCallbacks.canHandleReceivedData = true; 203 mDataRequester.processPendingAssistData(); 204 // Since we are posting the callback for the request-complete, flush the handler as well 205 mGate.countDown(); 206 waitForIdle(mHandler); 207 assertTrue(mCallbacks.receivedData.size() == 5); 208 assertTrue(mCallbacks.receivedScreenshots.size() == 1); 209 assertTrue(mCallbacks.requestCompleted); 210 211 // Clear the state and ensure that we only process pending data once 212 mCallbacks.reset(); 213 mDataRequester.processPendingAssistData(); 214 assertTrue(mCallbacks.receivedData.isEmpty()); 215 assertTrue(mCallbacks.receivedScreenshots.isEmpty()); 216 } 217 218 @Test 219 public void testNoFetchData_expectNoDataCallbacks() throws Exception { 220 setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED, 221 CALLER_ASSIST_SCREENSHOT_ALLOWED); 222 223 mDataRequester.requestAssistData(createActivityList(5), !FETCH_DATA, FETCH_SCREENSHOTS, 224 ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE); 225 assertReceivedDataCount(0, 0, 0, 1); 226 } 227 228 @Test 229 public void testDisallowAssistStructure_expectNullDataCallbacks() throws Exception { 230 setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, !CALLER_ASSIST_STRUCTURE_ALLOWED, 231 CALLER_ASSIST_SCREENSHOT_ALLOWED); 232 233 mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS, 234 ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE); 235 // Expect a single null data when the appops is denied 236 assertReceivedDataCount(0, 1, 0, 1); 237 } 238 239 @Test 240 public void testDisallowAssistContextExtras_expectNullDataCallbacks() throws Exception { 241 setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED, 242 CALLER_ASSIST_SCREENSHOT_ALLOWED); 243 doReturn(false).when(mAm).requestAssistContextExtras(anyInt(), any(), any(), any(), 244 anyBoolean(), anyBoolean()); 245 246 mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS, 247 ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE); 248 // Expect a single null data when requestAssistContextExtras() fails 249 assertReceivedDataCount(0, 1, 0, 1); 250 } 251 252 @Test 253 public void testNoFetchScreenshots_expectNoScreenshotCallbacks() throws Exception { 254 setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED, 255 CALLER_ASSIST_SCREENSHOT_ALLOWED); 256 257 mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, !FETCH_SCREENSHOTS, 258 ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE); 259 assertReceivedDataCount(5, 5, 0, 0); 260 } 261 262 @Test 263 public void testDisallowAssistScreenshot_expectNullScreenshotCallback() throws Exception { 264 setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED, 265 !CALLER_ASSIST_SCREENSHOT_ALLOWED); 266 267 mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS, 268 ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE); 269 // Expect a single null screenshot when the appops is denied 270 assertReceivedDataCount(5, 5, 0, 1); 271 } 272 273 @Test 274 public void testCanNotHandleReceivedData_expectNoCallbacks() throws Exception { 275 setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, !CALLER_ASSIST_STRUCTURE_ALLOWED, 276 !CALLER_ASSIST_SCREENSHOT_ALLOWED); 277 278 mCallbacks.canHandleReceivedData = false; 279 mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS, 280 ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE); 281 mGate.countDown(); 282 waitForIdle(mHandler); 283 assertTrue(mCallbacks.receivedData.isEmpty()); 284 assertTrue(mCallbacks.receivedScreenshots.isEmpty()); 285 } 286 287 @Test 288 public void testRequestDataNoneAllowed_expectNullCallbacks() throws Exception { 289 setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED, 290 CALLER_ASSIST_SCREENSHOT_ALLOWED); 291 292 mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS, 293 !ALLOW_FETCH_DATA, !ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE); 294 assertReceivedDataCount(0, 1, 0, 1); 295 } 296 297 private void assertReceivedDataCount(int numPendingData, int numReceivedData, 298 int numPendingScreenshots, int numReceivedScreenshots) throws Exception { 299 assertTrue("Expected " + numPendingData + " pending data, got " 300 + mDataRequester.getPendingDataCount(), 301 mDataRequester.getPendingDataCount() == numPendingData); 302 assertTrue("Expected " + numPendingScreenshots + " pending screenshots, got " 303 + mDataRequester.getPendingScreenshotCount(), 304 mDataRequester.getPendingScreenshotCount() == numPendingScreenshots); 305 assertFalse("Expected request NOT completed", mCallbacks.requestCompleted); 306 mGate.countDown(); 307 waitForIdle(mHandler); 308 assertTrue("Expected " + numReceivedData + " data, received " 309 + mCallbacks.receivedData.size(), 310 mCallbacks.receivedData.size() == numReceivedData); 311 assertTrue("Expected " + numReceivedScreenshots + " screenshots, received " 312 + mCallbacks.receivedScreenshots.size(), 313 mCallbacks.receivedScreenshots.size() == numReceivedScreenshots); 314 assertTrue("Expected request completed", mCallbacks.requestCompleted); 315 } 316 317 private List<IBinder> createActivityList(int size) { 318 ArrayList<IBinder> activities = new ArrayList<>(); 319 for (int i = 0; i < size; i++) { 320 activities.add(mock(IBinder.class)); 321 } 322 return activities; 323 } 324 325 public void waitForIdle(Handler h) throws Exception { 326 if (Looper.myLooper() == h.getLooper()) { 327 throw new RuntimeException("This method can not be called from the waiting looper"); 328 } 329 CountDownLatch latch = new CountDownLatch(1); 330 h.post(() -> latch.countDown()); 331 latch.await(2, TimeUnit.SECONDS); 332 } 333 334 private class Callbacks implements AssistDataRequesterCallbacks { 335 336 boolean canHandleReceivedData = true; 337 boolean requestCompleted = false; 338 ArrayList<Bundle> receivedData = new ArrayList<>(); 339 ArrayList<Bitmap> receivedScreenshots = new ArrayList<>(); 340 341 void reset() { 342 canHandleReceivedData = true; 343 receivedData.clear(); 344 receivedScreenshots.clear(); 345 } 346 347 @Override 348 public boolean canHandleReceivedAssistDataLocked() { 349 return canHandleReceivedData; 350 } 351 352 @Override 353 public void onAssistDataReceivedLocked(Bundle data, int activityIndex, int activityCount) { 354 receivedData.add(data); 355 } 356 357 @Override 358 public void onAssistScreenshotReceivedLocked(Bitmap screenshot) { 359 receivedScreenshots.add(screenshot); 360 } 361 362 @Override 363 public void onAssistRequestCompleted() { 364 mHandler.post(() -> { 365 try { 366 mGate.await(10, TimeUnit.SECONDS); 367 requestCompleted = true; 368 } catch (InterruptedException e) { 369 Log.e(TAG, "Failed to wait", e); 370 } 371 }); 372 } 373 } 374}