• 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 package com.android.systemui.qs;
17 
18 import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
19 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_MANAGEMENT;
20 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_MANAGEMENT_CA_CERT;
21 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_MANAGEMENT_NAMED_VPN;
22 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_MANAGEMENT_NETWORK;
23 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_MANAGEMENT_TITLE;
24 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_MANAGEMENT_TWO_NAMED_VPN;
25 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_MONITORING_CA_CERT_SUBTITLE;
26 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_MONITORING_NETWORK_SUBTITLE;
27 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_MONITORING_VPN_SUBTITLE;
28 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_NAMED_MANAGEMENT;
29 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_PERSONAL_PROFILE_NAMED_VPN;
30 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_VIEW_POLICIES;
31 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_WORK_PROFILE_CA_CERT;
32 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_WORK_PROFILE_NAMED_VPN;
33 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_WORK_PROFILE_NETWORK;
34 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_MANAGEMENT;
35 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_MANAGEMENT_MONITORING;
36 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_MANAGEMENT_MULTIPLE_VPNS;
37 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_MANAGEMENT_NAMED_VPN;
38 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_NAMED_MANAGEMENT;
39 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_NAMED_MANAGEMENT_MONITORING;
40 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_NAMED_MANAGEMENT_MULTIPLE_VPNS;
41 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_NAMED_MANAGEMENT_NAMED_VPN;
42 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_NAMED_WORK_PROFILE_MONITORING;
43 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_PERSONAL_PROFILE_NAMED_VPN;
44 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_WORK_PROFILE_MONITORING;
45 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_WORK_PROFILE_NAMED_VPN;
46 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_WORK_PROFILE_NETWORK;
47 
48 import android.app.AlertDialog;
49 import android.app.Dialog;
50 import android.app.admin.DeviceAdminInfo;
51 import android.app.admin.DevicePolicyManager;
52 import android.content.Context;
53 import android.content.DialogInterface;
54 import android.content.Intent;
55 import android.content.pm.UserInfo;
56 import android.graphics.drawable.Drawable;
57 import android.os.Handler;
58 import android.os.Looper;
59 import android.os.UserManager;
60 import android.provider.DeviceConfig;
61 import android.provider.Settings;
62 import android.text.SpannableStringBuilder;
63 import android.text.method.LinkMovementMethod;
64 import android.text.style.ClickableSpan;
65 import android.util.Log;
66 import android.view.LayoutInflater;
67 import android.view.View;
68 import android.view.Window;
69 import android.widget.ImageView;
70 import android.widget.TextView;
71 
72 import androidx.annotation.Nullable;
73 import androidx.annotation.VisibleForTesting;
74 
75 import com.android.internal.jank.InteractionJankMonitor;
76 import com.android.systemui.animation.DialogCuj;
77 import com.android.systemui.animation.DialogTransitionAnimator;
78 import com.android.systemui.animation.Expandable;
79 import com.android.systemui.common.shared.model.ContentDescription;
80 import com.android.systemui.common.shared.model.Icon;
81 import com.android.systemui.dagger.SysUISingleton;
82 import com.android.systemui.dagger.qualifiers.Application;
83 import com.android.systemui.dagger.qualifiers.Background;
84 import com.android.systemui.dagger.qualifiers.Main;
85 import com.android.systemui.plugins.ActivityStarter;
86 import com.android.systemui.qs.footer.domain.model.SecurityButtonConfig;
87 import com.android.systemui.res.R;
88 import com.android.systemui.security.data.model.SecurityModel;
89 import com.android.systemui.settings.UserTracker;
90 import com.android.systemui.shade.ShadeDisplayAware;
91 import com.android.systemui.statusbar.phone.SystemUIDialog;
92 import com.android.systemui.statusbar.policy.SecurityController;
93 import com.android.systemui.supervision.shared.DeprecateDpmSupervisionApis;
94 
95 import java.util.concurrent.atomic.AtomicBoolean;
96 import java.util.function.Supplier;
97 
98 import javax.inject.Inject;
99 
100 /** Helper class for the configuration of the QS security footer button. */
101 @SysUISingleton
102 public class QSSecurityFooterUtils implements DialogInterface.OnClickListener {
103     protected static final String TAG = "QSSecurityFooterUtils";
104     protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
105     private static final boolean DEBUG_FORCE_VISIBLE = false;
106 
107     private static final String INTERACTION_JANK_TAG = "managed_device_info";
108 
109     @Application private Context mContext;
110     private final DevicePolicyManager mDpm;
111 
112     private final SecurityController mSecurityController;
113     private final ActivityStarter mActivityStarter;
114     private final Handler mMainHandler;
115     private final UserTracker mUserTracker;
116     private final DialogTransitionAnimator mDialogTransitionAnimator;
117 
118     private final AtomicBoolean mShouldUseSettingsButton = new AtomicBoolean(false);
119 
120     protected Handler mBgHandler;
121     private AlertDialog mDialog;
122 
123     private Supplier<String> mManagementTitleSupplier = () ->
124             mContext == null ? null : mContext.getString(R.string.monitoring_title_device_owned);
125 
126     private Supplier<String> mManagementMessageSupplier = () ->
127             mContext == null ? null : mContext.getString(
128                     R.string.quick_settings_disclosure_management);
129 
130     private Supplier<String> mManagementMonitoringStringSupplier = () ->
131             mContext == null ? null : mContext.getString(
132                     R.string.quick_settings_disclosure_management_monitoring);
133 
134     private Supplier<String> mManagementMultipleVpnStringSupplier = () ->
135             mContext == null ? null : mContext.getString(
136                     R.string.quick_settings_disclosure_management_vpns);
137 
138     private Supplier<String> mWorkProfileMonitoringStringSupplier = () ->
139             mContext == null ? null : mContext.getString(
140                     R.string.quick_settings_disclosure_managed_profile_monitoring);
141 
142     private Supplier<String> mWorkProfileNetworkStringSupplier = () ->
143             mContext == null ? null : mContext.getString(
144                     R.string.quick_settings_disclosure_managed_profile_network_activity);
145 
146     private Supplier<String> mMonitoringSubtitleCaCertStringSupplier = () ->
147             mContext == null ? null : mContext.getString(
148                     R.string.monitoring_subtitle_ca_certificate);
149 
150     private Supplier<String> mMonitoringSubtitleNetworkStringSupplier = () ->
151             mContext == null ? null : mContext.getString(
152                     R.string.monitoring_subtitle_network_logging);
153 
154     private Supplier<String> mMonitoringSubtitleVpnStringSupplier = () ->
155             mContext == null ? null : mContext.getString(R.string.monitoring_subtitle_vpn);
156 
157     private Supplier<String> mViewPoliciesButtonStringSupplier = () ->
158             mContext == null ? null : mContext.getString(R.string.monitoring_button_view_policies);
159 
160     private Supplier<String> mManagementDialogStringSupplier = () ->
161             mContext == null ? null : mContext.getString(
162                     R.string.monitoring_description_management);
163 
164     private Supplier<String> mManagementDialogCaCertStringSupplier = () ->
165             mContext == null ? null : mContext.getString(
166                     R.string.monitoring_description_management_ca_certificate);
167 
168     private Supplier<String> mWorkProfileDialogCaCertStringSupplier = () ->
169             mContext == null ? null : mContext.getString(
170                     R.string.monitoring_description_managed_profile_ca_certificate);
171 
172     private Supplier<String> mManagementDialogNetworkStringSupplier = () ->
173             mContext == null ? null : mContext.getString(
174                     R.string.monitoring_description_management_network_logging);
175 
176     private Supplier<String> mWorkProfileDialogNetworkStringSupplier = () ->
177             mContext == null ? null : mContext.getString(
178                     R.string.monitoring_description_managed_profile_network_logging);
179 
180     @Inject
QSSecurityFooterUtils( @hadeDisplayAware Context context, DevicePolicyManager devicePolicyManager, UserTracker userTracker, @Main Handler mainHandler, ActivityStarter activityStarter, SecurityController securityController, @Background Looper bgLooper, DialogTransitionAnimator dialogTransitionAnimator)181     QSSecurityFooterUtils(
182             @ShadeDisplayAware Context context, DevicePolicyManager devicePolicyManager,
183             UserTracker userTracker, @Main Handler mainHandler, ActivityStarter activityStarter,
184             SecurityController securityController, @Background Looper bgLooper,
185             DialogTransitionAnimator dialogTransitionAnimator) {
186         mContext = context;
187         mDpm = devicePolicyManager;
188         mUserTracker = userTracker;
189         mMainHandler = mainHandler;
190         mActivityStarter = activityStarter;
191         mSecurityController = securityController;
192         mBgHandler = new Handler(bgLooper);
193         mDialogTransitionAnimator = dialogTransitionAnimator;
194     }
195 
196     /** Show the device monitoring dialog. */
showDeviceMonitoringDialog(Context quickSettingsContext, @Nullable Expandable expandable)197     public void showDeviceMonitoringDialog(Context quickSettingsContext,
198             @Nullable Expandable expandable) {
199         createDialog(quickSettingsContext, expandable);
200     }
201 
202     /**
203      * Return the {@link SecurityButtonConfig} of the security button, or {@code null} if no
204      * security button should be shown.
205      */
206     @Nullable
getButtonConfig(SecurityModel securityModel)207     public SecurityButtonConfig getButtonConfig(SecurityModel securityModel) {
208         final boolean isDeviceManaged = securityModel.isDeviceManaged();
209         final UserInfo currentUser = mUserTracker.getUserInfo();
210         final boolean isDemoDevice = UserManager.isDeviceInDemoMode(mContext) && currentUser != null
211                 && currentUser.isDemo();
212         final boolean hasWorkProfile = securityModel.getHasWorkProfile();
213         final boolean hasCACerts = securityModel.getHasCACertInCurrentUser();
214         final boolean hasCACertsInWorkProfile = securityModel.getHasCACertInWorkProfile();
215         final boolean isNetworkLoggingEnabled = securityModel.isNetworkLoggingEnabled();
216         final String vpnName = securityModel.getPrimaryVpnName();
217         final String vpnNameWorkProfile = securityModel.getWorkProfileVpnName();
218         final CharSequence organizationName = securityModel.getDeviceOwnerOrganizationName();
219         final CharSequence workProfileOrganizationName =
220                 securityModel.getWorkProfileOrganizationName();
221         final boolean isProfileOwnerOfOrganizationOwnedDevice =
222                 securityModel.isProfileOwnerOfOrganizationOwnedDevice();
223         final boolean isParentalControlsEnabled = securityModel.isParentalControlsEnabled();
224         final boolean isWorkProfileOn = securityModel.isWorkProfileOn();
225         final boolean hasDisclosableWorkProfilePolicy = hasCACertsInWorkProfile
226                 || vpnNameWorkProfile != null || (hasWorkProfile && isNetworkLoggingEnabled);
227         // Update visibility of footer
228         boolean isVisible = (isDeviceManaged && !isDemoDevice)
229                 || hasCACerts
230                 || vpnName != null
231                 || isProfileOwnerOfOrganizationOwnedDevice
232                 || isParentalControlsEnabled
233                 || (hasDisclosableWorkProfilePolicy && isWorkProfileOn);
234         if (!isVisible && !DEBUG_FORCE_VISIBLE) {
235             return null;
236         }
237 
238         // Update the view to be untappable if the device is an organization-owned device with a
239         // managed profile and there is either:
240         // a) no policy set which requires a privacy disclosure.
241         // b) a specific work policy set but the work profile is turned off.
242         boolean isClickable = !(isProfileOwnerOfOrganizationOwnedDevice
243                 && (!hasDisclosableWorkProfilePolicy || !isWorkProfileOn));
244 
245         String text = getFooterText(isDeviceManaged, hasWorkProfile,
246                 hasCACerts, hasCACertsInWorkProfile, isNetworkLoggingEnabled, vpnName,
247                 vpnNameWorkProfile, organizationName, workProfileOrganizationName,
248                 isProfileOwnerOfOrganizationOwnedDevice, isParentalControlsEnabled,
249                 isWorkProfileOn).toString();
250 
251         Icon icon;
252         ContentDescription contentDescription = null;
253         if (isParentalControlsEnabled && securityModel.getDeviceAdminIcon() != null) {
254             icon = new Icon.Loaded(securityModel.getDeviceAdminIcon(), contentDescription);
255         } else if (vpnName != null || vpnNameWorkProfile != null) {
256             if (securityModel.isVpnBranded()) {
257                 icon = new Icon.Resource(R.drawable.stat_sys_branded_vpn, contentDescription);
258             } else {
259                 icon = new Icon.Resource(R.drawable.stat_sys_vpn_ic, contentDescription);
260             }
261         } else {
262             icon = new Icon.Resource(R.drawable.ic_info_outline, contentDescription);
263         }
264 
265         return new SecurityButtonConfig(icon, text, isClickable);
266     }
267 
268     @Nullable
getFooterText(boolean isDeviceManaged, boolean hasWorkProfile, boolean hasCACerts, boolean hasCACertsInWorkProfile, boolean isNetworkLoggingEnabled, String vpnName, String vpnNameWorkProfile, CharSequence organizationName, CharSequence workProfileOrganizationName, boolean isProfileOwnerOfOrganizationOwnedDevice, boolean isParentalControlsEnabled, boolean isWorkProfileOn)269     protected CharSequence getFooterText(boolean isDeviceManaged, boolean hasWorkProfile,
270             boolean hasCACerts, boolean hasCACertsInWorkProfile, boolean isNetworkLoggingEnabled,
271             String vpnName, String vpnNameWorkProfile, CharSequence organizationName,
272             CharSequence workProfileOrganizationName,
273             boolean isProfileOwnerOfOrganizationOwnedDevice, boolean isParentalControlsEnabled,
274             boolean isWorkProfileOn) {
275         if (isParentalControlsEnabled) {
276             return mContext.getString(R.string.quick_settings_disclosure_parental_controls);
277         }
278         if (isDeviceManaged || DEBUG_FORCE_VISIBLE) {
279             return getManagedDeviceFooterText(hasCACerts, hasCACertsInWorkProfile,
280                     isNetworkLoggingEnabled, vpnName, vpnNameWorkProfile, organizationName);
281         }
282         return getManagedAndPersonalProfileFooterText(hasWorkProfile, hasCACerts,
283                 hasCACertsInWorkProfile, isNetworkLoggingEnabled, vpnName, vpnNameWorkProfile,
284                 workProfileOrganizationName, isProfileOwnerOfOrganizationOwnedDevice,
285                 isWorkProfileOn);
286     }
287 
getManagedDeviceFooterText( boolean hasCACerts, boolean hasCACertsInWorkProfile, boolean isNetworkLoggingEnabled, String vpnName, String vpnNameWorkProfile, CharSequence organizationName)288     private String getManagedDeviceFooterText(
289             boolean hasCACerts, boolean hasCACertsInWorkProfile, boolean isNetworkLoggingEnabled,
290             String vpnName, String vpnNameWorkProfile, CharSequence organizationName) {
291         if (hasCACerts || hasCACertsInWorkProfile || isNetworkLoggingEnabled) {
292             return getManagedDeviceMonitoringText(organizationName);
293         }
294         if (vpnName != null || vpnNameWorkProfile != null) {
295             return getManagedDeviceVpnText(vpnName, vpnNameWorkProfile, organizationName);
296         }
297         return getMangedDeviceGeneralText(organizationName);
298     }
299 
getManagedDeviceMonitoringText(CharSequence organizationName)300     private String getManagedDeviceMonitoringText(CharSequence organizationName) {
301         if (organizationName == null) {
302             return mDpm.getResources().getString(
303                     QS_MSG_MANAGEMENT_MONITORING, mManagementMonitoringStringSupplier);
304         }
305         return mDpm.getResources().getString(
306                 QS_MSG_NAMED_MANAGEMENT_MONITORING,
307                 () -> mContext.getString(
308                         R.string.quick_settings_disclosure_named_management_monitoring,
309                         organizationName),
310                 organizationName);
311     }
312 
getManagedDeviceVpnText( String vpnName, String vpnNameWorkProfile, CharSequence organizationName)313     private String getManagedDeviceVpnText(
314             String vpnName, String vpnNameWorkProfile, CharSequence organizationName) {
315         if (vpnName != null && vpnNameWorkProfile != null) {
316             if (organizationName == null) {
317                 return mDpm.getResources().getString(
318                         QS_MSG_MANAGEMENT_MULTIPLE_VPNS, mManagementMultipleVpnStringSupplier);
319             }
320             return mDpm.getResources().getString(
321                     QS_MSG_NAMED_MANAGEMENT_MULTIPLE_VPNS,
322                     () -> mContext.getString(
323                             R.string.quick_settings_disclosure_named_management_vpns,
324                             organizationName),
325                     organizationName);
326         }
327         String name = vpnName != null ? vpnName : vpnNameWorkProfile;
328         if (organizationName == null) {
329             return mDpm.getResources().getString(
330                     QS_MSG_MANAGEMENT_NAMED_VPN,
331                     () -> mContext.getString(
332                             R.string.quick_settings_disclosure_management_named_vpn,
333                             name),
334                     name);
335         }
336         return mDpm.getResources().getString(
337                 QS_MSG_NAMED_MANAGEMENT_NAMED_VPN,
338                 () -> mContext.getString(
339                         R.string.quick_settings_disclosure_named_management_named_vpn,
340                         organizationName,
341                         name),
342                 organizationName,
343                 name);
344     }
345 
getMangedDeviceGeneralText(CharSequence organizationName)346     private String getMangedDeviceGeneralText(CharSequence organizationName) {
347         if (organizationName == null) {
348             return mDpm.getResources().getString(QS_MSG_MANAGEMENT, mManagementMessageSupplier);
349         }
350         if (isFinancedDevice()) {
351             return mContext.getString(
352                     R.string.quick_settings_financed_disclosure_named_management,
353                     organizationName);
354         } else {
355             return mDpm.getResources().getString(
356                     QS_MSG_NAMED_MANAGEMENT,
357                     () -> mContext.getString(
358                             R.string.quick_settings_disclosure_named_management,
359                             organizationName),
360                     organizationName);
361         }
362     }
363 
getManagedAndPersonalProfileFooterText(boolean hasWorkProfile, boolean hasCACerts, boolean hasCACertsInWorkProfile, boolean isNetworkLoggingEnabled, String vpnName, String vpnNameWorkProfile, CharSequence workProfileOrganizationName, boolean isProfileOwnerOfOrganizationOwnedDevice, boolean isWorkProfileOn)364     private String getManagedAndPersonalProfileFooterText(boolean hasWorkProfile,
365             boolean hasCACerts, boolean hasCACertsInWorkProfile, boolean isNetworkLoggingEnabled,
366             String vpnName, String vpnNameWorkProfile, CharSequence workProfileOrganizationName,
367             boolean isProfileOwnerOfOrganizationOwnedDevice, boolean isWorkProfileOn) {
368         if (hasCACerts || (hasCACertsInWorkProfile && isWorkProfileOn)) {
369             return getMonitoringText(
370                     hasCACerts, hasCACertsInWorkProfile, workProfileOrganizationName,
371                     isWorkProfileOn);
372         }
373         if (vpnName != null || (vpnNameWorkProfile != null && isWorkProfileOn)) {
374             return getVpnText(hasWorkProfile, vpnName, vpnNameWorkProfile, isWorkProfileOn);
375         }
376         if (hasWorkProfile && isNetworkLoggingEnabled && isWorkProfileOn) {
377             return getManagedProfileNetworkActivityText();
378         }
379         if (isProfileOwnerOfOrganizationOwnedDevice) {
380             return getMangedDeviceGeneralText(workProfileOrganizationName);
381         }
382         return null;
383     }
384 
getMonitoringText(boolean hasCACerts, boolean hasCACertsInWorkProfile, CharSequence workProfileOrganizationName, boolean isWorkProfileOn)385     private String getMonitoringText(boolean hasCACerts, boolean hasCACertsInWorkProfile,
386             CharSequence workProfileOrganizationName, boolean isWorkProfileOn) {
387         if (hasCACertsInWorkProfile && isWorkProfileOn) {
388             if (workProfileOrganizationName == null) {
389                 return mDpm.getResources().getString(
390                         QS_MSG_WORK_PROFILE_MONITORING, mWorkProfileMonitoringStringSupplier);
391             }
392             return mDpm.getResources().getString(
393                     QS_MSG_NAMED_WORK_PROFILE_MONITORING,
394                     () -> mContext.getString(
395                             R.string.quick_settings_disclosure_named_managed_profile_monitoring,
396                             workProfileOrganizationName),
397                     workProfileOrganizationName);
398         }
399         if (hasCACerts) {
400             return mContext.getString(R.string.quick_settings_disclosure_monitoring);
401         }
402         return null;
403     }
404 
getVpnText(boolean hasWorkProfile, String vpnName, String vpnNameWorkProfile, boolean isWorkProfileOn)405     private String getVpnText(boolean hasWorkProfile, String vpnName, String vpnNameWorkProfile,
406             boolean isWorkProfileOn) {
407         if (vpnName != null && vpnNameWorkProfile != null) {
408             return mContext.getString(R.string.quick_settings_disclosure_vpns);
409         }
410         if (vpnNameWorkProfile != null && isWorkProfileOn) {
411             return mDpm.getResources().getString(
412                     QS_MSG_WORK_PROFILE_NAMED_VPN,
413                     () -> mContext.getString(
414                             R.string.quick_settings_disclosure_managed_profile_named_vpn,
415                             vpnNameWorkProfile),
416                     vpnNameWorkProfile);
417         }
418         if (vpnName != null) {
419             if (hasWorkProfile) {
420                 return mDpm.getResources().getString(
421                         QS_MSG_PERSONAL_PROFILE_NAMED_VPN,
422                         () -> mContext.getString(
423                                 R.string.quick_settings_disclosure_personal_profile_named_vpn,
424                                 vpnName),
425                         vpnName);
426             }
427             return mContext.getString(R.string.quick_settings_disclosure_named_vpn,
428                     vpnName);
429         }
430         return null;
431     }
432 
getManagedProfileNetworkActivityText()433     private String getManagedProfileNetworkActivityText() {
434         return mDpm.getResources().getString(
435                 QS_MSG_WORK_PROFILE_NETWORK, mWorkProfileNetworkStringSupplier);
436     }
437 
438     @Override
onClick(DialogInterface dialog, int which)439     public void onClick(DialogInterface dialog, int which) {
440         if (which == DialogInterface.BUTTON_NEGATIVE) {
441             final Intent intent = new Intent(Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS);
442             dialog.dismiss();
443             // This dismisses the shade on opening the activity
444             mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
445         }
446     }
447 
createDialog(Context quickSettingsContext, @Nullable Expandable expandable)448     private void createDialog(Context quickSettingsContext, @Nullable Expandable expandable) {
449         mShouldUseSettingsButton.set(false);
450         mBgHandler.post(() -> {
451             String settingsButtonText = getSettingsButton();
452             final View dialogView = createDialogView(quickSettingsContext);
453             mMainHandler.post(() -> {
454                 mDialog = new SystemUIDialog(quickSettingsContext, 0);
455                 mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
456                 mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this);
457                 mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, mShouldUseSettingsButton.get()
458                         ? settingsButtonText : getNegativeButton(), this);
459 
460                 mDialog.setView(dialogView);
461                 DialogTransitionAnimator.Controller controller =
462                         expandable != null ? expandable.dialogTransitionController(new DialogCuj(
463                                 InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, INTERACTION_JANK_TAG))
464                                 : null;
465                 if (controller != null) {
466                     mDialogTransitionAnimator.show(mDialog, controller);
467                 } else {
468                     mDialog.show();
469                 }
470             });
471         });
472     }
473 
474     @VisibleForTesting
getDialog()475     Dialog getDialog() {
476         return mDialog;
477     }
478 
479     @VisibleForTesting
createDialogView(Context quickSettingsContext)480     View createDialogView(Context quickSettingsContext) {
481         if (mSecurityController.isParentalControlsEnabled()) {
482             return createParentalControlsDialogView(quickSettingsContext);
483         }
484         return createOrganizationDialogView(quickSettingsContext);
485     }
486 
createOrganizationDialogView(Context quickSettingsContext)487     private View createOrganizationDialogView(Context quickSettingsContext) {
488         final boolean isDeviceManaged = mSecurityController.isDeviceManaged();
489         final boolean hasWorkProfile = mSecurityController.hasWorkProfile();
490         final CharSequence deviceOwnerOrganization =
491                 mSecurityController.getDeviceOwnerOrganizationName();
492         final boolean hasCACerts = mSecurityController.hasCACertInCurrentUser();
493         final boolean hasCACertsInWorkProfile = mSecurityController.hasCACertInWorkProfile();
494         final boolean isNetworkLoggingEnabled = mSecurityController.isNetworkLoggingEnabled();
495         final String vpnName = mSecurityController.getPrimaryVpnName();
496         final String vpnNameWorkProfile = mSecurityController.getWorkProfileVpnName();
497 
498         View dialogView = LayoutInflater.from(quickSettingsContext)
499                 .inflate(R.layout.quick_settings_footer_dialog, null, false);
500 
501         // device management section
502         TextView deviceManagementSubtitle =
503                 dialogView.findViewById(R.id.device_management_subtitle);
504         deviceManagementSubtitle.setText(getManagementTitle(deviceOwnerOrganization));
505 
506         CharSequence managementMessage = getManagementMessage(isDeviceManaged,
507                 deviceOwnerOrganization);
508         if (managementMessage == null) {
509             dialogView.findViewById(R.id.device_management_disclosures).setVisibility(View.GONE);
510         } else {
511             dialogView.findViewById(R.id.device_management_disclosures).setVisibility(View.VISIBLE);
512             TextView deviceManagementWarning =
513                     (TextView) dialogView.findViewById(R.id.device_management_warning);
514             deviceManagementWarning.setText(managementMessage);
515             mShouldUseSettingsButton.set(true);
516         }
517 
518         // ca certificate section
519         CharSequence caCertsMessage = getCaCertsMessage(isDeviceManaged, hasCACerts,
520                 hasCACertsInWorkProfile);
521         if (caCertsMessage == null) {
522             dialogView.findViewById(R.id.ca_certs_disclosures).setVisibility(View.GONE);
523         } else {
524             dialogView.findViewById(R.id.ca_certs_disclosures).setVisibility(View.VISIBLE);
525             TextView caCertsWarning = (TextView) dialogView.findViewById(R.id.ca_certs_warning);
526             caCertsWarning.setText(caCertsMessage);
527             // Make "Open trusted credentials"-link clickable
528             caCertsWarning.setMovementMethod(new LinkMovementMethod());
529 
530             TextView caCertsSubtitle = (TextView) dialogView.findViewById(R.id.ca_certs_subtitle);
531             String caCertsSubtitleMessage = mDpm.getResources().getString(
532                     QS_DIALOG_MONITORING_CA_CERT_SUBTITLE, mMonitoringSubtitleCaCertStringSupplier);
533             caCertsSubtitle.setText(caCertsSubtitleMessage);
534 
535         }
536 
537         // network logging section
538         CharSequence networkLoggingMessage = getNetworkLoggingMessage(isDeviceManaged,
539                 isNetworkLoggingEnabled);
540         if (networkLoggingMessage == null) {
541             dialogView.findViewById(R.id.network_logging_disclosures).setVisibility(View.GONE);
542         } else {
543             dialogView.findViewById(R.id.network_logging_disclosures).setVisibility(View.VISIBLE);
544             TextView networkLoggingWarning =
545                     (TextView) dialogView.findViewById(R.id.network_logging_warning);
546             networkLoggingWarning.setText(networkLoggingMessage);
547 
548             TextView networkLoggingSubtitle = (TextView) dialogView.findViewById(
549                     R.id.network_logging_subtitle);
550             String networkLoggingSubtitleMessage = mDpm.getResources().getString(
551                     QS_DIALOG_MONITORING_NETWORK_SUBTITLE,
552                     mMonitoringSubtitleNetworkStringSupplier);
553             networkLoggingSubtitle.setText(networkLoggingSubtitleMessage);
554         }
555 
556         // vpn section
557         CharSequence vpnMessage = getVpnMessage(isDeviceManaged, hasWorkProfile, vpnName,
558                 vpnNameWorkProfile);
559         if (vpnMessage == null) {
560             dialogView.findViewById(R.id.vpn_disclosures).setVisibility(View.GONE);
561         } else {
562             dialogView.findViewById(R.id.vpn_disclosures).setVisibility(View.VISIBLE);
563             TextView vpnWarning = (TextView) dialogView.findViewById(R.id.vpn_warning);
564             vpnWarning.setText(vpnMessage);
565             // Make "Open VPN Settings"-link clickable
566             vpnWarning.setMovementMethod(new LinkMovementMethod());
567 
568             TextView vpnSubtitle = (TextView) dialogView.findViewById(R.id.vpn_subtitle);
569             String vpnSubtitleMessage = mDpm.getResources().getString(
570                     QS_DIALOG_MONITORING_VPN_SUBTITLE, mMonitoringSubtitleVpnStringSupplier);
571             vpnSubtitle.setText(vpnSubtitleMessage);
572         }
573 
574         // Note: if a new section is added, should update configSubtitleVisibility to include
575         // the handling of the subtitle
576         configSubtitleVisibility(managementMessage != null,
577                 caCertsMessage != null,
578                 networkLoggingMessage != null,
579                 vpnMessage != null,
580                 dialogView);
581 
582         return dialogView;
583     }
584 
createParentalControlsDialogView(Context quickSettingsContext)585     private View createParentalControlsDialogView(Context quickSettingsContext) {
586         View dialogView = LayoutInflater.from(quickSettingsContext)
587                 .inflate(R.layout.quick_settings_footer_dialog_parental_controls, null, false);
588 
589         Drawable icon;
590         CharSequence label;
591         if (DeprecateDpmSupervisionApis.isEnabled()) {
592             icon = mSecurityController.getIcon();
593             label = mSecurityController.getLabel();
594         } else {
595             DeviceAdminInfo info = mSecurityController.getDeviceAdminInfo();
596             icon = mSecurityController.getIcon(info);
597             label = mSecurityController.getLabel(info);
598         }
599 
600         if (icon != null) {
601             ImageView imageView = (ImageView) dialogView.findViewById(R.id.parental_controls_icon);
602             imageView.setImageDrawable(icon);
603         }
604 
605         TextView parentalControlsTitle =
606                 (TextView) dialogView.findViewById(R.id.parental_controls_title);
607         parentalControlsTitle.setText(label);
608 
609         return dialogView;
610     }
611 
configSubtitleVisibility(boolean showDeviceManagement, boolean showCaCerts, boolean showNetworkLogging, boolean showVpn, View dialogView)612     protected void configSubtitleVisibility(boolean showDeviceManagement, boolean showCaCerts,
613             boolean showNetworkLogging, boolean showVpn, View dialogView) {
614         // Device Management title should always been shown
615         // When there is a Device Management message, all subtitles should be shown
616         if (showDeviceManagement) {
617             return;
618         }
619         // Hide the subtitle if there is only 1 message shown
620         int mSectionCountExcludingDeviceMgt = 0;
621         if (showCaCerts) {
622             mSectionCountExcludingDeviceMgt++;
623         }
624         if (showNetworkLogging) {
625             mSectionCountExcludingDeviceMgt++;
626         }
627         if (showVpn) {
628             mSectionCountExcludingDeviceMgt++;
629         }
630 
631         // No work needed if there is no sections or more than 1 section
632         if (mSectionCountExcludingDeviceMgt != 1) {
633             return;
634         }
635         if (showCaCerts) {
636             dialogView.findViewById(R.id.ca_certs_subtitle).setVisibility(View.GONE);
637         }
638         if (showNetworkLogging) {
639             dialogView.findViewById(R.id.network_logging_subtitle).setVisibility(View.GONE);
640         }
641         if (showVpn) {
642             dialogView.findViewById(R.id.vpn_subtitle).setVisibility(View.GONE);
643         }
644     }
645 
646     // This should not be called on the main thread to avoid making an IPC.
647     @VisibleForTesting
getSettingsButton()648     String getSettingsButton() {
649         return mDpm.getResources().getString(
650                 QS_DIALOG_VIEW_POLICIES, mViewPoliciesButtonStringSupplier);
651     }
652 
getPositiveButton()653     private String getPositiveButton() {
654         return mContext.getString(R.string.ok);
655     }
656 
657     @Nullable
getNegativeButton()658     private String getNegativeButton() {
659         if (mSecurityController.isParentalControlsEnabled()) {
660             return mContext.getString(R.string.monitoring_button_view_controls);
661         }
662         return null;
663     }
664 
665     @Nullable
getManagementMessage(boolean isDeviceManaged, CharSequence organizationName)666     protected CharSequence getManagementMessage(boolean isDeviceManaged,
667             CharSequence organizationName) {
668         if (!isDeviceManaged) {
669             return null;
670         }
671         if (organizationName != null) {
672             if (isFinancedDevice()) {
673                 return mContext.getString(R.string.monitoring_financed_description_named_management,
674                         organizationName, organizationName);
675             } else {
676                 return mDpm.getResources().getString(
677                         QS_DIALOG_NAMED_MANAGEMENT,
678                         () -> mContext.getString(
679                                 R.string.monitoring_description_named_management,
680                                 organizationName),
681                         organizationName);
682             }
683         }
684         return mDpm.getResources().getString(QS_DIALOG_MANAGEMENT, mManagementDialogStringSupplier);
685     }
686 
687     @Nullable
getCaCertsMessage(boolean isDeviceManaged, boolean hasCACerts, boolean hasCACertsInWorkProfile)688     protected CharSequence getCaCertsMessage(boolean isDeviceManaged, boolean hasCACerts,
689             boolean hasCACertsInWorkProfile) {
690         if (!(hasCACerts || hasCACertsInWorkProfile)) return null;
691         if (isDeviceManaged) {
692             return mDpm.getResources().getString(
693                     QS_DIALOG_MANAGEMENT_CA_CERT, mManagementDialogCaCertStringSupplier);
694         }
695         if (hasCACertsInWorkProfile) {
696             return mDpm.getResources().getString(
697                     QS_DIALOG_WORK_PROFILE_CA_CERT, mWorkProfileDialogCaCertStringSupplier);
698         }
699         return mContext.getString(R.string.monitoring_description_ca_certificate);
700     }
701 
702     @Nullable
getNetworkLoggingMessage(boolean isDeviceManaged, boolean isNetworkLoggingEnabled)703     protected CharSequence getNetworkLoggingMessage(boolean isDeviceManaged,
704             boolean isNetworkLoggingEnabled) {
705         if (!isNetworkLoggingEnabled) return null;
706         if (isDeviceManaged) {
707             return mDpm.getResources().getString(
708                     QS_DIALOG_MANAGEMENT_NETWORK, mManagementDialogNetworkStringSupplier);
709         } else {
710             return mDpm.getResources().getString(
711                     QS_DIALOG_WORK_PROFILE_NETWORK, mWorkProfileDialogNetworkStringSupplier);
712         }
713     }
714 
715     @Nullable
getVpnMessage(boolean isDeviceManaged, boolean hasWorkProfile, String vpnName, String vpnNameWorkProfile)716     protected CharSequence getVpnMessage(boolean isDeviceManaged, boolean hasWorkProfile,
717             String vpnName, String vpnNameWorkProfile) {
718         if (vpnName == null && vpnNameWorkProfile == null) return null;
719         final SpannableStringBuilder message = new SpannableStringBuilder();
720         if (isDeviceManaged) {
721             if (vpnName != null && vpnNameWorkProfile != null) {
722                 String namedVpns = mDpm.getResources().getString(
723                         QS_DIALOG_MANAGEMENT_TWO_NAMED_VPN,
724                         () -> mContext.getString(
725                                 R.string.monitoring_description_two_named_vpns,
726                                 vpnName, vpnNameWorkProfile),
727                         vpnName, vpnNameWorkProfile);
728                 message.append(namedVpns);
729             } else {
730                 String name = vpnName != null ? vpnName : vpnNameWorkProfile;
731                 String namedVp = mDpm.getResources().getString(
732                         QS_DIALOG_MANAGEMENT_NAMED_VPN,
733                         () -> mContext.getString(
734                                 R.string.monitoring_description_managed_device_named_vpn, name),
735                         name);
736                 message.append(namedVp);
737             }
738         } else {
739             if (vpnName != null && vpnNameWorkProfile != null) {
740                 String namedVpns = mDpm.getResources().getString(
741                         QS_DIALOG_MANAGEMENT_TWO_NAMED_VPN,
742                         () -> mContext.getString(
743                                 R.string.monitoring_description_two_named_vpns,
744                                 vpnName, vpnNameWorkProfile),
745                         vpnName, vpnNameWorkProfile);
746                 message.append(namedVpns);
747             } else if (vpnNameWorkProfile != null) {
748                 String namedVpn = mDpm.getResources().getString(
749                         QS_DIALOG_WORK_PROFILE_NAMED_VPN,
750                         () -> mContext.getString(
751                                 R.string.monitoring_description_managed_profile_named_vpn,
752                                 vpnNameWorkProfile),
753                         vpnNameWorkProfile);
754                 message.append(namedVpn);
755             } else if (hasWorkProfile) {
756                 String namedVpn = mDpm.getResources().getString(
757                         QS_DIALOG_PERSONAL_PROFILE_NAMED_VPN,
758                         () -> mContext.getString(
759                                 R.string.monitoring_description_personal_profile_named_vpn,
760                                 vpnName),
761                         vpnName);
762                 message.append(namedVpn);
763             } else {
764                 message.append(mContext.getString(R.string.monitoring_description_named_vpn,
765                         vpnName));
766             }
767         }
768         message.append(mContext.getString(R.string.monitoring_description_vpn_settings_separator));
769         message.append(mContext.getString(R.string.monitoring_description_vpn_settings),
770                 new VpnSpan(), 0);
771         return message;
772     }
773 
774     @VisibleForTesting
getManagementTitle(CharSequence deviceOwnerOrganization)775     CharSequence getManagementTitle(CharSequence deviceOwnerOrganization) {
776         if (deviceOwnerOrganization != null && isFinancedDevice()) {
777             return mContext.getString(R.string.monitoring_title_financed_device,
778                     deviceOwnerOrganization);
779         } else {
780             return mDpm.getResources().getString(
781                     QS_DIALOG_MANAGEMENT_TITLE,
782                     mManagementTitleSupplier);
783         }
784     }
785 
786     protected class VpnSpan extends ClickableSpan {
787         @Override
onClick(View widget)788         public void onClick(View widget) {
789             final Intent intent = new Intent(Settings.ACTION_VPN_SETTINGS);
790             mDialog.dismiss();
791             // This dismisses the shade on opening the activity
792             mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
793         }
794 
795         // for testing, to compare two CharSequences containing VpnSpans
796         @Override
equals(Object object)797         public boolean equals(Object object) {
798             return object instanceof VpnSpan;
799         }
800 
801         @Override
hashCode()802         public int hashCode() {
803             return 314159257; // prime
804         }
805     }
806 
807     // TODO(b/259908270): remove and inline direct call to mSecurityController.isFinancedDevice()
isFinancedDevice()808     private boolean isFinancedDevice() {
809         if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
810                 DevicePolicyManager.ADD_ISFINANCED_DEVICE_FLAG,
811                 DevicePolicyManager.ADD_ISFINANCED_FEVICE_DEFAULT)) {
812             return mSecurityController.isFinancedDevice();
813         } else {
814             return mSecurityController.isDeviceManaged()
815                     && mSecurityController.getDeviceOwnerType(
816                     mSecurityController.getDeviceOwnerComponentOnAnyUser())
817                     == DEVICE_OWNER_TYPE_FINANCED;
818         }
819     }
820 }
821