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.CONTROLLED_BY_ADMIN_SUMMARY;
20 
21 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
22 
23 import android.app.admin.DevicePolicyManager;
24 import android.content.Context;
25 import android.content.res.TypedArray;
26 import android.os.Build;
27 import android.os.UserHandle;
28 import android.text.TextUtils;
29 import android.util.AttributeSet;
30 import android.util.TypedValue;
31 import android.widget.TextView;
32 
33 import androidx.annotation.RequiresApi;
34 import androidx.preference.Preference;
35 import androidx.preference.PreferenceViewHolder;
36 
37 import com.android.settingslib.utils.BuildCompatUtils;
38 
39 /**
40  * Helper class for managing settings preferences that can be disabled
41  * by device admins via user restrictions.
42  */
43 public class RestrictedPreferenceHelper {
44     private final Context mContext;
45     private final Preference mPreference;
46     String packageName;
47     int uid;
48 
49     private boolean mDisabledByAdmin;
50     private EnforcedAdmin mEnforcedAdmin;
51     private String mAttrUserRestriction = null;
52     private boolean mDisabledSummary = false;
53 
54     private boolean mDisabledByAppOps;
55 
RestrictedPreferenceHelper(Context context, Preference preference, AttributeSet attrs, String packageName, int uid)56     public RestrictedPreferenceHelper(Context context, Preference preference,
57             AttributeSet attrs, String packageName, int uid) {
58         mContext = context;
59         mPreference = preference;
60         this.packageName = packageName;
61         this.uid = uid;
62 
63         if (attrs != null) {
64             final TypedArray attributes = context.obtainStyledAttributes(attrs,
65                     R.styleable.RestrictedPreference);
66             final TypedValue userRestriction =
67                     attributes.peekValue(R.styleable.RestrictedPreference_userRestriction);
68             CharSequence data = null;
69             if (userRestriction != null && userRestriction.type == TypedValue.TYPE_STRING) {
70                 if (userRestriction.resourceId != 0) {
71                     data = context.getText(userRestriction.resourceId);
72                 } else {
73                     data = userRestriction.string;
74                 }
75             }
76             mAttrUserRestriction = data == null ? null : data.toString();
77             // If the system has set the user restriction, then we shouldn't add the padlock.
78             if (RestrictedLockUtilsInternal.hasBaseUserRestriction(mContext, mAttrUserRestriction,
79                     UserHandle.myUserId())) {
80                 mAttrUserRestriction = null;
81                 return;
82             }
83 
84             final TypedValue useAdminDisabledSummary =
85                     attributes.peekValue(R.styleable.RestrictedPreference_useAdminDisabledSummary);
86             if (useAdminDisabledSummary != null) {
87                 mDisabledSummary =
88                         (useAdminDisabledSummary.type == TypedValue.TYPE_INT_BOOLEAN
89                                 && useAdminDisabledSummary.data != 0);
90             }
91         }
92     }
93 
RestrictedPreferenceHelper(Context context, Preference preference, AttributeSet attrs)94     public RestrictedPreferenceHelper(Context context, Preference preference,
95             AttributeSet attrs) {
96         this(context, preference, attrs, null, android.os.Process.INVALID_UID);
97     }
98 
99     /**
100      * Modify PreferenceViewHolder to add padlock if restriction is disabled.
101      */
onBindViewHolder(PreferenceViewHolder holder)102     public void onBindViewHolder(PreferenceViewHolder holder) {
103         if (mDisabledByAdmin || mDisabledByAppOps) {
104             holder.itemView.setEnabled(true);
105         }
106         if (mDisabledSummary) {
107             final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary);
108             if (summaryView != null) {
109                 final CharSequence disabledText = BuildCompatUtils.isAtLeastT()
110                         ? getDisabledByAdminUpdatableString()
111                         : mContext.getString(R.string.disabled_by_admin_summary_text);
112                 if (mDisabledByAdmin) {
113                     summaryView.setText(disabledText);
114                 } else if (mDisabledByAppOps) {
115                     summaryView.setText(R.string.disabled_by_app_ops_text);
116                 } else if (TextUtils.equals(disabledText, summaryView.getText())) {
117                     // It's previously set to disabled text, clear it.
118                     summaryView.setText(null);
119                 }
120             }
121         }
122     }
123 
124     @RequiresApi(Build.VERSION_CODES.TIRAMISU)
getDisabledByAdminUpdatableString()125     private String getDisabledByAdminUpdatableString() {
126         return mContext.getSystemService(DevicePolicyManager.class).getResources().getString(
127                 CONTROLLED_BY_ADMIN_SUMMARY,
128                 () -> mContext.getString(R.string.disabled_by_admin_summary_text));
129     }
130 
useAdminDisabledSummary(boolean useSummary)131     public void useAdminDisabledSummary(boolean useSummary) {
132         mDisabledSummary = useSummary;
133     }
134 
135     /**
136      * Check if the preference is disabled if so handle the click by informing the user.
137      *
138      * @return true if the method handled the click.
139      */
140     @SuppressWarnings("NewApi")
performClick()141     public boolean performClick() {
142         if (mDisabledByAdmin) {
143             RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, mEnforcedAdmin);
144             return true;
145         }
146         if (mDisabledByAppOps) {
147             RestrictedLockUtilsInternal.sendShowRestrictedSettingDialogIntent(mContext, packageName,
148                     uid);
149             return true;
150         }
151         return false;
152     }
153 
154     /**
155      * Disable / enable if we have been passed the restriction in the xml.
156      */
onAttachedToHierarchy()157     public void onAttachedToHierarchy() {
158         if (mAttrUserRestriction != null) {
159             checkRestrictionAndSetDisabled(mAttrUserRestriction, UserHandle.myUserId());
160         }
161     }
162 
163     /**
164      * Set the user restriction that is used to disable this preference.
165      *
166      * @param userRestriction constant from {@link android.os.UserManager}
167      * @param userId user to check the restriction for.
168      */
checkRestrictionAndSetDisabled(String userRestriction, int userId)169     public void checkRestrictionAndSetDisabled(String userRestriction, int userId) {
170         EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(mContext,
171                 userRestriction, userId);
172         setDisabledByAdmin(admin);
173     }
174 
175     /**
176      * @return EnforcedAdmin if we have been passed the restriction in the xml.
177      */
checkRestrictionEnforced()178     public EnforcedAdmin checkRestrictionEnforced() {
179         if (mAttrUserRestriction == null) {
180             return null;
181         }
182         return RestrictedLockUtilsInternal.checkIfRestrictionEnforced(mContext,
183                 mAttrUserRestriction, UserHandle.myUserId());
184     }
185 
186     /**
187      * Disable this preference based on the enforce admin.
188      *
189      * @param admin details of the admin who enforced the restriction. If it is
190      * {@code null}, then this preference will be enabled. Otherwise, it will be disabled.
191      * Only gray out the preference which is not {@link RestrictedTopLevelPreference}.
192      * @return true if the disabled state was changed.
193      */
setDisabledByAdmin(EnforcedAdmin admin)194     public boolean setDisabledByAdmin(EnforcedAdmin admin) {
195         final boolean disabled = (admin != null ? true : false);
196         mEnforcedAdmin = admin;
197         boolean changed = false;
198         if (mDisabledByAdmin != disabled) {
199             mDisabledByAdmin = disabled;
200             changed = true;
201             updateDisabledState();
202         }
203 
204         return changed;
205     }
206 
setDisabledByAppOps(boolean disabled)207     public boolean setDisabledByAppOps(boolean disabled) {
208         boolean changed = false;
209         if (mDisabledByAppOps != disabled) {
210             mDisabledByAppOps = disabled;
211             changed = true;
212             updateDisabledState();
213         }
214 
215         return changed;
216     }
217 
isDisabledByAdmin()218     public boolean isDisabledByAdmin() {
219         return mDisabledByAdmin;
220     }
221 
isDisabledByAppOps()222     public boolean isDisabledByAppOps() {
223         return mDisabledByAppOps;
224     }
225 
updatePackageDetails(String packageName, int uid)226     public void updatePackageDetails(String packageName, int uid) {
227         this.packageName = packageName;
228         this.uid = uid;
229     }
230 
updateDisabledState()231     private void updateDisabledState() {
232         if (!(mPreference instanceof RestrictedTopLevelPreference)) {
233             mPreference.setEnabled(!(mDisabledByAdmin || mDisabledByAppOps));
234         }
235 
236         if (mPreference instanceof PrimarySwitchPreference) {
237             ((PrimarySwitchPreference) mPreference)
238                     .setSwitchEnabled(!(mDisabledByAdmin || mDisabledByAppOps));
239         }
240     }
241 }
242