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.ActivityManager; 21 import android.app.AlertDialog; 22 import android.app.Dialog; 23 import android.app.Fragment; 24 import android.app.admin.DevicePolicyManager; 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.support.v14.preference.SwitchPreference; 34 import android.support.v7.preference.PreferenceScreen; 35 import android.util.IconDrawableFactory; 36 import android.util.Log; 37 import android.view.View; 38 39 import com.android.internal.logging.nano.MetricsProto; 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.notification.EmptyTextSettings; 44 import com.android.settings.widget.AppSwitchPreference; 45 import com.android.settingslib.applications.ServiceListing; 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 if (!ActivityManager.isLowRamDeviceStatic()) { 94 mServiceListing.reload(); 95 mServiceListing.setListening(true); 96 } else { 97 setEmptyText(R.string.disabled_low_ram_device); 98 } 99 } 100 101 @Override onPause()102 public void onPause() { 103 super.onPause(); 104 mServiceListing.setListening(false); 105 } 106 updateList(List<ServiceInfo> services)107 private void updateList(List<ServiceInfo> services) { 108 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 109 final int managedProfileId = Utils.getManagedProfileId(um, UserHandle.myUserId()); 110 111 final PreferenceScreen screen = getPreferenceScreen(); 112 screen.removeAll(); 113 services.sort(new PackageItemInfo.DisplayNameComparator(mPm)); 114 for (ServiceInfo service : services) { 115 final ComponentName cn = new ComponentName(service.packageName, service.name); 116 CharSequence title = null; 117 try { 118 title = mPm.getApplicationInfoAsUser( 119 service.packageName, 0, getCurrentUser(managedProfileId)).loadLabel(mPm); 120 } catch (PackageManager.NameNotFoundException e) { 121 // unlikely, as we are iterating over live services. 122 Log.e(TAG, "can't find package name", e); 123 } 124 final String summary = service.loadLabel(mPm).toString(); 125 final SwitchPreference pref = new AppSwitchPreference(getPrefContext()); 126 pref.setPersistent(false); 127 pref.setIcon(mIconDrawableFactory.getBadgedIcon(service, service.applicationInfo, 128 UserHandle.getUserId(service.applicationInfo.uid))); 129 if (title != null && !title.equals(summary)) { 130 pref.setTitle(title); 131 pref.setSummary(summary); 132 } else { 133 pref.setTitle(summary); 134 } 135 pref.setKey(cn.flattenToString()); 136 pref.setChecked(isServiceEnabled(cn)); 137 if (managedProfileId != UserHandle.USER_NULL 138 && !mDpm.isNotificationListenerServicePermitted( 139 service.packageName, managedProfileId)) { 140 pref.setSummary(R.string.work_profile_notification_access_blocked_summary); 141 } 142 pref.setOnPreferenceChangeListener((preference, newValue) -> { 143 final boolean enable = (boolean) newValue; 144 return setEnabled(cn, summary, enable); 145 }); 146 pref.setKey(cn.flattenToString()); 147 screen.addPreference(pref); 148 } 149 highlightPreferenceIfNeeded(); 150 } 151 getCurrentUser(int managedProfileId)152 private int getCurrentUser(int managedProfileId) { 153 if (managedProfileId != UserHandle.USER_NULL) { 154 return managedProfileId; 155 } 156 return UserHandle.myUserId(); 157 } 158 isServiceEnabled(ComponentName cn)159 protected boolean isServiceEnabled(ComponentName cn) { 160 return mServiceListing.isEnabled(cn); 161 } 162 setEnabled(ComponentName service, String title, boolean enable)163 protected boolean setEnabled(ComponentName service, String title, boolean enable) { 164 if (!enable) { 165 // the simple version: disabling 166 mServiceListing.setEnabled(service, false); 167 return true; 168 } else { 169 if (mServiceListing.isEnabled(service)) { 170 return true; // already enabled 171 } 172 // show a scary dialog 173 new ScaryWarningDialogFragment() 174 .setServiceInfo(service, title, this) 175 .show(getFragmentManager(), "dialog"); 176 return false; 177 } 178 } 179 enable(ComponentName service)180 protected void enable(ComponentName service) { 181 mServiceListing.setEnabled(service, true); 182 } 183 184 public static class ScaryWarningDialogFragment extends InstrumentedDialogFragment { 185 private static final String KEY_COMPONENT = "c"; 186 private static final String KEY_LABEL = "l"; 187 188 @Override getMetricsCategory()189 public int getMetricsCategory() { 190 return MetricsProto.MetricsEvent.DIALOG_SERVICE_ACCESS_WARNING; 191 } 192 setServiceInfo(ComponentName cn, String label, Fragment target)193 public ScaryWarningDialogFragment setServiceInfo(ComponentName cn, String label, 194 Fragment target) { 195 Bundle args = new Bundle(); 196 args.putString(KEY_COMPONENT, cn.flattenToString()); 197 args.putString(KEY_LABEL, label); 198 setArguments(args); 199 setTargetFragment(target, 0); 200 return this; 201 } 202 203 @Override onCreateDialog(Bundle savedInstanceState)204 public Dialog onCreateDialog(Bundle savedInstanceState) { 205 final Bundle args = getArguments(); 206 final String label = args.getString(KEY_LABEL); 207 final ComponentName cn = ComponentName.unflattenFromString(args 208 .getString(KEY_COMPONENT)); 209 ManagedServiceSettings parent = (ManagedServiceSettings) getTargetFragment(); 210 211 final String title = getResources().getString(parent.mConfig.warningDialogTitle, label); 212 final String summary = getResources().getString(parent.mConfig.warningDialogSummary, 213 label); 214 return new AlertDialog.Builder(getContext()) 215 .setMessage(summary) 216 .setTitle(title) 217 .setCancelable(true) 218 .setPositiveButton(R.string.allow, 219 (dialog, id) -> parent.enable(cn)) 220 .setNegativeButton(R.string.deny, 221 (dialog, id) -> { 222 // pass 223 }) 224 .create(); 225 } 226 } 227 228 public static class Config { 229 public final String tag; 230 public final String setting; 231 public final String intentAction; 232 public final String permission; 233 public final String noun; 234 public final int warningDialogTitle; 235 public final int warningDialogSummary; 236 public final int emptyText; 237 Config(String tag, String setting, String intentAction, String permission, String noun, int warningDialogTitle, int warningDialogSummary, int emptyText)238 private Config(String tag, String setting, String intentAction, String permission, 239 String noun, int warningDialogTitle, int warningDialogSummary, int emptyText) { 240 this.tag = tag; 241 this.setting = setting; 242 this.intentAction = intentAction; 243 this.permission = permission; 244 this.noun = noun; 245 this.warningDialogTitle = warningDialogTitle; 246 this.warningDialogSummary = warningDialogSummary; 247 this.emptyText = emptyText; 248 } 249 250 public static class Builder{ 251 private String mTag; 252 private String mSetting; 253 private String mIntentAction; 254 private String mPermission; 255 private String mNoun; 256 private int mWarningDialogTitle; 257 private int mWarningDialogSummary; 258 private int mEmptyText; 259 setTag(String tag)260 public Builder setTag(String tag) { 261 mTag = tag; 262 return this; 263 } 264 setSetting(String setting)265 public Builder setSetting(String setting) { 266 mSetting = setting; 267 return this; 268 } 269 setIntentAction(String intentAction)270 public Builder setIntentAction(String intentAction) { 271 mIntentAction = intentAction; 272 return this; 273 } 274 setPermission(String permission)275 public Builder setPermission(String permission) { 276 mPermission = permission; 277 return this; 278 } 279 setNoun(String noun)280 public Builder setNoun(String noun) { 281 mNoun = noun; 282 return this; 283 } 284 setWarningDialogTitle(int warningDialogTitle)285 public Builder setWarningDialogTitle(int warningDialogTitle) { 286 mWarningDialogTitle = warningDialogTitle; 287 return this; 288 } 289 setWarningDialogSummary(int warningDialogSummary)290 public Builder setWarningDialogSummary(int warningDialogSummary) { 291 mWarningDialogSummary = warningDialogSummary; 292 return this; 293 } 294 setEmptyText(int emptyText)295 public Builder setEmptyText(int emptyText) { 296 mEmptyText = emptyText; 297 return this; 298 } 299 build()300 public Config build() { 301 return new Config(mTag, mSetting, mIntentAction, mPermission, mNoun, 302 mWarningDialogTitle, mWarningDialogSummary, mEmptyText); 303 } 304 } 305 } 306 307 } 308