diff --git a/res/drawable/ic_analytics_grey.xml b/res/drawable/ic_analytics_grey.xml new file mode 100644 index 00000000000..f0bbdb93fc3 --- /dev/null +++ b/res/drawable/ic_analytics_grey.xml @@ -0,0 +1,21 @@ + + + + + diff --git a/res/drawable/ic_storage_grey.xml b/res/drawable/ic_storage_grey.xml new file mode 100644 index 00000000000..420aba831ff --- /dev/null +++ b/res/drawable/ic_storage_grey.xml @@ -0,0 +1,21 @@ + + + + + diff --git a/res/layout/interact_across_profiles_consent_dialog.xml b/res/layout/interact_across_profiles_consent_dialog.xml new file mode 100644 index 00000000000..6052508add2 --- /dev/null +++ b/res/layout/interact_across_profiles_consent_dialog.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index eb138764536..90bd59ecb85 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -8422,33 +8422,55 @@ - Connected work and personal apps + Connected personal and work apps - + No connected apps - + cross profile connected app apps work and personal - Connected work and personal apps + Connected personal and work apps - - Connect these apps + + Connected - + + Connect these apps + + Connected apps share permissions and can access each other\u2019s data. - + Only connect apps that you trust with your personal data.Your data may be exposed to your IT admin. - - - Trust work Calendar with your personal data? + + Trust work %1$s with your personal data? - - - Calendar may expose your personal data to your IT admin + + %1$s may expose your personal data to your IT admin. + + + App data + + + It can access data in your personal %1$s app. + + + Permissions + + + It can use your personal %1$s app\u2019s permissions, like access to location, storage, or contacts. + + + No apps connected + + + + %d app connected + %d apps connected + Do Not Disturb access diff --git a/res/values/styles.xml b/res/values/styles.xml index a4c82b29a91..07642c7c7e5 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -637,4 +637,49 @@ true + + + + + + + + + diff --git a/res/xml/interact_across_profiles_permissions_details.xml b/res/xml/interact_across_profiles_permissions_details.xml index e9a48038a6d..8b1e043eee8 100644 --- a/res/xml/interact_across_profiles_permissions_details.xml +++ b/res/xml/interact_across_profiles_permissions_details.xml @@ -24,15 +24,13 @@ android:selectable="false"/> + android:key="interact_across_profiles_settings_switch" /> + android:selectable="false" /> - + android:selectable="false" /> diff --git a/res/xml/special_access.xml b/res/xml/special_access.xml index 132d50e0c59..b0498f83b61 100644 --- a/res/xml/special_access.xml +++ b/res/xml/special_access.xml @@ -39,6 +39,13 @@ android:value="com.android.settings.Settings$HighPowerApplicationsActivity" /> + + - - > crossProfileApps = - collectConfigurableApps(); + collectConfigurableApps(mPackageManager, mUserManager, mCrossProfileApps); final Context prefContext = getPrefContext(); for (final Pair appData : crossProfileApps) { @@ -124,21 +124,37 @@ public class InteractAcrossProfilesSettings extends EmptyTextSettings { * @return the list of applications for the personal profile in the calling user's profile group * that can configure interact across profiles. */ - ArrayList> collectConfigurableApps() { - final UserHandle personalProfile = getPersonalProfileForCallingUser(); + static ArrayList> collectConfigurableApps( + PackageManager packageManager, UserManager userManager, + CrossProfileApps crossProfileApps) { + final UserHandle personalProfile = getPersonalProfileForCallingUser(userManager); if (personalProfile == null) { return new ArrayList<>(); } - final ArrayList> crossProfileApps = new ArrayList<>(); - final List installedPackages = mPackageManager.getInstalledPackagesAsUser( + final ArrayList> apps = new ArrayList<>(); + final List installedPackages = packageManager.getInstalledPackagesAsUser( GET_ACTIVITIES, personalProfile.getIdentifier()); for (PackageInfo packageInfo : installedPackages) { - if (mCrossProfileApps.canConfigureInteractAcrossProfiles(packageInfo.packageName)) { - crossProfileApps.add(new Pair<>(packageInfo.applicationInfo, personalProfile)); + if (crossProfileApps.canConfigureInteractAcrossProfiles(packageInfo.packageName)) { + apps.add(new Pair<>(packageInfo.applicationInfo, personalProfile)); } } - return crossProfileApps; + return apps; + } + + /** + * @return the number of applications that can interact across profiles. + */ + static int getNumberOfEnabledApps( + Context context, PackageManager packageManager, UserManager userManager, + CrossProfileApps crossProfileApps) { + final ArrayList> apps = + collectConfigurableApps(packageManager, userManager, crossProfileApps); + apps.removeIf( + app -> !InteractAcrossProfilesDetails.isInteractAcrossProfilesEnabled( + context, app.first.packageName, app.first.uid)); + return apps.size(); } /** @@ -146,12 +162,12 @@ public class InteractAcrossProfilesSettings extends EmptyTextSettings { * Returns null if user is not in a profile group. */ @Nullable - private UserHandle getPersonalProfileForCallingUser() { + private static UserHandle getPersonalProfileForCallingUser(UserManager userManager) { final int callingUser = UserHandle.myUserId(); - if (mUserManager.getProfiles(callingUser).isEmpty()) { + if (userManager.getProfiles(callingUser).isEmpty()) { return null; } - final UserInfo parentProfile = mUserManager.getProfileParent(callingUser); + final UserInfo parentProfile = userManager.getProfileParent(callingUser); return parentProfile == null ? UserHandle.of(callingUser) : parentProfile.getUserHandle(); } diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesSettingsTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesSettingsTest.java index 9a4c56b34ce..db5507a10c8 100644 --- a/tests/robotests/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesSettingsTest.java +++ b/tests/robotests/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesSettingsTest.java @@ -20,10 +20,12 @@ import static com.google.common.truth.Truth.assertThat; import static org.robolectric.Shadows.shadowOf; +import android.app.AppOpsManager; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.CrossProfileApps; import android.content.pm.PackageManager; +import android.content.pm.PermissionInfo; import android.os.UserHandle; import android.os.UserManager; import android.util.Pair; @@ -32,12 +34,10 @@ import androidx.test.core.app.ApplicationProvider; import com.google.common.collect.ImmutableList; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import org.robolectric.shadows.ShadowProcess; -import org.robolectric.util.ReflectionHelpers; import java.util.List; @@ -47,6 +47,7 @@ public class InteractAcrossProfilesSettingsTest { private static final int PERSONAL_PROFILE_ID = 0; private static final int WORK_PROFILE_ID = 10; private static final int WORK_UID = UserHandle.PER_USER_RANGE * WORK_PROFILE_ID; + private static final int PACKAGE_UID = 0; private static final String PERSONAL_CROSS_PROFILE_PACKAGE = "personalCrossProfilePackage"; private static final String PERSONAL_NON_CROSS_PROFILE_PACKAGE = @@ -58,21 +59,17 @@ public class InteractAcrossProfilesSettingsTest { ImmutableList.of(PERSONAL_CROSS_PROFILE_PACKAGE, PERSONAL_NON_CROSS_PROFILE_PACKAGE); private static final List WORK_PROFILE_INSTALLED_PACKAGES = ImmutableList.of(WORK_CROSS_PROFILE_PACKAGE, WORK_NON_CROSS_PROFILE_PACKAGE); + public static final String INTERACT_ACROSS_PROFILES_PERMISSION = + "android.permission.INTERACT_ACROSS_PROFILES"; private final Context mContext = ApplicationProvider.getApplicationContext(); private final PackageManager mPackageManager = mContext.getPackageManager(); private final UserManager mUserManager = mContext.getSystemService(UserManager.class); private final CrossProfileApps mCrossProfileApps = mContext.getSystemService(CrossProfileApps.class); + private final AppOpsManager mAppOpsManager = mContext.getSystemService(AppOpsManager.class); private final InteractAcrossProfilesSettings mFragment = new InteractAcrossProfilesSettings(); - @Before - public void setup() { - ReflectionHelpers.setField(mFragment, "mPackageManager", mPackageManager); - ReflectionHelpers.setField(mFragment, "mUserManager", mUserManager); - ReflectionHelpers.setField(mFragment, "mCrossProfileApps", mCrossProfileApps); - } - @Test public void collectConfigurableApps_fromPersonal_returnsPersonalPackages() { shadowOf(mUserManager).addUser( @@ -87,7 +84,8 @@ public class InteractAcrossProfilesSettingsTest { shadowOf(mCrossProfileApps).addCrossProfilePackage(PERSONAL_CROSS_PROFILE_PACKAGE); shadowOf(mCrossProfileApps).addCrossProfilePackage(WORK_CROSS_PROFILE_PACKAGE); - List> apps = mFragment.collectConfigurableApps(); + List> apps = mFragment.collectConfigurableApps( + mPackageManager, mUserManager, mCrossProfileApps); assertThat(apps.size()).isEqualTo(1); assertThat(apps.get(0).first.packageName).isEqualTo(PERSONAL_CROSS_PROFILE_PACKAGE); @@ -108,7 +106,8 @@ public class InteractAcrossProfilesSettingsTest { shadowOf(mCrossProfileApps).addCrossProfilePackage(PERSONAL_CROSS_PROFILE_PACKAGE); shadowOf(mCrossProfileApps).addCrossProfilePackage(WORK_CROSS_PROFILE_PACKAGE); - List> apps = mFragment.collectConfigurableApps(); + List> apps = mFragment.collectConfigurableApps( + mPackageManager, mUserManager, mCrossProfileApps); assertThat(apps.size()).isEqualTo(1); assertThat(apps.get(0).first.packageName).isEqualTo(PERSONAL_CROSS_PROFILE_PACKAGE); @@ -122,8 +121,40 @@ public class InteractAcrossProfilesSettingsTest { PERSONAL_PROFILE_ID, PERSONAL_PROFILE_INSTALLED_PACKAGES); shadowOf(mCrossProfileApps).addCrossProfilePackage(PERSONAL_CROSS_PROFILE_PACKAGE); - List> apps = mFragment.collectConfigurableApps(); + List> apps = mFragment.collectConfigurableApps( + mPackageManager, mUserManager, mCrossProfileApps); assertThat(apps).isEmpty(); } + + @Test + public void getNumberOfEnabledApps_returnsNumberOfEnabledApps() { + shadowOf(mUserManager).addUser( + PERSONAL_PROFILE_ID, "personal-profile"/* name */, 0/* flags */); + shadowOf(mUserManager).addProfile( + PERSONAL_PROFILE_ID, WORK_PROFILE_ID, + "work-profile"/* profileName */, 0/* profileFlags */); + shadowOf(mPackageManager).setInstalledPackagesForUserId( + PERSONAL_PROFILE_ID, PERSONAL_PROFILE_INSTALLED_PACKAGES); + shadowOf(mCrossProfileApps).addCrossProfilePackage(PERSONAL_CROSS_PROFILE_PACKAGE); + shadowOf(mCrossProfileApps).addCrossProfilePackage(PERSONAL_NON_CROSS_PROFILE_PACKAGE); + String appOp = AppOpsManager.permissionToOp(INTERACT_ACROSS_PROFILES_PERMISSION); + shadowOf(mAppOpsManager).setMode( + appOp, PACKAGE_UID, PERSONAL_CROSS_PROFILE_PACKAGE, AppOpsManager.MODE_ALLOWED); + shadowOf(mAppOpsManager).setMode( + appOp, PACKAGE_UID, PERSONAL_CROSS_PROFILE_PACKAGE, AppOpsManager.MODE_IGNORED); + shadowOf(mPackageManager).addPermissionInfo(createCrossProfilesPermissionInfo()); + + int numOfApps = mFragment.getNumberOfEnabledApps( + mContext, mPackageManager, mUserManager, mCrossProfileApps); + + assertThat(numOfApps).isEqualTo(1); + } + + private PermissionInfo createCrossProfilesPermissionInfo() { + PermissionInfo permissionInfo = new PermissionInfo(); + permissionInfo.name = INTERACT_ACROSS_PROFILES_PERMISSION; + permissionInfo.protectionLevel = PermissionInfo.PROTECTION_FLAG_APPOP; + return permissionInfo; + } }