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;