1 /* 2 * Copyright (C) 2023 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.intentresolver.logging; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.mockito.AdditionalMatchers.gt; 22 import static org.mockito.ArgumentMatchers.any; 23 import static org.mockito.ArgumentMatchers.anyBoolean; 24 import static org.mockito.ArgumentMatchers.anyInt; 25 import static org.mockito.ArgumentMatchers.anyString; 26 import static org.mockito.ArgumentMatchers.eq; 27 import static org.mockito.ArgumentMatchers.isNull; 28 import static org.mockito.Mockito.times; 29 import static org.mockito.Mockito.verify; 30 import static org.mockito.Mockito.verifyNoMoreInteractions; 31 32 import android.content.Intent; 33 import android.metrics.LogMaker; 34 35 import com.android.intentresolver.contentpreview.ContentPreviewType; 36 import com.android.intentresolver.logging.EventLogImpl.SharesheetStandardEvent; 37 import com.android.intentresolver.logging.EventLogImpl.SharesheetStartedEvent; 38 import com.android.intentresolver.logging.EventLogImpl.SharesheetTargetSelectedEvent; 39 import com.android.internal.logging.InstanceId; 40 import com.android.internal.logging.InstanceIdSequence; 41 import com.android.internal.logging.MetricsLogger; 42 import com.android.internal.logging.UiEventLogger; 43 import com.android.internal.logging.UiEventLogger.UiEventEnum; 44 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 45 import com.android.internal.util.FrameworkStatsLog; 46 47 import org.junit.After; 48 import org.junit.Before; 49 import org.junit.Test; 50 import org.junit.runner.RunWith; 51 import org.mockito.ArgumentCaptor; 52 import org.mockito.Mock; 53 import org.mockito.junit.MockitoJUnitRunner; 54 55 @RunWith(MockitoJUnitRunner.class) 56 public final class EventLogImplTest { 57 @Mock private UiEventLogger mUiEventLog; 58 @Mock private FrameworkStatsLogger mFrameworkLog; 59 @Mock private MetricsLogger mMetricsLogger; 60 61 private EventLogImpl mChooserLogger; 62 63 private final InstanceIdSequence mSequence = EventLogImpl.newIdSequence(); 64 65 @Before setUp()66 public void setUp() { 67 mChooserLogger = new EventLogImpl(mUiEventLog, mFrameworkLog, mMetricsLogger, 68 mSequence.newInstanceId()); 69 } 70 71 @After tearDown()72 public void tearDown() { 73 verifyNoMoreInteractions(mUiEventLog); 74 verifyNoMoreInteractions(mFrameworkLog); 75 verifyNoMoreInteractions(mMetricsLogger); 76 } 77 78 @Test testLogChooserActivityShown_personalProfile()79 public void testLogChooserActivityShown_personalProfile() { 80 final boolean isWorkProfile = false; 81 final String mimeType = "application/TestType"; 82 final long systemCost = 456; 83 84 mChooserLogger.logChooserActivityShown(isWorkProfile, mimeType, systemCost); 85 86 ArgumentCaptor<LogMaker> eventCaptor = ArgumentCaptor.forClass(LogMaker.class); 87 verify(mMetricsLogger).write(eventCaptor.capture()); 88 LogMaker event = eventCaptor.getValue(); 89 90 assertThat(event.getCategory()).isEqualTo(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN); 91 assertThat(event.getSubtype()).isEqualTo(MetricsEvent.PARENT_PROFILE); 92 assertThat(event.getTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE)).isEqualTo(mimeType); 93 assertThat(event.getTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS)) 94 .isEqualTo(systemCost); 95 } 96 97 @Test testLogChooserActivityShown_workProfile()98 public void testLogChooserActivityShown_workProfile() { 99 final boolean isWorkProfile = true; 100 final String mimeType = "application/TestType"; 101 final long systemCost = 456; 102 103 mChooserLogger.logChooserActivityShown(isWorkProfile, mimeType, systemCost); 104 105 ArgumentCaptor<LogMaker> eventCaptor = ArgumentCaptor.forClass(LogMaker.class); 106 verify(mMetricsLogger).write(eventCaptor.capture()); 107 LogMaker event = eventCaptor.getValue(); 108 109 assertThat(event.getCategory()).isEqualTo(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN); 110 assertThat(event.getSubtype()).isEqualTo(MetricsEvent.MANAGED_PROFILE); 111 assertThat(event.getTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE)).isEqualTo(mimeType); 112 assertThat(event.getTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS)) 113 .isEqualTo(systemCost); 114 } 115 116 @Test testLogShareStarted()117 public void testLogShareStarted() { 118 final String packageName = "com.test.foo"; 119 final String mimeType = "text/plain"; 120 final int appProvidedDirectTargets = 123; 121 final int appProvidedAppTargets = 456; 122 final boolean workProfile = true; 123 final int previewType = ContentPreviewType.CONTENT_PREVIEW_FILE; 124 final String intentAction = Intent.ACTION_SENDTO; 125 final int numCustomActions = 3; 126 final boolean modifyShareProvided = true; 127 128 mChooserLogger.logShareStarted( 129 packageName, 130 mimeType, 131 appProvidedDirectTargets, 132 appProvidedAppTargets, 133 workProfile, 134 previewType, 135 intentAction, 136 numCustomActions, 137 modifyShareProvided); 138 139 verify(mFrameworkLog).write( 140 eq(FrameworkStatsLog.SHARESHEET_STARTED), 141 eq(SharesheetStartedEvent.SHARE_STARTED.getId()), 142 eq(packageName), 143 /* instanceId=*/ gt(0), 144 eq(mimeType), 145 eq(appProvidedDirectTargets), 146 eq(appProvidedAppTargets), 147 eq(workProfile), 148 eq(FrameworkStatsLog.SHARESHEET_STARTED__PREVIEW_TYPE__CONTENT_PREVIEW_FILE), 149 eq(FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_SENDTO), 150 /* custom actions provided */ eq(numCustomActions), 151 /* reselection action provided */ eq(modifyShareProvided)); 152 } 153 154 @Test shareStartedWithShareouselAndEnabledReportingFlag_imagePreviewTypeReported()155 public void shareStartedWithShareouselAndEnabledReportingFlag_imagePreviewTypeReported() { 156 final String packageName = "com.test.foo"; 157 final String mimeType = "text/plain"; 158 final int appProvidedDirectTargets = 123; 159 final int appProvidedAppTargets = 456; 160 final boolean workProfile = true; 161 final int previewType = ContentPreviewType.CONTENT_PREVIEW_PAYLOAD_SELECTION; 162 final String intentAction = Intent.ACTION_SENDTO; 163 final int numCustomActions = 3; 164 final boolean modifyShareProvided = true; 165 166 mChooserLogger.logShareStarted( 167 packageName, 168 mimeType, 169 appProvidedDirectTargets, 170 appProvidedAppTargets, 171 workProfile, 172 previewType, 173 intentAction, 174 numCustomActions, 175 modifyShareProvided); 176 177 verify(mFrameworkLog).write( 178 eq(FrameworkStatsLog.SHARESHEET_STARTED), 179 eq(SharesheetStartedEvent.SHARE_STARTED.getId()), 180 eq(packageName), 181 /* instanceId=*/ gt(0), 182 eq(mimeType), 183 eq(appProvidedDirectTargets), 184 eq(appProvidedAppTargets), 185 eq(workProfile), 186 eq(FrameworkStatsLog 187 .SHARESHEET_STARTED__PREVIEW_TYPE__CONTENT_PREVIEW_TOGGLEABLE_MEDIA), 188 eq(FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_SENDTO), 189 /* custom actions provided */ eq(numCustomActions), 190 /* reselection action provided */ eq(modifyShareProvided)); 191 } 192 193 @Test testLogShareTargetSelected()194 public void testLogShareTargetSelected() { 195 final int targetType = EventLogImpl.SELECTION_TYPE_SERVICE; 196 final String packageName = "com.test.foo"; 197 final int positionPicked = 123; 198 final int directTargetAlsoRanked = -1; 199 final int callerTargetCount = 0; 200 final boolean isPinned = true; 201 final boolean isSuccessfullySelected = true; 202 final long selectionCost = 456; 203 204 mChooserLogger.logShareTargetSelected( 205 targetType, 206 packageName, 207 positionPicked, 208 directTargetAlsoRanked, 209 callerTargetCount, 210 /* directTargetHashed= */ null, 211 isPinned, 212 isSuccessfullySelected, 213 selectionCost); 214 215 verify(mFrameworkLog).write( 216 eq(FrameworkStatsLog.RANKING_SELECTED), 217 eq(SharesheetTargetSelectedEvent.SHARESHEET_SERVICE_TARGET_SELECTED.getId()), 218 eq(packageName), 219 /* instanceId=*/ gt(0), 220 eq(positionPicked), 221 eq(isPinned)); 222 223 ArgumentCaptor<LogMaker> eventCaptor = ArgumentCaptor.forClass(LogMaker.class); 224 verify(mMetricsLogger).write(eventCaptor.capture()); 225 LogMaker event = eventCaptor.getValue(); 226 assertThat(event.getCategory()).isEqualTo( 227 MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SERVICE_TARGET); 228 assertThat(event.getSubtype()).isEqualTo(positionPicked); 229 } 230 231 @Test testLogActionSelected()232 public void testLogActionSelected() { 233 mChooserLogger.logActionSelected(EventLogImpl.SELECTION_TYPE_COPY); 234 235 verify(mFrameworkLog).write( 236 eq(FrameworkStatsLog.RANKING_SELECTED), 237 eq(SharesheetTargetSelectedEvent.SHARESHEET_COPY_TARGET_SELECTED.getId()), 238 eq(""), 239 /* instanceId=*/ gt(0), 240 eq(-1), 241 eq(false)); 242 243 ArgumentCaptor<LogMaker> eventCaptor = ArgumentCaptor.forClass(LogMaker.class); 244 verify(mMetricsLogger).write(eventCaptor.capture()); 245 LogMaker event = eventCaptor.getValue(); 246 assertThat(event.getCategory()).isEqualTo( 247 MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SYSTEM_TARGET); 248 assertThat(event.getSubtype()).isEqualTo(1); 249 } 250 251 @Test testLogCustomActionSelected()252 public void testLogCustomActionSelected() { 253 final int position = 4; 254 mChooserLogger.logCustomActionSelected(position); 255 256 verify(mFrameworkLog).write( 257 eq(FrameworkStatsLog.RANKING_SELECTED), 258 eq(SharesheetTargetSelectedEvent.SHARESHEET_CUSTOM_ACTION_SELECTED.getId()), 259 any(), anyInt(), eq(position), eq(false)); 260 } 261 262 @Test testLogDirectShareTargetReceived()263 public void testLogDirectShareTargetReceived() { 264 final int category = MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_SHORTCUT_MANAGER; 265 final int latency = 123; 266 267 mChooserLogger.logDirectShareTargetReceived(category, latency); 268 269 ArgumentCaptor<LogMaker> eventCaptor = ArgumentCaptor.forClass(LogMaker.class); 270 verify(mMetricsLogger).write(eventCaptor.capture()); 271 LogMaker event = eventCaptor.getValue(); 272 assertThat(event.getCategory()).isEqualTo(category); 273 assertThat(event.getSubtype()).isEqualTo(latency); 274 } 275 276 @Test testLogActionShareWithPreview()277 public void testLogActionShareWithPreview() { 278 final int previewType = ContentPreviewType.CONTENT_PREVIEW_TEXT; 279 280 mChooserLogger.logActionShareWithPreview(previewType); 281 282 ArgumentCaptor<LogMaker> eventCaptor = ArgumentCaptor.forClass(LogMaker.class); 283 verify(mMetricsLogger).write(eventCaptor.capture()); 284 LogMaker event = eventCaptor.getValue(); 285 assertThat(event.getCategory()).isEqualTo(MetricsEvent.ACTION_SHARE_WITH_PREVIEW); 286 assertThat(event.getSubtype()).isEqualTo(previewType); 287 } 288 289 @Test testLogSharesheetTriggered()290 public void testLogSharesheetTriggered() { 291 mChooserLogger.logSharesheetTriggered(); 292 verify(mUiEventLog).logWithInstanceId( 293 eq(SharesheetStandardEvent.SHARESHEET_TRIGGERED), eq(0), isNull(), any()); 294 } 295 296 @Test testLogSharesheetAppLoadComplete()297 public void testLogSharesheetAppLoadComplete() { 298 mChooserLogger.logSharesheetAppLoadComplete(); 299 verify(mUiEventLog).logWithInstanceId( 300 eq(SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE), eq(0), isNull(), any()); 301 } 302 303 @Test testLogSharesheetDirectLoadComplete()304 public void testLogSharesheetDirectLoadComplete() { 305 mChooserLogger.logSharesheetDirectLoadComplete(); 306 verify(mUiEventLog).logWithInstanceId( 307 eq(SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_COMPLETE), 308 eq(0), 309 isNull(), 310 any()); 311 } 312 313 @Test testLogSharesheetDirectLoadTimeout()314 public void testLogSharesheetDirectLoadTimeout() { 315 mChooserLogger.logSharesheetDirectLoadTimeout(); 316 verify(mUiEventLog).logWithInstanceId( 317 eq(SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_TIMEOUT), eq(0), isNull(), any()); 318 } 319 320 @Test testLogSharesheetProfileChanged()321 public void testLogSharesheetProfileChanged() { 322 mChooserLogger.logSharesheetProfileChanged(); 323 verify(mUiEventLog).logWithInstanceId( 324 eq(SharesheetStandardEvent.SHARESHEET_PROFILE_CHANGED), eq(0), isNull(), any()); 325 } 326 327 @Test testLogSharesheetExpansionChanged_collapsed()328 public void testLogSharesheetExpansionChanged_collapsed() { 329 mChooserLogger.logSharesheetExpansionChanged(/* isCollapsed=*/ true); 330 verify(mUiEventLog).logWithInstanceId( 331 eq(SharesheetStandardEvent.SHARESHEET_COLLAPSED), eq(0), isNull(), any()); 332 } 333 334 @Test testLogSharesheetExpansionChanged_expanded()335 public void testLogSharesheetExpansionChanged_expanded() { 336 mChooserLogger.logSharesheetExpansionChanged(/* isCollapsed=*/ false); 337 verify(mUiEventLog).logWithInstanceId( 338 eq(SharesheetStandardEvent.SHARESHEET_EXPANDED), eq(0), isNull(), any()); 339 } 340 341 @Test testLogSharesheetAppShareRankingTimeout()342 public void testLogSharesheetAppShareRankingTimeout() { 343 mChooserLogger.logSharesheetAppShareRankingTimeout(); 344 verify(mUiEventLog).logWithInstanceId( 345 eq(SharesheetStandardEvent.SHARESHEET_APP_SHARE_RANKING_TIMEOUT), 346 eq(0), 347 isNull(), 348 any()); 349 } 350 351 @Test testLogSharesheetEmptyDirectShareRow()352 public void testLogSharesheetEmptyDirectShareRow() { 353 mChooserLogger.logSharesheetEmptyDirectShareRow(); 354 verify(mUiEventLog).logWithInstanceId( 355 eq(SharesheetStandardEvent.SHARESHEET_EMPTY_DIRECT_SHARE_ROW), 356 eq(0), 357 isNull(), 358 any()); 359 } 360 361 @Test testDifferentLoggerInstancesUseDifferentInstanceIds()362 public void testDifferentLoggerInstancesUseDifferentInstanceIds() { 363 ArgumentCaptor<Integer> idIntCaptor = ArgumentCaptor.forClass(Integer.class); 364 EventLogImpl chooserLogger2 = 365 new EventLogImpl(mUiEventLog, mFrameworkLog, mMetricsLogger, 366 mSequence.newInstanceId()); 367 368 final int targetType = EventLogImpl.SELECTION_TYPE_COPY; 369 final String packageName = "com.test.foo"; 370 final int positionPicked = 123; 371 final int directTargetAlsoRanked = -1; 372 final int callerTargetCount = 0; 373 final boolean isPinned = true; 374 final boolean isSuccessfullySelected = true; 375 final long selectionCost = 456; 376 377 mChooserLogger.logShareTargetSelected( 378 targetType, 379 packageName, 380 positionPicked, 381 directTargetAlsoRanked, 382 callerTargetCount, 383 /* directTargetHashed= */ null, 384 isPinned, 385 isSuccessfullySelected, 386 selectionCost); 387 388 chooserLogger2.logShareTargetSelected( 389 targetType, 390 packageName, 391 positionPicked, 392 directTargetAlsoRanked, 393 callerTargetCount, 394 /* directTargetHashed= */ null, 395 isPinned, 396 isSuccessfullySelected, 397 selectionCost); 398 399 verify(mFrameworkLog, times(2)).write( 400 anyInt(), anyInt(), anyString(), idIntCaptor.capture(), anyInt(), anyBoolean()); 401 402 int id1 = idIntCaptor.getAllValues().get(0); 403 int id2 = idIntCaptor.getAllValues().get(1); 404 405 assertThat(id1).isGreaterThan(0); 406 assertThat(id2).isGreaterThan(0); 407 assertThat(id1).isNotEqualTo(id2); 408 } 409 410 @Test testUiAndFrameworkEventsUseSameInstanceIdForSameLoggerInstance()411 public void testUiAndFrameworkEventsUseSameInstanceIdForSameLoggerInstance() { 412 ArgumentCaptor<Integer> idIntCaptor = ArgumentCaptor.forClass(Integer.class); 413 ArgumentCaptor<InstanceId> idObjectCaptor = ArgumentCaptor.forClass(InstanceId.class); 414 415 final int targetType = EventLogImpl.SELECTION_TYPE_COPY; 416 final String packageName = "com.test.foo"; 417 final int positionPicked = 123; 418 final int directTargetAlsoRanked = -1; 419 final int callerTargetCount = 0; 420 final boolean isPinned = true; 421 final boolean isSuccessfullySelected = true; 422 final long selectionCost = 456; 423 424 mChooserLogger.logShareTargetSelected( 425 targetType, 426 packageName, 427 positionPicked, 428 directTargetAlsoRanked, 429 callerTargetCount, 430 /* directTargetHashed= */ null, 431 isPinned, 432 isSuccessfullySelected, 433 selectionCost); 434 435 verify(mFrameworkLog).write( 436 anyInt(), anyInt(), anyString(), idIntCaptor.capture(), anyInt(), anyBoolean()); 437 438 mChooserLogger.logSharesheetTriggered(); 439 verify(mUiEventLog).logWithInstanceId( 440 any(UiEventEnum.class), anyInt(), any(), idObjectCaptor.capture()); 441 442 assertThat(idIntCaptor.getValue()).isGreaterThan(0); 443 assertThat(idObjectCaptor.getValue().getId()).isEqualTo(idIntCaptor.getValue()); 444 } 445 446 @Test testTargetSelectionCategories()447 public void testTargetSelectionCategories() { 448 assertThat(EventLogImpl.getTargetSelectionCategory( 449 EventLogImpl.SELECTION_TYPE_SERVICE)) 450 .isEqualTo(MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SERVICE_TARGET); 451 assertThat(EventLogImpl.getTargetSelectionCategory( 452 EventLogImpl.SELECTION_TYPE_APP)) 453 .isEqualTo(MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_APP_TARGET); 454 assertThat(EventLogImpl.getTargetSelectionCategory( 455 EventLogImpl.SELECTION_TYPE_STANDARD)) 456 .isEqualTo(MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_STANDARD_TARGET); 457 assertThat(EventLogImpl.getTargetSelectionCategory( 458 EventLogImpl.SELECTION_TYPE_COPY)).isEqualTo(0); 459 assertThat(EventLogImpl.getTargetSelectionCategory( 460 EventLogImpl.SELECTION_TYPE_NEARBY)).isEqualTo(0); 461 assertThat(EventLogImpl.getTargetSelectionCategory( 462 EventLogImpl.SELECTION_TYPE_EDIT)).isEqualTo(0); 463 } 464 } 465