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.PASSWORD_QUALITY_COMPLEX;
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.ActivityManagerNative;
import android.app.AlarmManager;
import android.app.AppGlobals;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.IDevicePolicyManager;
+import android.app.admin.NetworkEvent;
import android.app.admin.SecurityLog;
import android.app.admin.SecurityLog.SecurityEvent;
import android.app.admin.SystemUpdatePolicy;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
+import android.content.pm.PermissionInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.media.AudioManager;
import android.media.IAudioService;
import android.net.ConnectivityManager;
+import android.net.IIpConnectivityMetrics;
import android.net.ProxyInfo;
import android.net.Uri;
+import android.net.metrics.IpConnectivityLog;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.security.IKeyChainService;
import android.security.KeyChain;
import android.security.KeyChain.KeyChainConnection;
+import android.security.KeyStore;
import android.service.persistentdata.PersistentDataBlockManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.EventLog;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
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";
boolean mIsWatch;
private final SecurityLogMonitor mSecurityLogMonitor;
+ private NetworkLogger mNetworkLogger;
private final AtomicBoolean mRemoteBugreportServiceIsActive = new AtomicBoolean();
private final AtomicBoolean mRemoteBugreportSharingAccepted = new AtomicBoolean();
final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
getSendingUserId());
+ /*
+ * Network logging would ideally be started in setDeviceOwnerSystemPropertyLocked(),
+ * however it's too early in the boot process to register with IIpConnectivityMetrics
+ * to listen for events.
+ */
+ if (Intent.ACTION_USER_STARTED.equals(action)
+ && userHandle == mOwners.getDeviceOwnerUserId()) {
+ synchronized (DevicePolicyManagerService.this) {
+ if (isNetworkLoggingEnabledInternalLocked()) {
+ setNetworkLoggingActiveInternal(true);
+ }
+ }
+ }
if (Intent.ACTION_BOOT_COMPLETED.equals(action)
&& userHandle == mOwners.getDeviceOwnerUserId()
&& getDeviceOwnerRemoteBugreportUri() != null) {
private static final String TAG_DISABLE_ACCOUNT_MANAGEMENT = "disable-account-management";
private static final String TAG_REQUIRE_AUTO_TIME = "require_auto_time";
private static final String TAG_FORCE_EPHEMERAL_USERS = "force_ephemeral_users";
+ private static final String TAG_IS_NETWORK_LOGGING_ENABLED = "is_network_logging_enabled";
private static final String TAG_ACCOUNT_TYPE = "account-type";
private static final String TAG_PERMITTED_ACCESSIBILITY_SERVICES
= "permitted-accessiblity-services";
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;
boolean disableScreenCapture = false; // Can only be set by a device/profile owner.
boolean requireAutoTime = false; // Can only be set by a device owner.
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;
out.attribute(null, ATTR_VALUE, Boolean.toString(forceEphemeralUsers));
out.endTag(null, TAG_FORCE_EPHEMERAL_USERS);
}
+ 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) {
out.startTag(null, TAG_DISABLE_KEYGUARD_FEATURES);
out.attribute(null, ATTR_VALUE, Integer.toString(disabledKeyguardFeatures));
} else if (TAG_FORCE_EPHEMERAL_USERS.equals(tag)) {
forceEphemeralUsers = Boolean.parseBoolean(
parser.getAttributeValue(null, ATTR_VALUE));
+ } 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));
pw.println(requireAutoTime);
pw.print(prefix); pw.print("forceEphemeralUsers=");
pw.println(forceEphemeralUsers);
+ pw.print(prefix); pw.print("isNetworkLoggingEnabled=");
+ pw.println(isNetworkLoggingEnabled);
pw.print(prefix); pw.print("disabledKeyguardFeatures=");
pw.println(disabledKeyguardFeatures);
pw.print(prefix); pw.print("crossProfileWidgetProviders=");
return mContext.getSystemService(NotificationManager.class);
}
+ IIpConnectivityMetrics getIIpConnectivityMetrics() {
+ return (IIpConnectivityMetrics) IIpConnectivityMetrics.Stub.asInterface(
+ ServiceManager.getService(IpConnectivityLog.SERVICE_NAME));
+ }
+
PowerManagerInternal getPowerManagerInternal() {
return LocalServices.getService(PowerManagerInternal.class);
}
return (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
}
+ AlarmManager getAlarmManager() {
+ return (AlarmManager) mContext.getSystemService(AlarmManager.class);
+ }
+
IWindowManager getIWindowManager() {
return IWindowManager.Stub
.asInterface(ServiceManager.getService(Context.WINDOW_SERVICE));
}
private void setDeviceOwnerSystemPropertyLocked() {
- // Device owner may still be provisioned, do not set the read-only system property yet.
- // Wear devices don't set device_provisioned until the device is paired, so allow
- // device_owner property to be set without that.
- if (!mIsWatch
- && mInjector.settingsGlobalGetInt(Settings.Global.DEVICE_PROVISIONED, 0) == 0) {
+ final boolean deviceProvisioned =
+ mInjector.settingsGlobalGetInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0;
+ // If the device is not provisioned and there is currently no device owner, do not set the
+ // read-only system property yet, since Device owner may still be provisioned. For Wear
+ // devices, if there is already a device owner then it's OK to set the property to true now,
+ // regardless the provision state.
+ final boolean isWatchWithDeviceOwner = mIsWatch && mOwners.hasDeviceOwner();
+ if (!isWatchWithDeviceOwner && !deviceProvisioned) {
return;
}
// Still at the first stage of CryptKeeper double bounce, mOwners.hasDeviceOwner is
long token = mInjector.binderClearCallingIdentity();
try {
int affectedUserHandle = parent ? getProfileParentId(userHandle) : userHandle;
- AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ AlarmManager am = mInjector.getAlarmManager();
PendingIntent pi = PendingIntent.getBroadcastAsUser(context, REQUEST_EXPIRE_PASSWORD,
new Intent(ACTION_EXPIRED_PASSWORD_NOTIFICATION),
PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT,
@Override
public boolean isSeparateProfileChallengeAllowed(int userHandle) {
+ if (!isCallerWithSystemUid()) {
+ throw new SecurityException("Caller must be system");
+ }
ComponentName profileOwner = getProfileOwner(userHandle);
// Profile challenge is supported on N or newer release.
return profileOwner != null &&
}
boolean legacyApp = false;
- if (ai.targetSdkVersion <= Build.VERSION_CODES.M) {
- legacyApp = true;
+ if (ai != null) {
+ if (ai.targetSdkVersion <= Build.VERSION_CODES.M) {
+ legacyApp = true;
+ }
}
final int rawStatus = getEncryptionStatus();
intent.setComponent(mOwners.getDeviceOwnerComponent());
intent.setDataAndType(bugreportUri, RemoteBugreportUtils.BUGREPORT_MIMETYPE);
intent.putExtra(DeviceAdminReceiver.EXTRA_BUGREPORT_HASH, bugreportHash);
- mContext.grantUriPermission(mOwners.getDeviceOwnerComponent().getPackageName(),
- bugreportUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ LocalServices.getService(ActivityManagerInternal.class)
+ .grantUriPermissionFromIntent(Process.SHELL_UID,
+ mOwners.getDeviceOwnerComponent().getPackageName(),
+ intent, mOwners.getDeviceOwnerUserId());
mContext.sendBroadcastAsUser(intent, UserHandle.of(mOwners.getDeviceOwnerUserId()));
}
} catch (FileNotFoundException e) {
}
/**
+ * @hide
+ */
+ @Override
+ public boolean requireSecureKeyguard(int userHandle) {
+ if (!mHasFeature) {
+ return false;
+ }
+
+ int passwordQuality = getPasswordQuality(null, userHandle, false);
+ if (passwordQuality > DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
+ return true;
+ }
+
+ int encryptionStatus = getStorageEncryptionStatus(null, userHandle);
+ if (encryptionStatus == DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE
+ || encryptionStatus == DevicePolicyManager.ENCRYPTION_STATUS_ACTIVATING) {
+ return true;
+ }
+
+ final int keyguardDisabledFeatures = getKeyguardDisabledFeatures(null, userHandle, false);
+ return (keyguardDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0;
+ }
+
+
+ /**
* Gets the disabled state for features in keyguard for the given admin,
* or the aggregate of all active admins if who is null.
*/
throw new IllegalArgumentException("Invalid component " + admin
+ " for device owner");
}
+ final boolean hasIncompatibleAccountsOrNonAdb =
+ hasIncompatibleAccountsOrNonAdbNoLock(userId, admin);
synchronized (this) {
- enforceCanSetDeviceOwnerLocked(admin, userId);
+ enforceCanSetDeviceOwnerLocked(admin, userId, hasIncompatibleAccountsOrNonAdb);
if (getActiveAdminUncheckedLocked(admin, userId) == null
|| getUserData(userId).mRemovingAdmins.contains(admin)) {
throw new IllegalArgumentException("Not active admin: " + admin);
throw new IllegalArgumentException("Component " + who
+ " not installed for userId:" + userHandle);
}
+ final boolean hasIncompatibleAccountsOrNonAdb =
+ hasIncompatibleAccountsOrNonAdbNoLock(userHandle, who);
synchronized (this) {
- enforceCanSetProfileOwnerLocked(who, userHandle);
+ enforceCanSetProfileOwnerLocked(who, userHandle, hasIncompatibleAccountsOrNonAdb);
if (getActiveAdminUncheckedLocked(who, userHandle) == null
|| getUserData(userHandle).mRemovingAdmins.contains(who)) {
* The profile owner can only be set before the user setup phase has completed,
* except for:
* - SYSTEM_UID
- * - adb if there are no accounts. (But see {@link #hasIncompatibleAccountsLocked})
+ * - adb unless hasIncompatibleAccountsOrNonAdb is true.
*/
- private void enforceCanSetProfileOwnerLocked(@Nullable ComponentName owner, int userHandle) {
+ private void enforceCanSetProfileOwnerLocked(@Nullable ComponentName owner, int userHandle,
+ boolean hasIncompatibleAccountsOrNonAdb) {
UserInfo info = getUserInfo(userHandle);
if (info == null) {
// User doesn't exist.
int callingUid = mInjector.binderGetCallingUid();
if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
if ((mIsWatch || hasUserSetupCompleted(userHandle))
- && hasIncompatibleAccountsLocked(userHandle, owner)) {
+ && hasIncompatibleAccountsOrNonAdb) {
throw new IllegalStateException("Not allowed to set the profile owner because "
+ "there are already some accounts on the profile");
}
* The Device owner can only be set by adb or an app with the MANAGE_PROFILE_AND_DEVICE_OWNERS
* permission.
*/
- private void enforceCanSetDeviceOwnerLocked(@Nullable ComponentName owner, int userId) {
+ private void enforceCanSetDeviceOwnerLocked(@Nullable ComponentName owner, int userId,
+ boolean hasIncompatibleAccountsOrNonAdb) {
int callingUid = mInjector.binderGetCallingUid();
boolean isAdb = callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
if (!isAdb) {
enforceCanManageProfileAndDeviceOwners();
}
- final int code = checkSetDeviceOwnerPreConditionLocked(owner, userId, isAdb);
+ final int code = checkSetDeviceOwnerPreConditionLocked(owner, userId, isAdb,
+ hasIncompatibleAccountsOrNonAdb);
switch (code) {
case CODE_OK:
return;
}
}
+ private void enforceDeviceOwnerOrManageUsers() {
+ synchronized (this) {
+ if (getActiveAdminWithPolicyForUidLocked(null, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER,
+ mInjector.binderGetCallingUid()) != null) {
+ return;
+ }
+ }
+ enforceManageUsers();
+ }
+
private void ensureCallerPackage(@Nullable String packageName) {
if (packageName == null) {
Preconditions.checkState(isCallerWithSystemUid(),
if (!mHasFeature) {
return null;
}
+ enforceManageUsers();
synchronized (this) {
List<String> result = null;
// If we have multiple profiles we return the intersection of the
< android.os.Build.VERSION_CODES.M) {
return false;
}
+ if (!isRuntimePermission(permission)) {
+ EventLog.writeEvent(0x534e4554, "62623498", user.getIdentifier(), "");
+ return false;
+ }
final PackageManager packageManager = mContext.getPackageManager();
switch (grantState) {
case DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED: {
return true;
} catch (SecurityException se) {
return false;
+ } catch (NameNotFoundException e) {
+ return false;
} finally {
mInjector.binderRestoreCallingIdentity(ident);
}
}
}
+ public boolean isRuntimePermission(String permissionName) throws NameNotFoundException {
+ final PackageManager packageManager = mContext.getPackageManager();
+ PermissionInfo permissionInfo = packageManager.getPermissionInfo(permissionName, 0);
+ return (permissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+ == PermissionInfo.PROTECTION_DANGEROUS;
+ }
+
@Override
public boolean isProvisioningAllowed(String action) {
if (!mHasFeature) {
* except for adb command if no accounts or additional users are present on the device.
*/
private synchronized @DeviceOwnerPreConditionCode int checkSetDeviceOwnerPreConditionLocked(
- @Nullable ComponentName owner, int deviceOwnerUserId, boolean isAdb) {
+ @Nullable ComponentName owner, int deviceOwnerUserId, boolean isAdb,
+ boolean hasIncompatibleAccountsOrNonAdb) {
if (mOwners.hasDeviceOwner()) {
return CODE_HAS_DEVICE_OWNER;
}
if (mUserManager.getUserCount() > 1) {
return CODE_NONSYSTEM_USER_EXISTS;
}
- if (hasIncompatibleAccountsLocked(UserHandle.USER_SYSTEM, owner)) {
+ if (hasIncompatibleAccountsOrNonAdb) {
return CODE_ACCOUNTS_NOT_EMPTY;
}
} else {
private boolean isDeviceOwnerProvisioningAllowed(int deviceOwnerUserId) {
synchronized (this) {
return CODE_OK == checkSetDeviceOwnerPreConditionLocked(
- /* owner unknown */ null, deviceOwnerUserId, /* isAdb */ false);
+ /* owner unknown */ null, deviceOwnerUserId, /* isAdb */ false,
+ /* hasIncompatibleAccountsOrNonAdb=*/ true);
}
}
}
private synchronized void disableDeviceOwnerManagedSingleUserFeaturesIfNeeded() {
- if (!isDeviceOwnerManagedSingleUserDevice()) {
+ final boolean isSingleUserManagedDevice = isDeviceOwnerManagedSingleUserDevice();
+
+ // disable security logging if needed
+ if (!isSingleUserManagedDevice) {
mInjector.securityLogSetLoggingEnabledProperty(false);
- Slog.w(LOG_TAG, "Security logging turned off as it's no longer a single user device.");
+ Slog.w(LOG_TAG, "Security logging turned off as it's no longer a single user managed"
+ + " device.");
+ }
+
+ // disable backup service if needed
+ // note: when clearing DO, the backup service shouldn't be disabled if it was enabled by
+ // the device owner
+ if (mOwners.hasDeviceOwner() && !isSingleUserManagedDevice) {
+ setBackupServiceEnabledInternal(false);
+ Slog.w(LOG_TAG, "Backup is off as it's a managed device that has more that one user.");
+ }
+
+ // disable network logging if needed
+ if (!isSingleUserManagedDevice) {
+ setNetworkLoggingActiveInternal(false);
+ Slog.w(LOG_TAG, "Network logging turned off as it's no longer a single user managed"
+ + " device.");
+ // if there still is a device owner, disable logging policy, otherwise the admin
+ // has been nuked
if (mOwners.hasDeviceOwner()) {
- setBackupServiceEnabledInternal(false);
- Slog.w(LOG_TAG, "Backup is off as it's a managed device that has more that one user.");
+ getDeviceOwnerAdminLocked().isNetworkLoggingEnabled = false;
+ saveSettingsLocked(mOwners.getDeviceOwnerUserId());
}
}
}
* - Otherwise, if there's any account that does not have ..._ALLOWED, or does have
* ..._DISALLOWED, return true.
* - Otherwise return false.
+ *
+ * If the caller is *not* ADB, it also returns true. The returned value shouldn't be used
+ * when the caller is not ADB.
+ *
+ * DO NOT CALL IT WITH THE DPMS LOCK HELD.
*/
- private boolean hasIncompatibleAccountsLocked(int userId, @Nullable ComponentName owner) {
+ private boolean hasIncompatibleAccountsOrNonAdbNoLock(
+ int userId, @Nullable ComponentName owner) {
+ final boolean isAdb = (mInjector.binderGetCallingUid() == Process.SHELL_UID)
+ || (mInjector.binderGetCallingUid() == Process.ROOT_UID);
+ if (!isAdb) {
+ return true;
+ }
+
+ if (Thread.holdsLock(this)) {
+ Slog.wtf(LOG_TAG, "hasIncompatibleAccountsNoLock() called with the DPMS lock held.");
+ return true;
+ }
+
final long token = mInjector.binderClearCallingIdentity();
try {
final AccountManager am = AccountManager.get(mContext);
if (accounts.length == 0) {
return false;
}
+ synchronized (this) {
+ if (owner == null || !isAdminTestOnlyLocked(owner, userId)) {
+ Log.w(LOG_TAG,
+ "Non test-only owner can't be installed with existing accounts.");
+ return true;
+ }
+ }
+
final String[] feature_allow =
{ DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED };
final String[] feature_disallow =
{ DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED };
- // Even if we find incompatible accounts along the way, we still check all accounts
- // for logging.
boolean compatible = true;
for (Account account : accounts) {
if (hasAccountFeatures(am, account, feature_disallow)) {
Log.e(LOG_TAG, account + " has " + feature_disallow[0]);
compatible = false;
+ break;
}
if (!hasAccountFeatures(am, account, feature_allow)) {
Log.e(LOG_TAG, account + " doesn't have " + feature_allow[0]);
compatible = false;
+ break;
}
}
if (compatible) {
} else {
Log.e(LOG_TAG, "Found incompatible accounts");
}
-
- // Then check if the owner is test-only.
- String log;
- if (owner == null) {
- // Owner is unknown. Suppose it's not test-only
- compatible = false;
- log = "Only test-only device/profile owner can be installed with accounts";
- } else if (isAdminTestOnlyLocked(owner, userId)) {
- if (compatible) {
- log = "Installing test-only owner " + owner;
- } else {
- log = "Can't install test-only owner " + owner + " with incompatible accounts";
- }
- } else {
- compatible = false;
- log = "Can't install non test-only owner " + owner + " with accounts";
- }
- if (compatible) {
- Log.w(LOG_TAG, log);
- } else {
- Log.e(LOG_TAG, log);
- }
return !compatible;
} finally {
mInjector.binderRestoreCallingIdentity(token);
}
}
}
+
+ @Override
+ public synchronized void setNetworkLoggingEnabled(ComponentName admin, boolean enabled) {
+ if (!mHasFeature) {
+ return;
+ }
+ Preconditions.checkNotNull(admin);
+ ensureDeviceOwnerManagingSingleUser(admin);
+
+ if (enabled == isNetworkLoggingEnabledInternalLocked()) {
+ // already in the requested state
+ return;
+ }
+ ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+ deviceOwner.isNetworkLoggingEnabled = enabled;
+ if (!enabled) {
+ deviceOwner.numNetworkLoggingNotifications = 0;
+ deviceOwner.lastNetworkLoggingNotificationTimeMs = 0;
+ }
+ saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+
+ setNetworkLoggingActiveInternal(enabled);
+ }
+
+ private synchronized void setNetworkLoggingActiveInternal(boolean active) {
+ final long callingIdentity = mInjector.binderClearCallingIdentity();
+ try {
+ if (active) {
+ mNetworkLogger = new NetworkLogger(this, mInjector.getPackageManagerInternal());
+ if (!mNetworkLogger.startNetworkLogging()) {
+ mNetworkLogger = null;
+ 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;
+ Slog.wtf(LOG_TAG, "Network logging could not be stopped due to the logging"
+ + " service not being available yet.");
+ }
+ mNetworkLogger = null;
+ mInjector.getNotificationManager().cancel(NETWORK_LOGGING_NOTIFICATION_ID);
+ }
+ } finally {
+ mInjector.binderRestoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override
+ public boolean isNetworkLoggingEnabled(ComponentName admin) {
+ if (!mHasFeature) {
+ return false;
+ }
+ synchronized (this) {
+ enforceDeviceOwnerOrManageUsers();
+ return isNetworkLoggingEnabledInternalLocked();
+ }
+ }
+
+ private boolean isNetworkLoggingEnabledInternalLocked() {
+ ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+ return (deviceOwner != null) && deviceOwner.isNetworkLoggingEnabled;
+ }
+
+ /*
+ * A maximum of 1200 events are returned, and the total marshalled size is in the order of
+ * 100kB, so returning a List instead of ParceledListSlice is acceptable.
+ * Ideally this would be done with ParceledList, however it only supports homogeneous types.
+ *
+ * @see NetworkLoggingHandler#MAX_EVENTS_PER_BATCH
+ */
+ @Override
+ public synchronized List<NetworkEvent> retrieveNetworkLogs(ComponentName admin,
+ long batchToken) {
+ if (!mHasFeature) {
+ return null;
+ }
+ Preconditions.checkNotNull(admin);
+ ensureDeviceOwnerManagingSingleUser(admin);
+
+ if (mNetworkLogger == null) {
+ return null;
+ }
+ return isNetworkLoggingEnabledInternalLocked()
+ ? mNetworkLogger.retrieveLogs(batchToken)
+ : null;
+ }
+
+ 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))
+ .setTicker(mContext.getString(R.string.network_logging_notification_title))
+ .setShowWhen(true)
+ .setContentIntent(pendingIntent)
+ .build();
+ mInjector.getNotificationManager().notify(NETWORK_LOGGING_NOTIFICATION_ID, notification);
+ saveSettingsLocked(mOwners.getDeviceOwnerUserId());
+ }
}