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