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;
+ }
}