1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.car.settings.enterprise; 18 19 import android.annotation.Nullable; 20 import android.app.AppOpsManager; 21 import android.car.drivingstate.CarUxRestrictions; 22 import android.content.ComponentName; 23 import android.content.Context; 24 import android.graphics.drawable.Drawable; 25 import android.os.Binder; 26 import android.os.IBinder; 27 import android.text.TextUtils; 28 29 import androidx.preference.TwoStatePreference; 30 31 import com.android.car.settings.common.FragmentController; 32 import com.android.internal.annotations.VisibleForTesting; 33 34 /** 35 * Controller for the header preference the device admin details screen. 36 */ 37 public final class DeviceAdminAddHeaderPreferenceController 38 extends BaseDeviceAdminAddPreferenceController<TwoStatePreference> { 39 40 private AppOpsManager mAppOps; 41 private final IBinder mToken = new Binder(); 42 private @Nullable ActivationListener mActivationListener; 43 DeviceAdminAddHeaderPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)44 public DeviceAdminAddHeaderPreferenceController(Context context, String preferenceKey, 45 FragmentController fragmentController, CarUxRestrictions uxRestrictions) { 46 super(context, preferenceKey, fragmentController, uxRestrictions); 47 48 mAppOps = context.getSystemService(AppOpsManager.class); 49 } 50 setActivationListener(ActivationListener listener)51 DeviceAdminAddHeaderPreferenceController setActivationListener(ActivationListener listener) { 52 mActivationListener = listener; 53 return this; 54 } 55 56 @Override getPreferenceType()57 protected Class<TwoStatePreference> getPreferenceType() { 58 return TwoStatePreference.class; 59 } 60 61 @Override onResumeInternal()62 protected void onResumeInternal() { 63 super.onResumeInternal(); 64 65 // Split as a separate method for easier testing. 66 onResumeInternal((TwoStatePreference) getPreference()); 67 } 68 69 @VisibleForTesting onResumeInternal(TwoStatePreference preference)70 void onResumeInternal(TwoStatePreference preference) { 71 setCurrentStatus(preference); 72 73 // As long as we are running, don't let anyone overlay stuff on top of the screen. 74 mAppOps.setUserRestriction(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, true, mToken); 75 mAppOps.setUserRestriction(AppOpsManager.OP_TOAST_WINDOW, true, mToken); 76 } 77 78 @Override onPauseInternal()79 protected void onPauseInternal() { 80 super.onPauseInternal(); 81 82 // Split as a separate method for easier testing. 83 onPauseInternal((TwoStatePreference) getPreference()); 84 } 85 86 @VisibleForTesting onPauseInternal(TwoStatePreference preference)87 void onPauseInternal(TwoStatePreference preference) { 88 // Disable the toggle button when paused, to prevent tapjacking. 89 preference.setEnabled(false); 90 91 mAppOps.setUserRestriction(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, false, mToken); 92 mAppOps.setUserRestriction(AppOpsManager.OP_TOAST_WINDOW, false, mToken); 93 } 94 95 @Override updateState(TwoStatePreference preference)96 protected void updateState(TwoStatePreference preference) { 97 CharSequence name = mDeviceAdminInfo.loadLabel(mPm); 98 Drawable icon = mDeviceAdminInfo.loadIcon(mPm); 99 CharSequence description = getDescription(mDeviceAdminInfo); 100 101 mLogger.d("updateState: name=" + name + ", description=" + description); 102 preference.setTitle(name); 103 preference.setIcon(icon); 104 if (!TextUtils.isEmpty(description)) { 105 preference.setSummary(description); 106 } 107 108 setCurrentStatus(preference); 109 } 110 111 @Override handlePreferenceChanged(TwoStatePreference preference, Object newValue)112 protected boolean handlePreferenceChanged(TwoStatePreference preference, Object newValue) { 113 boolean activated = (boolean) newValue; 114 ComponentName admin = mDeviceAdminInfo.getComponent(); 115 if (activated) { 116 mLogger.i("Activating " + ComponentName.flattenToShortString(admin)); 117 // TODO(b/192372143): support refreshing 118 mDpm.setActiveAdmin(admin, /* refreshing= */ false); 119 } else { 120 mLogger.i("Deactivating " + ComponentName.flattenToShortString(admin)); 121 mDpm.removeActiveAdmin(admin); 122 } 123 if (mActivationListener != null) { 124 mActivationListener.onChanged(activated); 125 } 126 return true; 127 } 128 129 /** Sets the checked status and enabled status according to the device admin */ setCurrentStatus(TwoStatePreference preference)130 private void setCurrentStatus(TwoStatePreference preference) { 131 ComponentName componentName = mDeviceAdminInfo.getComponent(); 132 preference.setChecked(isActive(componentName)); 133 if (isProfileOrDeviceOwner(componentName)) { 134 // TODO(b/170332519): once work profiles are supported, they could be removed 135 mLogger.d("updateState(): " + ComponentName.flattenToShortString(componentName) 136 + " is PO or DO"); 137 preference.setEnabled(false); 138 } else { 139 preference.setEnabled(true); 140 } 141 } 142 isActive(ComponentName componentName)143 private boolean isActive(ComponentName componentName) { 144 return mDpm.isAdminActive(componentName); 145 } 146 147 interface ActivationListener { onChanged(boolean active)148 void onChanged(boolean active); 149 } 150 } 151