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 59 abstract protected Config getConfig(); 60 61 public ManagedServiceSettings() { 62 mConfig = getConfig(); 63 } 64 65 @Override 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 85 public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 86 super.onViewCreated(view, savedInstanceState); 87 setEmptyText(mConfig.emptyText); 88 } 89 90 @Override 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 102 public void onPause() { 103 super.onPause(); 104 mServiceListing.setListening(false); 105 } 106 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 152 private int getCurrentUser(int managedProfileId) { 153 if (managedProfileId != UserHandle.USER_NULL) { 154 return managedProfileId; 155 } 156 return UserHandle.myUserId(); 157 } 158 159 protected boolean isServiceEnabled(ComponentName cn) { 160 return mServiceListing.isEnabled(cn); 161 } 162 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 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 189 public int getMetricsCategory() { 190 return MetricsProto.MetricsEvent.DIALOG_SERVICE_ACCESS_WARNING; 191 } 192 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 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 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 260 public Builder setTag(String tag) { 261 mTag = tag; 262 return this; 263 } 264 265 public Builder setSetting(String setting) { 266 mSetting = setting; 267 return this; 268 } 269 270 public Builder setIntentAction(String intentAction) { 271 mIntentAction = intentAction; 272 return this; 273 } 274 275 public Builder setPermission(String permission) { 276 mPermission = permission; 277 return this; 278 } 279 280 public Builder setNoun(String noun) { 281 mNoun = noun; 282 return this; 283 } 284 285 public Builder setWarningDialogTitle(int warningDialogTitle) { 286 mWarningDialogTitle = warningDialogTitle; 287 return this; 288 } 289 290 public Builder setWarningDialogSummary(int warningDialogSummary) { 291 mWarningDialogSummary = warningDialogSummary; 292 return this; 293 } 294 295 public Builder setEmptyText(int emptyText) { 296 mEmptyText = emptyText; 297 return this; 298 } 299 300 public Config build() { 301 return new Config(mTag, mSetting, mIntentAction, mPermission, mNoun, 302 mWarningDialogTitle, mWarningDialogSummary, mEmptyText); 303 } 304 } 305 } 306 307 } 308