diff --git a/res/drawable/ic_download_for_offline.xml b/res/drawable/ic_download_for_offline.xml
new file mode 100644
index 00000000000..f9ea3050f55
--- /dev/null
+++ b/res/drawable/ic_download_for_offline.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index d91de5b5a3d..c1b65126df1 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -8537,6 +8537,12 @@
When a user connects select work and personal apps, they can access work and personal data together. [CHAR LIMIT=50] -->
Connected work & personal apps
+
+ Connected
+
+
+ Not connected
+
No connected apps
@@ -8603,27 +8609,18 @@
- Install %1$s in your work profile
-
-
- To connect these apps, install the %1$s app in your work profile
+ Install work %1$s to connect these apps
- Install %1$s in your personal profile
+ The placeholder would be the app name (e.g. Calendar). [CHAR LIMIT=NONE]-->
+ Install personal %1$s to connect these apps
- To connect these apps, install the %1$s app in your personal profile
-
-
- Get the app
+ Tap to get the app
Do Not Disturb access
diff --git a/res/xml/interact_across_profiles_permissions_details.xml b/res/xml/interact_across_profiles_permissions_details.xml
index 8b1e043eee8..1baccf227e9 100644
--- a/res/xml/interact_across_profiles_permissions_details.xml
+++ b/res/xml/interact_across_profiles_permissions_details.xml
@@ -15,22 +15,28 @@
~ limitations under the License.
-->
-
-
+
-
+
-
+
-
+
+
+
diff --git a/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetails.java b/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetails.java
index ff616268063..474a5b6ec79 100644
--- a/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetails.java
+++ b/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetails.java
@@ -15,15 +15,25 @@
*/
package com.android.settings.applications.specialaccess.interactacrossprofiles;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+
import android.Manifest;
+import android.annotation.UserIdInt;
+import android.app.ActionBar;
import android.app.AppOpsManager;
+import android.app.admin.DevicePolicyManager;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.Intent;
import android.content.PermissionChecker;
import android.content.pm.CrossProfileApps;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
@@ -32,13 +42,15 @@ import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
-import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.preference.Preference;
-import androidx.preference.SwitchPreference;
import com.android.settings.R;
import com.android.settings.applications.AppInfoBase;
+import com.android.settings.applications.AppStoreUtil;
+import com.android.settings.widget.CardPreference;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.widget.LayoutPreference;
public class InteractAcrossProfilesDetails extends AppInfoBase
@@ -47,13 +59,21 @@ public class InteractAcrossProfilesDetails extends AppInfoBase
private static final String INTERACT_ACROSS_PROFILES_SETTINGS_SWITCH =
"interact_across_profiles_settings_switch";
private static final String INTERACT_ACROSS_PROFILES_HEADER = "interact_across_profiles_header";
+ public static final String INSTALL_APP_BANNER_KEY = "install_app_banner";
private Context mContext;
private CrossProfileApps mCrossProfileApps;
private UserManager mUserManager;
- private SwitchPreference mSwitchPref;
+ private RestrictedSwitchPreference mSwitchPref;
private LayoutPreference mHeader;
+ private CardPreference mInstallBanner;
private PackageManager mPackageManager;
+ private UserHandle mPersonalProfile;
+ private UserHandle mWorkProfile;
+ private boolean mInstalledInPersonal;
+ private boolean mInstalledInWork;
+ private String mAppLabel;
+ private Intent mInstallAppIntent;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -64,19 +84,30 @@ public class InteractAcrossProfilesDetails extends AppInfoBase
mUserManager = mContext.getSystemService(UserManager.class);
mPackageManager = mContext.getPackageManager();
+ mWorkProfile = InteractAcrossProfilesSettings.getWorkProfile(mUserManager);
+ mPersonalProfile = mUserManager.getProfileParent(mWorkProfile);
+ mInstalledInWork = isPackageInstalled(mPackageName, mWorkProfile.getIdentifier());
+ mInstalledInPersonal = isPackageInstalled(mPackageName, mPersonalProfile.getIdentifier());
+
+ mAppLabel = mPackageInfo.applicationInfo.loadLabel(mPackageManager).toString();
+ mInstallAppIntent = AppStoreUtil.getAppStoreLink(mContext, mPackageName);
+
addPreferencesFromResource(R.xml.interact_across_profiles_permissions_details);
mSwitchPref = findPreference(INTERACT_ACROSS_PROFILES_SETTINGS_SWITCH);
mSwitchPref.setOnPreferenceClickListener(this);
+
mHeader = findPreference(INTERACT_ACROSS_PROFILES_HEADER);
+ mInstallBanner = findPreference(INSTALL_APP_BANNER_KEY);
+ mInstallBanner.setOnPreferenceClickListener(this);
+
// refreshUi checks that the user can still configure the appOp, return to the
// previous page if it can't.
if (!refreshUi()) {
setIntentAndFinish(true/* appChanged */);
}
- final UserHandle workProfile = getWorkProfile();
- final UserHandle personalProfile = mUserManager.getProfileParent(workProfile);
- addAppTitleAndIcons(personalProfile, workProfile);
+ addAppTitleAndIcons(mPersonalProfile, mWorkProfile);
+ styleActionBar();
}
private void addAppTitleAndIcons(UserHandle personalProfile, UserHandle workProfile) {
@@ -89,70 +120,101 @@ public class InteractAcrossProfilesDetails extends AppInfoBase
final ImageView personalIconView = mHeader.findViewById(R.id.entity_header_icon_personal);
if (personalIconView != null) {
- personalIconView.setImageDrawable(IconDrawableFactory.newInstance(mContext)
- .getBadgedIcon(mPackageInfo.applicationInfo, personalProfile.getIdentifier()));
+ Drawable icon = IconDrawableFactory.newInstance(mContext)
+ .getBadgedIcon(mPackageInfo.applicationInfo, personalProfile.getIdentifier())
+ .mutate();
+ if (!mInstalledInPersonal) {
+ icon.setColorFilter(createSuspendedColorMatrix());
+ }
+ personalIconView.setImageDrawable(icon);
}
- final ImageView workIconView2 = mHeader.findViewById(R.id.entity_header_icon_work);
- if (workIconView2 != null) {
- workIconView2.setImageDrawable(IconDrawableFactory.newInstance(mContext)
- .getBadgedIcon(mPackageInfo.applicationInfo, workProfile.getIdentifier()));
+
+ final ImageView workIconView = mHeader.findViewById(R.id.entity_header_icon_work);
+ if (workIconView != null) {
+ Drawable icon = IconDrawableFactory.newInstance(mContext)
+ .getBadgedIcon(mPackageInfo.applicationInfo, workProfile.getIdentifier())
+ .mutate();
+ if (!mInstalledInWork) {
+ icon.setColorFilter(createSuspendedColorMatrix());
+ }
+ workIconView.setImageDrawable(icon);
}
}
- @Nullable
- private UserHandle getWorkProfile() {
- for (UserInfo user : mUserManager.getProfiles(UserHandle.myUserId())) {
- if (mUserManager.isManagedProfile(user.id)) {
- return user.getUserHandle();
- }
+ private void styleActionBar() {
+ final ActionBar actionBar = getActivity().getActionBar();
+ if (actionBar != null) {
+ actionBar.setElevation(0);
}
- return null;
+ }
+
+ private ColorMatrixColorFilter createSuspendedColorMatrix() {
+ int grayValue = 127;
+ float scale = 0.5f; // half bright
+
+ ColorMatrix tempBrightnessMatrix = new ColorMatrix();
+ float[] mat = tempBrightnessMatrix.getArray();
+ mat[0] = scale;
+ mat[6] = scale;
+ mat[12] = scale;
+ mat[4] = grayValue;
+ mat[9] = grayValue;
+ mat[14] = grayValue;
+
+ ColorMatrix matrix = new ColorMatrix();
+ matrix.setSaturation(0.0f);
+ matrix.preConcat(tempBrightnessMatrix);
+ return new ColorMatrixColorFilter(matrix);
}
@Override
public boolean onPreferenceClick(Preference preference) {
- if (preference != mSwitchPref) {
- return false;
- }
// refreshUi checks that the user can still configure the appOp, return to the
// previous page if it can't.
if (!refreshUi()) {
setIntentAndFinish(true/* appChanged */);
}
+ if (preference == mSwitchPref) {
+ handleSwitchPreferenceClick();
+ return true;
+ }
+ if (preference == mInstallBanner) {
+ handleInstallBannerClick();
+ return true;
+ }
+ return false;
+ }
+
+ private void handleSwitchPreferenceClick() {
if (isInteractAcrossProfilesEnabled()) {
enableInteractAcrossProfiles(false);
refreshUi();
- return true;
- }
- if (!isInteractAcrossProfilesEnabled()) {
+ } else {
showConsentDialog();
}
- return true;
}
private void showConsentDialog() {
- final String appLabel = mPackageInfo.applicationInfo.loadLabel(mPackageManager).toString();
-
final View dialogView = getLayoutInflater().inflate(
R.layout.interact_across_profiles_consent_dialog, null);
final TextView dialogTitle = dialogView.findViewById(
R.id.interact_across_profiles_consent_dialog_title);
dialogTitle.setText(
- getString(R.string.interact_across_profiles_consent_dialog_title, appLabel));
+ getString(R.string.interact_across_profiles_consent_dialog_title, mAppLabel));
final TextView dialogSummary = dialogView.findViewById(
R.id.interact_across_profiles_consent_dialog_summary);
dialogSummary.setText(
- getString(R.string.interact_across_profiles_consent_dialog_summary, appLabel));
+ getString(R.string.interact_across_profiles_consent_dialog_summary, mAppLabel));
final TextView appDataSummary = dialogView.findViewById(R.id.app_data_summary);
appDataSummary.setText(getString(
- R.string.interact_across_profiles_consent_dialog_app_data_summary, appLabel));
+ R.string.interact_across_profiles_consent_dialog_app_data_summary, mAppLabel));
final TextView permissionsSummary = dialogView.findViewById(R.id.permissions_summary);
permissionsSummary.setText(getString(
- R.string.interact_across_profiles_consent_dialog_permissions_summary, appLabel));
+ R.string.interact_across_profiles_consent_dialog_permissions_summary, mAppLabel));
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setView(dialogView)
@@ -171,18 +233,38 @@ public class InteractAcrossProfilesDetails extends AppInfoBase
}
private boolean isInteractAcrossProfilesEnabled() {
- return isInteractAcrossProfilesEnabled(
- mContext, mPackageName, mPackageInfo.applicationInfo.uid);
+ return isInteractAcrossProfilesEnabled(mContext, mPackageName);
}
- static boolean isInteractAcrossProfilesEnabled(Context context, String packageName, int uid) {
+ static boolean isInteractAcrossProfilesEnabled(
+ Context context, String packageName) {
+ UserManager userManager = context.getSystemService(UserManager.class);
+ UserHandle workProfile = InteractAcrossProfilesSettings.getWorkProfile(userManager);
+ UserHandle personalProfile = userManager.getProfileParent(workProfile);
+ return context.getSystemService(
+ CrossProfileApps.class).canConfigureInteractAcrossProfiles(packageName)
+ && isInteractAcrossProfilesEnabledInProfile(context, packageName, personalProfile)
+ && isInteractAcrossProfilesEnabledInProfile(context, packageName, workProfile);
+
+ }
+
+ private static boolean isInteractAcrossProfilesEnabledInProfile(
+ Context context, String packageName, UserHandle userHandle) {
+ final PackageManager packageManager = context.getPackageManager();
+ final int uid;
+ try {
+ uid = packageManager.getApplicationInfoAsUser(
+ packageName, /* flags= */0, userHandle).uid;
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
return PermissionChecker.PERMISSION_GRANTED
== PermissionChecker.checkPermissionForPreflight(
- context,
- Manifest.permission.INTERACT_ACROSS_PROFILES,
- PermissionChecker.PID_UNKNOWN,
- uid,
- packageName);
+ context,
+ Manifest.permission.INTERACT_ACROSS_PROFILES,
+ PermissionChecker.PID_UNKNOWN,
+ uid,
+ packageName);
}
private void enableInteractAcrossProfiles(boolean newState) {
@@ -190,14 +272,28 @@ public class InteractAcrossProfilesDetails extends AppInfoBase
mPackageName, newState ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
}
+ private void handleInstallBannerClick() {
+ if (mInstallAppIntent == null) {
+ return;
+ }
+ if (!mInstalledInWork) {
+ mContext.startActivityAsUser(mInstallAppIntent, mWorkProfile);
+ return;
+ }
+ if (!mInstalledInPersonal) {
+ mContext.startActivityAsUser(mInstallAppIntent, mPersonalProfile);
+ }
+ }
+
/**
* @return the summary for the current state of whether the app associated with the given
* {@code packageName} is allowed to interact across profiles.
*/
- public static CharSequence getPreferenceSummary(Context context, String packageName, int uid) {
- return context.getString(isInteractAcrossProfilesEnabled(context, packageName, uid)
- ? R.string.app_permission_summary_allowed
- : R.string.app_permission_summary_not_allowed);
+ public static CharSequence getPreferenceSummary(
+ Context context, String packageName) {
+ return context.getString(isInteractAcrossProfilesEnabled(context, packageName)
+ ? R.string.interact_across_profiles_summary_allowed
+ : R.string.interact_across_profiles_summary_not_allowed);
}
@Override
@@ -205,31 +301,99 @@ public class InteractAcrossProfilesDetails extends AppInfoBase
if (mPackageInfo == null || mPackageInfo.applicationInfo == null) {
return false;
}
- if (!mCrossProfileApps.canConfigureInteractAcrossProfiles(mPackageName)) {
+ if (!mCrossProfileApps.canUserAttemptToConfigureInteractAcrossProfiles(mPackageName)) {
// Invalid app entry. Should not allow changing permission
mSwitchPref.setEnabled(false);
return false;
}
-
- final ImageView horizontalArrowIcon = mHeader.findViewById(R.id.entity_header_swap_horiz);
- if (isInteractAcrossProfilesEnabled()) {
- mSwitchPref.setChecked(true);
- mSwitchPref.setTitle(R.string.interact_across_profiles_switch_enabled);
- if (horizontalArrowIcon != null) {
- horizontalArrowIcon.setImageDrawable(
- mContext.getDrawable(R.drawable.ic_swap_horiz_blue));
- }
- } else {
- mSwitchPref.setChecked(false);
- mSwitchPref.setTitle(R.string.interact_across_profiles_switch_disabled);
- if (horizontalArrowIcon != null) {
- horizontalArrowIcon.setImageDrawable(
- mContext.getDrawable(R.drawable.ic_swap_horiz_grey));
- }
+ if (!mCrossProfileApps.canConfigureInteractAcrossProfiles(mPackageName)) {
+ return refreshUiForNonConfigurableApps();
}
+ refreshUiForConfigurableApps();
return true;
}
+ private boolean refreshUiForNonConfigurableApps() {
+ mSwitchPref.setChecked(false);
+ mSwitchPref.setTitle(R.string.interact_across_profiles_switch_disabled);
+ if (!isCrossProfilePackageWhitelisted(mPackageName)) {
+ mInstallBanner.setVisible(false);
+ mSwitchPref.setDisabledByAdmin(RestrictedLockUtils.getProfileOrDeviceOwner(
+ mContext, mWorkProfile));
+ return true;
+ }
+ mSwitchPref.setEnabled(false);
+ if (!mInstalledInPersonal && !mInstalledInWork) {
+ return false;
+ }
+ if (!mInstalledInPersonal) {
+ mInstallBanner.setTitle(getString(
+ R.string.interact_across_profiles_install_personal_app_title,
+ mAppLabel));
+ mInstallBanner.setSummary(
+ R.string.interact_across_profiles_install_app_summary);
+ mInstallBanner.setVisible(true);
+ return true;
+ }
+ if (!mInstalledInWork) {
+ mInstallBanner.setTitle(getString(
+ R.string.interact_across_profiles_install_work_app_title,
+ mAppLabel));
+ mInstallBanner.setSummary(
+ R.string.interact_across_profiles_install_app_summary);
+ mInstallBanner.setVisible(true);
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isCrossProfilePackageWhitelisted(String packageName) {
+ return mContext.getSystemService(DevicePolicyManager.class)
+ .getAllCrossProfilePackages().contains(packageName);
+ }
+
+ private boolean isPackageInstalled(String packageName, @UserIdInt int userId) {
+ final PackageInfo info;
+ try {
+ info = mContext.createContextAsUser(UserHandle.of(userId), /* flags= */0)
+ .getPackageManager().getPackageInfo(packageName,
+ MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ return info != null;
+ }
+
+ private void refreshUiForConfigurableApps() {
+ mInstallBanner.setVisible(false);
+ mSwitchPref.setEnabled(true);
+ if (isInteractAcrossProfilesEnabled()) {
+ enableSwitchPref();
+ } else {
+ disableSwitchPref();
+ }
+ }
+
+ private void enableSwitchPref() {
+ mSwitchPref.setChecked(true);
+ mSwitchPref.setTitle(R.string.interact_across_profiles_switch_enabled);
+ final ImageView horizontalArrowIcon = mHeader.findViewById(R.id.entity_header_swap_horiz);
+ if (horizontalArrowIcon != null) {
+ horizontalArrowIcon.setImageDrawable(
+ mContext.getDrawable(R.drawable.ic_swap_horiz_blue));
+ }
+ }
+
+ private void disableSwitchPref() {
+ mSwitchPref.setChecked(false);
+ mSwitchPref.setTitle(R.string.interact_across_profiles_switch_disabled);
+ final ImageView horizontalArrowIcon = mHeader.findViewById(R.id.entity_header_swap_horiz);
+ if (horizontalArrowIcon != null) {
+ horizontalArrowIcon.setImageDrawable(
+ mContext.getDrawable(R.drawable.ic_swap_horiz_grey));
+ }
+ }
+
@Override
protected AlertDialog createDialog(int id, int errorCode) {
return null;
diff --git a/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetailsPreferenceController.java b/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetailsPreferenceController.java
index 41e25a7d1d0..f57a6ed7c25 100644
--- a/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetailsPreferenceController.java
+++ b/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetailsPreferenceController.java
@@ -35,7 +35,7 @@ public class InteractAcrossProfilesDetailsPreferenceController
@Override
public int getAvailabilityStatus() {
- return canConfigureInteractAcrossProfiles() ? AVAILABLE : DISABLED_FOR_USER;
+ return canUserAttemptToConfigureInteractAcrossProfiles() ? AVAILABLE : DISABLED_FOR_USER;
}
@Override
@@ -49,13 +49,12 @@ public class InteractAcrossProfilesDetailsPreferenceController
}
private CharSequence getPreferenceSummary() {
- return InteractAcrossProfilesDetails.getPreferenceSummary(mContext, mPackageName,
- mParent.getPackageInfo().applicationInfo.uid);
+ return InteractAcrossProfilesDetails.getPreferenceSummary(mContext, mPackageName);
}
- private boolean canConfigureInteractAcrossProfiles() {
+ private boolean canUserAttemptToConfigureInteractAcrossProfiles() {
return mContext.getSystemService(CrossProfileApps.class)
- .canConfigureInteractAcrossProfiles(mPackageName);
+ .canUserAttemptToConfigureInteractAcrossProfiles(mPackageName);
}
public void setPackageName(String packageName) {
diff --git a/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesSettings.java b/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesSettings.java
index d686978fde4..c5d848af7a3 100644
--- a/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesSettings.java
+++ b/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesSettings.java
@@ -85,8 +85,8 @@ public class InteractAcrossProfilesSettings extends EmptyTextSettings {
final Preference pref = new AppPreference(prefContext);
pref.setIcon(mIconDrawableFactory.getBadgedIcon(appInfo, user.getIdentifier()));
pref.setTitle(mPackageManager.getUserBadgedLabel(label, user));
- pref.setSummary(InteractAcrossProfilesDetails.getPreferenceSummary(prefContext,
- packageName, appInfo.uid));
+ pref.setSummary(InteractAcrossProfilesDetails.getPreferenceSummary(
+ prefContext, packageName));
pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
@@ -127,49 +127,78 @@ public class InteractAcrossProfilesSettings extends EmptyTextSettings {
static ArrayList> collectConfigurableApps(
PackageManager packageManager, UserManager userManager,
CrossProfileApps crossProfileApps) {
- final UserHandle personalProfile = getPersonalProfileForCallingUser(userManager);
+ final UserHandle workProfile = getWorkProfile(userManager);
+ if (workProfile == null) {
+ return new ArrayList<>();
+ }
+ final UserHandle personalProfile = userManager.getProfileParent(workProfile);
if (personalProfile == null) {
return new ArrayList<>();
}
final ArrayList> apps = new ArrayList<>();
- final List installedPackages = packageManager.getInstalledPackagesAsUser(
- GET_ACTIVITIES, personalProfile.getIdentifier());
- for (PackageInfo packageInfo : installedPackages) {
- if (crossProfileApps.canConfigureInteractAcrossProfiles(packageInfo.packageName)) {
+ for (PackageInfo packageInfo : getAllInstalledPackages(
+ packageManager, personalProfile, workProfile)) {
+ if (crossProfileApps.canUserAttemptToConfigureInteractAcrossProfiles(
+ packageInfo.packageName)) {
apps.add(new Pair<>(packageInfo.applicationInfo, personalProfile));
}
}
return apps;
}
+ private static List getAllInstalledPackages(
+ PackageManager packageManager, UserHandle personalProfile, UserHandle workProfile) {
+ List personalPackages = packageManager.getInstalledPackagesAsUser(
+ GET_ACTIVITIES, personalProfile.getIdentifier());
+ List workPackages = packageManager.getInstalledPackagesAsUser(
+ GET_ACTIVITIES, workProfile.getIdentifier());
+ List allPackages = new ArrayList<>(personalPackages);
+ for (PackageInfo workPackage : workPackages) {
+ if (allPackages.stream().noneMatch(
+ p -> workPackage.packageName.equals(p.packageName))) {
+ allPackages.add(workPackage);
+ }
+ }
+ return allPackages;
+ }
+
/**
* @return the number of applications that can interact across profiles.
*/
static int getNumberOfEnabledApps(
Context context, PackageManager packageManager, UserManager userManager,
CrossProfileApps crossProfileApps) {
+ UserHandle workProfile = getWorkProfile(userManager);
+ if (workProfile == null) {
+ return 0;
+ }
+ UserHandle personalProfile = userManager.getProfileParent(workProfile);
+ if (personalProfile == null) {
+ return 0;
+ }
final ArrayList> apps =
collectConfigurableApps(packageManager, userManager, crossProfileApps);
apps.removeIf(
app -> !InteractAcrossProfilesDetails.isInteractAcrossProfilesEnabled(
- context, app.first.packageName, app.first.uid));
+ context, app.first.packageName)
+ || !crossProfileApps.canConfigureInteractAcrossProfiles(
+ app.first.packageName));
return apps.size();
}
/**
- * Returns the personal profile in the profile group of the calling user.
- * Returns null if user is not in a profile group.
+ * Returns the work profile in the profile group of the calling user.
+ * Returns null if not found.
*/
@Nullable
- private static UserHandle getPersonalProfileForCallingUser(UserManager userManager) {
- final int callingUser = UserHandle.myUserId();
- if (userManager.getProfiles(callingUser).isEmpty()) {
- return null;
+ static UserHandle getWorkProfile(UserManager userManager) {
+ for (UserInfo user : userManager.getProfiles(UserHandle.myUserId())) {
+ if (userManager.isManagedProfile(user.id)) {
+ return user.getUserHandle();
+ }
}
- final UserInfo parentProfile = userManager.getProfileParent(callingUser);
- return parentProfile == null
- ? UserHandle.of(callingUser) : parentProfile.getUserHandle();
+ return null;
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetailsTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetailsTest.java
index 9e48b9ea3bb..bae9a12670b 100644
--- a/tests/robotests/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetailsTest.java
+++ b/tests/robotests/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetailsTest.java
@@ -22,14 +22,18 @@ import static org.robolectric.Shadows.shadowOf;
import android.app.AppOpsManager;
import android.content.Context;
+import android.content.pm.CrossProfileApps;
import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
-import android.os.Process;
+import android.content.pm.UserInfo;
+import android.os.UserManager;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
+import com.google.common.collect.ImmutableList;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@@ -37,6 +41,9 @@ import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class InteractAcrossProfilesDetailsTest {
+ private static final int PERSONAL_PROFILE_ID = 0;
+ private static final int WORK_PROFILE_ID = 10;
+ private static final int PACKAGE_UID = 0;
private static final String CROSS_PROFILE_PACKAGE_NAME = "crossProfilePackage";
public static final String INTERACT_ACROSS_PROFILES_PERMISSION =
"android.permission.INTERACT_ACROSS_PROFILES";
@@ -44,29 +51,53 @@ public class InteractAcrossProfilesDetailsTest {
private final Context mContext = ApplicationProvider.getApplicationContext();
private final AppOpsManager mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
private final PackageManager mPackageManager = mContext.getPackageManager();
- private final InteractAcrossProfilesDetails mFragment = new InteractAcrossProfilesDetails();
+ private final UserManager mUserManager = mContext.getSystemService(UserManager.class);
+ private final CrossProfileApps mCrossProfileApps = mContext.getSystemService(
+ CrossProfileApps.class);
@Test
public void getPreferenceSummary_appOpAllowed_returnsAllowed() {
+ shadowOf(mUserManager).addUser(
+ PERSONAL_PROFILE_ID, "personal-profile"/* name */, 0/* flags */);
+ shadowOf(mUserManager).addProfile(
+ PERSONAL_PROFILE_ID, WORK_PROFILE_ID,
+ "work-profile"/* profileName */, UserInfo.FLAG_MANAGED_PROFILE);
+ shadowOf(mPackageManager).setInstalledPackagesForUserId(
+ PERSONAL_PROFILE_ID, ImmutableList.of(CROSS_PROFILE_PACKAGE_NAME));
+ shadowOf(mPackageManager).setInstalledPackagesForUserId(
+ WORK_PROFILE_ID, ImmutableList.of(CROSS_PROFILE_PACKAGE_NAME));
+ shadowOf(mCrossProfileApps).addCrossProfilePackage(
+ CROSS_PROFILE_PACKAGE_NAME);
String appOp = AppOpsManager.permissionToOp(INTERACT_ACROSS_PROFILES_PERMISSION);
shadowOf(mAppOpsManager).setMode(
- appOp, Process.myUid(), CROSS_PROFILE_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
+ appOp, PACKAGE_UID, CROSS_PROFILE_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
shadowOf(mPackageManager).addPermissionInfo(createCrossProfilesPermissionInfo());
- assertThat(mFragment.getPreferenceSummary(
- mContext, CROSS_PROFILE_PACKAGE_NAME, Process.myUid()))
+ assertThat(InteractAcrossProfilesDetails.getPreferenceSummary(
+ mContext, CROSS_PROFILE_PACKAGE_NAME))
.isEqualTo(mContext.getString(R.string.app_permission_summary_allowed));
}
@Test
public void getPreferenceSummary_appOpNotAllowed_returnsNotAllowed() {
+ shadowOf(mUserManager).addUser(
+ PERSONAL_PROFILE_ID, "personal-profile"/* name */, 0/* flags */);
+ shadowOf(mUserManager).addProfile(
+ PERSONAL_PROFILE_ID, WORK_PROFILE_ID,
+ "work-profile"/* profileName */, UserInfo.FLAG_MANAGED_PROFILE);
+ shadowOf(mPackageManager).setInstalledPackagesForUserId(
+ PERSONAL_PROFILE_ID, ImmutableList.of(CROSS_PROFILE_PACKAGE_NAME));
+ shadowOf(mPackageManager).setInstalledPackagesForUserId(
+ WORK_PROFILE_ID, ImmutableList.of(CROSS_PROFILE_PACKAGE_NAME));
+ shadowOf(mCrossProfileApps).addCrossProfilePackage(
+ CROSS_PROFILE_PACKAGE_NAME);
String appOp = AppOpsManager.permissionToOp(INTERACT_ACROSS_PROFILES_PERMISSION);
shadowOf(mAppOpsManager).setMode(
- appOp, Process.myUid(), CROSS_PROFILE_PACKAGE_NAME, AppOpsManager.MODE_IGNORED);
+ appOp, PACKAGE_UID, CROSS_PROFILE_PACKAGE_NAME, AppOpsManager.MODE_IGNORED);
shadowOf(mPackageManager).addPermissionInfo(createCrossProfilesPermissionInfo());
- assertThat(mFragment.getPreferenceSummary(
- mContext, CROSS_PROFILE_PACKAGE_NAME, Process.myUid()))
+ assertThat(InteractAcrossProfilesDetails.getPreferenceSummary(
+ mContext, CROSS_PROFILE_PACKAGE_NAME))
.isEqualTo(mContext.getString(R.string.app_permission_summary_not_allowed));
}
diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesPreferenceControllerTest.java
index bac7437d81d..8479035804b 100644
--- a/tests/robotests/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesPreferenceControllerTest.java
@@ -21,12 +21,15 @@ import static com.google.common.truth.Truth.assertThat;
import static org.robolectric.Shadows.shadowOf;
import android.content.Context;
-import android.content.pm.CrossProfileApps;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.core.BasePreferenceController;
+import com.google.common.collect.ImmutableList;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@@ -36,25 +39,34 @@ public class InteractAcrossProfilesPreferenceControllerTest {
private static final String CROSS_PROFILE_PACKAGE_NAME = "crossProfilePackage";
private static final String NOT_CROSS_PROFILE_PACKAGE_NAME = "notCrossProfilePackage";
+ public static final String INTERACT_ACROSS_PROFILES_PERMISSION =
+ "android.permission.INTERACT_ACROSS_PROFILES";
+ private static final int PROFILE_ID = 0;
private final Context mContext = ApplicationProvider.getApplicationContext();
- private final CrossProfileApps mCrossProfileApps =
- mContext.getSystemService(CrossProfileApps.class);
+ private final PackageManager mPackageManager = mContext.getPackageManager();
private final InteractAcrossProfilesDetailsPreferenceController mController =
new InteractAcrossProfilesDetailsPreferenceController(mContext, "test_key");
@Test
- public void getAvailabilityStatus_crossProfilePackage_returnsAvailable() {
+ public void getAvailabilityStatus_requestedCrossProfilePermission_returnsAvailable() {
mController.setPackageName(CROSS_PROFILE_PACKAGE_NAME);
- shadowOf(mCrossProfileApps).addCrossProfilePackage(CROSS_PROFILE_PACKAGE_NAME);
+ shadowOf(mPackageManager).setInstalledPackagesForUserId(
+ PROFILE_ID, ImmutableList.of(CROSS_PROFILE_PACKAGE_NAME));
+ PackageInfo packageInfo = shadowOf(mPackageManager).getInternalMutablePackageInfo(
+ CROSS_PROFILE_PACKAGE_NAME);
+ packageInfo.requestedPermissions = new String[]{
+ INTERACT_ACROSS_PROFILES_PERMISSION};
assertThat(mController.getAvailabilityStatus())
.isEqualTo(BasePreferenceController.AVAILABLE);
}
@Test
- public void getAvailabilityStatus_notCrossProfilePackage_returnsDisabled() {
+ public void getAvailabilityStatus_notRequestedCrossProfilePermission_returnsDisabled() {
mController.setPackageName(NOT_CROSS_PROFILE_PACKAGE_NAME);
+ shadowOf(mPackageManager).setInstalledPackagesForUserId(
+ PROFILE_ID, ImmutableList.of(NOT_CROSS_PROFILE_PACKAGE_NAME));
assertThat(mController.getAvailabilityStatus())
.isEqualTo(BasePreferenceController.DISABLED_FOR_USER);
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 19ea50b6572..dac3e22913f 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
@@ -18,14 +18,17 @@ package com.android.settings.applications.specialaccess.interactacrossprofiles;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertTrue;
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.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
+import android.content.pm.UserInfo;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Pair;
@@ -68,49 +71,56 @@ public class InteractAcrossProfilesSettingsTest {
private final CrossProfileApps mCrossProfileApps =
mContext.getSystemService(CrossProfileApps.class);
private final AppOpsManager mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
- private final InteractAcrossProfilesSettings mFragment = new InteractAcrossProfilesSettings();
@Test
- public void collectConfigurableApps_fromPersonal_returnsPersonalPackages() {
+ public void collectConfigurableApps_fromPersonal_returnsCombinedPackages() {
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 */);
+ "work-profile"/* profileName */, UserInfo.FLAG_MANAGED_PROFILE);
shadowOf(mPackageManager).setInstalledPackagesForUserId(
PERSONAL_PROFILE_ID, PERSONAL_PROFILE_INSTALLED_PACKAGES);
shadowOf(mPackageManager).setInstalledPackagesForUserId(
WORK_PROFILE_ID, WORK_PROFILE_INSTALLED_PACKAGES);
- shadowOf(mCrossProfileApps).addCrossProfilePackage(PERSONAL_CROSS_PROFILE_PACKAGE);
- shadowOf(mCrossProfileApps).addCrossProfilePackage(WORK_CROSS_PROFILE_PACKAGE);
+ installCrossProfilePackage(PERSONAL_PROFILE_ID, PERSONAL_CROSS_PROFILE_PACKAGE);
+ installCrossProfilePackage(WORK_PROFILE_ID, WORK_CROSS_PROFILE_PACKAGE);
- List> apps = mFragment.collectConfigurableApps(
- mPackageManager, mUserManager, mCrossProfileApps);
+ List> apps =
+ InteractAcrossProfilesSettings.collectConfigurableApps(
+ mPackageManager, mUserManager, mCrossProfileApps);
- assertThat(apps.size()).isEqualTo(1);
- assertThat(apps.get(0).first.packageName).isEqualTo(PERSONAL_CROSS_PROFILE_PACKAGE);
+ assertThat(apps.size()).isEqualTo(2);
+ assertTrue(apps.stream().anyMatch(
+ app -> app.first.packageName.equals(PERSONAL_CROSS_PROFILE_PACKAGE)));
+ assertTrue(apps.stream().anyMatch(
+ app -> app.first.packageName.equals(WORK_CROSS_PROFILE_PACKAGE)));
}
@Test
- public void collectConfigurableApps_fromWork_returnsPersonalPackages() {
+ public void collectConfigurableApps_fromWork_returnsCombinedPackages() {
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 */);
+ "work-profile"/* profileName */, UserInfo.FLAG_MANAGED_PROFILE);
ShadowProcess.setUid(WORK_UID);
shadowOf(mPackageManager).setInstalledPackagesForUserId(
PERSONAL_PROFILE_ID, PERSONAL_PROFILE_INSTALLED_PACKAGES);
shadowOf(mPackageManager).setInstalledPackagesForUserId(
WORK_PROFILE_ID, WORK_PROFILE_INSTALLED_PACKAGES);
- shadowOf(mCrossProfileApps).addCrossProfilePackage(PERSONAL_CROSS_PROFILE_PACKAGE);
- shadowOf(mCrossProfileApps).addCrossProfilePackage(WORK_CROSS_PROFILE_PACKAGE);
+ installCrossProfilePackage(PERSONAL_PROFILE_ID, PERSONAL_CROSS_PROFILE_PACKAGE);
+ installCrossProfilePackage(WORK_PROFILE_ID, WORK_CROSS_PROFILE_PACKAGE);
- List> apps = mFragment.collectConfigurableApps(
- mPackageManager, mUserManager, mCrossProfileApps);
+ List> apps =
+ InteractAcrossProfilesSettings.collectConfigurableApps(
+ mPackageManager, mUserManager, mCrossProfileApps);
- assertThat(apps.size()).isEqualTo(1);
- assertThat(apps.get(0).first.packageName).isEqualTo(PERSONAL_CROSS_PROFILE_PACKAGE);
+ assertThat(apps.size()).isEqualTo(2);
+ assertTrue(apps.stream().anyMatch(
+ app -> app.first.packageName.equals(PERSONAL_CROSS_PROFILE_PACKAGE)));
+ assertTrue(apps.stream().anyMatch(
+ app -> app.first.packageName.equals(WORK_CROSS_PROFILE_PACKAGE)));
}
@Test
@@ -119,10 +129,11 @@ public class InteractAcrossProfilesSettingsTest {
PERSONAL_PROFILE_ID, "personal-profile"/* name */, 0/* flags */);
shadowOf(mPackageManager).setInstalledPackagesForUserId(
PERSONAL_PROFILE_ID, PERSONAL_PROFILE_INSTALLED_PACKAGES);
- shadowOf(mCrossProfileApps).addCrossProfilePackage(PERSONAL_CROSS_PROFILE_PACKAGE);
+ installCrossProfilePackage(PERSONAL_PROFILE_ID, PERSONAL_CROSS_PROFILE_PACKAGE);
- List> apps = mFragment.collectConfigurableApps(
- mPackageManager, mUserManager, mCrossProfileApps);
+ List> apps =
+ InteractAcrossProfilesSettings.collectConfigurableApps(
+ mPackageManager, mUserManager, mCrossProfileApps);
assertThat(apps).isEmpty();
}
@@ -133,11 +144,14 @@ public class InteractAcrossProfilesSettingsTest {
PERSONAL_PROFILE_ID, "personal-profile"/* name */, 0/* flags */);
shadowOf(mUserManager).addProfile(
PERSONAL_PROFILE_ID, WORK_PROFILE_ID,
- "work-profile"/* profileName */, 0/* profileFlags */);
+ "work-profile"/* profileName */, UserInfo.FLAG_MANAGED_PROFILE);
shadowOf(mPackageManager).setInstalledPackagesForUserId(
PERSONAL_PROFILE_ID, PERSONAL_PROFILE_INSTALLED_PACKAGES);
+ shadowOf(mPackageManager).setInstalledPackagesForUserId(
+ WORK_PROFILE_ID, WORK_PROFILE_INSTALLED_PACKAGES);
+ installCrossProfilePackage(PERSONAL_PROFILE_ID, PERSONAL_CROSS_PROFILE_PACKAGE);
+ installCrossProfilePackage(WORK_PROFILE_ID, WORK_CROSS_PROFILE_PACKAGE);
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);
@@ -145,12 +159,19 @@ public class InteractAcrossProfilesSettingsTest {
appOp, PACKAGE_UID, PERSONAL_NON_CROSS_PROFILE_PACKAGE, AppOpsManager.MODE_IGNORED);
shadowOf(mPackageManager).addPermissionInfo(createCrossProfilesPermissionInfo());
- int numOfApps = mFragment.getNumberOfEnabledApps(
+ int numOfApps = InteractAcrossProfilesSettings.getNumberOfEnabledApps(
mContext, mPackageManager, mUserManager, mCrossProfileApps);
assertThat(numOfApps).isEqualTo(1);
}
+ private void installCrossProfilePackage(int profileId, String packageName) {
+ PackageInfo personalPackageInfo = shadowOf(mPackageManager).getInternalMutablePackageInfo(
+ packageName);
+ personalPackageInfo.requestedPermissions = new String[]{
+ INTERACT_ACROSS_PROFILES_PERMISSION};
+ }
+
private PermissionInfo createCrossProfilesPermissionInfo() {
PermissionInfo permissionInfo = new PermissionInfo();
permissionInfo.name = INTERACT_ACROSS_PROFILES_PERMISSION;