• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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