• 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 android.app.AlertDialog;
19 import android.content.Context;
20 import android.content.DialogInterface;
21 import android.content.Intent;
22 import android.os.Handler;
23 import android.os.Looper;
24 import android.os.Message;
25 import android.provider.Settings;
26 import android.text.SpannableStringBuilder;
27 import android.text.method.LinkMovementMethod;
28 import android.text.style.ClickableSpan;
29 import android.util.Log;
30 import android.view.LayoutInflater;
31 import android.view.View;
32 import android.view.ViewGroup;
33 import android.view.View.OnClickListener;
34 import android.view.Window;
35 import android.widget.ImageView;
36 import android.widget.TextView;
37 
38 import com.android.systemui.Dependency;
39 import com.android.systemui.FontSizeUtils;
40 import com.android.systemui.R;
41 import com.android.systemui.plugins.ActivityStarter;
42 import com.android.systemui.statusbar.phone.SystemUIDialog;
43 import com.android.systemui.statusbar.policy.SecurityController;
44 
45 import static android.provider.Settings.ACTION_VPN_SETTINGS;
46 
47 public class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListener {
48     protected static final String TAG = "QSSecurityFooter";
49     protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
50 
51     private final View mRootView;
52     private final TextView mFooterText;
53     private final ImageView mFooterIcon;
54     private final Context mContext;
55     private final Callback mCallback = new Callback();
56     private final SecurityController mSecurityController;
57     private final ActivityStarter mActivityStarter;
58     private final Handler mMainHandler;
59     private final View mDivider;
60 
61     private AlertDialog mDialog;
62     private QSTileHost mHost;
63     protected H mHandler;
64 
65     private boolean mIsVisible;
66     private CharSequence mFooterTextContent = null;
67     private int mFooterTextId;
68     private int mFooterIconId;
69 
QSSecurityFooter(QSPanel qsPanel, Context context)70     public QSSecurityFooter(QSPanel qsPanel, Context context) {
71         mRootView = LayoutInflater.from(context)
72                 .inflate(R.layout.quick_settings_footer, qsPanel, false);
73         mRootView.setOnClickListener(this);
74         mFooterText = (TextView) mRootView.findViewById(R.id.footer_text);
75         mFooterIcon = (ImageView) mRootView.findViewById(R.id.footer_icon);
76         mFooterIconId = R.drawable.ic_info_outline;
77         mContext = context;
78         mMainHandler = new Handler(Looper.getMainLooper());
79         mActivityStarter = Dependency.get(ActivityStarter.class);
80         mSecurityController = Dependency.get(SecurityController.class);
81         mHandler = new H(Dependency.get(Dependency.BG_LOOPER));
82         mDivider = qsPanel == null ? null : qsPanel.getDivider();
83     }
84 
setHostEnvironment(QSTileHost host)85     public void setHostEnvironment(QSTileHost host) {
86         mHost = host;
87     }
88 
setListening(boolean listening)89     public void setListening(boolean listening) {
90         if (listening) {
91             mSecurityController.addCallback(mCallback);
92         } else {
93             mSecurityController.removeCallback(mCallback);
94         }
95     }
96 
onConfigurationChanged()97     public void onConfigurationChanged() {
98         FontSizeUtils.updateFontSize(mFooterText, R.dimen.qs_tile_text_size);
99     }
100 
getView()101     public View getView() {
102         return mRootView;
103     }
104 
hasFooter()105     public boolean hasFooter() {
106         return mRootView.getVisibility() != View.GONE;
107     }
108 
109     @Override
onClick(View v)110     public void onClick(View v) {
111         mHandler.sendEmptyMessage(H.CLICK);
112     }
113 
handleClick()114     private void handleClick() {
115         showDeviceMonitoringDialog();
116     }
117 
showDeviceMonitoringDialog()118     public void showDeviceMonitoringDialog() {
119         mHost.collapsePanels();
120         // TODO: Delay dialog creation until after panels are collapsed.
121         createDialog();
122     }
123 
refreshState()124     public void refreshState() {
125         mHandler.sendEmptyMessage(H.REFRESH_STATE);
126     }
127 
handleRefreshState()128     private void handleRefreshState() {
129         final boolean isDeviceManaged = mSecurityController.isDeviceManaged();
130         final boolean hasWorkProfile = mSecurityController.hasWorkProfile();
131         final boolean hasCACerts = mSecurityController.hasCACertInCurrentUser();
132         final boolean hasCACertsInWorkProfile = mSecurityController.hasCACertInWorkProfile();
133         final boolean isNetworkLoggingEnabled = mSecurityController.isNetworkLoggingEnabled();
134         final String vpnName = mSecurityController.getPrimaryVpnName();
135         final String vpnNameWorkProfile = mSecurityController.getWorkProfileVpnName();
136         final CharSequence organizationName = mSecurityController.getDeviceOwnerOrganizationName();
137         final CharSequence workProfileName = mSecurityController.getWorkProfileOrganizationName();
138         // Update visibility of footer
139         mIsVisible = isDeviceManaged || hasCACerts || hasCACertsInWorkProfile ||
140             vpnName != null || vpnNameWorkProfile != null;
141         // Update the string
142         mFooterTextContent = getFooterText(isDeviceManaged, hasWorkProfile,
143                 hasCACerts, hasCACertsInWorkProfile, isNetworkLoggingEnabled, vpnName,
144                 vpnNameWorkProfile, organizationName, workProfileName);
145         // Update the icon
146         int footerIconId = vpnName != null || vpnNameWorkProfile != null
147                 ? R.drawable.ic_qs_vpn
148                 : R.drawable.ic_info_outline;
149         if (mFooterIconId != footerIconId) {
150             mFooterIconId = footerIconId;
151             mMainHandler.post(mUpdateIcon);
152         }
153         mMainHandler.post(mUpdateDisplayState);
154     }
155 
getFooterText(boolean isDeviceManaged, boolean hasWorkProfile, boolean hasCACerts, boolean hasCACertsInWorkProfile, boolean isNetworkLoggingEnabled, String vpnName, String vpnNameWorkProfile, CharSequence organizationName, CharSequence workProfileName)156     protected CharSequence getFooterText(boolean isDeviceManaged, boolean hasWorkProfile,
157             boolean hasCACerts, boolean hasCACertsInWorkProfile, boolean isNetworkLoggingEnabled,
158             String vpnName, String vpnNameWorkProfile, CharSequence organizationName,
159             CharSequence workProfileName) {
160         if (isDeviceManaged) {
161             if (hasCACerts || hasCACertsInWorkProfile || isNetworkLoggingEnabled) {
162                 if (organizationName == null) {
163                     return mContext.getString(
164                             R.string.quick_settings_disclosure_management_monitoring);
165                 }
166                 return mContext.getString(
167                         R.string.quick_settings_disclosure_named_management_monitoring,
168                         organizationName);
169             }
170             if (vpnName != null && vpnNameWorkProfile != null) {
171                 if (organizationName == null) {
172                     return mContext.getString(R.string.quick_settings_disclosure_management_vpns);
173                 }
174                 return mContext.getString(R.string.quick_settings_disclosure_named_management_vpns,
175                         organizationName);
176             }
177             if (vpnName != null || vpnNameWorkProfile != null) {
178                 if (organizationName == null) {
179                     return mContext.getString(
180                             R.string.quick_settings_disclosure_management_named_vpn,
181                             vpnName != null ? vpnName : vpnNameWorkProfile);
182                 }
183                 return mContext.getString(
184                         R.string.quick_settings_disclosure_named_management_named_vpn,
185                         organizationName,
186                         vpnName != null ? vpnName : vpnNameWorkProfile);
187             }
188             if (organizationName == null) {
189                 return mContext.getString(R.string.quick_settings_disclosure_management);
190             }
191             return mContext.getString(R.string.quick_settings_disclosure_named_management,
192                     organizationName);
193         } // end if(isDeviceManaged)
194         if (hasCACertsInWorkProfile) {
195             if (workProfileName == null) {
196                 return mContext.getString(
197                         R.string.quick_settings_disclosure_managed_profile_monitoring);
198             }
199             return mContext.getString(
200                     R.string.quick_settings_disclosure_named_managed_profile_monitoring,
201                     workProfileName);
202         }
203         if (hasCACerts) {
204             return mContext.getString(R.string.quick_settings_disclosure_monitoring);
205         }
206         if (vpnName != null && vpnNameWorkProfile != null) {
207             return mContext.getString(R.string.quick_settings_disclosure_vpns);
208         }
209         if (vpnNameWorkProfile != null) {
210             return mContext.getString(R.string.quick_settings_disclosure_managed_profile_named_vpn,
211                     vpnNameWorkProfile);
212         }
213         if (vpnName != null) {
214             if (hasWorkProfile) {
215                 return mContext.getString(
216                         R.string.quick_settings_disclosure_personal_profile_named_vpn,
217                         vpnName);
218             }
219             return mContext.getString(R.string.quick_settings_disclosure_named_vpn,
220                     vpnName);
221         }
222         return null;
223     }
224 
225     @Override
onClick(DialogInterface dialog, int which)226     public void onClick(DialogInterface dialog, int which) {
227         if (which == DialogInterface.BUTTON_NEGATIVE) {
228             final Intent intent = new Intent(Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS);
229             mDialog.dismiss();
230             mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
231         }
232     }
233 
createDialog()234     private void createDialog() {
235         final boolean isDeviceManaged = mSecurityController.isDeviceManaged();
236         final boolean hasWorkProfile = mSecurityController.hasWorkProfile();
237         final CharSequence deviceOwnerOrganization =
238                 mSecurityController.getDeviceOwnerOrganizationName();
239         final boolean hasCACerts = mSecurityController.hasCACertInCurrentUser();
240         final boolean hasCACertsInWorkProfile = mSecurityController.hasCACertInWorkProfile();
241         final boolean isNetworkLoggingEnabled = mSecurityController.isNetworkLoggingEnabled();
242         final String vpnName = mSecurityController.getPrimaryVpnName();
243         final String vpnNameWorkProfile = mSecurityController.getWorkProfileVpnName();
244 
245         mDialog = new SystemUIDialog(mContext);
246         mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
247         View dialogView = LayoutInflater.from(mContext)
248                .inflate(R.layout.quick_settings_footer_dialog, null, false);
249         mDialog.setView(dialogView);
250         mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this);
251 
252         // device management section
253         CharSequence managementMessage = getManagementMessage(isDeviceManaged,
254                 deviceOwnerOrganization);
255         if (managementMessage == null) {
256             dialogView.findViewById(R.id.device_management_disclosures).setVisibility(View.GONE);
257         } else {
258             dialogView.findViewById(R.id.device_management_disclosures).setVisibility(View.VISIBLE);
259             TextView deviceManagementWarning =
260                     (TextView) dialogView.findViewById(R.id.device_management_warning);
261             deviceManagementWarning.setText(managementMessage);
262             mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getSettingsButton(), this);
263         }
264 
265         // ca certificate section
266         CharSequence caCertsMessage = getCaCertsMessage(isDeviceManaged, hasCACerts,
267                 hasCACertsInWorkProfile);
268         if (caCertsMessage == null) {
269             dialogView.findViewById(R.id.ca_certs_disclosures).setVisibility(View.GONE);
270         } else {
271             dialogView.findViewById(R.id.ca_certs_disclosures).setVisibility(View.VISIBLE);
272             TextView caCertsWarning = (TextView) dialogView.findViewById(R.id.ca_certs_warning);
273             caCertsWarning.setText(caCertsMessage);
274             // Make "Open trusted credentials"-link clickable
275             caCertsWarning.setMovementMethod(new LinkMovementMethod());
276         }
277 
278         // network logging section
279         CharSequence networkLoggingMessage = getNetworkLoggingMessage(isNetworkLoggingEnabled);
280         if (networkLoggingMessage == null) {
281             dialogView.findViewById(R.id.network_logging_disclosures).setVisibility(View.GONE);
282         } else {
283             dialogView.findViewById(R.id.network_logging_disclosures).setVisibility(View.VISIBLE);
284             TextView networkLoggingWarning =
285                     (TextView) dialogView.findViewById(R.id.network_logging_warning);
286             networkLoggingWarning.setText(networkLoggingMessage);
287         }
288 
289         // vpn section
290         CharSequence vpnMessage = getVpnMessage(isDeviceManaged, hasWorkProfile, vpnName,
291                 vpnNameWorkProfile);
292         if (vpnMessage == null) {
293             dialogView.findViewById(R.id.vpn_disclosures).setVisibility(View.GONE);
294         } else {
295             dialogView.findViewById(R.id.vpn_disclosures).setVisibility(View.VISIBLE);
296             TextView vpnWarning = (TextView) dialogView.findViewById(R.id.vpn_warning);
297             vpnWarning.setText(vpnMessage);
298             // Make "Open VPN Settings"-link clickable
299             vpnWarning.setMovementMethod(new LinkMovementMethod());
300         }
301 
302         mDialog.show();
303         mDialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT,
304                 ViewGroup.LayoutParams.WRAP_CONTENT);
305     }
306 
getSettingsButton()307     private String getSettingsButton() {
308         return mContext.getString(R.string.monitoring_button_view_policies);
309     }
310 
getPositiveButton()311     private String getPositiveButton() {
312         return mContext.getString(R.string.quick_settings_done);
313     }
314 
getManagementMessage(boolean isDeviceManaged, CharSequence organizationName)315     protected CharSequence getManagementMessage(boolean isDeviceManaged,
316             CharSequence organizationName) {
317         if (!isDeviceManaged) return null;
318         if (organizationName != null)
319             return mContext.getString(
320                     R.string.monitoring_description_named_management, organizationName);
321         return mContext.getString(R.string.monitoring_description_management);
322     }
323 
getCaCertsMessage(boolean isDeviceManaged, boolean hasCACerts, boolean hasCACertsInWorkProfile)324     protected CharSequence getCaCertsMessage(boolean isDeviceManaged, boolean hasCACerts,
325             boolean hasCACertsInWorkProfile) {
326         if (!(hasCACerts || hasCACertsInWorkProfile)) return null;
327         if (isDeviceManaged) {
328             return mContext.getString(R.string.monitoring_description_management_ca_certificate);
329         }
330         if (hasCACertsInWorkProfile) {
331             return mContext.getString(
332                     R.string.monitoring_description_managed_profile_ca_certificate);
333         }
334         return mContext.getString(R.string.monitoring_description_ca_certificate);
335     }
336 
getNetworkLoggingMessage(boolean isNetworkLoggingEnabled)337     protected CharSequence getNetworkLoggingMessage(boolean isNetworkLoggingEnabled) {
338         if (!isNetworkLoggingEnabled) return null;
339         return mContext.getString(R.string.monitoring_description_management_network_logging);
340     }
341 
getVpnMessage(boolean isDeviceManaged, boolean hasWorkProfile, String vpnName, String vpnNameWorkProfile)342     protected CharSequence getVpnMessage(boolean isDeviceManaged, boolean hasWorkProfile,
343             String vpnName, String vpnNameWorkProfile) {
344         if (vpnName == null && vpnNameWorkProfile == null) return null;
345         final SpannableStringBuilder message = new SpannableStringBuilder();
346         if (isDeviceManaged) {
347             if (vpnName != null && vpnNameWorkProfile != null) {
348                 message.append(mContext.getString(R.string.monitoring_description_two_named_vpns,
349                         vpnName, vpnNameWorkProfile));
350             } else {
351                 message.append(mContext.getString(R.string.monitoring_description_named_vpn,
352                         vpnName != null ? vpnName : vpnNameWorkProfile));
353             }
354         } else {
355             if (vpnName != null && vpnNameWorkProfile != null) {
356                 message.append(mContext.getString(R.string.monitoring_description_two_named_vpns,
357                         vpnName, vpnNameWorkProfile));
358             } else if (vpnNameWorkProfile != null) {
359                 message.append(mContext.getString(
360                         R.string.monitoring_description_managed_profile_named_vpn,
361                         vpnNameWorkProfile));
362             } else if (hasWorkProfile) {
363                 message.append(mContext.getString(
364                         R.string.monitoring_description_personal_profile_named_vpn, vpnName));
365             } else {
366                 message.append(mContext.getString(R.string.monitoring_description_named_vpn,
367                         vpnName));
368             }
369         }
370         message.append(mContext.getString(R.string.monitoring_description_vpn_settings_separator));
371         message.append(mContext.getString(R.string.monitoring_description_vpn_settings),
372                 new VpnSpan(), 0);
373         return message;
374     }
375 
getTitle(String deviceOwner)376     private int getTitle(String deviceOwner) {
377         if (deviceOwner != null) {
378             return R.string.monitoring_title_device_owned;
379         } else {
380             return R.string.monitoring_title;
381         }
382     }
383 
384     private final Runnable mUpdateIcon = new Runnable() {
385         @Override
386         public void run() {
387             mFooterIcon.setImageResource(mFooterIconId);
388         }
389     };
390 
391     private final Runnable mUpdateDisplayState = new Runnable() {
392         @Override
393         public void run() {
394             if (mFooterTextContent != null) {
395                 mFooterText.setText(mFooterTextContent);
396             }
397             mRootView.setVisibility(mIsVisible ? View.VISIBLE : View.GONE);
398             if (mDivider != null) mDivider.setVisibility(mIsVisible ? View.GONE : View.VISIBLE);
399         }
400     };
401 
402     private class Callback implements SecurityController.SecurityControllerCallback {
403         @Override
onStateChanged()404         public void onStateChanged() {
405             refreshState();
406         }
407     }
408 
409     private class H extends Handler {
410         private static final int CLICK = 0;
411         private static final int REFRESH_STATE = 1;
412 
H(Looper looper)413         private H(Looper looper) {
414             super(looper);
415         }
416 
417         @Override
handleMessage(Message msg)418         public void handleMessage(Message msg) {
419             String name = null;
420             try {
421                 if (msg.what == REFRESH_STATE) {
422                     name = "handleRefreshState";
423                     handleRefreshState();
424                 } else if (msg.what == CLICK) {
425                     name = "handleClick";
426                     handleClick();
427                 }
428             } catch (Throwable t) {
429                 final String error = "Error in " + name;
430                 Log.w(TAG, error, t);
431                 mHost.warn(error, t);
432             }
433         }
434     }
435 
436     protected class VpnSpan extends ClickableSpan {
437         @Override
onClick(View widget)438         public void onClick(View widget) {
439             final Intent intent = new Intent(Settings.ACTION_VPN_SETTINGS);
440             mDialog.dismiss();
441             mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
442         }
443 
444         // for testing, to compare two CharSequences containing VpnSpans
445         @Override
equals(Object object)446         public boolean equals(Object object) {
447             return object instanceof VpnSpan;
448         }
449 
450         @Override
hashCode()451         public int hashCode() {
452             return 314159257; // prime
453         }
454     }
455 }
456