Merge "Show all apps that requested cross profile permission" into rvc-dev

This commit is contained in:
Kholoud Mohamed
2020-03-31 13:09:31 +00:00
committed by Android (Google) Code Review
9 changed files with 428 additions and 148 deletions

View File

@@ -0,0 +1,21 @@
<!--
~ Copyright (C) 2020 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<vector android:autoMirrored="true" android:height="24dp"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#4285F4" android:pathData="M12,2C6.49,2 2,6.49 2,12s4.49,10 10,10 10,-4.49 10,-10S17.51,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM14.59,8.59L16,10l-4,4 -4,-4 1.41,-1.41L11,10.17L11,6h2v4.17l1.59,-1.58zM17,17L7,17v-2h10v2z"/>
</vector>

View File

@@ -8537,6 +8537,12 @@
When a user connects select work and personal apps, they can access work and personal data together. [CHAR LIMIT=50] --> When a user connects select work and personal apps, they can access work and personal data together. [CHAR LIMIT=50] -->
<string name="interact_across_profiles_title">Connected work &amp; personal apps</string> <string name="interact_across_profiles_title">Connected work &amp; personal apps</string>
<!-- Settings subtext. This text is shown when the work and personal apps are connected. [CHAR LIMIT=45] -->
<string name="interact_across_profiles_summary_allowed">Connected</string>
<!-- Settings subtext. This text is shown when the work and personal apps are not connected. [CHAR LIMIT=45] -->
<string name="interact_across_profiles_summary_not_allowed">Not connected</string>
<!-- Settings subtext. This text is shown when a user doesn't have any connected apps. [CHAR LIMIT=NONE] --> <!-- Settings subtext. This text is shown when a user doesn't have any connected apps. [CHAR LIMIT=NONE] -->
<string name="interact_across_profiles_empty_text">No connected apps</string> <string name="interact_across_profiles_empty_text">No connected apps</string>
@@ -8603,27 +8609,18 @@
<!-- Banner title. This banner lets a user know that they need to install an app in their <!-- Banner title. This banner lets a user know that they need to install an app in their
work profile in order to connect it to the corresponding personal app. work profile in order to connect it to the corresponding personal app.
The placeholder would be the app name (e.g. Calendar). [CHAR LIMIT=50]-->
<string name="interact_across_profiles_install_work_app_title">Install <xliff:g id="name" example="Calendar">%1$s</xliff:g> in your work profile</string>
<!-- Banner text. This banner lets a user know that they need to install an app in their
work profile in order to connect it to the corresponding personal app.
The placeholder would be the app name (e.g. Calendar). [CHAR LIMIT=NONE]--> The placeholder would be the app name (e.g. Calendar). [CHAR LIMIT=NONE]-->
<string name="interact_across_profiles_install_work_app_summary">To connect these apps, install the <xliff:g id="name" example="Calendar">%1$s</xliff:g> app in your work profile</string> <string name="interact_across_profiles_install_work_app_title">Install work <xliff:g id="name" example="Calendar">%1$s</xliff:g> to connect these apps</string>
<!-- Banner title. This banner lets a user know that they need to install an app in their <!-- Banner title. This banner lets a user know that they need to install an app in their
personal profile in order to connect it to the corresponding work app. personal profile in order to connect it to the corresponding work app.
The placeholder would be the app name (e.g. Calendar). [CHAR LIMIT=50]--> The placeholder would be the app name (e.g. Calendar). [CHAR LIMIT=NONE]-->
<string name="interact_across_profiles_install_personal_app_title">Install <xliff:g id="name" example="Calendar">%1$s</xliff:g> in your personal profile</string> <string name="interact_across_profiles_install_personal_app_title">Install personal <xliff:g id="name" example="Calendar">%1$s</xliff:g> to connect these apps</string>
<!-- Banner text. This banner lets a user know that they need to install an app in their <!-- Banner text. This banner lets a user know that they need to install an app in their
personal profile in order to connect it to the corresponding work app. work/personal profile in order to connect it to the corresponding personal app.
The placeholder would be the app name (e.g. Calendar). [CHAR LIMIT=NONE]--> The placeholder would be the app name (e.g. Calendar). [CHAR LIMIT=NONE]-->
<string name="interact_across_profiles_install_personal_app_summary">To connect these apps, install the <xliff:g id="name" example="Calendar">%1$s</xliff:g> app in your personal profile</string> <string name="interact_across_profiles_install_app_summary">Tap to get the app</string>
<!-- Button text. This button takes a user to the app store so they can download and
install the app they need. [CHAR LIMIT=50]-->
<string name="interact_across_profiles_install_app_action">Get the app</string>
<!-- Sound & notification > Advanced section: Title for managing Do Not Disturb access option. [CHAR LIMIT=40] --> <!-- Sound & notification > Advanced section: Title for managing Do Not Disturb access option. [CHAR LIMIT=40] -->
<string name="manage_zen_access_title">Do Not Disturb access</string> <string name="manage_zen_access_title">Do Not Disturb access</string>

View File

@@ -15,7 +15,8 @@
~ limitations under the License. ~ limitations under the License.
--> -->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" <PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/interact_across_profiles_title"> android:title="@string/interact_across_profiles_title">
<com.android.settingslib.widget.LayoutPreference <com.android.settingslib.widget.LayoutPreference
@@ -23,7 +24,12 @@
android:layout="@layout/cross_profiles_settings_entity_header" android:layout="@layout/cross_profiles_settings_entity_header"
android:selectable="false"/> android:selectable="false"/>
<SwitchPreference <com.android.settings.widget.CardPreference
android:key="install_app_banner"
android:icon="@drawable/ic_download_for_offline"
android:gravity="top"/>
<com.android.settingslib.RestrictedSwitchPreference
android:key="interact_across_profiles_settings_switch" /> android:key="interact_across_profiles_settings_switch" />
<Preference <Preference

View File

@@ -15,15 +15,25 @@
*/ */
package com.android.settings.applications.specialaccess.interactacrossprofiles; 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.Manifest;
import android.annotation.UserIdInt;
import android.app.ActionBar;
import android.app.AppOpsManager; import android.app.AppOpsManager;
import android.app.admin.DevicePolicyManager;
import android.app.settings.SettingsEnums; import android.app.settings.SettingsEnums;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent;
import android.content.PermissionChecker; import android.content.PermissionChecker;
import android.content.pm.CrossProfileApps; import android.content.pm.CrossProfileApps;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; 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.Bundle;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
@@ -32,13 +42,15 @@ import android.view.View;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.SwitchPreference;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.applications.AppInfoBase; 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; import com.android.settingslib.widget.LayoutPreference;
public class InteractAcrossProfilesDetails extends AppInfoBase public class InteractAcrossProfilesDetails extends AppInfoBase
@@ -47,13 +59,21 @@ public class InteractAcrossProfilesDetails extends AppInfoBase
private static final String INTERACT_ACROSS_PROFILES_SETTINGS_SWITCH = private static final String INTERACT_ACROSS_PROFILES_SETTINGS_SWITCH =
"interact_across_profiles_settings_switch"; "interact_across_profiles_settings_switch";
private static final String INTERACT_ACROSS_PROFILES_HEADER = "interact_across_profiles_header"; 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 Context mContext;
private CrossProfileApps mCrossProfileApps; private CrossProfileApps mCrossProfileApps;
private UserManager mUserManager; private UserManager mUserManager;
private SwitchPreference mSwitchPref; private RestrictedSwitchPreference mSwitchPref;
private LayoutPreference mHeader; private LayoutPreference mHeader;
private CardPreference mInstallBanner;
private PackageManager mPackageManager; private PackageManager mPackageManager;
private UserHandle mPersonalProfile;
private UserHandle mWorkProfile;
private boolean mInstalledInPersonal;
private boolean mInstalledInWork;
private String mAppLabel;
private Intent mInstallAppIntent;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
@@ -64,19 +84,30 @@ public class InteractAcrossProfilesDetails extends AppInfoBase
mUserManager = mContext.getSystemService(UserManager.class); mUserManager = mContext.getSystemService(UserManager.class);
mPackageManager = mContext.getPackageManager(); 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); addPreferencesFromResource(R.xml.interact_across_profiles_permissions_details);
mSwitchPref = findPreference(INTERACT_ACROSS_PROFILES_SETTINGS_SWITCH); mSwitchPref = findPreference(INTERACT_ACROSS_PROFILES_SETTINGS_SWITCH);
mSwitchPref.setOnPreferenceClickListener(this); mSwitchPref.setOnPreferenceClickListener(this);
mHeader = findPreference(INTERACT_ACROSS_PROFILES_HEADER); 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 // refreshUi checks that the user can still configure the appOp, return to the
// previous page if it can't. // previous page if it can't.
if (!refreshUi()) { if (!refreshUi()) {
setIntentAndFinish(true/* appChanged */); setIntentAndFinish(true/* appChanged */);
} }
final UserHandle workProfile = getWorkProfile(); addAppTitleAndIcons(mPersonalProfile, mWorkProfile);
final UserHandle personalProfile = mUserManager.getProfileParent(workProfile); styleActionBar();
addAppTitleAndIcons(personalProfile, workProfile);
} }
private void addAppTitleAndIcons(UserHandle personalProfile, UserHandle workProfile) { 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); final ImageView personalIconView = mHeader.findViewById(R.id.entity_header_icon_personal);
if (personalIconView != null) { if (personalIconView != null) {
personalIconView.setImageDrawable(IconDrawableFactory.newInstance(mContext) Drawable icon = IconDrawableFactory.newInstance(mContext)
.getBadgedIcon(mPackageInfo.applicationInfo, personalProfile.getIdentifier())); .getBadgedIcon(mPackageInfo.applicationInfo, personalProfile.getIdentifier())
.mutate();
if (!mInstalledInPersonal) {
icon.setColorFilter(createSuspendedColorMatrix());
} }
final ImageView workIconView2 = mHeader.findViewById(R.id.entity_header_icon_work); personalIconView.setImageDrawable(icon);
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 void styleActionBar() {
private UserHandle getWorkProfile() { final ActionBar actionBar = getActivity().getActionBar();
for (UserInfo user : mUserManager.getProfiles(UserHandle.myUserId())) { if (actionBar != null) {
if (mUserManager.isManagedProfile(user.id)) { actionBar.setElevation(0);
return user.getUserHandle();
} }
} }
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 @Override
public boolean onPreferenceClick(Preference preference) { public boolean onPreferenceClick(Preference preference) {
if (preference != mSwitchPref) {
return false;
}
// refreshUi checks that the user can still configure the appOp, return to the // refreshUi checks that the user can still configure the appOp, return to the
// previous page if it can't. // previous page if it can't.
if (!refreshUi()) { if (!refreshUi()) {
setIntentAndFinish(true/* appChanged */); setIntentAndFinish(true/* appChanged */);
} }
if (preference == mSwitchPref) {
handleSwitchPreferenceClick();
return true;
}
if (preference == mInstallBanner) {
handleInstallBannerClick();
return true;
}
return false;
}
private void handleSwitchPreferenceClick() {
if (isInteractAcrossProfilesEnabled()) { if (isInteractAcrossProfilesEnabled()) {
enableInteractAcrossProfiles(false); enableInteractAcrossProfiles(false);
refreshUi(); refreshUi();
return true; } else {
}
if (!isInteractAcrossProfilesEnabled()) {
showConsentDialog(); showConsentDialog();
} }
return true;
} }
private void showConsentDialog() { private void showConsentDialog() {
final String appLabel = mPackageInfo.applicationInfo.loadLabel(mPackageManager).toString();
final View dialogView = getLayoutInflater().inflate( final View dialogView = getLayoutInflater().inflate(
R.layout.interact_across_profiles_consent_dialog, null); R.layout.interact_across_profiles_consent_dialog, null);
final TextView dialogTitle = dialogView.findViewById( final TextView dialogTitle = dialogView.findViewById(
R.id.interact_across_profiles_consent_dialog_title); R.id.interact_across_profiles_consent_dialog_title);
dialogTitle.setText( 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( final TextView dialogSummary = dialogView.findViewById(
R.id.interact_across_profiles_consent_dialog_summary); R.id.interact_across_profiles_consent_dialog_summary);
dialogSummary.setText( 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); final TextView appDataSummary = dialogView.findViewById(R.id.app_data_summary);
appDataSummary.setText(getString( 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); final TextView permissionsSummary = dialogView.findViewById(R.id.permissions_summary);
permissionsSummary.setText(getString( 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()); AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setView(dialogView) builder.setView(dialogView)
@@ -171,11 +233,31 @@ public class InteractAcrossProfilesDetails extends AppInfoBase
} }
private boolean isInteractAcrossProfilesEnabled() { private boolean isInteractAcrossProfilesEnabled() {
return isInteractAcrossProfilesEnabled( return isInteractAcrossProfilesEnabled(mContext, mPackageName);
mContext, mPackageName, mPackageInfo.applicationInfo.uid);
} }
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 return PermissionChecker.PERMISSION_GRANTED
== PermissionChecker.checkPermissionForPreflight( == PermissionChecker.checkPermissionForPreflight(
context, context,
@@ -190,14 +272,28 @@ public class InteractAcrossProfilesDetails extends AppInfoBase
mPackageName, newState ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED); 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 * @return the summary for the current state of whether the app associated with the given
* {@code packageName} is allowed to interact across profiles. * {@code packageName} is allowed to interact across profiles.
*/ */
public static CharSequence getPreferenceSummary(Context context, String packageName, int uid) { public static CharSequence getPreferenceSummary(
return context.getString(isInteractAcrossProfilesEnabled(context, packageName, uid) Context context, String packageName) {
? R.string.app_permission_summary_allowed return context.getString(isInteractAcrossProfilesEnabled(context, packageName)
: R.string.app_permission_summary_not_allowed); ? R.string.interact_across_profiles_summary_allowed
: R.string.interact_across_profiles_summary_not_allowed);
} }
@Override @Override
@@ -205,30 +301,98 @@ public class InteractAcrossProfilesDetails extends AppInfoBase
if (mPackageInfo == null || mPackageInfo.applicationInfo == null) { if (mPackageInfo == null || mPackageInfo.applicationInfo == null) {
return false; return false;
} }
if (!mCrossProfileApps.canConfigureInteractAcrossProfiles(mPackageName)) { if (!mCrossProfileApps.canUserAttemptToConfigureInteractAcrossProfiles(mPackageName)) {
// Invalid app entry. Should not allow changing permission // Invalid app entry. Should not allow changing permission
mSwitchPref.setEnabled(false); mSwitchPref.setEnabled(false);
return false; return false;
} }
if (!mCrossProfileApps.canConfigureInteractAcrossProfiles(mPackageName)) {
return refreshUiForNonConfigurableApps();
}
refreshUiForConfigurableApps();
return true;
}
final ImageView horizontalArrowIcon = mHeader.findViewById(R.id.entity_header_swap_horiz); 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()) { if (isInteractAcrossProfilesEnabled()) {
enableSwitchPref();
} else {
disableSwitchPref();
}
}
private void enableSwitchPref() {
mSwitchPref.setChecked(true); mSwitchPref.setChecked(true);
mSwitchPref.setTitle(R.string.interact_across_profiles_switch_enabled); mSwitchPref.setTitle(R.string.interact_across_profiles_switch_enabled);
final ImageView horizontalArrowIcon = mHeader.findViewById(R.id.entity_header_swap_horiz);
if (horizontalArrowIcon != null) { if (horizontalArrowIcon != null) {
horizontalArrowIcon.setImageDrawable( horizontalArrowIcon.setImageDrawable(
mContext.getDrawable(R.drawable.ic_swap_horiz_blue)); mContext.getDrawable(R.drawable.ic_swap_horiz_blue));
} }
} else { }
private void disableSwitchPref() {
mSwitchPref.setChecked(false); mSwitchPref.setChecked(false);
mSwitchPref.setTitle(R.string.interact_across_profiles_switch_disabled); mSwitchPref.setTitle(R.string.interact_across_profiles_switch_disabled);
final ImageView horizontalArrowIcon = mHeader.findViewById(R.id.entity_header_swap_horiz);
if (horizontalArrowIcon != null) { if (horizontalArrowIcon != null) {
horizontalArrowIcon.setImageDrawable( horizontalArrowIcon.setImageDrawable(
mContext.getDrawable(R.drawable.ic_swap_horiz_grey)); mContext.getDrawable(R.drawable.ic_swap_horiz_grey));
} }
} }
return true;
}
@Override @Override
protected AlertDialog createDialog(int id, int errorCode) { protected AlertDialog createDialog(int id, int errorCode) {

View File

@@ -35,7 +35,7 @@ public class InteractAcrossProfilesDetailsPreferenceController
@Override @Override
public int getAvailabilityStatus() { public int getAvailabilityStatus() {
return canConfigureInteractAcrossProfiles() ? AVAILABLE : DISABLED_FOR_USER; return canUserAttemptToConfigureInteractAcrossProfiles() ? AVAILABLE : DISABLED_FOR_USER;
} }
@Override @Override
@@ -49,13 +49,12 @@ public class InteractAcrossProfilesDetailsPreferenceController
} }
private CharSequence getPreferenceSummary() { private CharSequence getPreferenceSummary() {
return InteractAcrossProfilesDetails.getPreferenceSummary(mContext, mPackageName, return InteractAcrossProfilesDetails.getPreferenceSummary(mContext, mPackageName);
mParent.getPackageInfo().applicationInfo.uid);
} }
private boolean canConfigureInteractAcrossProfiles() { private boolean canUserAttemptToConfigureInteractAcrossProfiles() {
return mContext.getSystemService(CrossProfileApps.class) return mContext.getSystemService(CrossProfileApps.class)
.canConfigureInteractAcrossProfiles(mPackageName); .canUserAttemptToConfigureInteractAcrossProfiles(mPackageName);
} }
public void setPackageName(String packageName) { public void setPackageName(String packageName) {

View File

@@ -85,8 +85,8 @@ public class InteractAcrossProfilesSettings extends EmptyTextSettings {
final Preference pref = new AppPreference(prefContext); final Preference pref = new AppPreference(prefContext);
pref.setIcon(mIconDrawableFactory.getBadgedIcon(appInfo, user.getIdentifier())); pref.setIcon(mIconDrawableFactory.getBadgedIcon(appInfo, user.getIdentifier()));
pref.setTitle(mPackageManager.getUserBadgedLabel(label, user)); pref.setTitle(mPackageManager.getUserBadgedLabel(label, user));
pref.setSummary(InteractAcrossProfilesDetails.getPreferenceSummary(prefContext, pref.setSummary(InteractAcrossProfilesDetails.getPreferenceSummary(
packageName, appInfo.uid)); prefContext, packageName));
pref.setOnPreferenceClickListener(new OnPreferenceClickListener() { pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override @Override
public boolean onPreferenceClick(Preference preference) { public boolean onPreferenceClick(Preference preference) {
@@ -127,49 +127,78 @@ public class InteractAcrossProfilesSettings extends EmptyTextSettings {
static ArrayList<Pair<ApplicationInfo, UserHandle>> collectConfigurableApps( static ArrayList<Pair<ApplicationInfo, UserHandle>> collectConfigurableApps(
PackageManager packageManager, UserManager userManager, PackageManager packageManager, UserManager userManager,
CrossProfileApps crossProfileApps) { 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) { if (personalProfile == null) {
return new ArrayList<>(); return new ArrayList<>();
} }
final ArrayList<Pair<ApplicationInfo, UserHandle>> apps = new ArrayList<>(); final ArrayList<Pair<ApplicationInfo, UserHandle>> apps = new ArrayList<>();
final List<PackageInfo> installedPackages = packageManager.getInstalledPackagesAsUser( for (PackageInfo packageInfo : getAllInstalledPackages(
GET_ACTIVITIES, personalProfile.getIdentifier()); packageManager, personalProfile, workProfile)) {
for (PackageInfo packageInfo : installedPackages) { if (crossProfileApps.canUserAttemptToConfigureInteractAcrossProfiles(
if (crossProfileApps.canConfigureInteractAcrossProfiles(packageInfo.packageName)) { packageInfo.packageName)) {
apps.add(new Pair<>(packageInfo.applicationInfo, personalProfile)); apps.add(new Pair<>(packageInfo.applicationInfo, personalProfile));
} }
} }
return apps; return apps;
} }
private static List<PackageInfo> getAllInstalledPackages(
PackageManager packageManager, UserHandle personalProfile, UserHandle workProfile) {
List<PackageInfo> personalPackages = packageManager.getInstalledPackagesAsUser(
GET_ACTIVITIES, personalProfile.getIdentifier());
List<PackageInfo> workPackages = packageManager.getInstalledPackagesAsUser(
GET_ACTIVITIES, workProfile.getIdentifier());
List<PackageInfo> 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. * @return the number of applications that can interact across profiles.
*/ */
static int getNumberOfEnabledApps( static int getNumberOfEnabledApps(
Context context, PackageManager packageManager, UserManager userManager, Context context, PackageManager packageManager, UserManager userManager,
CrossProfileApps crossProfileApps) { CrossProfileApps crossProfileApps) {
UserHandle workProfile = getWorkProfile(userManager);
if (workProfile == null) {
return 0;
}
UserHandle personalProfile = userManager.getProfileParent(workProfile);
if (personalProfile == null) {
return 0;
}
final ArrayList<Pair<ApplicationInfo, UserHandle>> apps = final ArrayList<Pair<ApplicationInfo, UserHandle>> apps =
collectConfigurableApps(packageManager, userManager, crossProfileApps); collectConfigurableApps(packageManager, userManager, crossProfileApps);
apps.removeIf( apps.removeIf(
app -> !InteractAcrossProfilesDetails.isInteractAcrossProfilesEnabled( app -> !InteractAcrossProfilesDetails.isInteractAcrossProfilesEnabled(
context, app.first.packageName, app.first.uid)); context, app.first.packageName)
|| !crossProfileApps.canConfigureInteractAcrossProfiles(
app.first.packageName));
return apps.size(); return apps.size();
} }
/** /**
* Returns the personal profile in the profile group of the calling user. * Returns the work profile in the profile group of the calling user.
* Returns null if user is not in a profile group. * Returns null if not found.
*/ */
@Nullable @Nullable
private static UserHandle getPersonalProfileForCallingUser(UserManager userManager) { static UserHandle getWorkProfile(UserManager userManager) {
final int callingUser = UserHandle.myUserId(); for (UserInfo user : userManager.getProfiles(UserHandle.myUserId())) {
if (userManager.getProfiles(callingUser).isEmpty()) { if (userManager.isManagedProfile(user.id)) {
return null; return user.getUserHandle();
} }
final UserInfo parentProfile = userManager.getProfileParent(callingUser); }
return parentProfile == null return null;
? UserHandle.of(callingUser) : parentProfile.getUserHandle();
} }
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =

View File

@@ -22,14 +22,18 @@ import static org.robolectric.Shadows.shadowOf;
import android.app.AppOpsManager; import android.app.AppOpsManager;
import android.content.Context; import android.content.Context;
import android.content.pm.CrossProfileApps;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo; 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 androidx.test.core.app.ApplicationProvider;
import com.android.settings.R; import com.android.settings.R;
import com.google.common.collect.ImmutableList;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
@@ -37,6 +41,9 @@ import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public class InteractAcrossProfilesDetailsTest { 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"; private static final String CROSS_PROFILE_PACKAGE_NAME = "crossProfilePackage";
public static final String INTERACT_ACROSS_PROFILES_PERMISSION = public static final String INTERACT_ACROSS_PROFILES_PERMISSION =
"android.permission.INTERACT_ACROSS_PROFILES"; "android.permission.INTERACT_ACROSS_PROFILES";
@@ -44,29 +51,53 @@ public class InteractAcrossProfilesDetailsTest {
private final Context mContext = ApplicationProvider.getApplicationContext(); private final Context mContext = ApplicationProvider.getApplicationContext();
private final AppOpsManager mAppOpsManager = mContext.getSystemService(AppOpsManager.class); private final AppOpsManager mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
private final PackageManager mPackageManager = mContext.getPackageManager(); 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 @Test
public void getPreferenceSummary_appOpAllowed_returnsAllowed() { 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); String appOp = AppOpsManager.permissionToOp(INTERACT_ACROSS_PROFILES_PERMISSION);
shadowOf(mAppOpsManager).setMode( 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()); shadowOf(mPackageManager).addPermissionInfo(createCrossProfilesPermissionInfo());
assertThat(mFragment.getPreferenceSummary( assertThat(InteractAcrossProfilesDetails.getPreferenceSummary(
mContext, CROSS_PROFILE_PACKAGE_NAME, Process.myUid())) mContext, CROSS_PROFILE_PACKAGE_NAME))
.isEqualTo(mContext.getString(R.string.app_permission_summary_allowed)); .isEqualTo(mContext.getString(R.string.app_permission_summary_allowed));
} }
@Test @Test
public void getPreferenceSummary_appOpNotAllowed_returnsNotAllowed() { 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); String appOp = AppOpsManager.permissionToOp(INTERACT_ACROSS_PROFILES_PERMISSION);
shadowOf(mAppOpsManager).setMode( 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()); shadowOf(mPackageManager).addPermissionInfo(createCrossProfilesPermissionInfo());
assertThat(mFragment.getPreferenceSummary( assertThat(InteractAcrossProfilesDetails.getPreferenceSummary(
mContext, CROSS_PROFILE_PACKAGE_NAME, Process.myUid())) mContext, CROSS_PROFILE_PACKAGE_NAME))
.isEqualTo(mContext.getString(R.string.app_permission_summary_not_allowed)); .isEqualTo(mContext.getString(R.string.app_permission_summary_not_allowed));
} }

View File

@@ -21,12 +21,15 @@ import static com.google.common.truth.Truth.assertThat;
import static org.robolectric.Shadows.shadowOf; import static org.robolectric.Shadows.shadowOf;
import android.content.Context; 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 androidx.test.core.app.ApplicationProvider;
import com.android.settings.core.BasePreferenceController; import com.android.settings.core.BasePreferenceController;
import com.google.common.collect.ImmutableList;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
@@ -36,25 +39,34 @@ public class InteractAcrossProfilesPreferenceControllerTest {
private static final String CROSS_PROFILE_PACKAGE_NAME = "crossProfilePackage"; private static final String CROSS_PROFILE_PACKAGE_NAME = "crossProfilePackage";
private static final String NOT_CROSS_PROFILE_PACKAGE_NAME = "notCrossProfilePackage"; 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 Context mContext = ApplicationProvider.getApplicationContext();
private final CrossProfileApps mCrossProfileApps = private final PackageManager mPackageManager = mContext.getPackageManager();
mContext.getSystemService(CrossProfileApps.class);
private final InteractAcrossProfilesDetailsPreferenceController mController = private final InteractAcrossProfilesDetailsPreferenceController mController =
new InteractAcrossProfilesDetailsPreferenceController(mContext, "test_key"); new InteractAcrossProfilesDetailsPreferenceController(mContext, "test_key");
@Test @Test
public void getAvailabilityStatus_crossProfilePackage_returnsAvailable() { public void getAvailabilityStatus_requestedCrossProfilePermission_returnsAvailable() {
mController.setPackageName(CROSS_PROFILE_PACKAGE_NAME); 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()) assertThat(mController.getAvailabilityStatus())
.isEqualTo(BasePreferenceController.AVAILABLE); .isEqualTo(BasePreferenceController.AVAILABLE);
} }
@Test @Test
public void getAvailabilityStatus_notCrossProfilePackage_returnsDisabled() { public void getAvailabilityStatus_notRequestedCrossProfilePermission_returnsDisabled() {
mController.setPackageName(NOT_CROSS_PROFILE_PACKAGE_NAME); mController.setPackageName(NOT_CROSS_PROFILE_PACKAGE_NAME);
shadowOf(mPackageManager).setInstalledPackagesForUserId(
PROFILE_ID, ImmutableList.of(NOT_CROSS_PROFILE_PACKAGE_NAME));
assertThat(mController.getAvailabilityStatus()) assertThat(mController.getAvailabilityStatus())
.isEqualTo(BasePreferenceController.DISABLED_FOR_USER); .isEqualTo(BasePreferenceController.DISABLED_FOR_USER);

View File

@@ -18,14 +18,17 @@ package com.android.settings.applications.specialaccess.interactacrossprofiles;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertTrue;
import static org.robolectric.Shadows.shadowOf; import static org.robolectric.Shadows.shadowOf;
import android.app.AppOpsManager; import android.app.AppOpsManager;
import android.content.Context; import android.content.Context;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.CrossProfileApps; import android.content.pm.CrossProfileApps;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo; import android.content.pm.PermissionInfo;
import android.content.pm.UserInfo;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
import android.util.Pair; import android.util.Pair;
@@ -68,49 +71,56 @@ public class InteractAcrossProfilesSettingsTest {
private final CrossProfileApps mCrossProfileApps = private final CrossProfileApps mCrossProfileApps =
mContext.getSystemService(CrossProfileApps.class); mContext.getSystemService(CrossProfileApps.class);
private final AppOpsManager mAppOpsManager = mContext.getSystemService(AppOpsManager.class); private final AppOpsManager mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
private final InteractAcrossProfilesSettings mFragment = new InteractAcrossProfilesSettings();
@Test @Test
public void collectConfigurableApps_fromPersonal_returnsPersonalPackages() { public void collectConfigurableApps_fromPersonal_returnsCombinedPackages() {
shadowOf(mUserManager).addUser( shadowOf(mUserManager).addUser(
PERSONAL_PROFILE_ID, "personal-profile"/* name */, 0/* flags */); PERSONAL_PROFILE_ID, "personal-profile"/* name */, 0/* flags */);
shadowOf(mUserManager).addProfile( shadowOf(mUserManager).addProfile(
PERSONAL_PROFILE_ID, WORK_PROFILE_ID, PERSONAL_PROFILE_ID, WORK_PROFILE_ID,
"work-profile"/* profileName */, 0/* profileFlags */); "work-profile"/* profileName */, UserInfo.FLAG_MANAGED_PROFILE);
shadowOf(mPackageManager).setInstalledPackagesForUserId( shadowOf(mPackageManager).setInstalledPackagesForUserId(
PERSONAL_PROFILE_ID, PERSONAL_PROFILE_INSTALLED_PACKAGES); PERSONAL_PROFILE_ID, PERSONAL_PROFILE_INSTALLED_PACKAGES);
shadowOf(mPackageManager).setInstalledPackagesForUserId( shadowOf(mPackageManager).setInstalledPackagesForUserId(
WORK_PROFILE_ID, WORK_PROFILE_INSTALLED_PACKAGES); WORK_PROFILE_ID, WORK_PROFILE_INSTALLED_PACKAGES);
shadowOf(mCrossProfileApps).addCrossProfilePackage(PERSONAL_CROSS_PROFILE_PACKAGE); installCrossProfilePackage(PERSONAL_PROFILE_ID, PERSONAL_CROSS_PROFILE_PACKAGE);
shadowOf(mCrossProfileApps).addCrossProfilePackage(WORK_CROSS_PROFILE_PACKAGE); installCrossProfilePackage(WORK_PROFILE_ID, WORK_CROSS_PROFILE_PACKAGE);
List<Pair<ApplicationInfo, UserHandle>> apps = mFragment.collectConfigurableApps( List<Pair<ApplicationInfo, UserHandle>> apps =
InteractAcrossProfilesSettings.collectConfigurableApps(
mPackageManager, mUserManager, mCrossProfileApps); mPackageManager, mUserManager, mCrossProfileApps);
assertThat(apps.size()).isEqualTo(1); assertThat(apps.size()).isEqualTo(2);
assertThat(apps.get(0).first.packageName).isEqualTo(PERSONAL_CROSS_PROFILE_PACKAGE); 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 @Test
public void collectConfigurableApps_fromWork_returnsPersonalPackages() { public void collectConfigurableApps_fromWork_returnsCombinedPackages() {
shadowOf(mUserManager).addUser( shadowOf(mUserManager).addUser(
PERSONAL_PROFILE_ID, "personal-profile"/* name */, 0/* flags */); PERSONAL_PROFILE_ID, "personal-profile"/* name */, 0/* flags */);
shadowOf(mUserManager).addProfile( shadowOf(mUserManager).addProfile(
PERSONAL_PROFILE_ID, WORK_PROFILE_ID, PERSONAL_PROFILE_ID, WORK_PROFILE_ID,
"work-profile"/* profileName */, 0/* profileFlags */); "work-profile"/* profileName */, UserInfo.FLAG_MANAGED_PROFILE);
ShadowProcess.setUid(WORK_UID); ShadowProcess.setUid(WORK_UID);
shadowOf(mPackageManager).setInstalledPackagesForUserId( shadowOf(mPackageManager).setInstalledPackagesForUserId(
PERSONAL_PROFILE_ID, PERSONAL_PROFILE_INSTALLED_PACKAGES); PERSONAL_PROFILE_ID, PERSONAL_PROFILE_INSTALLED_PACKAGES);
shadowOf(mPackageManager).setInstalledPackagesForUserId( shadowOf(mPackageManager).setInstalledPackagesForUserId(
WORK_PROFILE_ID, WORK_PROFILE_INSTALLED_PACKAGES); WORK_PROFILE_ID, WORK_PROFILE_INSTALLED_PACKAGES);
shadowOf(mCrossProfileApps).addCrossProfilePackage(PERSONAL_CROSS_PROFILE_PACKAGE); installCrossProfilePackage(PERSONAL_PROFILE_ID, PERSONAL_CROSS_PROFILE_PACKAGE);
shadowOf(mCrossProfileApps).addCrossProfilePackage(WORK_CROSS_PROFILE_PACKAGE); installCrossProfilePackage(WORK_PROFILE_ID, WORK_CROSS_PROFILE_PACKAGE);
List<Pair<ApplicationInfo, UserHandle>> apps = mFragment.collectConfigurableApps( List<Pair<ApplicationInfo, UserHandle>> apps =
InteractAcrossProfilesSettings.collectConfigurableApps(
mPackageManager, mUserManager, mCrossProfileApps); mPackageManager, mUserManager, mCrossProfileApps);
assertThat(apps.size()).isEqualTo(1); assertThat(apps.size()).isEqualTo(2);
assertThat(apps.get(0).first.packageName).isEqualTo(PERSONAL_CROSS_PROFILE_PACKAGE); 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 @Test
@@ -119,9 +129,10 @@ public class InteractAcrossProfilesSettingsTest {
PERSONAL_PROFILE_ID, "personal-profile"/* name */, 0/* flags */); PERSONAL_PROFILE_ID, "personal-profile"/* name */, 0/* flags */);
shadowOf(mPackageManager).setInstalledPackagesForUserId( shadowOf(mPackageManager).setInstalledPackagesForUserId(
PERSONAL_PROFILE_ID, PERSONAL_PROFILE_INSTALLED_PACKAGES); PERSONAL_PROFILE_ID, PERSONAL_PROFILE_INSTALLED_PACKAGES);
shadowOf(mCrossProfileApps).addCrossProfilePackage(PERSONAL_CROSS_PROFILE_PACKAGE); installCrossProfilePackage(PERSONAL_PROFILE_ID, PERSONAL_CROSS_PROFILE_PACKAGE);
List<Pair<ApplicationInfo, UserHandle>> apps = mFragment.collectConfigurableApps( List<Pair<ApplicationInfo, UserHandle>> apps =
InteractAcrossProfilesSettings.collectConfigurableApps(
mPackageManager, mUserManager, mCrossProfileApps); mPackageManager, mUserManager, mCrossProfileApps);
assertThat(apps).isEmpty(); assertThat(apps).isEmpty();
@@ -133,11 +144,14 @@ public class InteractAcrossProfilesSettingsTest {
PERSONAL_PROFILE_ID, "personal-profile"/* name */, 0/* flags */); PERSONAL_PROFILE_ID, "personal-profile"/* name */, 0/* flags */);
shadowOf(mUserManager).addProfile( shadowOf(mUserManager).addProfile(
PERSONAL_PROFILE_ID, WORK_PROFILE_ID, PERSONAL_PROFILE_ID, WORK_PROFILE_ID,
"work-profile"/* profileName */, 0/* profileFlags */); "work-profile"/* profileName */, UserInfo.FLAG_MANAGED_PROFILE);
shadowOf(mPackageManager).setInstalledPackagesForUserId( shadowOf(mPackageManager).setInstalledPackagesForUserId(
PERSONAL_PROFILE_ID, PERSONAL_PROFILE_INSTALLED_PACKAGES); 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_CROSS_PROFILE_PACKAGE);
shadowOf(mCrossProfileApps).addCrossProfilePackage(PERSONAL_NON_CROSS_PROFILE_PACKAGE);
String appOp = AppOpsManager.permissionToOp(INTERACT_ACROSS_PROFILES_PERMISSION); String appOp = AppOpsManager.permissionToOp(INTERACT_ACROSS_PROFILES_PERMISSION);
shadowOf(mAppOpsManager).setMode( shadowOf(mAppOpsManager).setMode(
appOp, PACKAGE_UID, PERSONAL_CROSS_PROFILE_PACKAGE, AppOpsManager.MODE_ALLOWED); 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); appOp, PACKAGE_UID, PERSONAL_NON_CROSS_PROFILE_PACKAGE, AppOpsManager.MODE_IGNORED);
shadowOf(mPackageManager).addPermissionInfo(createCrossProfilesPermissionInfo()); shadowOf(mPackageManager).addPermissionInfo(createCrossProfilesPermissionInfo());
int numOfApps = mFragment.getNumberOfEnabledApps( int numOfApps = InteractAcrossProfilesSettings.getNumberOfEnabledApps(
mContext, mPackageManager, mUserManager, mCrossProfileApps); mContext, mPackageManager, mUserManager, mCrossProfileApps);
assertThat(numOfApps).isEqualTo(1); 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() { private PermissionInfo createCrossProfilesPermissionInfo() {
PermissionInfo permissionInfo = new PermissionInfo(); PermissionInfo permissionInfo = new PermissionInfo();
permissionInfo.name = INTERACT_ACROSS_PROFILES_PERMISSION; permissionInfo.name = INTERACT_ACROSS_PROFILES_PERMISSION;