1 /* 2 * Copyright (C) 2014 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.settings.utils; 18 19 import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_NOTIFICATION_LISTENER_BLOCKED; 20 21 import android.app.Dialog; 22 import android.app.admin.DevicePolicyManager; 23 import android.app.settings.SettingsEnums; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.pm.PackageItemInfo; 27 import android.content.pm.PackageManager; 28 import android.content.pm.ServiceInfo; 29 import android.os.Bundle; 30 import android.os.UserHandle; 31 import android.os.UserManager; 32 import android.util.IconDrawableFactory; 33 import android.util.Log; 34 import android.view.View; 35 36 import androidx.annotation.Nullable; 37 import androidx.appcompat.app.AlertDialog; 38 import androidx.fragment.app.Fragment; 39 import androidx.preference.PreferenceScreen; 40 41 import com.android.settings.R; 42 import com.android.settings.Utils; 43 import com.android.settings.core.instrumentation.InstrumentedDialogFragment; 44 import com.android.settings.widget.EmptyTextSettings; 45 import com.android.settingslib.RestrictedSwitchPreference; 46 import com.android.settingslib.applications.ServiceListing; 47 import com.android.settingslib.widget.TwoTargetPreference; 48 49 import java.util.List; 50 51 public abstract class ManagedServiceSettings extends EmptyTextSettings { 52 private static final String TAG = "ManagedServiceSettings"; 53 private final Config mConfig; 54 55 protected Context mContext; 56 private PackageManager mPm; 57 private DevicePolicyManager mDpm; 58 private ServiceListing mServiceListing; 59 private IconDrawableFactory mIconDrawableFactory; 60 getConfig()61 abstract protected Config getConfig(); 62 ManagedServiceSettings()63 public ManagedServiceSettings() { 64 mConfig = getConfig(); 65 } 66 67 @Override onCreate(Bundle icicle)68 public void onCreate(Bundle icicle) { 69 super.onCreate(icicle); 70 71 mContext = getActivity(); 72 mPm = mContext.getPackageManager(); 73 mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); 74 mIconDrawableFactory = IconDrawableFactory.newInstance(mContext); 75 mServiceListing = new ServiceListing.Builder(mContext) 76 .setPermission(mConfig.permission) 77 .setIntentAction(mConfig.intentAction) 78 .setNoun(mConfig.noun) 79 .setSetting(mConfig.setting) 80 .setTag(mConfig.tag) 81 .build(); 82 mServiceListing.addCallback(this::updateList); 83 setPreferenceScreen(getPreferenceManager().createPreferenceScreen(mContext)); 84 } 85 86 @Override onViewCreated(View view, @Nullable Bundle savedInstanceState)87 public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 88 super.onViewCreated(view, savedInstanceState); 89 setEmptyText(mConfig.emptyText); 90 } 91 92 @Override onResume()93 public void onResume() { 94 super.onResume(); 95 mServiceListing.reload(); 96 mServiceListing.setListening(true); 97 } 98 99 @Override onPause()100 public void onPause() { 101 super.onPause(); 102 mServiceListing.setListening(false); 103 } 104 updateList(List<ServiceInfo> services)105 private void updateList(List<ServiceInfo> services) { 106 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 107 final int managedProfileId = Utils.getManagedProfileId(um, UserHandle.myUserId()); 108 109 final PreferenceScreen screen = getPreferenceScreen(); 110 screen.removeAll(); 111 services.sort(new PackageItemInfo.DisplayNameComparator(mPm)); 112 for (ServiceInfo service : services) { 113 final ComponentName cn = new ComponentName(service.packageName, service.name); 114 CharSequence title = null; 115 try { 116 title = mPm.getApplicationInfoAsUser( 117 service.packageName, 0, UserHandle.myUserId()).loadLabel(mPm); 118 } catch (PackageManager.NameNotFoundException e) { 119 // unlikely, as we are iterating over live services. 120 Log.e(TAG, "can't find package name", e); 121 } 122 final CharSequence finalTitle = title; 123 final String summary = service.loadLabel(mPm).toString(); 124 final RestrictedSwitchPreference pref = 125 new RestrictedSwitchPreference(getPrefContext()); 126 pref.setPersistent(false); 127 pref.setIcon(mIconDrawableFactory.getBadgedIcon(service, service.applicationInfo, 128 UserHandle.getUserId(service.applicationInfo.uid))); 129 pref.setIconSize(TwoTargetPreference.ICON_SIZE_MEDIUM); 130 if (title != null && !title.equals(summary)) { 131 pref.setTitle(title); 132 pref.setSummary(summary); 133 } else { 134 pref.setTitle(summary); 135 } 136 pref.setKey(cn.flattenToString()); 137 pref.setChecked(isServiceEnabled(cn)); 138 if (managedProfileId != UserHandle.USER_NULL 139 && !mDpm.isNotificationListenerServicePermitted( 140 service.packageName, managedProfileId)) { 141 pref.setSummary(mDpm.getResources().getString( 142 WORK_PROFILE_NOTIFICATION_LISTENER_BLOCKED, 143 () -> getString( 144 R.string.work_profile_notification_access_blocked_summary))); 145 } 146 pref.setOnPreferenceChangeListener((preference, newValue) -> { 147 final boolean enable = (boolean) newValue; 148 if (finalTitle != null) { 149 return setEnabled(cn, finalTitle.toString(), enable); 150 } else { 151 return setEnabled(cn, null, enable); 152 } 153 }); 154 pref.setKey(cn.flattenToString()); 155 if (!pref.isChecked()) { 156 pref.checkEcmRestrictionAndSetDisabled(mConfig.permission, service.packageName); 157 } 158 screen.addPreference(pref); 159 } 160 highlightPreferenceIfNeeded(); 161 } 162 getCurrentUser(int managedProfileId)163 private int getCurrentUser(int managedProfileId) { 164 if (managedProfileId != UserHandle.USER_NULL) { 165 return managedProfileId; 166 } 167 return UserHandle.myUserId(); 168 } 169 isServiceEnabled(ComponentName cn)170 protected boolean isServiceEnabled(ComponentName cn) { 171 return mServiceListing.isEnabled(cn); 172 } 173 setEnabled(ComponentName service, String title, boolean enable)174 protected boolean setEnabled(ComponentName service, String title, boolean enable) { 175 if (!enable) { 176 // the simple version: disabling 177 mServiceListing.setEnabled(service, false); 178 return true; 179 } else { 180 if (mServiceListing.isEnabled(service)) { 181 return true; // already enabled 182 } 183 // show a scary dialog 184 new ScaryWarningDialogFragment() 185 .setServiceInfo(service, title, this) 186 .show(getFragmentManager(), "dialog"); 187 return false; 188 } 189 } 190 enable(ComponentName service)191 protected void enable(ComponentName service) { 192 mServiceListing.setEnabled(service, true); 193 } 194 195 public static class ScaryWarningDialogFragment extends InstrumentedDialogFragment { 196 private static final String KEY_COMPONENT = "c"; 197 private static final String KEY_LABEL = "l"; 198 199 @Override getMetricsCategory()200 public int getMetricsCategory() { 201 return SettingsEnums.DIALOG_SERVICE_ACCESS_WARNING; 202 } 203 setServiceInfo(ComponentName cn, String label, Fragment target)204 public ScaryWarningDialogFragment setServiceInfo(ComponentName cn, String label, 205 Fragment target) { 206 Bundle args = new Bundle(); 207 args.putString(KEY_COMPONENT, cn.flattenToString()); 208 args.putString(KEY_LABEL, label); 209 setArguments(args); 210 setTargetFragment(target, 0); 211 return this; 212 } 213 214 @Override onCreateDialog(Bundle savedInstanceState)215 public Dialog onCreateDialog(Bundle savedInstanceState) { 216 final Bundle args = getArguments(); 217 final String label = args.getString(KEY_LABEL); 218 final ComponentName cn = ComponentName.unflattenFromString(args 219 .getString(KEY_COMPONENT)); 220 ManagedServiceSettings parent = (ManagedServiceSettings) getTargetFragment(); 221 222 final String title = getResources().getString(parent.mConfig.warningDialogTitle, label); 223 final String summary = getResources().getString(parent.mConfig.warningDialogSummary, 224 label); 225 return new AlertDialog.Builder(getContext()) 226 .setMessage(summary) 227 .setTitle(title) 228 .setCancelable(true) 229 .setPositiveButton(R.string.allow, 230 (dialog, id) -> parent.enable(cn)) 231 .setNegativeButton(R.string.deny, 232 (dialog, id) -> { 233 // pass 234 }) 235 .create(); 236 } 237 } 238 239 public static class Config { 240 public final String tag; 241 public final String setting; 242 public final String intentAction; 243 public final String permission; 244 public final String noun; 245 public final int warningDialogTitle; 246 public final int warningDialogSummary; 247 public final int emptyText; 248 public final String configIntentAction; 249 Config(String tag, String setting, String intentAction, String configIntentAction, String permission, String noun, int warningDialogTitle, int warningDialogSummary, int emptyText)250 private Config(String tag, String setting, String intentAction, String configIntentAction, 251 String permission, String noun, int warningDialogTitle, int warningDialogSummary, 252 int emptyText) { 253 this.tag = tag; 254 this.setting = setting; 255 this.intentAction = intentAction; 256 this.permission = permission; 257 this.noun = noun; 258 this.warningDialogTitle = warningDialogTitle; 259 this.warningDialogSummary = warningDialogSummary; 260 this.emptyText = emptyText; 261 this.configIntentAction = configIntentAction; 262 } 263 264 public static class Builder{ 265 private String mTag; 266 private String mSetting; 267 private String mIntentAction; 268 private String mPermission; 269 private String mNoun; 270 private int mWarningDialogTitle; 271 private int mWarningDialogSummary; 272 private int mEmptyText; 273 private String mConfigIntentAction; 274 setTag(String tag)275 public Builder setTag(String tag) { 276 mTag = tag; 277 return this; 278 } 279 setSetting(String setting)280 public Builder setSetting(String setting) { 281 mSetting = setting; 282 return this; 283 } 284 setIntentAction(String intentAction)285 public Builder setIntentAction(String intentAction) { 286 mIntentAction = intentAction; 287 return this; 288 } 289 setConfigurationIntentAction(String action)290 public Builder setConfigurationIntentAction(String action) { 291 mConfigIntentAction = action; 292 return this; 293 } 294 setPermission(String permission)295 public Builder setPermission(String permission) { 296 mPermission = permission; 297 return this; 298 } 299 setNoun(String noun)300 public Builder setNoun(String noun) { 301 mNoun = noun; 302 return this; 303 } 304 setWarningDialogTitle(int warningDialogTitle)305 public Builder setWarningDialogTitle(int warningDialogTitle) { 306 mWarningDialogTitle = warningDialogTitle; 307 return this; 308 } 309 setWarningDialogSummary(int warningDialogSummary)310 public Builder setWarningDialogSummary(int warningDialogSummary) { 311 mWarningDialogSummary = warningDialogSummary; 312 return this; 313 } 314 setEmptyText(int emptyText)315 public Builder setEmptyText(int emptyText) { 316 mEmptyText = emptyText; 317 return this; 318 } 319 build()320 public Config build() { 321 return new Config(mTag, mSetting, mIntentAction, mConfigIntentAction, mPermission, 322 mNoun, mWarningDialogTitle, mWarningDialogSummary, mEmptyText); 323 } 324 } 325 } 326 327 } 328