blob: 7f02c1570be2603c74cbe475ac43a37632a9cb72 [file] [log] [blame]
/*
* Copyright (C) 2021 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.car.settings.enterprise;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.Dialog;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Process;
import android.os.UserHandle;
import android.util.IconDrawableFactory;
import androidx.annotation.DrawableRes;
import androidx.annotation.VisibleForTesting;
import com.android.car.settings.R;
import com.android.car.settings.common.Logger;
import com.android.car.ui.AlertDialogBuilder;
import com.android.car.ui.preference.CarUiDialogFragment;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.enterprise.ActionDisabledByAdminController;
import com.android.settingslib.enterprise.ActionDisabledByAdminControllerFactory;
/**
* Shows a dialog explaining that an action is not enabled due to restrictions imposed by an active
* device administrator.
*/
// TODO(b/186905050): add unit tests
// TODO(b/188836559): move most of this class' logic to settingslib
public final class ActionDisabledByAdminDialogFragment extends CarUiDialogFragment {
public static final String DISABLED_BY_ADMIN_CONFIRM_DIALOG_TAG =
"com.android.car.settings.applications.DisabledByAdminConfirmDialog";
private static final String TAG = ActionDisabledByAdminDialogFragment.class.getSimpleName();
private static final Logger LOG = new Logger(TAG);
private static final String EXTRA_RESTRICTION = TAG + "_restriction";
private static final String EXTRA_RESTRICTED_PKG = TAG + "_pkg";
private static final String EXTRA_ADMIN_USER_ID = TAG + "_userId";
@VisibleForTesting
String mRestriction;
String mRestrictedPackage;
@UserIdInt
private int mAdminUserId;
private ActionDisabledByAdminController mActionDisabledByAdminController;
private DismissListener mDismissListener;
/**
* Gets the dialog for the given user and restriction.
*/
public static ActionDisabledByAdminDialogFragment newInstance(String restriction,
@UserIdInt int userId) {
return newInstance(restriction, null, userId);
}
/**
* Gets the dialog for the given user and restriction.
*/
public static ActionDisabledByAdminDialogFragment newInstance(String restriction,
@Nullable String restrictedPackage, @UserIdInt int userId) {
ActionDisabledByAdminDialogFragment instance = new ActionDisabledByAdminDialogFragment();
instance.mRestriction = restriction;
instance.mRestrictedPackage = restrictedPackage;
instance.mAdminUserId = userId;
return instance;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
if (savedInstanceState != null) {
mRestriction = savedInstanceState.getString(EXTRA_RESTRICTION);
mRestrictedPackage = savedInstanceState.getString(EXTRA_RESTRICTED_PKG);
mAdminUserId = savedInstanceState.getInt(EXTRA_ADMIN_USER_ID);
}
return initialize(getContext()).create();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(EXTRA_RESTRICTION, mRestriction);
outState.putString(EXTRA_RESTRICTED_PKG, mRestrictedPackage);
outState.putInt(EXTRA_ADMIN_USER_ID, mAdminUserId);
}
@Override
protected void onDialogClosed(boolean positiveResult) {
if (mDismissListener != null) {
mDismissListener.onDismiss(positiveResult);
mDismissListener = null;
}
}
/** Sets the listeners which listens for the dialog dismissed */
public void setDismissListener(DismissListener dismissListener) {
mDismissListener = dismissListener;
}
private AlertDialogBuilder initialize(Context context) {
Intent intent = getActivity().getIntent();
boolean hasValidIntent = intent != null
&& intent.getParcelableExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN) != null;
EnforcedAdmin enforcedAdmin = hasValidIntent
? EnterpriseUtils.getEnforcedAdminFromIntent(context, intent)
: EnterpriseUtils.getEnforcedAdmin(context, mAdminUserId,
mRestriction, mRestrictedPackage);
LOG.i("hasValidIntent: " + hasValidIntent + " enforcedAdmin: " + enforcedAdmin
+ " mAdminUserId: " + mAdminUserId);
AlertDialogBuilder builder = new AlertDialogBuilder(context)
.setPositiveButton(R.string.okay, /* listener= */ null);
mActionDisabledByAdminController = ActionDisabledByAdminControllerFactory
.createInstance(context, mRestriction, new DeviceAdminStringProviderImpl(context),
context.getUser());
// Learn more button should launch admin policy information on the current user
mActionDisabledByAdminController.initialize(
new ActionDisabledLearnMoreButtonLauncherImpl(builder,
/* preferredUser= */ context.getUser()));
if (enforcedAdmin != null) {
mActionDisabledByAdminController.updateEnforcedAdmin(enforcedAdmin, mAdminUserId);
mActionDisabledByAdminController.setupLearnMoreButton(context);
}
initializeDialogViews(context, builder, enforcedAdmin,
getEnforcementAdminUserId(enforcedAdmin));
return builder;
}
// NOTE: methods below were copied from phone Settings
// (com.android.settings.enterprise.ActionDisabledByAdminDialogHelper), but adjusted to
// use a AlertDialogBuilder directly, instead of an Activity hosting a dialog.
private static @UserIdInt int getEnforcementAdminUserId(@Nullable EnforcedAdmin admin) {
return admin == null || admin.user == null ? UserHandle.USER_NULL
: admin.user.getIdentifier();
}
private void initializeDialogViews(Context context, AlertDialogBuilder builder,
@Nullable EnforcedAdmin enforcedAdmin, @UserIdInt int userId) {
ComponentName admin = null;
if (enforcedAdmin != null) {
admin = enforcedAdmin.component;
if (admin == null) {
return;
}
mActionDisabledByAdminController.updateEnforcedAdmin(enforcedAdmin, userId);
}
if (isNotValidEnforcedAdmin(context, enforcedAdmin)) {
admin = null;
}
setIcon(builder, R.drawable.ic_lock);
setAdminSupportTitle(context, builder, mRestriction);
if (enforcedAdmin != null) {
setAdminSupportDetails(context, builder, enforcedAdmin);
}
}
private boolean isNotValidEnforcedAdmin(Context context, EnforcedAdmin enforcedAdmin) {
if (enforcedAdmin == null) {
LOG.w("isNotValidEnforcedAdmin(): enforcedAdmin is null");
return true;
}
ComponentName admin = enforcedAdmin.component;
int userId = getEnforcementAdminUserId(enforcedAdmin);
if (isNotCurrentUserOrProfile(context, admin, userId)
&& isNotDeviceOwner(context, admin, userId)) {
LOG.w("isNotValidEnforcedAdmin(): is not current user or profile/device owner");
return true;
}
return false;
}
private boolean isNotCurrentUserOrProfile(Context context, ComponentName admin,
@UserIdInt int userId) {
return !RestrictedLockUtilsInternal.isAdminInCurrentUserOrProfile(context, admin)
|| !RestrictedLockUtils.isCurrentUserOrProfile(context, userId);
}
private boolean isNotDeviceOwner(Context context, ComponentName admin,
@UserIdInt int userId) {
EnforcedAdmin deviceOwner = RestrictedLockUtilsInternal.getDeviceOwner(context);
return !((deviceOwner.component).equals(admin) && userId == UserHandle.USER_SYSTEM);
}
private void setAdminSupportTitle(Context context, AlertDialogBuilder builder,
String restriction) {
builder.setTitle(mActionDisabledByAdminController.getAdminSupportTitle(restriction));
}
private void setIcon(AlertDialogBuilder builder, @DrawableRes int iconId) {
builder.setIcon(iconId);
}
private void setAdminSupportDetails(Context context, AlertDialogBuilder builder,
@Nullable EnforcedAdmin enforcedAdmin) {
if (enforcedAdmin == null || enforcedAdmin.component == null) {
LOG.i("setAdminSupportDetails(): no admin on " + enforcedAdmin);
return;
}
CharSequence supportMessage = null;
if (isNotValidEnforcedAdmin(context, enforcedAdmin)) {
enforcedAdmin.component = null;
} else {
if (enforcedAdmin.user == null) {
enforcedAdmin.user = UserHandle.of(UserHandle.myUserId());
}
if (UserHandle.isSameApp(Process.myUid(), Process.SYSTEM_UID)) {
supportMessage = context.getSystemService(DevicePolicyManager.class)
.getShortSupportMessageForUser(enforcedAdmin.component,
getEnforcementAdminUserId(enforcedAdmin));
}
}
CharSequence supportContentString =
mActionDisabledByAdminController.getAdminSupportContentString(
context, supportMessage);
if (supportContentString != null) {
builder.setMessage(supportContentString);
}
}
// Copied from com.android.settings.Utils
private static Drawable getBadgedIcon(IconDrawableFactory iconDrawableFactory,
PackageManager packageManager, String packageName, int userId) {
try {
ApplicationInfo appInfo = packageManager.getApplicationInfoAsUser(
packageName, PackageManager.GET_META_DATA, userId);
return iconDrawableFactory.getBadgedIcon(appInfo, userId);
} catch (PackageManager.NameNotFoundException e) {
return packageManager.getDefaultActivityIcon();
}
}
/** Listens to the dismiss action. */
public interface DismissListener {
/**
* Defines the action to take when the dialog is closed.
*
* @param positiveResult - whether or not the dialog was dismissed because of a positive
* button press.
*/
void onDismiss(boolean positiveResult);
}
}