• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.settingslib;
18 
19 import static android.app.admin.DevicePolicyResources.Strings.Settings.DISABLED_BY_ADMIN_SWITCH_SUMMARY;
20 import static android.app.admin.DevicePolicyResources.Strings.Settings.ENABLED_BY_ADMIN_SWITCH_SUMMARY;
21 
22 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
23 
24 import android.app.AppOpsManager;
25 import android.app.admin.DevicePolicyManager;
26 import android.content.Context;
27 import android.content.res.TypedArray;
28 import android.os.Process;
29 import android.os.UserHandle;
30 import android.util.AttributeSet;
31 import android.util.TypedValue;
32 import android.view.View;
33 import android.widget.ImageView;
34 import android.widget.LinearLayout;
35 import android.widget.TextView;
36 
37 import androidx.annotation.NonNull;
38 import androidx.annotation.VisibleForTesting;
39 import androidx.preference.PreferenceManager;
40 import androidx.preference.PreferenceViewHolder;
41 import androidx.preference.SwitchPreferenceCompat;
42 
43 import com.android.settingslib.utils.BuildCompatUtils;
44 
45 /**
46  * Version of SwitchPreferenceCompat that can be disabled by a device admin
47  * using a user restriction.
48  */
49 public class RestrictedSwitchPreference extends SwitchPreferenceCompat {
50     RestrictedPreferenceHelper mHelper;
51     AppOpsManager mAppOpsManager;
52     boolean mUseAdditionalSummary = false;
53     CharSequence mRestrictedSwitchSummary;
54     private int mIconSize;
55 
RestrictedSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)56     public RestrictedSwitchPreference(Context context, AttributeSet attrs,
57             int defStyleAttr, int defStyleRes) {
58         super(context, attrs, defStyleAttr, defStyleRes);
59         mHelper = new RestrictedPreferenceHelper(context, this, attrs);
60         if (attrs != null) {
61             final TypedArray attributes = context.obtainStyledAttributes(attrs,
62                     R.styleable.RestrictedSwitchPreference);
63             final TypedValue useAdditionalSummary = attributes.peekValue(
64                     R.styleable.RestrictedSwitchPreference_useAdditionalSummary);
65             if (useAdditionalSummary != null) {
66                 mUseAdditionalSummary =
67                         (useAdditionalSummary.type == TypedValue.TYPE_INT_BOOLEAN
68                                 && useAdditionalSummary.data != 0);
69             }
70 
71             final TypedValue restrictedSwitchSummary = attributes.peekValue(
72                     R.styleable.RestrictedSwitchPreference_restrictedSwitchSummary);
73             attributes.recycle();
74             if (restrictedSwitchSummary != null
75                     && restrictedSwitchSummary.type == TypedValue.TYPE_STRING) {
76                 if (restrictedSwitchSummary.resourceId != 0) {
77                     mRestrictedSwitchSummary =
78                             context.getText(restrictedSwitchSummary.resourceId);
79                 } else {
80                     mRestrictedSwitchSummary = restrictedSwitchSummary.string;
81                 }
82             }
83         }
84         if (mUseAdditionalSummary) {
85             setLayoutResource(R.layout.restricted_switch_preference);
86             useAdminDisabledSummary(false);
87         }
88     }
89 
RestrictedSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr)90     public RestrictedSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) {
91         this(context, attrs, defStyleAttr, 0);
92     }
93 
RestrictedSwitchPreference(Context context, AttributeSet attrs)94     public RestrictedSwitchPreference(Context context, AttributeSet attrs) {
95         this(context, attrs, androidx.preference.R.attr.switchPreferenceCompatStyle);
96     }
97 
RestrictedSwitchPreference(Context context)98     public RestrictedSwitchPreference(Context context) {
99         this(context, null);
100     }
101 
102     @VisibleForTesting
setAppOps(AppOpsManager appOps)103     public void setAppOps(AppOpsManager appOps) {
104         mAppOpsManager = appOps;
105     }
106 
setIconSize(int iconSize)107     public void setIconSize(int iconSize) {
108         mIconSize = iconSize;
109     }
110 
111     @Override
onBindViewHolder(PreferenceViewHolder holder)112     public void onBindViewHolder(PreferenceViewHolder holder) {
113         super.onBindViewHolder(holder);
114         final View switchView = holder.findViewById(androidx.preference.R.id.switchWidget);
115         if (switchView != null) {
116             final View rootView = switchView.getRootView();
117             rootView.setFilterTouchesWhenObscured(true);
118         }
119 
120         mHelper.onBindViewHolder(holder);
121 
122         CharSequence switchSummary;
123         if (mRestrictedSwitchSummary == null) {
124             switchSummary = isChecked()
125                     ? getUpdatableEnterpriseString(
126                             getContext(), ENABLED_BY_ADMIN_SWITCH_SUMMARY,
127                             com.android.settingslib.widget.restricted.R.string.enabled_by_admin)
128                     : getUpdatableEnterpriseString(
129                             getContext(), DISABLED_BY_ADMIN_SWITCH_SUMMARY,
130                             com.android.settingslib.widget.restricted.R.string.disabled_by_admin);
131         } else {
132             switchSummary = mRestrictedSwitchSummary;
133         }
134 
135         final ImageView icon = holder.itemView.findViewById(android.R.id.icon);
136 
137         if (mIconSize > 0) {
138             icon.setLayoutParams(new LinearLayout.LayoutParams(mIconSize, mIconSize));
139         }
140 
141         if (mUseAdditionalSummary) {
142             final TextView additionalSummaryView = (TextView) holder.findViewById(
143                     R.id.additional_summary);
144             if (additionalSummaryView != null) {
145                 if (isDisabledByAdmin()) {
146                     additionalSummaryView.setText(switchSummary);
147                     additionalSummaryView.setVisibility(View.VISIBLE);
148                 } else {
149                     additionalSummaryView.setVisibility(View.GONE);
150                 }
151             }
152         } else {
153             final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary);
154             if (summaryView != null) {
155                 if (isDisabledByAdmin()) {
156                     summaryView.setText(switchSummary);
157                     summaryView.setVisibility(View.VISIBLE);
158                 }
159                 // No need to change the visibility to GONE in the else case here since Preference
160                 // class would have already changed it if there is no summary to display.
161             }
162         }
163     }
164 
getUpdatableEnterpriseString( Context context, String updatableStringId, int resId)165     private static String getUpdatableEnterpriseString(
166             Context context, String updatableStringId, int resId) {
167         if (!BuildCompatUtils.isAtLeastT()) {
168             return context.getString(resId);
169         }
170         return context.getSystemService(DevicePolicyManager.class).getResources().getString(
171                 updatableStringId,
172                 () -> context.getString(resId));
173     }
174 
175     @Override
performClick()176     public void performClick() {
177         if (!mHelper.performClick()) {
178             super.performClick();
179         }
180     }
181 
useAdminDisabledSummary(boolean useSummary)182     public void useAdminDisabledSummary(boolean useSummary) {
183         mHelper.useAdminDisabledSummary(useSummary);
184     }
185 
186     @Override
onAttachedToHierarchy(PreferenceManager preferenceManager)187     protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
188         mHelper.onAttachedToHierarchy();
189         super.onAttachedToHierarchy(preferenceManager);
190     }
191 
checkRestrictionAndSetDisabled(String userRestriction)192     public void checkRestrictionAndSetDisabled(String userRestriction) {
193         mHelper.checkRestrictionAndSetDisabled(userRestriction, UserHandle.myUserId());
194     }
195 
checkRestrictionAndSetDisabled(String userRestriction, int userId)196     public void checkRestrictionAndSetDisabled(String userRestriction, int userId) {
197         mHelper.checkRestrictionAndSetDisabled(userRestriction, userId);
198     }
199 
200     /**
201      * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
202      * package. Marks the preference as disabled if so.
203      * @param settingIdentifier The key identifying the setting
204      * @param packageName the package to check the settingIdentifier for
205      */
checkEcmRestrictionAndSetDisabled(@onNull String settingIdentifier, @NonNull String packageName)206     public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
207             @NonNull  String packageName) {
208         mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName);
209     }
210 
211     @Override
setEnabled(boolean enabled)212     public void setEnabled(boolean enabled) {
213         boolean changed = false;
214         if (enabled && isDisabledByAdmin()) {
215             mHelper.setDisabledByAdmin(null);
216             changed = true;
217         }
218         if (enabled && isDisabledByEcm()) {
219             mHelper.setDisabledByEcm(null);
220             changed = true;
221         }
222         if (!changed) {
223             super.setEnabled(enabled);
224         }
225     }
226 
setDisabledByAdmin(EnforcedAdmin admin)227     public void setDisabledByAdmin(EnforcedAdmin admin) {
228         if (mHelper.setDisabledByAdmin(admin)) {
229             notifyChanged();
230         }
231     }
232 
isDisabledByAdmin()233     public boolean isDisabledByAdmin() {
234         return mHelper.isDisabledByAdmin();
235     }
236 
isDisabledByEcm()237     public boolean isDisabledByEcm() {
238         return mHelper.isDisabledByEcm();
239     }
240 
241     /**
242      * @deprecated TODO(b/308921175): This will be deleted with the
243      * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new
244      * code.
245      */
246     @Deprecated
setDisabledByAppOps(boolean disabled)247     private void setDisabledByAppOps(boolean disabled) {
248         if (mHelper.setDisabledByAppOps(disabled)) {
249             notifyChanged();
250         }
251     }
252 
253     /**
254      * @deprecated TODO(b/308921175): This will be deleted with the
255      * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new
256      * code.
257      */
258     @Deprecated
getUid()259     public int getUid() {
260         return mHelper != null ? mHelper.uid : Process.INVALID_UID;
261     }
262 
263     /**
264      * @deprecated TODO(b/308921175): This will be deleted with the
265      * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new
266      * code.
267      */
268     @Deprecated
getPackageName()269     public String getPackageName() {
270         return mHelper != null ? mHelper.packageName : null;
271     }
272 
273     /**
274      * Updates enabled state based on associated package
275      *
276      * @deprecated TODO(b/308921175): This will be deleted with the
277      * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new
278      * code.
279      */
280     @Deprecated
updateState( @onNull String packageName, int uid, boolean isEnableAllowed, boolean isEnabled)281     public void updateState(
282             @NonNull String packageName, int uid, boolean isEnableAllowed, boolean isEnabled) {
283         mHelper.updatePackageDetails(packageName, uid);
284         if (mAppOpsManager == null) {
285             mAppOpsManager = getContext().getSystemService(AppOpsManager.class);
286         }
287         final int mode = mAppOpsManager.noteOpNoThrow(
288                 AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
289                 uid, packageName);
290         final boolean ecmEnabled = getContext().getResources().getBoolean(
291                 com.android.internal.R.bool.config_enhancedConfirmationModeEnabled);
292         final boolean appOpsAllowed = !ecmEnabled || mode == AppOpsManager.MODE_ALLOWED
293                 || mode == AppOpsManager.MODE_DEFAULT;
294         if (!isEnableAllowed && !isEnabled) {
295             setEnabled(false);
296         } else if (isEnabled) {
297             setEnabled(true);
298         } else if (appOpsAllowed && isDisabledByEcm()) {
299             setEnabled(true);
300         } else if (!appOpsAllowed){
301             setDisabledByAppOps(true);
302         }
303     }
304 }
305