OSDN Git Service

Limit IsSeparateProfileChallengeAllowed to system callers
[android-x86/frameworks-base.git] / services / devicepolicy / java / com / android / server / devicepolicy / DevicePolicyManagerService.java
index daffefe..fe0ee09 100644 (file)
@@ -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.PASSWORD_QUALITY_COMPLEX;
 import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
 import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
@@ -37,6 +38,7 @@ import android.annotation.Nullable;
 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;
@@ -50,6 +52,7 @@ import android.app.admin.DeviceAdminReceiver;
 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;
@@ -68,6 +71,7 @@ import android.content.pm.PackageManager;
 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;
@@ -77,8 +81,10 @@ import android.graphics.Color;
 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;
@@ -113,11 +119,13 @@ import android.security.IKeyChainAliasCallback;
 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;
@@ -215,6 +223,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";
@@ -352,6 +361,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
     boolean mIsWatch;
 
     private final SecurityLogMonitor mSecurityLogMonitor;
+    private NetworkLogger mNetworkLogger;
 
     private final AtomicBoolean mRemoteBugreportServiceIsActive = new AtomicBoolean();
     private final AtomicBoolean mRemoteBugreportSharingAccepted = new AtomicBoolean();
@@ -478,6 +488,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
             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) {
@@ -549,6 +572,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
         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";
@@ -587,6 +611,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;
 
@@ -643,6 +669,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
         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;
@@ -851,6 +883,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
                 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));
@@ -1037,6 +1078,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
                 } 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));
@@ -1277,6 +1325,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
                     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=");
@@ -1403,6 +1453,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
             return mContext.getSystemService(NotificationManager.class);
         }
 
+        IIpConnectivityMetrics getIIpConnectivityMetrics() {
+            return (IIpConnectivityMetrics) IIpConnectivityMetrics.Stub.asInterface(
+                ServiceManager.getService(IpConnectivityLog.SERVICE_NAME));
+        }
+
         PowerManagerInternal getPowerManagerInternal() {
             return LocalServices.getService(PowerManagerInternal.class);
         }
@@ -1415,6 +1470,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
             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));
@@ -1725,11 +1784,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
     }
 
     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
@@ -1937,7 +1999,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
         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,
@@ -3094,6 +3156,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
 
     @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 &&
@@ -5286,8 +5351,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
         }
 
         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();
@@ -5674,8 +5741,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
                 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) {
@@ -5784,6 +5855,31 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
     }
 
     /**
+     * @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.
      */
@@ -5881,8 +5977,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
             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);
@@ -6073,8 +6171,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
             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)) {
@@ -6395,9 +6495,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
      * 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.
@@ -6418,7 +6519,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
         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");
             }
@@ -6435,14 +6536,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
      * 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;
@@ -6531,6 +6634,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
         }
     }
 
+    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(),
@@ -7035,6 +7148,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
         if (!mHasFeature) {
             return null;
         }
+        enforceManageUsers();
         synchronized (this) {
             List<String> result = null;
             // If we have multiple profiles we return the intersection of the
@@ -8541,6 +8655,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
                         < 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: {
@@ -8566,6 +8684,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
                 return true;
             } catch (SecurityException se) {
                 return false;
+            } catch (NameNotFoundException e) {
+                return false;
             } finally {
                 mInjector.binderRestoreCallingIdentity(ident);
             }
@@ -8611,6 +8731,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
         }
     }
 
+    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) {
@@ -8689,7 +8816,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
      * 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;
         }
@@ -8709,7 +8837,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
                     if (mUserManager.getUserCount() > 1) {
                         return CODE_NONSYSTEM_USER_EXISTS;
                     }
-                    if (hasIncompatibleAccountsLocked(UserHandle.USER_SYSTEM, owner)) {
+                    if (hasIncompatibleAccountsOrNonAdb) {
                         return CODE_ACCOUNTS_NOT_EMPTY;
                     }
                 } else {
@@ -8737,7 +8865,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
     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);
         }
     }
 
@@ -9071,12 +9200,33 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
     }
 
     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());
             }
         }
     }
@@ -9310,8 +9460,25 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
      * - 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);
@@ -9319,22 +9486,30 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
             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) {
@@ -9342,28 +9517,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
             } 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);
@@ -9420,4 +9573,128 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
             }
         }
     }
+
+    @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());
+    }
 }