Merge "Show all apps that requested cross profile permission" into rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
7fb6f35c3a
21
res/drawable/ic_download_for_offline.xml
Normal file
21
res/drawable/ic_download_for_offline.xml
Normal 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>
|
@@ -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 & personal apps</string>
|
<string name="interact_across_profiles_title">Connected work & 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>
|
||||||
|
@@ -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
|
||||||
|
@@ -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) {
|
||||||
|
@@ -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) {
|
||||||
|
@@ -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 =
|
||||||
|
@@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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);
|
||||||
|
@@ -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;
|
||||||
|
Reference in New Issue
Block a user