diff --git a/api/system-current.txt b/api/system-current.txt
index f23c1fc7cf98b..48869eb396c8f 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -35532,6 +35532,7 @@ package android.provider {
field public static final java.lang.String ACTION_DEVICE_INFO_SETTINGS = "android.settings.DEVICE_INFO_SETTINGS";
field public static final java.lang.String ACTION_DISPLAY_SETTINGS = "android.settings.DISPLAY_SETTINGS";
field public static final java.lang.String ACTION_DREAM_SETTINGS = "android.settings.DREAM_SETTINGS";
+ field public static final java.lang.String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
field public static final java.lang.String ACTION_HARD_KEYBOARD_SETTINGS = "android.settings.HARD_KEYBOARD_SETTINGS";
field public static final java.lang.String ACTION_HOME_SETTINGS = "android.settings.HOME_SETTINGS";
field public static final java.lang.String ACTION_IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS = "android.settings.IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS";
diff --git a/api/test-current.txt b/api/test-current.txt
index 810680d9dddc1..6519605dd208c 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -32831,6 +32831,7 @@ package android.provider {
field public static final java.lang.String ACTION_DEVICE_INFO_SETTINGS = "android.settings.DEVICE_INFO_SETTINGS";
field public static final java.lang.String ACTION_DISPLAY_SETTINGS = "android.settings.DISPLAY_SETTINGS";
field public static final java.lang.String ACTION_DREAM_SETTINGS = "android.settings.DREAM_SETTINGS";
+ field public static final java.lang.String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
field public static final java.lang.String ACTION_HARD_KEYBOARD_SETTINGS = "android.settings.HARD_KEYBOARD_SETTINGS";
field public static final java.lang.String ACTION_HOME_SETTINGS = "android.settings.HOME_SETTINGS";
field public static final java.lang.String ACTION_IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS = "android.settings.IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS";
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b5c7f7b0bd8cd..0b78d6b779ae8 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1321,6 +1321,20 @@ public final class Settings {
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_WEBVIEW_SETTINGS = "android.settings.WEBVIEW_SETTINGS";
+ /**
+ * Activity Action: Show enterprise privacy section.
+ *
+ * Input: Nothing.
+ *
+ * Output: Nothing.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS
+ = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
+
// End of Intent actions for Settings
/**
diff --git a/core/tests/coretests/src/android/provider/SettingsProviderTest.java b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
index 0a32e4353e1c6..b0ce2c85d04e1 100644
--- a/core/tests/coretests/src/android/provider/SettingsProviderTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
@@ -351,6 +351,10 @@ public class SettingsProviderTest extends AndroidTestCase {
assertCanBeHandled(new Intent(Settings.ACTION_WIFI_SETTINGS));
assertCanBeHandled(new Intent(Settings.ACTION_WIFI_SAVED_NETWORK_SETTINGS));
assertCanBeHandled(new Intent(Settings.ACTION_WIRELESS_SETTINGS));
+
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)) {
+ assertCanBeHandled(new Intent(Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS));
+ }
}
private void assertCanBeHandled(final Intent intent) {
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index af1fc593ccccb..c8b3b69dce374 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -840,10 +840,10 @@
Swipe up to unlock
-
+
This device is managed
-
+
This device is managed by %s
@@ -992,9 +992,6 @@
No notifications
-
- Device may be monitored
-
Profile may be monitored
@@ -1019,15 +1016,27 @@
Disconnect VPN
-
- Your device is managed by %1$s.\n\nYour administrator can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information. For more information, contact your administrator.
+
+ Your device is managed by %1$s.
+
+
+ %1$s uses %2$s to manage your device.
+
+
+ Your administrator can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.
+
+
+ You\'re connected to %1$s, which can monitor your network activity, including emails, apps, and websites.
+
+
+ " "
+
+
+ Learn more
You gave an app permission to set up a VPN connection.\n\nThis app can monitor your device and network activity, including emails, apps, and websites.
-
- Your device is managed by %1$s.\n\nYour administrator can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nYou\'re connected to a VPN, which can monitor your network activity, including emails, apps, and websites.\n\nFor more information, contact your administrator.
-
Your work profile is managed by %1$s.\n\nYour administrator is capable of monitoring your network activity including emails, apps, and websites.\n\nFor more information, contact your administrator.\n\nYou\'re also connected to a VPN, which can monitor your network activity.
@@ -1049,9 +1058,6 @@
Your work profile is managed by %1$s. It is connected to %2$s, which can monitor your work network activity, including emails, apps, and websites.\n\nYou\'re also connected to %3$s, which can monitor your personal network activity.
-
- Your device is managed by %1$s.\n\nYour administrator can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nYou\'re connected to %2$s, which can monitor your network activity, including emails, apps, and websites.\n\nFor more information, contact your administrator.
-
Device will stay locked until you manually unlock
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index ccb28e95802e0..43308def0f4ad 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -24,10 +24,15 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.SpannableStringBuilder;
+import android.text.method.LinkMovementMethod;
+import android.text.style.ClickableSpan;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
+import android.view.Window;
import android.widget.ImageView;
import android.widget.TextView;
@@ -52,12 +57,12 @@ public class QSFooter implements OnClickListener, DialogInterface.OnClickListene
private SecurityController mSecurityController;
private AlertDialog mDialog;
private QSTileHost mHost;
- private Handler mHandler;
+ protected Handler mHandler;
private final Handler mMainHandler;
private boolean mIsVisible;
private boolean mIsIconVisible;
- private int mFooterTextId;
+ private CharSequence mFooterTextContent = null;
private int mFooterIconId;
public QSFooter(QSPanel qsPanel, Context context) {
@@ -68,13 +73,14 @@ public class QSFooter implements OnClickListener, DialogInterface.OnClickListene
mFooterIcon = (ImageView) mRootView.findViewById(R.id.footer_icon);
mFooterIconId = R.drawable.ic_qs_vpn;
mContext = context;
- mMainHandler = new Handler();
+ mMainHandler = new Handler(Looper.getMainLooper());
}
- public void setHost(QSTileHost host) {
+ public void setHostEnvironment(QSTileHost host, SecurityController securityController,
+ Looper looper) {
mHost = host;
- mSecurityController = host.getSecurityController();
- mHandler = new H(host.getLooper());
+ mSecurityController = securityController;
+ mHandler = new H(looper);
}
public void setListening(boolean listening) {
@@ -114,14 +120,21 @@ public class QSFooter implements OnClickListener, DialogInterface.OnClickListene
private void handleRefreshState() {
mIsIconVisible = mSecurityController.isVpnEnabled();
- // If the device has device owner, show "Device may be monitored", but --
- // TODO See b/25779452 -- device owner doesn't actually have monitoring power.
if (mSecurityController.isDeviceManaged()) {
- mFooterTextId = R.string.device_owned_footer;
+ final CharSequence organizationName =
+ mSecurityController.getDeviceOwnerOrganizationName();
+ if (organizationName != null) {
+ mFooterTextContent = mContext.getResources().getString(
+ R.string.do_disclosure_with_name, organizationName);
+ } else {
+ mFooterTextContent =
+ mContext.getResources().getString(R.string.do_disclosure_generic);
+ }
mIsVisible = true;
} else {
boolean isBranded = mSecurityController.isVpnBranded();
- mFooterTextId = isBranded ? R.string.branded_vpn_footer : R.string.vpn_footer;
+ mFooterTextContent = mContext.getResources().getText(
+ isBranded ? R.string.branded_vpn_footer : R.string.vpn_footer);
// Update the VPN footer icon, if needed.
int footerIconId = isBranded ? R.drawable.ic_qs_branded_vpn : R.drawable.ic_qs_vpn;
if (mFooterIconId != footerIconId) {
@@ -142,23 +155,36 @@ public class QSFooter implements OnClickListener, DialogInterface.OnClickListene
}
private void createDialog() {
- String deviceOwner = mSecurityController.getDeviceOwnerName();
- String profileOwner = mSecurityController.getProfileOwnerName();
- String primaryVpn = mSecurityController.getPrimaryVpnName();
- String profileVpn = mSecurityController.getProfileVpnName();
- boolean managed = mSecurityController.hasProfileOwner();
- boolean isBranded = deviceOwner == null && mSecurityController.isVpnBranded();
+ final String deviceOwnerPackage = mSecurityController.getDeviceOwnerName();
+ final String profileOwnerPackage = mSecurityController.getProfileOwnerName();
+ final String primaryVpn = mSecurityController.getPrimaryVpnName();
+ final String profileVpn = mSecurityController.getProfileVpnName();
+ final CharSequence deviceOwnerOrganization =
+ mSecurityController.getDeviceOwnerOrganizationName();
+ boolean hasProfileOwner = mSecurityController.hasProfileOwner();
+ boolean isBranded = deviceOwnerPackage == null && mSecurityController.isVpnBranded();
mDialog = new SystemUIDialog(mContext);
if (!isBranded) {
- mDialog.setTitle(getTitle(deviceOwner));
+ mDialog.setTitle(getTitle(deviceOwnerPackage));
}
- mDialog.setMessage(getMessage(deviceOwner, profileOwner, primaryVpn, profileVpn, managed,
- isBranded));
+ mDialog.setMessage(getMessage(deviceOwnerPackage, profileOwnerPackage, primaryVpn,
+ profileVpn, deviceOwnerOrganization, hasProfileOwner, isBranded));
+
mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(isBranded), this);
if (mSecurityController.isVpnEnabled() && !mSecurityController.isVpnRestricted()) {
mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getSettingsButton(), this);
}
+
+ // Make the link "learn more" clickable.
+ // TODO: Reaching into SystemUIDialog's internal View hierarchy is ugly and error-prone.
+ // This is a temporary solution. b/33126622 will introduce a custom View hierarchy for this
+ // dialog, allowing us to set the movement method on the appropriate View without any
+ // knowledge of SystemUIDialog's internals.
+ mDialog.create();
+ ((TextView) mDialog.getWindow().findViewById(com.android.internal.R.id.message))
+ .setMovementMethod(new LinkMovementMethod());
+
mDialog.show();
}
@@ -170,22 +196,35 @@ public class QSFooter implements OnClickListener, DialogInterface.OnClickListene
return mContext.getString(isBranded ? android.R.string.ok : R.string.quick_settings_done);
}
- private String getMessage(String deviceOwner, String profileOwner, String primaryVpn,
- String profileVpn, boolean primaryUserIsManaged, boolean isBranded) {
- // Show a special warning when the device has device owner, but --
- // TODO See b/25779452 -- device owner doesn't actually have monitoring power.
- if (deviceOwner != null) {
- if (primaryVpn != null) {
- return mContext.getString(R.string.monitoring_description_vpn_app_device_owned,
- deviceOwner, primaryVpn);
+ protected CharSequence getMessage(String deviceOwnerPackage, String profileOwnerPackage,
+ String primaryVpn, String profileVpn, CharSequence deviceOwnerOrganization,
+ boolean hasProfileOwner, boolean isBranded) {
+ if (deviceOwnerPackage != null) {
+ final SpannableStringBuilder message = new SpannableStringBuilder();
+ if (deviceOwnerOrganization != null) {
+ message.append(mContext.getString(
+ R.string.monitoring_description_do_header_with_name,
+ deviceOwnerOrganization, deviceOwnerPackage));
} else {
- return mContext.getString(R.string.monitoring_description_device_owned,
- deviceOwner);
+ message.append(mContext.getString(R.string.monitoring_description_do_header_generic,
+ deviceOwnerPackage));
}
+ message.append("\n\n");
+ message.append(mContext.getString(R.string.monitoring_description_do_body));
+ if (primaryVpn != null) {
+ message.append("\n\n");
+ message.append(mContext.getString(R.string.monitoring_description_do_body_vpn,
+ primaryVpn));
+ }
+ message.append(mContext.getString(
+ R.string.monitoring_description_do_learn_more_separator));
+ message.append(mContext.getString(R.string.monitoring_description_do_learn_more),
+ new EnterprisePrivacySpan(), 0);
+ return message;
} else if (primaryVpn != null) {
if (profileVpn != null) {
return mContext.getString(R.string.monitoring_description_app_personal_work,
- profileOwner, profileVpn, primaryVpn);
+ profileOwnerPackage, profileVpn, primaryVpn);
} else {
if (isBranded) {
return mContext.getString(R.string.branded_monitoring_description_app_personal,
@@ -197,10 +236,10 @@ public class QSFooter implements OnClickListener, DialogInterface.OnClickListene
}
} else if (profileVpn != null) {
return mContext.getString(R.string.monitoring_description_app_work,
- profileOwner, profileVpn);
- } else if (profileOwner != null && primaryUserIsManaged) {
+ profileOwnerPackage, profileVpn);
+ } else if (profileOwnerPackage != null && hasProfileOwner) {
return mContext.getString(R.string.monitoring_description_device_owned,
- profileOwner);
+ profileOwnerPackage);
} else {
// No device owner, no personal VPN, no work VPN, no user owner. Why are we here?
return null;
@@ -225,8 +264,8 @@ public class QSFooter implements OnClickListener, DialogInterface.OnClickListene
private final Runnable mUpdateDisplayState = new Runnable() {
@Override
public void run() {
- if (mFooterTextId != 0) {
- mFooterText.setText(mFooterTextId);
+ if (mFooterTextContent != null) {
+ mFooterText.setText(mFooterTextContent);
}
mRootView.setVisibility(mIsVisible ? View.VISIBLE : View.GONE);
mFooterIcon.setVisibility(mIsIconVisible ? View.VISIBLE : View.INVISIBLE);
@@ -267,4 +306,18 @@ public class QSFooter implements OnClickListener, DialogInterface.OnClickListene
}
}
+ protected class EnterprisePrivacySpan extends ClickableSpan {
+ @Override
+ public void onClick(View widget) {
+ final Intent intent = new Intent(Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ mDialog.dismiss();
+ mContext.startActivity(intent);
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ return object instanceof EnterprisePrivacySpan;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index e55ff70c35c2b..d8855c87210ee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -179,7 +179,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback {
mHost = host;
mHost.addCallback(this);
setTiles(mHost.getTiles());
- mFooter.setHost(host);
+ mFooter.setHostEnvironment(host, host.getSecurityController(), host.getLooper());
mCustomizePanel = customizer;
if (mCustomizePanel != null) {
mCustomizePanel.setHost(mHost);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
index 43ced4898365d..69281b52142cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
@@ -23,6 +23,7 @@ public interface SecurityController extends CallbackController