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 testLogShareTargetSelected()155 public void testLogShareTargetSelected() { 156 final int targetType = EventLogImpl.SELECTION_TYPE_SERVICE; 157 final String packageName = "com.test.foo"; 158 final int positionPicked = 123; 159 final int directTargetAlsoRanked = -1; 160 final int callerTargetCount = 0; 161 final boolean isPinned = true; 162 final boolean isSuccessfullySelected = true; 163 final long selectionCost = 456; 164 165 mChooserLogger.logShareTargetSelected( 166 targetType, 167 packageName, 168 positionPicked, 169 directTargetAlsoRanked, 170 callerTargetCount, 171 /* directTargetHashed= */ null, 172 isPinned, 173 isSuccessfullySelected, 174 selectionCost); 175 176 verify(mFrameworkLog).write( 177 eq(FrameworkStatsLog.RANKING_SELECTED), 178 eq(SharesheetTargetSelectedEvent.SHARESHEET_SERVICE_TARGET_SELECTED.getId()), 179 eq(packageName), 180 /* instanceId=*/ gt(0), 181 eq(positionPicked), 182 eq(isPinned)); 183 184 ArgumentCaptor<LogMaker> eventCaptor = ArgumentCaptor.forClass(LogMaker.class); 185 verify(mMetricsLogger).write(eventCaptor.capture()); 186 LogMaker event = eventCaptor.getValue(); 187 assertThat(event.getCategory()).isEqualTo( 188 MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SERVICE_TARGET); 189 assertThat(event.getSubtype()).isEqualTo(positionPicked); 190 } 191 192 @Test testLogActionSelected()193 public void testLogActionSelected() { 194 mChooserLogger.logActionSelected(EventLogImpl.SELECTION_TYPE_COPY); 195 196 verify(mFrameworkLog).write( 197 eq(FrameworkStatsLog.RANKING_SELECTED), 198 eq(SharesheetTargetSelectedEvent.SHARESHEET_COPY_TARGET_SELECTED.getId()), 199 eq(""), 200 /* instanceId=*/ gt(0), 201 eq(-1), 202 eq(false)); 203 204 ArgumentCaptor<LogMaker> eventCaptor = ArgumentCaptor.forClass(LogMaker.class); 205 verify(mMetricsLogger).write(eventCaptor.capture()); 206 LogMaker event = eventCaptor.getValue(); 207 assertThat(event.getCategory()).isEqualTo( 208 MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SYSTEM_TARGET); 209 assertThat(event.getSubtype()).isEqualTo(1); 210 } 211 212 @Test testLogCustomActionSelected()213 public void testLogCustomActionSelected() { 214 final int position = 4; 215 mChooserLogger.logCustomActionSelected(position); 216 217 verify(mFrameworkLog).write( 218 eq(FrameworkStatsLog.RANKING_SELECTED), 219 eq(SharesheetTargetSelectedEvent.SHARESHEET_CUSTOM_ACTION_SELECTED.getId()), 220 any(), anyInt(), eq(position), eq(false)); 221 } 222 223 @Test testLogDirectShareTargetReceived()224 public void testLogDirectShareTargetReceived() { 225 final int category = MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_SHORTCUT_MANAGER; 226 final int latency = 123; 227 228 mChooserLogger.logDirectShareTargetReceived(category, latency); 229 230 ArgumentCaptor<LogMaker> eventCaptor = ArgumentCaptor.forClass(LogMaker.class); 231 verify(mMetricsLogger).write(eventCaptor.capture()); 232 LogMaker event = eventCaptor.getValue(); 233 assertThat(event.getCategory()).isEqualTo(category); 234 assertThat(event.getSubtype()).isEqualTo(latency); 235 } 236 237 @Test testLogActionShareWithPreview()238 public void testLogActionShareWithPreview() { 239 final int previewType = ContentPreviewType.CONTENT_PREVIEW_TEXT; 240 241 mChooserLogger.logActionShareWithPreview(previewType); 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(MetricsEvent.ACTION_SHARE_WITH_PREVIEW); 247 assertThat(event.getSubtype()).isEqualTo(previewType); 248 } 249 250 @Test testLogSharesheetTriggered()251 public void testLogSharesheetTriggered() { 252 mChooserLogger.logSharesheetTriggered(); 253 verify(mUiEventLog).logWithInstanceId( 254 eq(SharesheetStandardEvent.SHARESHEET_TRIGGERED), eq(0), isNull(), any()); 255 } 256 257 @Test testLogSharesheetAppLoadComplete()258 public void testLogSharesheetAppLoadComplete() { 259 mChooserLogger.logSharesheetAppLoadComplete(); 260 verify(mUiEventLog).logWithInstanceId( 261 eq(SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE), eq(0), isNull(), any()); 262 } 263 264 @Test testLogSharesheetDirectLoadComplete()265 public void testLogSharesheetDirectLoadComplete() { 266 mChooserLogger.logSharesheetDirectLoadComplete(); 267 verify(mUiEventLog).logWithInstanceId( 268 eq(SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_COMPLETE), 269 eq(0), 270 isNull(), 271 any()); 272 } 273 274 @Test testLogSharesheetDirectLoadTimeout()275 public void testLogSharesheetDirectLoadTimeout() { 276 mChooserLogger.logSharesheetDirectLoadTimeout(); 277 verify(mUiEventLog).logWithInstanceId( 278 eq(SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_TIMEOUT), eq(0), isNull(), any()); 279 } 280 281 @Test testLogSharesheetProfileChanged()282 public void testLogSharesheetProfileChanged() { 283 mChooserLogger.logSharesheetProfileChanged(); 284 verify(mUiEventLog).logWithInstanceId( 285 eq(SharesheetStandardEvent.SHARESHEET_PROFILE_CHANGED), eq(0), isNull(), any()); 286 } 287 288 @Test testLogSharesheetExpansionChanged_collapsed()289 public void testLogSharesheetExpansionChanged_collapsed() { 290 mChooserLogger.logSharesheetExpansionChanged(/* isCollapsed=*/ true); 291 verify(mUiEventLog).logWithInstanceId( 292 eq(SharesheetStandardEvent.SHARESHEET_COLLAPSED), eq(0), isNull(), any()); 293 } 294 295 @Test testLogSharesheetExpansionChanged_expanded()296 public void testLogSharesheetExpansionChanged_expanded() { 297 mChooserLogger.logSharesheetExpansionChanged(/* isCollapsed=*/ false); 298 verify(mUiEventLog).logWithInstanceId( 299 eq(SharesheetStandardEvent.SHARESHEET_EXPANDED), eq(0), isNull(), any()); 300 } 301 302 @Test testLogSharesheetAppShareRankingTimeout()303 public void testLogSharesheetAppShareRankingTimeout() { 304 mChooserLogger.logSharesheetAppShareRankingTimeout(); 305 verify(mUiEventLog).logWithInstanceId( 306 eq(SharesheetStandardEvent.SHARESHEET_APP_SHARE_RANKING_TIMEOUT), 307 eq(0), 308 isNull(), 309 any()); 310 } 311 312 @Test testLogSharesheetEmptyDirectShareRow()313 public void testLogSharesheetEmptyDirectShareRow() { 314 mChooserLogger.logSharesheetEmptyDirectShareRow(); 315 verify(mUiEventLog).logWithInstanceId( 316 eq(SharesheetStandardEvent.SHARESHEET_EMPTY_DIRECT_SHARE_ROW), 317 eq(0), 318 isNull(), 319 any()); 320 } 321 322 @Test testDifferentLoggerInstancesUseDifferentInstanceIds()323 public void testDifferentLoggerInstancesUseDifferentInstanceIds() { 324 ArgumentCaptor<Integer> idIntCaptor = ArgumentCaptor.forClass(Integer.class); 325 EventLogImpl chooserLogger2 = 326 new EventLogImpl(mUiEventLog, mFrameworkLog, mMetricsLogger, 327 mSequence.newInstanceId()); 328 329 final int targetType = EventLogImpl.SELECTION_TYPE_COPY; 330 final String packageName = "com.test.foo"; 331 final int positionPicked = 123; 332 final int directTargetAlsoRanked = -1; 333 final int callerTargetCount = 0; 334 final boolean isPinned = true; 335 final boolean isSuccessfullySelected = true; 336 final long selectionCost = 456; 337 338 mChooserLogger.logShareTargetSelected( 339 targetType, 340 packageName, 341 positionPicked, 342 directTargetAlsoRanked, 343 callerTargetCount, 344 /* directTargetHashed= */ null, 345 isPinned, 346 isSuccessfullySelected, 347 selectionCost); 348 349 chooserLogger2.logShareTargetSelected( 350 targetType, 351 packageName, 352 positionPicked, 353 directTargetAlsoRanked, 354 callerTargetCount, 355 /* directTargetHashed= */ null, 356 isPinned, 357 isSuccessfullySelected, 358 selectionCost); 359 360 verify(mFrameworkLog, times(2)).write( 361 anyInt(), anyInt(), anyString(), idIntCaptor.capture(), anyInt(), anyBoolean()); 362 363 int id1 = idIntCaptor.getAllValues().get(0); 364 int id2 = idIntCaptor.getAllValues().get(1); 365 366 assertThat(id1).isGreaterThan(0); 367 assertThat(id2).isGreaterThan(0); 368 assertThat(id1).isNotEqualTo(id2); 369 } 370 371 @Test testUiAndFrameworkEventsUseSameInstanceIdForSameLoggerInstance()372 public void testUiAndFrameworkEventsUseSameInstanceIdForSameLoggerInstance() { 373 ArgumentCaptor<Integer> idIntCaptor = ArgumentCaptor.forClass(Integer.class); 374 ArgumentCaptor<InstanceId> idObjectCaptor = ArgumentCaptor.forClass(InstanceId.class); 375 376 final int targetType = EventLogImpl.SELECTION_TYPE_COPY; 377 final String packageName = "com.test.foo"; 378 final int positionPicked = 123; 379 final int directTargetAlsoRanked = -1; 380 final int callerTargetCount = 0; 381 final boolean isPinned = true; 382 final boolean isSuccessfullySelected = true; 383 final long selectionCost = 456; 384 385 mChooserLogger.logShareTargetSelected( 386 targetType, 387 packageName, 388 positionPicked, 389 directTargetAlsoRanked, 390 callerTargetCount, 391 /* directTargetHashed= */ null, 392 isPinned, 393 isSuccessfullySelected, 394 selectionCost); 395 396 verify(mFrameworkLog).write( 397 anyInt(), anyInt(), anyString(), idIntCaptor.capture(), anyInt(), anyBoolean()); 398 399 mChooserLogger.logSharesheetTriggered(); 400 verify(mUiEventLog).logWithInstanceId( 401 any(UiEventEnum.class), anyInt(), any(), idObjectCaptor.capture()); 402 403 assertThat(idIntCaptor.getValue()).isGreaterThan(0); 404 assertThat(idObjectCaptor.getValue().getId()).isEqualTo(idIntCaptor.getValue()); 405 } 406 407 @Test testTargetSelectionCategories()408 public void testTargetSelectionCategories() { 409 assertThat(EventLogImpl.getTargetSelectionCategory( 410 EventLogImpl.SELECTION_TYPE_SERVICE)) 411 .isEqualTo(MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SERVICE_TARGET); 412 assertThat(EventLogImpl.getTargetSelectionCategory( 413 EventLogImpl.SELECTION_TYPE_APP)) 414 .isEqualTo(MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_APP_TARGET); 415 assertThat(EventLogImpl.getTargetSelectionCategory( 416 EventLogImpl.SELECTION_TYPE_STANDARD)) 417 .isEqualTo(MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_STANDARD_TARGET); 418 assertThat(EventLogImpl.getTargetSelectionCategory( 419 EventLogImpl.SELECTION_TYPE_COPY)).isEqualTo(0); 420 assertThat(EventLogImpl.getTargetSelectionCategory( 421 EventLogImpl.SELECTION_TYPE_NEARBY)).isEqualTo(0); 422 assertThat(EventLogImpl.getTargetSelectionCategory( 423 EventLogImpl.SELECTION_TYPE_EDIT)).isEqualTo(0); 424 } 425 } 426