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