• 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.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