• 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 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