diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 0fbd4dca700b3..89e3d6b6460fa 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -381,6 +381,8 @@ public class ChooserActivity extends ResolverActivity { final long systemCost = mChooserShownTime - intentReceivedTime; getMetricsLogger().write(new LogMaker(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN) + .setSubtype(isWorkProfile() ? MetricsEvent.MANAGED_PROFILE : + MetricsEvent.PARENT_PROFILE) .addTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE, target.getType()) .addTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS, systemCost)); @@ -417,6 +419,16 @@ public class ChooserActivity extends ResolverActivity { } } + /** + * Check if the profile currently used is a work profile. + * @return true if it is work profile, false if it is parent profile (or no work profile is + * set up) + */ + protected boolean isWorkProfile() { + return ((UserManager) getSystemService(Context.USER_SERVICE)) + .getUserInfo(UserHandle.myUserId()).isManagedProfile(); + } + /** * Override method to add content preview area, specific to the chooser activity. */ diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java index d13bcf2aa186b..64f00103eb4bf 100644 --- a/core/java/com/android/internal/app/IntentForwarderActivity.java +++ b/core/java/com/android/internal/app/IntentForwarderActivity.java @@ -31,6 +31,7 @@ import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; +import android.metrics.LogMaker; import android.os.Bundle; import android.os.RemoteException; import android.os.UserHandle; @@ -39,6 +40,8 @@ import android.util.Slog; import android.widget.Toast; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import java.util.Arrays; import java.util.HashSet; @@ -66,6 +69,8 @@ public class IntentForwarderActivity extends Activity { private Injector mInjector; + private MetricsLogger mMetricsLogger; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -78,9 +83,17 @@ public class IntentForwarderActivity extends Activity { if (className.equals(FORWARD_INTENT_TO_PARENT)) { userMessageId = com.android.internal.R.string.forward_intent_to_owner; targetUserId = getProfileParent(); + + getMetricsLogger().write( + new LogMaker(MetricsEvent.ACTION_SWITCH_SHARE_PROFILE) + .setSubtype(MetricsEvent.PARENT_PROFILE)); } else if (className.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) { userMessageId = com.android.internal.R.string.forward_intent_to_work; targetUserId = getManagedProfile(); + + getMetricsLogger().write( + new LogMaker(MetricsEvent.ACTION_SWITCH_SHARE_PROFILE) + .setSubtype(MetricsEvent.MANAGED_PROFILE)); } else { Slog.wtf(TAG, IntentForwarderActivity.class.getName() + " cannot be called directly"); userMessageId = -1; @@ -257,6 +270,13 @@ public class IntentForwarderActivity extends Activity { intent.setComponent(null); } + protected MetricsLogger getMetricsLogger() { + if (mMetricsLogger == null) { + mMetricsLogger = new MetricsLogger(); + } + return mMetricsLogger; + } + @VisibleForTesting protected Injector createInjector() { return new InjectorImpl(); diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java index f27f3f9ca427c..9fbc1666aed16 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java @@ -57,7 +57,7 @@ import androidx.test.rule.ActivityTestRule; import com.android.internal.R; import com.android.internal.app.ResolverActivity.ResolvedComponentInfo; import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import org.junit.Before; import org.junit.Rule; @@ -524,15 +524,45 @@ public class ChooserActivityTest { waitForIdle(); verify(mockLogger, atLeastOnce()).write(logMakerCaptor.capture()); assertThat(logMakerCaptor.getAllValues().get(0).getCategory(), - is(MetricsProto.MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN)); + is(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN)); assertThat(logMakerCaptor .getAllValues().get(0) - .getTaggedData(MetricsProto.MetricsEvent.FIELD_TIME_TO_APP_TARGETS), + .getTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS), is(notNullValue())); assertThat(logMakerCaptor .getAllValues().get(0) - .getTaggedData(MetricsProto.MetricsEvent.FIELD_SHARESHEET_MIMETYPE), + .getTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE), is("TestType")); + assertThat(logMakerCaptor + .getAllValues().get(0) + .getSubtype(), + is(MetricsEvent.PARENT_PROFILE)); + } + + @Test + public void testOnCreateLoggingFromWorkProfile() { + Intent sendIntent = createSendTextIntent(); + sendIntent.setType("TestType"); + sOverrides.alternateProfileSetting = MetricsEvent.MANAGED_PROFILE; + MetricsLogger mockLogger = sOverrides.metricsLogger; + ArgumentCaptor logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class); + mActivityRule.launchActivity(Intent.createChooser(sendIntent, "logger test")); + waitForIdle(); + verify(mockLogger, atLeastOnce()).write(logMakerCaptor.capture()); + assertThat(logMakerCaptor.getAllValues().get(0).getCategory(), + is(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN)); + assertThat(logMakerCaptor + .getAllValues().get(0) + .getTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS), + is(notNullValue())); + assertThat(logMakerCaptor + .getAllValues().get(0) + .getTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE), + is("TestType")); + assertThat(logMakerCaptor + .getAllValues().get(0) + .getSubtype(), + is(MetricsEvent.MANAGED_PROFILE)); } @Test @@ -547,7 +577,7 @@ public class ChooserActivityTest { verify(mockLogger, Mockito.times(1)).write(logMakerCaptor.capture()); // First invocation is from onCreate assertThat(logMakerCaptor.getAllValues().get(0).getCategory(), - is(MetricsProto.MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN)); + is(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN)); } @Test @@ -569,7 +599,7 @@ public class ChooserActivityTest { verify(mockLogger, Mockito.times(3)).write(logMakerCaptor.capture()); // First invocation is from onCreate assertThat(logMakerCaptor.getAllValues().get(1).getCategory(), - is(MetricsProto.MetricsEvent.ACTION_SHARE_WITH_PREVIEW)); + is(MetricsEvent.ACTION_SHARE_WITH_PREVIEW)); assertThat(logMakerCaptor.getAllValues().get(1).getSubtype(), is(CONTENT_PREVIEW_TEXT)); } @@ -599,11 +629,11 @@ public class ChooserActivityTest { verify(mockLogger, Mockito.times(3)).write(logMakerCaptor.capture()); // First invocation is from onCreate assertThat(logMakerCaptor.getAllValues().get(1).getCategory(), - is(MetricsProto.MetricsEvent.ACTION_SHARE_WITH_PREVIEW)); + is(MetricsEvent.ACTION_SHARE_WITH_PREVIEW)); assertThat(logMakerCaptor.getAllValues().get(1).getSubtype(), is(CONTENT_PREVIEW_IMAGE)); assertThat(logMakerCaptor.getAllValues().get(2).getCategory(), - is(MetricsProto.MetricsEvent.ACTION_SHARE_WITH_PREVIEW)); + is(MetricsEvent.ACTION_SHARE_WITH_PREVIEW)); assertThat(logMakerCaptor.getAllValues().get(2).getSubtype(), is(CONTENT_PREVIEW_IMAGE)); } diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java index 57c84ff5c8acc..a8dd69a2666d7 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java @@ -28,6 +28,7 @@ import android.net.Uri; import android.util.Size; import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import java.util.function.Function; @@ -112,6 +113,14 @@ public class ChooserWrapperActivity extends ChooserActivity { return super.queryResolver(resolver, uri); } + @Override + protected boolean isWorkProfile() { + if (sOverrides.alternateProfileSetting != 0) { + return sOverrides.alternateProfileSetting == MetricsEvent.MANAGED_PROFILE; + } + return super.isWorkProfile(); + } + /** * We cannot directly mock the activity created since instrumentation creates it. *

@@ -128,6 +137,7 @@ public class ChooserWrapperActivity extends ChooserActivity { public boolean resolverForceException; public Bitmap previewThumbnail; public MetricsLogger metricsLogger; + public int alternateProfileSetting; public void reset() { onSafelyStartCallback = null; @@ -139,6 +149,7 @@ public class ChooserWrapperActivity extends ChooserActivity { resolverForceException = false; resolverListController = mock(ResolverListController.class); metricsLogger = mock(MetricsLogger.class); + alternateProfileSetting = 0; } } } diff --git a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java index 9b13af2a43577..abfb4fb3cedc3 100644 --- a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java @@ -25,6 +25,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -40,6 +41,7 @@ import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; +import android.metrics.LogMaker; import android.net.Uri; import android.os.Bundle; import android.os.IBinder; @@ -51,6 +53,9 @@ import androidx.test.InstrumentationRegistry; import androidx.test.rule.ActivityTestRule; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -70,6 +75,11 @@ public class IntentForwarderActivityTest { "android", IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE ); + private static final ComponentName FORWARD_TO_PARENT_COMPONENT_NAME = + new ComponentName( + "android", + IntentForwarderActivity.FORWARD_INTENT_TO_PARENT + ); private static final String TYPE_PLAIN_TEXT = "text/plain"; private static UserInfo MANAGED_PROFILE_INFO = new UserInfo(); @@ -522,6 +532,60 @@ public class IntentForwarderActivityTest { verify(sInjector).showToast(anyInt(), anyInt()); } + @Test + public void forwardToManagedProfile_LoggingTest() throws Exception { + sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME; + + // Intent can be forwarded. + when(mIPm.canForwardTo( + any(Intent.class), nullable(String.class), anyInt(), anyInt())).thenReturn(true); + + // Managed profile exists. + List profiles = new ArrayList<>(); + profiles.add(CURRENT_USER_INFO); + profiles.add(MANAGED_PROFILE_INFO); + when(mUserManager.getProfiles(anyInt())).thenReturn(profiles); + + Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class); + intent.setAction(Intent.ACTION_SEND); + intent.setType(TYPE_PLAIN_TEXT); + IntentForwarderWrapperActivity activity = mActivityRule.launchActivity(intent); + + ArgumentCaptor logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class); + verify(activity.getMetricsLogger()).write(logMakerCaptor.capture()); + assertEquals(MetricsEvent.ACTION_SWITCH_SHARE_PROFILE, + logMakerCaptor.getValue().getCategory()); + assertEquals(MetricsEvent.MANAGED_PROFILE, + logMakerCaptor.getValue().getSubtype()); + } + + @Test + public void forwardToParent_LoggingTest() throws Exception { + sComponentName = FORWARD_TO_PARENT_COMPONENT_NAME; + + // Intent can be forwarded. + when(mIPm.canForwardTo( + any(Intent.class), nullable(String.class), anyInt(), anyInt())).thenReturn(true); + + // Managed profile exists. + List profiles = new ArrayList<>(); + profiles.add(CURRENT_USER_INFO); + profiles.add(MANAGED_PROFILE_INFO); + when(mUserManager.getProfiles(anyInt())).thenReturn(profiles); + + Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class); + intent.setAction(Intent.ACTION_SEND); + intent.setType(TYPE_PLAIN_TEXT); + IntentForwarderWrapperActivity activity = mActivityRule.launchActivity(intent); + + ArgumentCaptor logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class); + verify(activity.getMetricsLogger()).write(logMakerCaptor.capture()); + assertEquals(MetricsEvent.ACTION_SWITCH_SHARE_PROFILE, + logMakerCaptor.getValue().getCategory()); + assertEquals(MetricsEvent.PARENT_PROFILE, + logMakerCaptor.getValue().getSubtype()); + } + private void setupShouldSkipDisclosureTest() throws RemoteException { sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME; sActivityName = "MyTestActivity"; @@ -541,6 +605,7 @@ public class IntentForwarderActivityTest { private Intent mStartActivityIntent; private int mUserIdActivityLaunchedIn; + private MetricsLogger mMetricsLogger = mock(MetricsLogger.class); @Override public void onCreate(@Nullable Bundle savedInstanceState) { @@ -559,6 +624,11 @@ public class IntentForwarderActivityTest { mStartActivityIntent = intent; mUserIdActivityLaunchedIn = userId; } + + @Override + protected MetricsLogger getMetricsLogger() { + return mMetricsLogger; + } } public class TestInjector implements IntentForwarderActivity.Injector { diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index 4584306ea163c..33555c43d928e 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -247,6 +247,12 @@ message MetricsEvent { LOCATION_GONE = 6; // the view isn't laid out at all } + // Subtypes for profile logging + enum ActiveUserProfile { + PARENT_PROFILE = 1; + MANAGED_PROFILE = 2; + } + // Known visual elements: views or controls. enum View { // Unknown view @@ -7007,6 +7013,10 @@ message MetricsEvent { // Different display can have different orientations, so need to log display id FIELD_DISPLAY_ID = 1660; + // ACTION: Changing from work to parent profile or vice versa + // OS: Q + ACTION_SWITCH_SHARE_PROFILE = 1661; + // ---- End Q Constants, all Q constants go above this line ---- // Add new aosp constants above this line. // END OF AOSP CONSTANTS