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