Loading api/current.txt +2 −0 Original line number Diff line number Diff line Loading @@ -11260,6 +11260,8 @@ package android.content.pm { package android.content.pm.crossprofile { public class CrossProfileApps { method public android.graphics.drawable.Drawable getProfileSwitchingIcon(android.os.UserHandle); method public java.lang.CharSequence getProfileSwitchingLabel(android.os.UserHandle); method public java.util.List<android.os.UserHandle> getTargetUserProfiles(); method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle); } core/java/android/content/pm/crossprofile/CrossProfileApps.java +63 −0 Original line number Diff line number Diff line Loading @@ -19,12 +19,17 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.content.res.Resources; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import com.android.internal.R; import com.android.internal.util.UserIcons; import java.util.List; /** Loading @@ -35,11 +40,15 @@ import java.util.List; public class CrossProfileApps { private final Context mContext; private final ICrossProfileApps mService; private final UserManager mUserManager; private final Resources mResources; /** @hide */ public CrossProfileApps(Context context, ICrossProfileApps service) { mContext = context; mService = service; mUserManager = context.getSystemService(UserManager.class); mResources = context.getResources(); } /** Loading Loading @@ -87,4 +96,58 @@ public class CrossProfileApps { throw ex.rethrowFromSystemServer(); } } /** * Return a label that calling app can show to user for the semantic of profile switching -- * launching its own activity in specified user profile. For example, it may return * "Switch to work" if the given user handle is the managed profile one. * * @param userHandle The UserHandle of the target profile, must be one of the users returned by * {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will * be thrown. * @return a label that calling app can show user for the semantic of launching its own * activity in the specified user profile. * * @see #startMainActivity(ComponentName, UserHandle, Rect, Bundle) */ public @NonNull CharSequence getProfileSwitchingLabel(@NonNull UserHandle userHandle) { verifyCanAccessUser(userHandle); final int stringRes = mUserManager.isManagedProfile(userHandle.getIdentifier()) ? R.string.managed_profile_label : R.string.user_owner_label; return mResources.getString(stringRes); } /** * Return an icon that calling app can show to user for the semantic of profile switching -- * launching its own activity in specified user profile. For example, it may return a briefcase * icon if the given user handle is the managed profile one. * * @param userHandle The UserHandle of the target profile, must be one of the users returned by * {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will * be thrown. * @return an icon that calling app can show user for the semantic of launching its own * activity in specified user profile. * * @see #startMainActivity(ComponentName, UserHandle, Rect, Bundle) */ public @NonNull Drawable getProfileSwitchingIcon(@NonNull UserHandle userHandle) { verifyCanAccessUser(userHandle); final boolean isManagedProfile = mUserManager.isManagedProfile(userHandle.getIdentifier()); if (isManagedProfile) { return mResources.getDrawable(R.drawable.ic_corp_badge, null); } else { return UserIcons.getDefaultUserIcon( mResources, UserHandle.USER_SYSTEM, true /* light */); } } private void verifyCanAccessUser(UserHandle userHandle) { if (!getTargetUserProfiles().contains(userHandle)) { throw new SecurityException("Not allowed to access " + userHandle); } } } core/res/res/values/symbols.xml +2 −0 Original line number Diff line number Diff line Loading @@ -1067,6 +1067,8 @@ <java-symbol type="string" name="action_bar_home_description_format" /> <java-symbol type="string" name="action_bar_home_subtitle_description_format" /> <java-symbol type="string" name="wireless_display_route_description" /> <java-symbol type="string" name="user_owner_label" /> <java-symbol type="string" name="managed_profile_label" /> <java-symbol type="string" name="managed_profile_label_badge" /> <java-symbol type="string" name="managed_profile_label_badge_2" /> <java-symbol type="string" name="managed_profile_label_badge_3" /> Loading core/tests/coretests/src/android/content/pm/crossprofile/CrossProfileAppsTest.java 0 → 100644 +141 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.content.pm.crossprofile; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.UserHandle; import android.os.UserManager; import android.platform.test.annotations.Presubmit; import com.android.internal.R; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import java.util.ArrayList; import java.util.List; /** * Build/Install/Run: * bit FrameworksCoreTests:android.content.pm.crossprofile.CrossProfileAppsTest */ @Presubmit @RunWith(MockitoJUnitRunner.class) public class CrossProfileAppsTest { private static final UserHandle PERSONAL_PROFILE = UserHandle.of(0); private static final UserHandle MANAGED_PROFILE = UserHandle.of(10); private static final String MY_PACKAGE = "my.package"; private List<UserHandle> mTargetProfiles; @Mock private Context mContext; @Mock private UserManager mUserManager; @Mock private ICrossProfileApps mService; @Mock private Resources mResources; @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Drawable mDrawable; private CrossProfileApps mCrossProfileApps; @Before public void initCrossProfileApps() { mCrossProfileApps = new CrossProfileApps(mContext, mService); } @Before public void mockContext() { when(mContext.getPackageName()).thenReturn(MY_PACKAGE); when(mContext.getSystemServiceName(UserManager.class)).thenReturn(Context.USER_SERVICE); when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); } @Before public void mockResources() { when(mContext.getResources()).thenReturn(mResources); when(mResources.getDrawable(anyInt(), nullable(Resources.Theme.class))) .thenReturn(mDrawable); } @Before public void initUsers() throws Exception { when(mUserManager.isManagedProfile(PERSONAL_PROFILE.getIdentifier())).thenReturn(false); when(mUserManager.isManagedProfile(MANAGED_PROFILE.getIdentifier())).thenReturn(true); mTargetProfiles = new ArrayList<>(); when(mService.getTargetUserProfiles(MY_PACKAGE)).thenReturn(mTargetProfiles); } @Test public void getProfileSwitchingLabel_managedProfile() { setValidTargetProfile(MANAGED_PROFILE); mCrossProfileApps.getProfileSwitchingLabel(MANAGED_PROFILE); verify(mResources).getString(R.string.managed_profile_label); } @Test public void getProfileSwitchingLabel_personalProfile() { setValidTargetProfile(PERSONAL_PROFILE); mCrossProfileApps.getProfileSwitchingLabel(PERSONAL_PROFILE); verify(mResources).getString(R.string.user_owner_label); } @Test(expected = SecurityException.class) public void getProfileSwitchingLabel_securityException() { mCrossProfileApps.getProfileSwitchingLabel(PERSONAL_PROFILE); } @Test public void getProfileSwitchingIcon_managedProfile() { setValidTargetProfile(MANAGED_PROFILE); mCrossProfileApps.getProfileSwitchingIcon(MANAGED_PROFILE); verify(mResources).getDrawable(R.drawable.ic_corp_badge, null); } @Test public void getProfileSwitchingIcon_personalProfile() { setValidTargetProfile(PERSONAL_PROFILE); mCrossProfileApps.getProfileSwitchingIcon(PERSONAL_PROFILE); verify(mResources).getDrawable(R.drawable.ic_account_circle, null); } @Test(expected = SecurityException.class) public void getProfileSwitchingIcon_securityException() { mCrossProfileApps.getProfileSwitchingIcon(PERSONAL_PROFILE); } private void setValidTargetProfile(UserHandle userHandle) { mTargetProfiles.add(userHandle); } } Loading
api/current.txt +2 −0 Original line number Diff line number Diff line Loading @@ -11260,6 +11260,8 @@ package android.content.pm { package android.content.pm.crossprofile { public class CrossProfileApps { method public android.graphics.drawable.Drawable getProfileSwitchingIcon(android.os.UserHandle); method public java.lang.CharSequence getProfileSwitchingLabel(android.os.UserHandle); method public java.util.List<android.os.UserHandle> getTargetUserProfiles(); method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle); }
core/java/android/content/pm/crossprofile/CrossProfileApps.java +63 −0 Original line number Diff line number Diff line Loading @@ -19,12 +19,17 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.content.res.Resources; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import com.android.internal.R; import com.android.internal.util.UserIcons; import java.util.List; /** Loading @@ -35,11 +40,15 @@ import java.util.List; public class CrossProfileApps { private final Context mContext; private final ICrossProfileApps mService; private final UserManager mUserManager; private final Resources mResources; /** @hide */ public CrossProfileApps(Context context, ICrossProfileApps service) { mContext = context; mService = service; mUserManager = context.getSystemService(UserManager.class); mResources = context.getResources(); } /** Loading Loading @@ -87,4 +96,58 @@ public class CrossProfileApps { throw ex.rethrowFromSystemServer(); } } /** * Return a label that calling app can show to user for the semantic of profile switching -- * launching its own activity in specified user profile. For example, it may return * "Switch to work" if the given user handle is the managed profile one. * * @param userHandle The UserHandle of the target profile, must be one of the users returned by * {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will * be thrown. * @return a label that calling app can show user for the semantic of launching its own * activity in the specified user profile. * * @see #startMainActivity(ComponentName, UserHandle, Rect, Bundle) */ public @NonNull CharSequence getProfileSwitchingLabel(@NonNull UserHandle userHandle) { verifyCanAccessUser(userHandle); final int stringRes = mUserManager.isManagedProfile(userHandle.getIdentifier()) ? R.string.managed_profile_label : R.string.user_owner_label; return mResources.getString(stringRes); } /** * Return an icon that calling app can show to user for the semantic of profile switching -- * launching its own activity in specified user profile. For example, it may return a briefcase * icon if the given user handle is the managed profile one. * * @param userHandle The UserHandle of the target profile, must be one of the users returned by * {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will * be thrown. * @return an icon that calling app can show user for the semantic of launching its own * activity in specified user profile. * * @see #startMainActivity(ComponentName, UserHandle, Rect, Bundle) */ public @NonNull Drawable getProfileSwitchingIcon(@NonNull UserHandle userHandle) { verifyCanAccessUser(userHandle); final boolean isManagedProfile = mUserManager.isManagedProfile(userHandle.getIdentifier()); if (isManagedProfile) { return mResources.getDrawable(R.drawable.ic_corp_badge, null); } else { return UserIcons.getDefaultUserIcon( mResources, UserHandle.USER_SYSTEM, true /* light */); } } private void verifyCanAccessUser(UserHandle userHandle) { if (!getTargetUserProfiles().contains(userHandle)) { throw new SecurityException("Not allowed to access " + userHandle); } } }
core/res/res/values/symbols.xml +2 −0 Original line number Diff line number Diff line Loading @@ -1067,6 +1067,8 @@ <java-symbol type="string" name="action_bar_home_description_format" /> <java-symbol type="string" name="action_bar_home_subtitle_description_format" /> <java-symbol type="string" name="wireless_display_route_description" /> <java-symbol type="string" name="user_owner_label" /> <java-symbol type="string" name="managed_profile_label" /> <java-symbol type="string" name="managed_profile_label_badge" /> <java-symbol type="string" name="managed_profile_label_badge_2" /> <java-symbol type="string" name="managed_profile_label_badge_3" /> Loading
core/tests/coretests/src/android/content/pm/crossprofile/CrossProfileAppsTest.java 0 → 100644 +141 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.content.pm.crossprofile; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.UserHandle; import android.os.UserManager; import android.platform.test.annotations.Presubmit; import com.android.internal.R; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import java.util.ArrayList; import java.util.List; /** * Build/Install/Run: * bit FrameworksCoreTests:android.content.pm.crossprofile.CrossProfileAppsTest */ @Presubmit @RunWith(MockitoJUnitRunner.class) public class CrossProfileAppsTest { private static final UserHandle PERSONAL_PROFILE = UserHandle.of(0); private static final UserHandle MANAGED_PROFILE = UserHandle.of(10); private static final String MY_PACKAGE = "my.package"; private List<UserHandle> mTargetProfiles; @Mock private Context mContext; @Mock private UserManager mUserManager; @Mock private ICrossProfileApps mService; @Mock private Resources mResources; @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Drawable mDrawable; private CrossProfileApps mCrossProfileApps; @Before public void initCrossProfileApps() { mCrossProfileApps = new CrossProfileApps(mContext, mService); } @Before public void mockContext() { when(mContext.getPackageName()).thenReturn(MY_PACKAGE); when(mContext.getSystemServiceName(UserManager.class)).thenReturn(Context.USER_SERVICE); when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); } @Before public void mockResources() { when(mContext.getResources()).thenReturn(mResources); when(mResources.getDrawable(anyInt(), nullable(Resources.Theme.class))) .thenReturn(mDrawable); } @Before public void initUsers() throws Exception { when(mUserManager.isManagedProfile(PERSONAL_PROFILE.getIdentifier())).thenReturn(false); when(mUserManager.isManagedProfile(MANAGED_PROFILE.getIdentifier())).thenReturn(true); mTargetProfiles = new ArrayList<>(); when(mService.getTargetUserProfiles(MY_PACKAGE)).thenReturn(mTargetProfiles); } @Test public void getProfileSwitchingLabel_managedProfile() { setValidTargetProfile(MANAGED_PROFILE); mCrossProfileApps.getProfileSwitchingLabel(MANAGED_PROFILE); verify(mResources).getString(R.string.managed_profile_label); } @Test public void getProfileSwitchingLabel_personalProfile() { setValidTargetProfile(PERSONAL_PROFILE); mCrossProfileApps.getProfileSwitchingLabel(PERSONAL_PROFILE); verify(mResources).getString(R.string.user_owner_label); } @Test(expected = SecurityException.class) public void getProfileSwitchingLabel_securityException() { mCrossProfileApps.getProfileSwitchingLabel(PERSONAL_PROFILE); } @Test public void getProfileSwitchingIcon_managedProfile() { setValidTargetProfile(MANAGED_PROFILE); mCrossProfileApps.getProfileSwitchingIcon(MANAGED_PROFILE); verify(mResources).getDrawable(R.drawable.ic_corp_badge, null); } @Test public void getProfileSwitchingIcon_personalProfile() { setValidTargetProfile(PERSONAL_PROFILE); mCrossProfileApps.getProfileSwitchingIcon(PERSONAL_PROFILE); verify(mResources).getDrawable(R.drawable.ic_account_circle, null); } @Test(expected = SecurityException.class) public void getProfileSwitchingIcon_securityException() { mCrossProfileApps.getProfileSwitchingIcon(PERSONAL_PROFILE); } private void setValidTargetProfile(UserHandle userHandle) { mTargetProfiles.add(userHandle); } }