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);