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.view.ViewGroup.LayoutParams.MATCH_PARENT; 20 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; 21 22 import static com.android.systemui.qs.dagger.QSFragmentModule.QS_SECURITY_FOOTER_VIEW; 23 24 import android.app.AlertDialog; 25 import android.app.admin.DeviceAdminInfo; 26 import android.app.admin.DevicePolicyEventLogger; 27 import android.content.Context; 28 import android.content.DialogInterface; 29 import android.content.Intent; 30 import android.content.pm.UserInfo; 31 import android.content.res.Configuration; 32 import android.content.res.Resources; 33 import android.graphics.drawable.Drawable; 34 import android.os.Handler; 35 import android.os.Looper; 36 import android.os.Message; 37 import android.os.UserManager; 38 import android.provider.Settings; 39 import android.text.SpannableStringBuilder; 40 import android.text.method.LinkMovementMethod; 41 import android.text.style.ClickableSpan; 42 import android.util.Log; 43 import android.view.LayoutInflater; 44 import android.view.View; 45 import android.view.View.OnClickListener; 46 import android.view.ViewGroup; 47 import android.view.Window; 48 import android.widget.ImageView; 49 import android.widget.TextView; 50 51 import androidx.annotation.VisibleForTesting; 52 53 import com.android.internal.util.FrameworkStatsLog; 54 import com.android.systemui.FontSizeUtils; 55 import com.android.systemui.R; 56 import com.android.systemui.dagger.qualifiers.Background; 57 import com.android.systemui.dagger.qualifiers.Main; 58 import com.android.systemui.plugins.ActivityStarter; 59 import com.android.systemui.qs.dagger.QSScope; 60 import com.android.systemui.settings.UserTracker; 61 import com.android.systemui.statusbar.phone.SystemUIDialog; 62 import com.android.systemui.statusbar.policy.SecurityController; 63 64 import javax.inject.Inject; 65 import javax.inject.Named; 66 67 @QSScope 68 class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListener { 69 protected static final String TAG = "QSSecurityFooter"; 70 protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 71 private static final boolean DEBUG_FORCE_VISIBLE = false; 72 73 private final View mRootView; 74 private final TextView mFooterText; 75 private final ImageView mPrimaryFooterIcon; 76 private final Context mContext; 77 private final Callback mCallback = new Callback(); 78 private final SecurityController mSecurityController; 79 private final ActivityStarter mActivityStarter; 80 private final Handler mMainHandler; 81 private final UserTracker mUserTracker; 82 83 private AlertDialog mDialog; 84 private QSTileHost mHost; 85 protected H mHandler; 86 87 private boolean mIsVisible; 88 private CharSequence mFooterTextContent = null; 89 private int mFooterIconId; 90 private Drawable mPrimaryFooterIconDrawable; 91 92 @Inject QSSecurityFooter(@amedQS_SECURITY_FOOTER_VIEW) View rootView, UserTracker userTracker, @Main Handler mainHandler, ActivityStarter activityStarter, SecurityController securityController, @Background Looper bgLooper)93 QSSecurityFooter(@Named(QS_SECURITY_FOOTER_VIEW) View rootView, 94 UserTracker userTracker, @Main Handler mainHandler, ActivityStarter activityStarter, 95 SecurityController securityController, @Background Looper bgLooper) { 96 mRootView = rootView; 97 mRootView.setOnClickListener(this); 98 mFooterText = mRootView.findViewById(R.id.footer_text); 99 mPrimaryFooterIcon = mRootView.findViewById(R.id.primary_footer_icon); 100 mFooterIconId = R.drawable.ic_info_outline; 101 mContext = rootView.getContext(); 102 mMainHandler = mainHandler; 103 mActivityStarter = activityStarter; 104 mSecurityController = securityController; 105 mHandler = new H(bgLooper); 106 mUserTracker = userTracker; 107 } 108 setHostEnvironment(QSTileHost host)109 public void setHostEnvironment(QSTileHost host) { 110 mHost = host; 111 } 112 setListening(boolean listening)113 public void setListening(boolean listening) { 114 if (listening) { 115 mSecurityController.addCallback(mCallback); 116 refreshState(); 117 } else { 118 mSecurityController.removeCallback(mCallback); 119 } 120 } 121 onConfigurationChanged()122 public void onConfigurationChanged() { 123 FontSizeUtils.updateFontSize(mFooterText, R.dimen.qs_tile_text_size); 124 125 Resources r = mContext.getResources(); 126 127 mFooterText.setMaxLines(r.getInteger(R.integer.qs_security_footer_maxLines)); 128 int padding = r.getDimensionPixelSize(R.dimen.qs_footer_padding); 129 mRootView.setPaddingRelative(padding, padding, padding, padding); 130 131 int bottomMargin = r.getDimensionPixelSize(R.dimen.qs_footers_margin_bottom); 132 ViewGroup.MarginLayoutParams lp = 133 (ViewGroup.MarginLayoutParams) mRootView.getLayoutParams(); 134 lp.bottomMargin = bottomMargin; 135 lp.width = r.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT 136 ? MATCH_PARENT : WRAP_CONTENT; 137 mRootView.setLayoutParams(lp); 138 139 mRootView.setBackground(mContext.getDrawable(R.drawable.qs_security_footer_background)); 140 } 141 getView()142 public View getView() { 143 return mRootView; 144 } 145 hasFooter()146 public boolean hasFooter() { 147 return mRootView.getVisibility() != View.GONE; 148 } 149 150 @Override onClick(View v)151 public void onClick(View v) { 152 if (!hasFooter()) return; 153 mHandler.sendEmptyMessage(H.CLICK); 154 } 155 handleClick()156 private void handleClick() { 157 showDeviceMonitoringDialog(); 158 DevicePolicyEventLogger 159 .createEvent(FrameworkStatsLog.DEVICE_POLICY_EVENT__EVENT_ID__DO_USER_INFO_CLICKED) 160 .write(); 161 } 162 showDeviceMonitoringDialog()163 public void showDeviceMonitoringDialog() { 164 createDialog(); 165 } 166 refreshState()167 public void refreshState() { 168 mHandler.sendEmptyMessage(H.REFRESH_STATE); 169 } 170 handleRefreshState()171 private void handleRefreshState() { 172 final boolean isDeviceManaged = mSecurityController.isDeviceManaged(); 173 final UserInfo currentUser = mUserTracker.getUserInfo(); 174 final boolean isDemoDevice = UserManager.isDeviceInDemoMode(mContext) && currentUser != null 175 && currentUser.isDemo(); 176 final boolean hasWorkProfile = mSecurityController.hasWorkProfile(); 177 final boolean hasCACerts = mSecurityController.hasCACertInCurrentUser(); 178 final boolean hasCACertsInWorkProfile = mSecurityController.hasCACertInWorkProfile(); 179 final boolean isNetworkLoggingEnabled = mSecurityController.isNetworkLoggingEnabled(); 180 final String vpnName = mSecurityController.getPrimaryVpnName(); 181 final String vpnNameWorkProfile = mSecurityController.getWorkProfileVpnName(); 182 final CharSequence organizationName = mSecurityController.getDeviceOwnerOrganizationName(); 183 final CharSequence workProfileOrganizationName = 184 mSecurityController.getWorkProfileOrganizationName(); 185 final boolean isProfileOwnerOfOrganizationOwnedDevice = 186 mSecurityController.isProfileOwnerOfOrganizationOwnedDevice(); 187 final boolean isParentalControlsEnabled = mSecurityController.isParentalControlsEnabled(); 188 final boolean isWorkProfileOn = mSecurityController.isWorkProfileOn(); 189 final boolean hasDisclosableWorkProfilePolicy = hasCACertsInWorkProfile 190 || vpnNameWorkProfile != null || (hasWorkProfile && isNetworkLoggingEnabled); 191 // Update visibility of footer 192 mIsVisible = (isDeviceManaged && !isDemoDevice) 193 || hasCACerts 194 || vpnName != null 195 || isProfileOwnerOfOrganizationOwnedDevice 196 || isParentalControlsEnabled 197 || (hasDisclosableWorkProfilePolicy && isWorkProfileOn); 198 // Update the view to be untappable if the device is an organization-owned device with a 199 // managed profile and there is either: 200 // a) no policy set which requires a privacy disclosure. 201 // b) a specific work policy set but the work profile is turned off. 202 if (mIsVisible && isProfileOwnerOfOrganizationOwnedDevice 203 && (!hasDisclosableWorkProfilePolicy || !isWorkProfileOn)) { 204 mRootView.setClickable(false); 205 mRootView.findViewById(R.id.footer_icon).setVisibility(View.GONE); 206 } else { 207 mRootView.setClickable(true); 208 mRootView.findViewById(R.id.footer_icon).setVisibility(View.VISIBLE); 209 } 210 // Update the string 211 mFooterTextContent = getFooterText(isDeviceManaged, hasWorkProfile, 212 hasCACerts, hasCACertsInWorkProfile, isNetworkLoggingEnabled, vpnName, 213 vpnNameWorkProfile, organizationName, workProfileOrganizationName, 214 isProfileOwnerOfOrganizationOwnedDevice, isParentalControlsEnabled, 215 isWorkProfileOn); 216 // Update the icon 217 int footerIconId = R.drawable.ic_info_outline; 218 if (vpnName != null || vpnNameWorkProfile != null) { 219 if (mSecurityController.isVpnBranded()) { 220 footerIconId = R.drawable.stat_sys_branded_vpn; 221 } else { 222 footerIconId = R.drawable.stat_sys_vpn_ic; 223 } 224 } 225 if (mFooterIconId != footerIconId) { 226 mFooterIconId = footerIconId; 227 } 228 229 // Update the primary icon 230 if (isParentalControlsEnabled) { 231 if (mPrimaryFooterIconDrawable == null) { 232 DeviceAdminInfo info = mSecurityController.getDeviceAdminInfo(); 233 mPrimaryFooterIconDrawable = mSecurityController.getIcon(info); 234 } 235 } else { 236 mPrimaryFooterIconDrawable = null; 237 } 238 mMainHandler.post(mUpdatePrimaryIcon); 239 240 mMainHandler.post(mUpdateDisplayState); 241 } 242 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)243 protected CharSequence getFooterText(boolean isDeviceManaged, boolean hasWorkProfile, 244 boolean hasCACerts, boolean hasCACertsInWorkProfile, boolean isNetworkLoggingEnabled, 245 String vpnName, String vpnNameWorkProfile, CharSequence organizationName, 246 CharSequence workProfileOrganizationName, 247 boolean isProfileOwnerOfOrganizationOwnedDevice, boolean isParentalControlsEnabled, 248 boolean isWorkProfileOn) { 249 if (isParentalControlsEnabled) { 250 return mContext.getString(R.string.quick_settings_disclosure_parental_controls); 251 } 252 if (isDeviceManaged || DEBUG_FORCE_VISIBLE) { 253 if (hasCACerts || hasCACertsInWorkProfile || isNetworkLoggingEnabled) { 254 if (organizationName == null) { 255 return mContext.getString( 256 R.string.quick_settings_disclosure_management_monitoring); 257 } 258 return mContext.getString( 259 R.string.quick_settings_disclosure_named_management_monitoring, 260 organizationName); 261 } 262 if (vpnName != null && vpnNameWorkProfile != null) { 263 if (organizationName == null) { 264 return mContext.getString(R.string.quick_settings_disclosure_management_vpns); 265 } 266 return mContext.getString(R.string.quick_settings_disclosure_named_management_vpns, 267 organizationName); 268 } 269 if (vpnName != null || vpnNameWorkProfile != null) { 270 if (organizationName == null) { 271 return mContext.getString( 272 R.string.quick_settings_disclosure_management_named_vpn, 273 vpnName != null ? vpnName : vpnNameWorkProfile); 274 } 275 return mContext.getString( 276 R.string.quick_settings_disclosure_named_management_named_vpn, 277 organizationName, 278 vpnName != null ? vpnName : vpnNameWorkProfile); 279 } 280 if (organizationName == null) { 281 return mContext.getString(R.string.quick_settings_disclosure_management); 282 } 283 if (isFinancedDevice()) { 284 return mContext.getString( 285 R.string.quick_settings_financed_disclosure_named_management, 286 organizationName); 287 } else { 288 return mContext.getString(R.string.quick_settings_disclosure_named_management, 289 organizationName); 290 } 291 } // end if(isDeviceManaged) 292 if (hasCACertsInWorkProfile && isWorkProfileOn) { 293 if (workProfileOrganizationName == null) { 294 return mContext.getString( 295 R.string.quick_settings_disclosure_managed_profile_monitoring); 296 } 297 return mContext.getString( 298 R.string.quick_settings_disclosure_named_managed_profile_monitoring, 299 workProfileOrganizationName); 300 } 301 if (hasCACerts) { 302 return mContext.getString(R.string.quick_settings_disclosure_monitoring); 303 } 304 if (vpnName != null && vpnNameWorkProfile != null) { 305 return mContext.getString(R.string.quick_settings_disclosure_vpns); 306 } 307 if (vpnNameWorkProfile != null && isWorkProfileOn) { 308 return mContext.getString(R.string.quick_settings_disclosure_managed_profile_named_vpn, 309 vpnNameWorkProfile); 310 } 311 if (vpnName != null) { 312 if (hasWorkProfile) { 313 return mContext.getString( 314 R.string.quick_settings_disclosure_personal_profile_named_vpn, 315 vpnName); 316 } 317 return mContext.getString(R.string.quick_settings_disclosure_named_vpn, 318 vpnName); 319 } 320 if (hasWorkProfile && isNetworkLoggingEnabled && isWorkProfileOn) { 321 return mContext.getString( 322 R.string.quick_settings_disclosure_managed_profile_network_activity); 323 } 324 if (isProfileOwnerOfOrganizationOwnedDevice) { 325 if (workProfileOrganizationName == null) { 326 return mContext.getString(R.string.quick_settings_disclosure_management); 327 } 328 return mContext.getString(R.string.quick_settings_disclosure_named_management, 329 workProfileOrganizationName); 330 } 331 return null; 332 } 333 334 @Override onClick(DialogInterface dialog, int which)335 public void onClick(DialogInterface dialog, int which) { 336 if (which == DialogInterface.BUTTON_NEGATIVE) { 337 final Intent intent = new Intent(Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS); 338 mDialog.dismiss(); 339 // This dismisses the shade on opening the activity 340 mActivityStarter.postStartActivityDismissingKeyguard(intent, 0); 341 } 342 } 343 createDialog()344 private void createDialog() { 345 mDialog = new SystemUIDialog(mContext, 0); // Use mContext theme 346 mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); 347 mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this); 348 mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getNegativeButton(), this); 349 350 mDialog.setView(createDialogView()); 351 352 mDialog.show(); 353 mDialog.getWindow().setLayout(MATCH_PARENT, 354 ViewGroup.LayoutParams.WRAP_CONTENT); 355 } 356 357 @VisibleForTesting createDialogView()358 View createDialogView() { 359 if (mSecurityController.isParentalControlsEnabled()) { 360 return createParentalControlsDialogView(); 361 } 362 return createOrganizationDialogView(); 363 } 364 createOrganizationDialogView()365 private View createOrganizationDialogView() { 366 final boolean isDeviceManaged = mSecurityController.isDeviceManaged(); 367 final boolean hasWorkProfile = mSecurityController.hasWorkProfile(); 368 final CharSequence deviceOwnerOrganization = 369 mSecurityController.getDeviceOwnerOrganizationName(); 370 final boolean hasCACerts = mSecurityController.hasCACertInCurrentUser(); 371 final boolean hasCACertsInWorkProfile = mSecurityController.hasCACertInWorkProfile(); 372 final boolean isNetworkLoggingEnabled = mSecurityController.isNetworkLoggingEnabled(); 373 final String vpnName = mSecurityController.getPrimaryVpnName(); 374 final String vpnNameWorkProfile = mSecurityController.getWorkProfileVpnName(); 375 376 View dialogView = LayoutInflater.from(mContext) 377 .inflate(R.layout.quick_settings_footer_dialog, null, false); 378 379 // device management section 380 TextView deviceManagementSubtitle = 381 dialogView.findViewById(R.id.device_management_subtitle); 382 deviceManagementSubtitle.setText(getManagementTitle(deviceOwnerOrganization)); 383 384 CharSequence managementMessage = getManagementMessage(isDeviceManaged, 385 deviceOwnerOrganization); 386 if (managementMessage == null) { 387 dialogView.findViewById(R.id.device_management_disclosures).setVisibility(View.GONE); 388 } else { 389 dialogView.findViewById(R.id.device_management_disclosures).setVisibility(View.VISIBLE); 390 TextView deviceManagementWarning = 391 (TextView) dialogView.findViewById(R.id.device_management_warning); 392 deviceManagementWarning.setText(managementMessage); 393 mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getSettingsButton(), this); 394 } 395 396 // ca certificate section 397 CharSequence caCertsMessage = getCaCertsMessage(isDeviceManaged, hasCACerts, 398 hasCACertsInWorkProfile); 399 if (caCertsMessage == null) { 400 dialogView.findViewById(R.id.ca_certs_disclosures).setVisibility(View.GONE); 401 } else { 402 dialogView.findViewById(R.id.ca_certs_disclosures).setVisibility(View.VISIBLE); 403 TextView caCertsWarning = (TextView) dialogView.findViewById(R.id.ca_certs_warning); 404 caCertsWarning.setText(caCertsMessage); 405 // Make "Open trusted credentials"-link clickable 406 caCertsWarning.setMovementMethod(new LinkMovementMethod()); 407 } 408 409 // network logging section 410 CharSequence networkLoggingMessage = getNetworkLoggingMessage(isDeviceManaged, 411 isNetworkLoggingEnabled); 412 if (networkLoggingMessage == null) { 413 dialogView.findViewById(R.id.network_logging_disclosures).setVisibility(View.GONE); 414 } else { 415 dialogView.findViewById(R.id.network_logging_disclosures).setVisibility(View.VISIBLE); 416 TextView networkLoggingWarning = 417 (TextView) dialogView.findViewById(R.id.network_logging_warning); 418 networkLoggingWarning.setText(networkLoggingMessage); 419 } 420 421 // vpn section 422 CharSequence vpnMessage = getVpnMessage(isDeviceManaged, hasWorkProfile, vpnName, 423 vpnNameWorkProfile); 424 if (vpnMessage == null) { 425 dialogView.findViewById(R.id.vpn_disclosures).setVisibility(View.GONE); 426 } else { 427 dialogView.findViewById(R.id.vpn_disclosures).setVisibility(View.VISIBLE); 428 TextView vpnWarning = (TextView) dialogView.findViewById(R.id.vpn_warning); 429 vpnWarning.setText(vpnMessage); 430 // Make "Open VPN Settings"-link clickable 431 vpnWarning.setMovementMethod(new LinkMovementMethod()); 432 } 433 434 // Note: if a new section is added, should update configSubtitleVisibility to include 435 // the handling of the subtitle 436 configSubtitleVisibility(managementMessage != null, 437 caCertsMessage != null, 438 networkLoggingMessage != null, 439 vpnMessage != null, 440 dialogView); 441 442 return dialogView; 443 } 444 createParentalControlsDialogView()445 private View createParentalControlsDialogView() { 446 View dialogView = LayoutInflater.from(mContext) 447 .inflate(R.layout.quick_settings_footer_dialog_parental_controls, null, false); 448 449 DeviceAdminInfo info = mSecurityController.getDeviceAdminInfo(); 450 Drawable icon = mSecurityController.getIcon(info); 451 if (icon != null) { 452 ImageView imageView = (ImageView) dialogView.findViewById(R.id.parental_controls_icon); 453 imageView.setImageDrawable(icon); 454 } 455 456 TextView parentalControlsTitle = 457 (TextView) dialogView.findViewById(R.id.parental_controls_title); 458 parentalControlsTitle.setText(mSecurityController.getLabel(info)); 459 460 return dialogView; 461 } 462 configSubtitleVisibility(boolean showDeviceManagement, boolean showCaCerts, boolean showNetworkLogging, boolean showVpn, View dialogView)463 protected void configSubtitleVisibility(boolean showDeviceManagement, boolean showCaCerts, 464 boolean showNetworkLogging, boolean showVpn, View dialogView) { 465 // Device Management title should always been shown 466 // When there is a Device Management message, all subtitles should be shown 467 if (showDeviceManagement) { 468 return; 469 } 470 // Hide the subtitle if there is only 1 message shown 471 int mSectionCountExcludingDeviceMgt = 0; 472 if (showCaCerts) { mSectionCountExcludingDeviceMgt++; } 473 if (showNetworkLogging) { mSectionCountExcludingDeviceMgt++; } 474 if (showVpn) { mSectionCountExcludingDeviceMgt++; } 475 476 // No work needed if there is no sections or more than 1 section 477 if (mSectionCountExcludingDeviceMgt != 1) { 478 return; 479 } 480 if (showCaCerts) { 481 dialogView.findViewById(R.id.ca_certs_subtitle).setVisibility(View.GONE); 482 } 483 if (showNetworkLogging) { 484 dialogView.findViewById(R.id.network_logging_subtitle).setVisibility(View.GONE); 485 } 486 if (showVpn) { 487 dialogView.findViewById(R.id.vpn_subtitle).setVisibility(View.GONE); 488 } 489 } 490 491 @VisibleForTesting getSettingsButton()492 String getSettingsButton() { 493 return mContext.getString(R.string.monitoring_button_view_policies); 494 } 495 getPositiveButton()496 private String getPositiveButton() { 497 return mContext.getString(R.string.ok); 498 } 499 getNegativeButton()500 private String getNegativeButton() { 501 if (mSecurityController.isParentalControlsEnabled()) { 502 return mContext.getString(R.string.monitoring_button_view_controls); 503 } 504 return null; 505 } 506 getManagementMessage(boolean isDeviceManaged, CharSequence organizationName)507 protected CharSequence getManagementMessage(boolean isDeviceManaged, 508 CharSequence organizationName) { 509 if (!isDeviceManaged) { 510 return null; 511 } 512 if (organizationName != null) { 513 if (isFinancedDevice()) { 514 return mContext.getString(R.string.monitoring_financed_description_named_management, 515 organizationName, organizationName); 516 } else { 517 return mContext.getString( 518 R.string.monitoring_description_named_management, organizationName); 519 } 520 } 521 return mContext.getString(R.string.monitoring_description_management); 522 } 523 getCaCertsMessage(boolean isDeviceManaged, boolean hasCACerts, boolean hasCACertsInWorkProfile)524 protected CharSequence getCaCertsMessage(boolean isDeviceManaged, boolean hasCACerts, 525 boolean hasCACertsInWorkProfile) { 526 if (!(hasCACerts || hasCACertsInWorkProfile)) return null; 527 if (isDeviceManaged) { 528 return mContext.getString(R.string.monitoring_description_management_ca_certificate); 529 } 530 if (hasCACertsInWorkProfile) { 531 return mContext.getString( 532 R.string.monitoring_description_managed_profile_ca_certificate); 533 } 534 return mContext.getString(R.string.monitoring_description_ca_certificate); 535 } 536 getNetworkLoggingMessage(boolean isDeviceManaged, boolean isNetworkLoggingEnabled)537 protected CharSequence getNetworkLoggingMessage(boolean isDeviceManaged, 538 boolean isNetworkLoggingEnabled) { 539 if (!isNetworkLoggingEnabled) return null; 540 if (isDeviceManaged) { 541 return mContext.getString(R.string.monitoring_description_management_network_logging); 542 } else { 543 return mContext.getString( 544 R.string.monitoring_description_managed_profile_network_logging); 545 } 546 } 547 getVpnMessage(boolean isDeviceManaged, boolean hasWorkProfile, String vpnName, String vpnNameWorkProfile)548 protected CharSequence getVpnMessage(boolean isDeviceManaged, boolean hasWorkProfile, 549 String vpnName, String vpnNameWorkProfile) { 550 if (vpnName == null && vpnNameWorkProfile == null) return null; 551 final SpannableStringBuilder message = new SpannableStringBuilder(); 552 if (isDeviceManaged) { 553 if (vpnName != null && vpnNameWorkProfile != null) { 554 message.append(mContext.getString(R.string.monitoring_description_two_named_vpns, 555 vpnName, vpnNameWorkProfile)); 556 } else { 557 message.append(mContext.getString(R.string.monitoring_description_named_vpn, 558 vpnName != null ? vpnName : vpnNameWorkProfile)); 559 } 560 } else { 561 if (vpnName != null && vpnNameWorkProfile != null) { 562 message.append(mContext.getString(R.string.monitoring_description_two_named_vpns, 563 vpnName, vpnNameWorkProfile)); 564 } else if (vpnNameWorkProfile != null) { 565 message.append(mContext.getString( 566 R.string.monitoring_description_managed_profile_named_vpn, 567 vpnNameWorkProfile)); 568 } else if (hasWorkProfile) { 569 message.append(mContext.getString( 570 R.string.monitoring_description_personal_profile_named_vpn, vpnName)); 571 } else { 572 message.append(mContext.getString(R.string.monitoring_description_named_vpn, 573 vpnName)); 574 } 575 } 576 message.append(mContext.getString(R.string.monitoring_description_vpn_settings_separator)); 577 message.append(mContext.getString(R.string.monitoring_description_vpn_settings), 578 new VpnSpan(), 0); 579 return message; 580 } 581 582 @VisibleForTesting getManagementTitle(CharSequence deviceOwnerOrganization)583 CharSequence getManagementTitle(CharSequence deviceOwnerOrganization) { 584 if (deviceOwnerOrganization != null && isFinancedDevice()) { 585 return mContext.getString(R.string.monitoring_title_financed_device, 586 deviceOwnerOrganization); 587 } else { 588 return mContext.getString(R.string.monitoring_title_device_owned); 589 } 590 } 591 isFinancedDevice()592 private boolean isFinancedDevice() { 593 return mSecurityController.isDeviceManaged() 594 && mSecurityController.getDeviceOwnerType( 595 mSecurityController.getDeviceOwnerComponentOnAnyUser()) 596 == DEVICE_OWNER_TYPE_FINANCED; 597 } 598 599 private final Runnable mUpdatePrimaryIcon = new Runnable() { 600 @Override 601 public void run() { 602 if (mPrimaryFooterIconDrawable != null) { 603 mPrimaryFooterIcon.setImageDrawable(mPrimaryFooterIconDrawable); 604 } else { 605 mPrimaryFooterIcon.setImageResource(mFooterIconId); 606 } 607 } 608 }; 609 610 private final Runnable mUpdateDisplayState = new Runnable() { 611 @Override 612 public void run() { 613 if (mFooterTextContent != null) { 614 mFooterText.setText(mFooterTextContent); 615 } 616 mRootView.setVisibility(mIsVisible || DEBUG_FORCE_VISIBLE ? View.VISIBLE : View.GONE); 617 } 618 }; 619 620 private class Callback implements SecurityController.SecurityControllerCallback { 621 @Override onStateChanged()622 public void onStateChanged() { 623 refreshState(); 624 } 625 } 626 627 private class H extends Handler { 628 private static final int CLICK = 0; 629 private static final int REFRESH_STATE = 1; 630 H(Looper looper)631 private H(Looper looper) { 632 super(looper); 633 } 634 635 @Override handleMessage(Message msg)636 public void handleMessage(Message msg) { 637 String name = null; 638 try { 639 if (msg.what == REFRESH_STATE) { 640 name = "handleRefreshState"; 641 handleRefreshState(); 642 } else if (msg.what == CLICK) { 643 name = "handleClick"; 644 handleClick(); 645 } 646 } catch (Throwable t) { 647 final String error = "Error in " + name; 648 Log.w(TAG, error, t); 649 mHost.warn(error, t); 650 } 651 } 652 } 653 654 protected class VpnSpan extends ClickableSpan { 655 @Override onClick(View widget)656 public void onClick(View widget) { 657 final Intent intent = new Intent(Settings.ACTION_VPN_SETTINGS); 658 mDialog.dismiss(); 659 // This dismisses the shade on opening the activity 660 mActivityStarter.postStartActivityDismissingKeyguard(intent, 0); 661 } 662 663 // for testing, to compare two CharSequences containing VpnSpans 664 @Override equals(Object object)665 public boolean equals(Object object) { 666 return object instanceof VpnSpan; 667 } 668 669 @Override hashCode()670 public int hashCode() { 671 return 314159257; // prime 672 } 673 } 674 } 675