diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index b641e634a8005..21dca385a9796 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1059,6 +1059,15 @@ public class DevicePolicyManager {
public static final String ACTION_SET_NEW_PARENT_PROFILE_PASSWORD
= "android.app.action.SET_NEW_PARENT_PROFILE_PASSWORD";
+ /**
+ * Broadcast action: Tell the status bar to open the device monitoring dialog, e.g. when
+ * Network logging was enabled and the user tapped the notification.
+ *
This is a protected intent that can only be sent by the system.
+ * @hide
+ */
+ public static final String ACTION_SHOW_DEVICE_MONITORING_DIALOG
+ = "android.app.action.SHOW_DEVICE_MONITORING_DIALOG";
+
/**
* Flag used by {@link #addCrossProfileIntentFilter} to allow activities in
* the parent profile to access intents sent from the managed profile.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index bc04062563602..218083f3b87de 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -101,6 +101,7 @@
+
diff --git a/core/res/res/drawable/ic_qs_network_logging.xml b/core/res/res/drawable/ic_qs_network_logging.xml
new file mode 100644
index 0000000000000..9e08264156e6a
--- /dev/null
+++ b/core/res/res/drawable/ic_qs_network_logging.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 3423f0a11d67f..1cdfbf7e02200 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -386,6 +386,13 @@
This indicates that a work profile has been deleted. [CHAR LIMIT=NONE]-->
Your work profile is no longer available on this device.
+
+ Network traffic is being monitored
+
+ Tap for more details
+
Your device will be erased
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c452c6208ace0..1e6156bfde823 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1119,6 +1119,8 @@
+
+
@@ -1231,6 +1233,7 @@
+
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index f3da47b1ba82d..337b854c38b39 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -115,6 +115,10 @@ public class QSFooter implements OnClickListener, DialogInterface.OnClickListene
}
private void handleClick() {
+ showDeviceMonitoringDialog();
+ }
+
+ public void showDeviceMonitoringDialog() {
mHost.collapsePanels();
// TODO: Delay dialog creation until after panels are collapsed.
createDialog();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index d8855c87210ee..6b24a1eeef1ee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -519,6 +519,10 @@ public class QSPanel extends LinearLayout implements Tunable, Callback {
return mFooter;
}
+ public void showDeviceMonitoringDialog() {
+ mFooter.showDeviceMonitoringDialog();
+ }
+
private class H extends Handler {
private static final int SHOW_DETAIL = 1;
private static final int SET_TILE_VISIBILITY = 2;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 5da652db21565..888b9c64f5e6d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -36,6 +36,7 @@ import android.animation.AnimatorListenerAdapter;
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityOptions;
+import android.app.admin.DevicePolicyManager;
import android.app.IActivityManager;
import android.app.Notification;
import android.app.NotificationManager;
@@ -1013,6 +1014,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
+ filter.addAction(DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG);
context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
IntentFilter demoFilter = new IntentFilter();
@@ -3644,6 +3646,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
else if (Intent.ACTION_SCREEN_ON.equals(action)) {
notifyNavigationBarScreenOn(true);
}
+ else if (DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG.equals(action)) {
+ mQSPanel.showDeviceMonitoringDialog();
+ }
}
};
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 3281bd689589f..4d0f5c7f750b8 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -17,6 +17,7 @@
package com.android.server.devicepolicy;
import static android.Manifest.permission.MANAGE_CA_CERTIFICATES;
+import static android.app.admin.DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG;
import static android.app.admin.DevicePolicyManager.CODE_ACCOUNTS_NOT_EMPTY;
import static android.app.admin.DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE;
import static android.app.admin.DevicePolicyManager.CODE_DEVICE_ADMIN_NOT_SUPPORTED;
@@ -244,6 +245,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private static final int MONITORING_CERT_NOTIFICATION_ID = R.plurals.ssl_ca_cert_warning;
private static final int PROFILE_WIPED_NOTIFICATION_ID = 1001;
+ private static final int NETWORK_LOGGING_NOTIFICATION_ID = 1002;
private static final String ATTR_PERMISSION_PROVIDER = "permission-provider";
private static final String ATTR_SETUP_COMPLETE = "setup-complete";
@@ -635,6 +637,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private static final String TAG_PARENT_ADMIN = "parent-admin";
private static final String TAG_ORGANIZATION_COLOR = "organization-color";
private static final String TAG_ORGANIZATION_NAME = "organization-name";
+ private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification";
+ private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
final DeviceAdminInfo info;
@@ -685,6 +689,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
boolean forceEphemeralUsers = false; // Can only be set by a device owner.
boolean isNetworkLoggingEnabled = false; // Can only be set by a device owner.
+ // one notification after enabling + 3 more after reboots
+ static final int DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN = 4;
+ int numNetworkLoggingNotifications = 0;
+ long lastNetworkLoggingNotificationTimeMs = 0; // Time in milliseconds since epoch
+
ActiveAdmin parentAdmin;
final boolean isParent;
@@ -903,6 +912,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (isNetworkLoggingEnabled) {
out.startTag(null, TAG_IS_NETWORK_LOGGING_ENABLED);
out.attribute(null, ATTR_VALUE, Boolean.toString(isNetworkLoggingEnabled));
+ out.attribute(null, ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS,
+ Integer.toString(numNetworkLoggingNotifications));
+ out.attribute(null, ATTR_LAST_NETWORK_LOGGING_NOTIFICATION,
+ Long.toString(lastNetworkLoggingNotificationTimeMs));
out.endTag(null, TAG_IS_NETWORK_LOGGING_ENABLED);
}
if (disabledKeyguardFeatures != DEF_KEYGUARD_FEATURES_DISABLED) {
@@ -1094,6 +1107,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
} else if (TAG_IS_NETWORK_LOGGING_ENABLED.equals(tag)) {
isNetworkLoggingEnabled = Boolean.parseBoolean(
parser.getAttributeValue(null, ATTR_VALUE));
+ lastNetworkLoggingNotificationTimeMs = Long.parseLong(
+ parser.getAttributeValue(null, ATTR_LAST_NETWORK_LOGGING_NOTIFICATION));
+ numNetworkLoggingNotifications = Integer.parseInt(
+ parser.getAttributeValue(null, ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS));
} else if (TAG_DISABLE_KEYGUARD_FEATURES.equals(tag)) {
disabledKeyguardFeatures = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
@@ -9867,7 +9884,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// already in the requested state
return;
}
- getDeviceOwnerAdminLocked().isNetworkLoggingEnabled = enabled;
+ ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+ deviceOwner.isNetworkLoggingEnabled = enabled;
+ if (!enabled) {
+ deviceOwner.numNetworkLoggingNotifications = 0;
+ deviceOwner.lastNetworkLoggingNotificationTimeMs = 0;
+ }
saveSettingsLocked(mInjector.userHandleGetCallingUserId());
setNetworkLoggingActiveInternal(enabled);
@@ -9883,6 +9905,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
Slog.wtf(LOG_TAG, "Network logging could not be started due to the logging"
+ " service not being available yet.");
}
+ sendNetworkLoggingNotificationLocked();
} else {
if (mNetworkLogger != null && !mNetworkLogger.stopNetworkLogging()) {
mNetworkLogger = null;
@@ -9948,6 +9971,41 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return mNetworkLogger.retrieveLogs(batchToken);
}
+ private void sendNetworkLoggingNotificationLocked() {
+ final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+ if (deviceOwner == null || !deviceOwner.isNetworkLoggingEnabled) {
+ return;
+ }
+ if (deviceOwner.numNetworkLoggingNotifications >=
+ ActiveAdmin.DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN) {
+ return;
+ }
+ final long now = System.currentTimeMillis();
+ if (now - deviceOwner.lastNetworkLoggingNotificationTimeMs < MS_PER_DAY) {
+ return;
+ }
+ deviceOwner.numNetworkLoggingNotifications++;
+ if (deviceOwner.numNetworkLoggingNotifications
+ >= ActiveAdmin.DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN) {
+ deviceOwner.lastNetworkLoggingNotificationTimeMs = 0;
+ } else {
+ deviceOwner.lastNetworkLoggingNotificationTimeMs = now;
+ }
+ final Intent intent = new Intent(DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG);
+ intent.setPackage("com.android.systemui");
+ final PendingIntent pendingIntent = PendingIntent.getBroadcastAsUser(mContext, 0, intent, 0,
+ UserHandle.CURRENT);
+ Notification notification = new Notification.Builder(mContext)
+ .setSmallIcon(R.drawable.ic_qs_network_logging)
+ .setContentTitle(mContext.getString(R.string.network_logging_notification_title))
+ .setContentText(mContext.getString(R.string.network_logging_notification_text))
+ .setShowWhen(true)
+ .setContentIntent(pendingIntent)
+ .build();
+ mInjector.getNotificationManager().notify(NETWORK_LOGGING_NOTIFICATION_ID, notification);
+ saveSettingsLocked(mOwners.getDeviceOwnerUserId());
+ }
+
/**
* Return the package name of owner in a given user.
*/