Files
app_Settings/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetails.java
kholoud mohamed a8bd1bea4e Add metrics for the cross profile settings page
Also changed the install banner to not say "Tap to
install" if the intent to redirect to Play is missing.

Bug: 149774826
Test: tested manually that the metrics are being reported by running
./out/host/linux-x86/bin/statsd_testdrive 103

Change-Id: Ieebda237748b5dbff8e974465c5d67b147d92077
2020-04-03 11:21:10 +00:00

473 lines
19 KiB
Java

/*
* 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.
*/
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 static android.provider.Settings.ACTION_MANAGE_CROSS_PROFILE_ACCESS;
import android.Manifest;
import android.annotation.UserIdInt;
import android.app.ActionBar;
import android.app.AppOpsManager;
import android.app.admin.DevicePolicyEventLogger;
import android.app.admin.DevicePolicyManager;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.PermissionChecker;
import android.content.pm.CrossProfileApps;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.stats.devicepolicy.DevicePolicyEnums;
import android.util.IconDrawableFactory;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.applications.AppInfoBase;
import com.android.settings.applications.AppStoreUtil;
import com.android.settings.widget.CardPreference;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.widget.LayoutPreference;
public class InteractAcrossProfilesDetails extends AppInfoBase
implements Preference.OnPreferenceClickListener {
private static final String INTERACT_ACROSS_PROFILES_SETTINGS_SWITCH =
"interact_across_profiles_settings_switch";
private static final String INTERACT_ACROSS_PROFILES_HEADER = "interact_across_profiles_header";
public static final String INSTALL_APP_BANNER_KEY = "install_app_banner";
public static final String EXTRA_SHOW_FRAGMENT_ARGS = ":settings:show_fragment_args";
public static final String INTENT_KEY = "intent";
private Context mContext;
private CrossProfileApps mCrossProfileApps;
private UserManager mUserManager;
private RestrictedSwitchPreference mSwitchPref;
private LayoutPreference mHeader;
private CardPreference mInstallBanner;
private PackageManager mPackageManager;
private UserHandle mPersonalProfile;
private UserHandle mWorkProfile;
private boolean mInstalledInPersonal;
private boolean mInstalledInWork;
private String mAppLabel;
private Intent mInstallAppIntent;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = getContext();
mCrossProfileApps = mContext.getSystemService(CrossProfileApps.class);
mUserManager = mContext.getSystemService(UserManager.class);
mPackageManager = mContext.getPackageManager();
mWorkProfile = InteractAcrossProfilesSettings.getWorkProfile(mUserManager);
mPersonalProfile = mUserManager.getProfileParent(mWorkProfile);
mInstalledInWork = isPackageInstalled(mPackageName, mWorkProfile.getIdentifier());
mInstalledInPersonal = isPackageInstalled(mPackageName, mPersonalProfile.getIdentifier());
mAppLabel = mPackageInfo.applicationInfo.loadLabel(mPackageManager).toString();
mInstallAppIntent = AppStoreUtil.getAppStoreLink(mContext, mPackageName);
addPreferencesFromResource(R.xml.interact_across_profiles_permissions_details);
mSwitchPref = findPreference(INTERACT_ACROSS_PROFILES_SETTINGS_SWITCH);
mSwitchPref.setOnPreferenceClickListener(this);
mHeader = findPreference(INTERACT_ACROSS_PROFILES_HEADER);
mInstallBanner = findPreference(INSTALL_APP_BANNER_KEY);
mInstallBanner.setOnPreferenceClickListener(this);
// refreshUi checks that the user can still configure the appOp, return to the
// previous page if it can't.
if (!refreshUi()) {
setIntentAndFinish(true/* appChanged */);
}
addAppTitleAndIcons(mPersonalProfile, mWorkProfile);
styleActionBar();
logPageLaunchMetrics();
}
private void logPageLaunchMetrics() {
if (!mCrossProfileApps.canConfigureInteractAcrossProfiles(mPackageName)) {
logNonConfigurableAppMetrics();
}
Bundle bundle = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGS);
if (bundle == null) {
logEvent(DevicePolicyEnums.CROSS_PROFILE_SETTINGS_PAGE_LAUNCHED_FROM_SETTINGS);
return;
}
Intent intent = (Intent) bundle.get(INTENT_KEY);
if (intent == null) {
logEvent(DevicePolicyEnums.CROSS_PROFILE_SETTINGS_PAGE_LAUNCHED_FROM_SETTINGS);
return;
}
if (ACTION_MANAGE_CROSS_PROFILE_ACCESS.equals(intent.getAction())) {
logEvent(DevicePolicyEnums.CROSS_PROFILE_SETTINGS_PAGE_LAUNCHED_FROM_APP);
}
}
private void logNonConfigurableAppMetrics() {
if (!isCrossProfilePackageWhitelisted(mPackageName)) {
logEvent(DevicePolicyEnums.CROSS_PROFILE_SETTINGS_PAGE_ADMIN_RESTRICTED);
return;
}
if (mInstallBanner == null) {
logEvent(DevicePolicyEnums.CROSS_PROFILE_SETTINGS_PAGE_MISSING_INSTALL_BANNER_INTENT);
}
if (!mInstalledInPersonal) {
logEvent(DevicePolicyEnums.CROSS_PROFILE_SETTINGS_PAGE_MISSING_PERSONAL_APP);
return;
}
if (!mInstalledInWork) {
logEvent(DevicePolicyEnums.CROSS_PROFILE_SETTINGS_PAGE_MISSING_WORK_APP);
}
}
private void logEvent(int eventId) {
DevicePolicyEventLogger.createEvent(eventId)
.setStrings(mPackageName)
.setInt(UserHandle.myUserId())
.setAdmin(RestrictedLockUtils.getProfileOrDeviceOwner(
mContext, mWorkProfile).component)
.write();
}
private void addAppTitleAndIcons(UserHandle personalProfile, UserHandle workProfile) {
final TextView title = mHeader.findViewById(R.id.entity_header_title);
if (title != null) {
final String appLabel = mPackageInfo.applicationInfo.loadLabel(
mPackageManager).toString();
title.setText(appLabel);
}
final ImageView personalIconView = mHeader.findViewById(R.id.entity_header_icon_personal);
if (personalIconView != null) {
Drawable icon = IconDrawableFactory.newInstance(mContext)
.getBadgedIcon(mPackageInfo.applicationInfo, personalProfile.getIdentifier())
.mutate();
if (!mInstalledInPersonal) {
icon.setColorFilter(createSuspendedColorMatrix());
}
personalIconView.setImageDrawable(icon);
}
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);
}
}
private void styleActionBar() {
final ActionBar actionBar = getActivity().getActionBar();
if (actionBar != null) {
actionBar.setElevation(0);
}
}
private ColorMatrixColorFilter createSuspendedColorMatrix() {
int grayValue = 127;
float scale = 0.5f; // half bright
ColorMatrix tempBrightnessMatrix = new ColorMatrix();
float[] mat = tempBrightnessMatrix.getArray();
mat[0] = scale;
mat[6] = scale;
mat[12] = scale;
mat[4] = grayValue;
mat[9] = grayValue;
mat[14] = grayValue;
ColorMatrix matrix = new ColorMatrix();
matrix.setSaturation(0.0f);
matrix.preConcat(tempBrightnessMatrix);
return new ColorMatrixColorFilter(matrix);
}
@Override
public boolean onPreferenceClick(Preference preference) {
// refreshUi checks that the user can still configure the appOp, return to the
// previous page if it can't.
if (!refreshUi()) {
setIntentAndFinish(true/* appChanged */);
}
if (preference == mSwitchPref) {
handleSwitchPreferenceClick();
return true;
}
if (preference == mInstallBanner) {
handleInstallBannerClick();
return true;
}
return false;
}
private void handleSwitchPreferenceClick() {
if (isInteractAcrossProfilesEnabled()) {
logEvent(DevicePolicyEnums.CROSS_PROFILE_SETTINGS_PAGE_PERMISSION_REVOKED);
enableInteractAcrossProfiles(false);
refreshUi();
} else {
showConsentDialog();
}
}
private void showConsentDialog() {
final View dialogView = getLayoutInflater().inflate(
R.layout.interact_across_profiles_consent_dialog, null);
final TextView dialogTitle = dialogView.findViewById(
R.id.interact_across_profiles_consent_dialog_title);
dialogTitle.setText(
getString(R.string.interact_across_profiles_consent_dialog_title, mAppLabel));
final TextView dialogSummary = dialogView.findViewById(
R.id.interact_across_profiles_consent_dialog_summary);
dialogSummary.setText(
getString(R.string.interact_across_profiles_consent_dialog_summary, mAppLabel));
final TextView appDataSummary = dialogView.findViewById(R.id.app_data_summary);
appDataSummary.setText(getString(
R.string.interact_across_profiles_consent_dialog_app_data_summary, mAppLabel));
final TextView permissionsSummary = dialogView.findViewById(R.id.permissions_summary);
permissionsSummary.setText(getString(
R.string.interact_across_profiles_consent_dialog_permissions_summary, mAppLabel));
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setView(dialogView)
.setPositiveButton(R.string.allow, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
logEvent(DevicePolicyEnums.CROSS_PROFILE_SETTINGS_PAGE_USER_CONSENTED);
enableInteractAcrossProfiles(true);
refreshUi();
}
})
.setNegativeButton(R.string.deny, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
logEvent(
DevicePolicyEnums.CROSS_PROFILE_SETTINGS_PAGE_USER_DECLINED_CONSENT);
refreshUi();
}
})
.create().show();
}
private boolean isInteractAcrossProfilesEnabled() {
return isInteractAcrossProfilesEnabled(mContext, mPackageName);
}
static boolean isInteractAcrossProfilesEnabled(
Context context, String packageName) {
UserManager userManager = context.getSystemService(UserManager.class);
UserHandle workProfile = InteractAcrossProfilesSettings.getWorkProfile(userManager);
if (workProfile == null) {
return false;
}
UserHandle personalProfile = userManager.getProfileParent(workProfile);
return context.getSystemService(
CrossProfileApps.class).canConfigureInteractAcrossProfiles(packageName)
&& isInteractAcrossProfilesEnabledInProfile(context, packageName, personalProfile)
&& isInteractAcrossProfilesEnabledInProfile(context, packageName, workProfile);
}
private static boolean isInteractAcrossProfilesEnabledInProfile(
Context context, String packageName, UserHandle userHandle) {
final PackageManager packageManager = context.getPackageManager();
final int uid;
try {
uid = packageManager.getApplicationInfoAsUser(
packageName, /* flags= */0, userHandle).uid;
} catch (PackageManager.NameNotFoundException e) {
return false;
}
return PermissionChecker.PERMISSION_GRANTED
== PermissionChecker.checkPermissionForPreflight(
context,
Manifest.permission.INTERACT_ACROSS_PROFILES,
PermissionChecker.PID_UNKNOWN,
uid,
packageName);
}
private void enableInteractAcrossProfiles(boolean newState) {
mCrossProfileApps.setInteractAcrossProfilesAppOp(
mPackageName, newState ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
}
private void handleInstallBannerClick() {
if (mInstallAppIntent == null) {
logEvent(
DevicePolicyEnums.CROSS_PROFILE_SETTINGS_PAGE_INSTALL_BANNER_NO_INTENT_CLICKED);
return;
}
if (!mInstalledInWork) {
logEvent(DevicePolicyEnums.CROSS_PROFILE_SETTINGS_PAGE_INSTALL_BANNER_CLICKED);
mContext.startActivityAsUser(mInstallAppIntent, mWorkProfile);
return;
}
if (!mInstalledInPersonal) {
logEvent(DevicePolicyEnums.CROSS_PROFILE_SETTINGS_PAGE_INSTALL_BANNER_CLICKED);
mContext.startActivityAsUser(mInstallAppIntent, mPersonalProfile);
}
}
/**
* @return the summary for the current state of whether the app associated with the given
* {@code packageName} is allowed to interact across profiles.
*/
public static CharSequence getPreferenceSummary(
Context context, String packageName) {
return context.getString(isInteractAcrossProfilesEnabled(context, packageName)
? R.string.interact_across_profiles_summary_allowed
: R.string.interact_across_profiles_summary_not_allowed);
}
@Override
protected boolean refreshUi() {
if (mPackageInfo == null || mPackageInfo.applicationInfo == null) {
return false;
}
if (!mCrossProfileApps.canUserAttemptToConfigureInteractAcrossProfiles(mPackageName)) {
// Invalid app entry. Should not allow changing permission
mSwitchPref.setEnabled(false);
return false;
}
if (!mCrossProfileApps.canConfigureInteractAcrossProfiles(mPackageName)) {
return refreshUiForNonConfigurableApps();
}
refreshUiForConfigurableApps();
return true;
}
private boolean refreshUiForNonConfigurableApps() {
mSwitchPref.setChecked(false);
mSwitchPref.setTitle(R.string.interact_across_profiles_switch_disabled);
if (!isCrossProfilePackageWhitelisted(mPackageName)) {
mInstallBanner.setVisible(false);
mSwitchPref.setDisabledByAdmin(RestrictedLockUtils.getProfileOrDeviceOwner(
mContext, mWorkProfile));
return true;
}
mSwitchPref.setEnabled(false);
if (!mInstalledInPersonal && !mInstalledInWork) {
return false;
}
if (!mInstalledInPersonal) {
mInstallBanner.setTitle(getString(
R.string.interact_across_profiles_install_personal_app_title,
mAppLabel));
if (mInstallAppIntent != null) {
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));
if (mInstallAppIntent != null) {
mInstallBanner.setSummary(
R.string.interact_across_profiles_install_app_summary);
}
mInstallBanner.setVisible(true);
return true;
}
return false;
}
private boolean isCrossProfilePackageWhitelisted(String packageName) {
return mContext.getSystemService(DevicePolicyManager.class)
.getAllCrossProfilePackages().contains(packageName);
}
private boolean isPackageInstalled(String packageName, @UserIdInt int userId) {
final PackageInfo info;
try {
info = mContext.createContextAsUser(UserHandle.of(userId), /* flags= */0)
.getPackageManager().getPackageInfo(packageName,
MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
} catch (PackageManager.NameNotFoundException e) {
return false;
}
return info != null;
}
private void refreshUiForConfigurableApps() {
mInstallBanner.setVisible(false);
mSwitchPref.setEnabled(true);
if (isInteractAcrossProfilesEnabled()) {
enableSwitchPref();
} else {
disableSwitchPref();
}
}
private void enableSwitchPref() {
mSwitchPref.setChecked(true);
mSwitchPref.setTitle(R.string.interact_across_profiles_switch_enabled);
final ImageView horizontalArrowIcon = mHeader.findViewById(R.id.entity_header_swap_horiz);
if (horizontalArrowIcon != null) {
horizontalArrowIcon.setImageDrawable(
mContext.getDrawable(R.drawable.ic_swap_horiz_blue));
}
}
private void disableSwitchPref() {
mSwitchPref.setChecked(false);
mSwitchPref.setTitle(R.string.interact_across_profiles_switch_disabled);
final ImageView horizontalArrowIcon = mHeader.findViewById(R.id.entity_header_swap_horiz);
if (horizontalArrowIcon != null) {
horizontalArrowIcon.setImageDrawable(
mContext.getDrawable(R.drawable.ic_swap_horiz_grey));
}
}
@Override
protected AlertDialog createDialog(int id, int errorCode) {
return null;
}
@Override
public int getMetricsCategory() {
return SettingsEnums.INTERACT_ACROSS_PROFILES;
}
}