Send admin enabled broadcast in foreground queue

Currently admin enabled broadcast is sent as a background broadcast
upon ACTION_USER_STARTED. This can lead to delays on managed secondary
users. During that period the admin has no chance to apply any
policies, effectively leaving the user unmanaged. Sending it as a
foreground broadcast should speed up that process.

Using a foreground broadcast on ACTION_USER_STARTED can cause a race
condition where the user is not unlocked yet and therefore the
broadcast is omitted (unless the receiver is direct boot aware).
Sending the broadcast upon ACTION_USER_UNLOCKED fixes that issue.

Bug: 64382185
Test: manual
Change-Id: I3a89ba2e64cd6013723699cc1211b0144db254a6
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c7db794..be2b29a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -614,11 +614,15 @@
                 }
             } else if (Intent.ACTION_USER_STARTED.equals(action)) {
                 synchronized (DevicePolicyManagerService.this) {
+                    maybeSendAdminEnabledBroadcastLocked(userHandle);
                     // Reset the policy data
                     mUserData.remove(userHandle);
-                    sendAdminEnabledBroadcastLocked(userHandle);
                 }
                 handlePackagesChanged(null /* check all admins */, userHandle);
+            } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
+                synchronized (DevicePolicyManagerService.this) {
+                    maybeSendAdminEnabledBroadcastLocked(userHandle);
+                }
             } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
                 handlePackagesChanged(null /* check all admins */, userHandle);
             } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
@@ -1854,6 +1858,7 @@
         filter.addAction(Intent.ACTION_USER_ADDED);
         filter.addAction(Intent.ACTION_USER_REMOVED);
         filter.addAction(Intent.ACTION_USER_STARTED);
+        filter.addAction(Intent.ACTION_USER_UNLOCKED);
         filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
         mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
         filter = new IntentFilter();
@@ -2372,11 +2377,18 @@
         sendAdminCommandLocked(admin, action, null, result);
     }
 
-    /**
-     * Send an update to one specific admin, get notified when that admin returns a result.
-     */
     void sendAdminCommandLocked(ActiveAdmin admin, String action, Bundle adminExtras,
             BroadcastReceiver result) {
+        sendAdminCommandLocked(admin, action, adminExtras, result, false);
+    }
+
+    /**
+     * Send an update to one specific admin, get notified when that admin returns a result.
+     *
+     * @return whether the broadcast was successfully sent
+     */
+    boolean sendAdminCommandLocked(ActiveAdmin admin, String action, Bundle adminExtras,
+            BroadcastReceiver result, boolean inForeground) {
         Intent intent = new Intent(action);
         intent.setComponent(admin.info.getComponent());
         if (UserManager.isDeviceInDemoMode(mContext)) {
@@ -2385,15 +2397,25 @@
         if (action.equals(DeviceAdminReceiver.ACTION_PASSWORD_EXPIRING)) {
             intent.putExtra("expiration", admin.passwordExpirationDate);
         }
+        if (inForeground) {
+            intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        }
         if (adminExtras != null) {
             intent.putExtras(adminExtras);
         }
+        if (mInjector.getPackageManager().queryBroadcastReceiversAsUser(
+                intent,
+                PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
+                admin.getUserHandle()).isEmpty()) {
+            return false;
+        }
         if (result != null) {
             mContext.sendOrderedBroadcastAsUser(intent, admin.getUserHandle(),
                     null, result, mHandler, Activity.RESULT_OK, null, null);
         } else {
             mContext.sendBroadcastAsUser(intent, admin.getUserHandle());
         }
+        return true;
     }
 
     /**
@@ -8099,20 +8121,27 @@
         }
     }
 
-
-    private void sendAdminEnabledBroadcastLocked(int userHandle) {
+    private void maybeSendAdminEnabledBroadcastLocked(int userHandle) {
         DevicePolicyData policyData = getUserData(userHandle);
         if (policyData.mAdminBroadcastPending) {
             // Send the initialization data to profile owner and delete the data
             ActiveAdmin admin = getProfileOwnerAdminLocked(userHandle);
+            boolean clearInitBundle = true;
             if (admin != null) {
                 PersistableBundle initBundle = policyData.mInitBundle;
-                sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED,
-                        initBundle == null ? null : new Bundle(initBundle), null);
+                clearInitBundle = sendAdminCommandLocked(admin,
+                        DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED,
+                        initBundle == null ? null : new Bundle(initBundle),
+                        null /* result receiver */,
+                        true /* send in foreground */);
             }
-            policyData.mInitBundle = null;
-            policyData.mAdminBroadcastPending = false;
-            saveSettingsLocked(userHandle);
+            if (clearInitBundle) {
+                // If there's no admin or we've successfully called the admin, clear the init bundle
+                // otherwise, keep it around
+                policyData.mInitBundle = null;
+                policyData.mAdminBroadcastPending = false;
+                saveSettingsLocked(userHandle);
+            }
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 0fda0fe..e8a1811 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -58,6 +58,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.content.pm.StringParceledListSlice;
 import android.content.pm.UserInfo;
 import android.graphics.Color;
@@ -166,6 +167,11 @@
         mServiceContext.binder.callingUid = DpmMockContext.CALLER_UID;
         when(getServices().packageManager.hasSystemFeature(eq(PackageManager.FEATURE_DEVICE_ADMIN)))
                 .thenReturn(true);
+        doReturn(Collections.singletonList(new ResolveInfo()))
+                .when(getServices().packageManager).queryBroadcastReceiversAsUser(
+                        any(Intent.class),
+                        anyInt(),
+                        any(UserHandle.class));
 
         // By default, pretend all users are running and unlocked.
         when(getServices().userManager.isUserUnlocked(anyInt())).thenReturn(true);