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.annotation.Nullable; 22 import android.app.Dialog; 23 import android.app.admin.DevicePolicyManager; 24 import android.app.settings.SettingsEnums; 25 import android.content.ComponentName; 26 import android.content.Context; 27 import android.content.pm.PackageItemInfo; 28 import android.content.pm.PackageManager; 29 import android.content.pm.ServiceInfo; 30 import android.os.Bundle; 31 import android.os.UserHandle; 32 import android.os.UserManager; 33 import android.util.IconDrawableFactory; 34 import android.util.Log; 35 import android.view.View; 36 37 import androidx.appcompat.app.AlertDialog; 38 import androidx.fragment.app.Fragment; 39 import androidx.preference.PreferenceScreen; 40 import androidx.preference.SwitchPreference; 41 42 import com.android.settings.R; 43 import com.android.settings.Utils; 44 import com.android.settings.core.instrumentation.InstrumentedDialogFragment; 45 import com.android.settings.widget.EmptyTextSettings; 46 import com.android.settingslib.applications.ServiceListing; 47 import com.android.settingslib.widget.AppSwitchPreference; 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 SwitchPreference pref = new AppSwitchPreference(getPrefContext()); 125 pref.setPersistent(false); 126 pref.setIcon(mIconDrawableFactory.getBadgedIcon(service, service.applicationInfo, 127 UserHandle.getUserId(service.applicationInfo.uid))); 128 if (title != null && !title.equals(summary)) { 129 pref.setTitle(title); 130 pref.setSummary(summary); 131 } else { 132 pref.setTitle(summary); 133 } 134 pref.setKey(cn.flattenToString()); 135 pref.setChecked(isServiceEnabled(cn)); 136 if (managedProfileId != UserHandle.USER_NULL 137 && !mDpm.isNotificationListenerServicePermitted( 138 service.packageName, managedProfileId)) { 139 pref.setSummary(mDpm.getResources().getString( 140 WORK_PROFILE_NOTIFICATION_LISTENER_BLOCKED, 141 () -> getString( 142 R.string.work_profile_notification_access_blocked_summary))); 143 } 144 pref.setOnPreferenceChangeListener((preference, newValue) -> { 145 final boolean enable = (boolean) newValue; 146 if (finalTitle != null) { 147 return setEnabled(cn, finalTitle.toString(), enable); 148 } else { 149 return setEnabled(cn, null, enable); 150 } 151 }); 152 pref.setKey(cn.flattenToString()); 153 screen.addPreference(pref); 154 } 155 highlightPreferenceIfNeeded(); 156 } 157 getCurrentUser(int managedProfileId)158 private int getCurrentUser(int managedProfileId) { 159 if (managedProfileId != UserHandle.USER_NULL) { 160 return managedProfileId; 161 } 162 return UserHandle.myUserId(); 163 } 164 isServiceEnabled(ComponentName cn)165 protected boolean isServiceEnabled(ComponentName cn) { 166 return mServiceListing.isEnabled(cn); 167 } 168 setEnabled(ComponentName service, String title, boolean enable)169 protected boolean setEnabled(ComponentName service, String title, boolean enable) { 170 if (!enable) { 171 // the simple version: disabling 172 mServiceListing.setEnabled(service, false); 173 return true; 174 } else { 175 if (mServiceListing.isEnabled(service)) { 176 return true; // already enabled 177 } 178 // show a scary dialog 179 new ScaryWarningDialogFragment() 180 .setServiceInfo(service, title, this) 181 .show(getFragmentManager(), "dialog"); 182 return false; 183 } 184 } 185 enable(ComponentName service)186 protected void enable(ComponentName service) { 187 mServiceListing.setEnabled(service, true); 188 } 189 190 public static class ScaryWarningDialogFragment extends InstrumentedDialogFragment { 191 private static final String KEY_COMPONENT = "c"; 192 private static final String KEY_LABEL = "l"; 193 194 @Override getMetricsCategory()195 public int getMetricsCategory() { 196 return SettingsEnums.DIALOG_SERVICE_ACCESS_WARNING; 197 } 198 setServiceInfo(ComponentName cn, String label, Fragment target)199 public ScaryWarningDialogFragment setServiceInfo(ComponentName cn, String label, 200 Fragment target) { 201 Bundle args = new Bundle(); 202 args.putString(KEY_COMPONENT, cn.flattenToString()); 203 args.putString(KEY_LABEL, label); 204 setArguments(args); 205 setTargetFragment(target, 0); 206 return this; 207 } 208 209 @Override onCreateDialog(Bundle savedInstanceState)210 public Dialog onCreateDialog(Bundle savedInstanceState) { 211 final Bundle args = getArguments(); 212 final String label = args.getString(KEY_LABEL); 213 final ComponentName cn = ComponentName.unflattenFromString(args 214 .getString(KEY_COMPONENT)); 215 ManagedServiceSettings parent = (ManagedServiceSettings) getTargetFragment(); 216 217 final String title = getResources().getString(parent.mConfig.warningDialogTitle, label); 218 final String summary = getResources().getString(parent.mConfig.warningDialogSummary, 219 label); 220 return new AlertDialog.Builder(getContext()) 221 .setMessage(summary) 222 .setTitle(title) 223 .setCancelable(true) 224 .setPositiveButton(R.string.allow, 225 (dialog, id) -> parent.enable(cn)) 226 .setNegativeButton(R.string.deny, 227 (dialog, id) -> { 228 // pass 229 }) 230 .create(); 231 } 232 } 233 234 public static class Config { 235 public final String tag; 236 public final String setting; 237 public final String intentAction; 238 public final String permission; 239 public final String noun; 240 public final int warningDialogTitle; 241 public final int warningDialogSummary; 242 public final int emptyText; 243 public final String configIntentAction; 244 Config(String tag, String setting, String intentAction, String configIntentAction, String permission, String noun, int warningDialogTitle, int warningDialogSummary, int emptyText)245 private Config(String tag, String setting, String intentAction, String configIntentAction, 246 String permission, String noun, int warningDialogTitle, int warningDialogSummary, 247 int emptyText) { 248 this.tag = tag; 249 this.setting = setting; 250 this.intentAction = intentAction; 251 this.permission = permission; 252 this.noun = noun; 253 this.warningDialogTitle = warningDialogTitle; 254 this.warningDialogSummary = warningDialogSummary; 255 this.emptyText = emptyText; 256 this.configIntentAction = configIntentAction; 257 } 258 259 public static class Builder{ 260 private String mTag; 261 private String mSetting; 262 private String mIntentAction; 263 private String mPermission; 264 private String mNoun; 265 private int mWarningDialogTitle; 266 private int mWarningDialogSummary; 267 private int mEmptyText; 268 private String mConfigIntentAction; 269 setTag(String tag)270 public Builder setTag(String tag) { 271 mTag = tag; 272 return this; 273 } 274 setSetting(String setting)275 public Builder setSetting(String setting) { 276 mSetting = setting; 277 return this; 278 } 279 setIntentAction(String intentAction)280 public Builder setIntentAction(String intentAction) { 281 mIntentAction = intentAction; 282 return this; 283 } 284 setConfigurationIntentAction(String action)285 public Builder setConfigurationIntentAction(String action) { 286 mConfigIntentAction = action; 287 return this; 288 } 289 setPermission(String permission)290 public Builder setPermission(String permission) { 291 mPermission = permission; 292 return this; 293 } 294 setNoun(String noun)295 public Builder setNoun(String noun) { 296 mNoun = noun; 297 return this; 298 } 299 setWarningDialogTitle(int warningDialogTitle)300 public Builder setWarningDialogTitle(int warningDialogTitle) { 301 mWarningDialogTitle = warningDialogTitle; 302 return this; 303 } 304 setWarningDialogSummary(int warningDialogSummary)305 public Builder setWarningDialogSummary(int warningDialogSummary) { 306 mWarningDialogSummary = warningDialogSummary; 307 return this; 308 } 309 setEmptyText(int emptyText)310 public Builder setEmptyText(int emptyText) { 311 mEmptyText = emptyText; 312 return this; 313 } 314 build()315 public Config build() { 316 return new Config(mTag, mSetting, mIntentAction, mConfigIntentAction, mPermission, 317 mNoun, mWarningDialogTitle, mWarningDialogSummary, mEmptyText); 318 } 319 } 320 } 321 322 } 323