Loading services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +113 −79 Original line number Diff line number Diff line Loading @@ -403,11 +403,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private static final long EXPIRATION_GRACE_PERIOD_MS = 5 * MS_PER_DAY; // 5 days, in ms private static final long MANAGED_PROFILE_MAXIMUM_TIME_OFF_THRESHOLD = 3 * MS_PER_DAY; /** When to warn the user about the approaching work profile off deadline: 1 day before */ private static final long MANAGED_PROFILE_OFF_WARNING_PERIOD = 1 * MS_PER_DAY; private static final String ACTION_EXPIRED_PASSWORD_NOTIFICATION = "com.android.server.ACTION_EXPIRED_PASSWORD_NOTIFICATION"; private static final String ACTION_PROFILE_OFF_DEADLINE = @VisibleForTesting static final String ACTION_PROFILE_OFF_DEADLINE = "com.android.server.ACTION_PROFILE_OFF_DEADLINE"; private static final String ATTR_PERMISSION_PROVIDER = "permission-provider"; Loading Loading @@ -649,6 +652,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private static final boolean ENABLE_LOCK_GUARD = true; /** Profile off deadline is not set or more than MANAGED_PROFILE_OFF_WARNING_PERIOD away. */ private static final int PROFILE_OFF_DEADLINE_DEFAULT = 0; /** Profile off deadline is closer than MANAGED_PROFILE_OFF_WARNING_PERIOD. */ private static final int PROFILE_OFF_DEADLINE_WARNING = 1; /** Profile off deadline reached, notify the user that personal apps blocked. */ private static final int PROFILE_OFF_DEADLINE_REACHED = 2; interface Stats { int LOCK_GUARD_GUARD = 0; Loading Loading @@ -926,11 +936,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mUserData.remove(userHandle); } handlePackagesChanged(null /* check all admins */, userHandle); updatePersonalAppsSuspensionOnUserStart(userHandle); } else if (Intent.ACTION_USER_STOPPED.equals(action)) { sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_STOPPED, userHandle); if (isManagedProfile(userHandle)) { Slog.d(LOG_TAG, "Managed profile was stopped"); updatePersonalAppSuspension(userHandle, false /* profileIsOn */); updatePersonalAppsSuspension(userHandle, false /* unlocked */); } } else if (Intent.ACTION_USER_SWITCHED.equals(action)) { sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_SWITCHED, userHandle); Loading @@ -940,7 +951,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } if (isManagedProfile(userHandle)) { Slog.d(LOG_TAG, "Managed profile became unlocked"); updatePersonalAppSuspension(userHandle, true /* profileIsOn */); updatePersonalAppsSuspension(userHandle, true /* unlocked */); } } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { handlePackagesChanged(null /* check all admins */, userHandle); Loading @@ -967,7 +978,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Slog.i(LOG_TAG, "Profile off deadline alarm was triggered"); final int userId = getManagedUserId(UserHandle.USER_SYSTEM); if (userId >= 0) { updatePersonalAppSuspension(userId, mUserManager.isUserUnlocked(userId)); updatePersonalAppsSuspension(userId, mUserManager.isUserUnlocked(userId)); } else { Slog.wtf(LOG_TAG, "Got deadline alarm for nonexistent profile"); } Loading Loading @@ -2486,6 +2497,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public void runCryptoSelfTest() { CryptoTestHelper.runAndLogSelfTest(); } public String[] getPersonalAppsForSuspension(int userId) { return new PersonalAppsSuspensionHelper( mContext.createContextAsUser(UserHandle.of(userId), 0 /* flags */)) .getPersonalAppsForSuspension(); } public long systemCurrentTimeMillis() { return System.currentTimeMillis(); } } /** Loading Loading @@ -4045,10 +4066,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { applyManagedProfileRestrictionIfDeviceOwnerLocked(); } maybeStartSecurityLogMonitorOnActivityManagerReady(); final int userId = getManagedUserId(UserHandle.USER_SYSTEM); if (userId >= 0) { updatePersonalAppSuspension(userId, false /* running */); } break; case SystemService.PHASE_BOOT_COMPLETED: ensureDeviceOwnerUserStarted(); // TODO Consider better place to do this. Loading @@ -4056,6 +4073,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } private void updatePersonalAppsSuspensionOnUserStart(int userHandle) { final int profileUserHandle = getManagedUserId(userHandle); if (profileUserHandle >= 0) { // Given that the parent user has just started, profile should be locked. updatePersonalAppsSuspension(profileUserHandle, false /* unlocked */); } else { suspendPersonalAppsInternal(userHandle, false); } } private void onLockSettingsReady() { getUserData(UserHandle.USER_SYSTEM); loadOwners(); Loading Loading @@ -15893,11 +15920,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } final int suspendedState = suspended ? PERSONAL_APPS_SUSPENDED_EXPLICITLY : PERSONAL_APPS_NOT_SUSPENDED; mInjector.binderWithCleanCallingIdentity( () -> applyPersonalAppsSuspension(callingUserId, suspendedState)); mInjector.binderWithCleanCallingIdentity(() -> updatePersonalAppsSuspension( callingUserId, mUserManager.isUserUnlocked(callingUserId))); DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_PERSONAL_APPS_SUSPENDED) Loading @@ -15907,44 +15931,54 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } /** * Checks whether there is a policy that requires personal apps to be suspended and if so, * applies it. * @param running whether the profile is currently considered running. * Checks whether personal apps should be suspended according to the policy and applies the * change if needed. * * @param unlocked whether the profile is currently running unlocked. */ private void updatePersonalAppSuspension(int profileUserId, boolean running) { final int suspensionState; private void updatePersonalAppsSuspension(int profileUserId, boolean unlocked) { final boolean suspended; synchronized (getLockObject()) { final ActiveAdmin profileOwner = getProfileOwnerAdminLocked(profileUserId); if (profileOwner != null) { final boolean deadlineReached = updateProfileOffDeadlineLocked(profileUserId, profileOwner, running); suspensionState = makeSuspensionReasons( profileOwner.mSuspendPersonalApps, deadlineReached); Slog.d(LOG_TAG, String.format("New personal apps suspension state: %d", suspensionState)); final int deadlineState = updateProfileOffDeadlineLocked(profileUserId, profileOwner, unlocked); suspended = profileOwner.mSuspendPersonalApps || deadlineState == PROFILE_OFF_DEADLINE_REACHED; Slog.d(LOG_TAG, String.format("Personal apps suspended: %b, deadline state: %d", suspended, deadlineState)); updateProfileOffDeadlineNotificationLocked(profileUserId, profileOwner, unlocked ? PROFILE_OFF_DEADLINE_DEFAULT : deadlineState); } else { suspensionState = PERSONAL_APPS_NOT_SUSPENDED; suspended = false; } } applyPersonalAppsSuspension(profileUserId, suspensionState); final int parentUserId = getProfileParentId(profileUserId); suspendPersonalAppsInternal(parentUserId, suspended); } /** * Checks work profile time off policy, scheduling personal apps suspension via alarm if * necessary. * @return whether the apps should be suspended based on maximum time off policy. * @return profile deadline state */ private boolean updateProfileOffDeadlineLocked( private int updateProfileOffDeadlineLocked( int profileUserId, ActiveAdmin profileOwner, boolean unlocked) { final long now = System.currentTimeMillis(); final long now = mInjector.systemCurrentTimeMillis(); if (profileOwner.mProfileOffDeadline != 0 && now > profileOwner.mProfileOffDeadline) { // Profile off deadline is already reached. Slog.i(LOG_TAG, "Profile off deadline has been reached."); return true; return PROFILE_OFF_DEADLINE_REACHED; } boolean shouldSaveSettings = false; if (profileOwner.mProfileOffDeadline != 0 if (profileOwner.mSuspendPersonalApps) { // When explicit suspension is active, deadline shouldn't be set. if (profileOwner.mProfileOffDeadline != 0) { profileOwner.mProfileOffDeadline = 0; shouldSaveSettings = true; } } else if (profileOwner.mProfileOffDeadline != 0 && (profileOwner.mProfileMaximumTimeOffMillis == 0 || unlocked)) { // There is a deadline but either there is no policy or the profile is unlocked -> clear // the deadline. Loading @@ -15960,52 +15994,51 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { shouldSaveSettings = true; } updateProfileOffAlarm(profileOwner.mProfileOffDeadline); if (shouldSaveSettings) { saveSettingsLocked(profileUserId); } return false; final long alarmTime; final int deadlineState; if (profileOwner.mProfileOffDeadline == 0) { alarmTime = 0; deadlineState = PROFILE_OFF_DEADLINE_DEFAULT; } else if (profileOwner.mProfileOffDeadline - now < MANAGED_PROFILE_OFF_WARNING_PERIOD) { // The deadline is close, upon the alarm personal apps should be suspended. alarmTime = profileOwner.mProfileOffDeadline; deadlineState = PROFILE_OFF_DEADLINE_WARNING; } else { // The deadline is quite far, upon the alarm we should warn the user first, so the // alarm is scheduled earlier than the actual deadline. alarmTime = profileOwner.mProfileOffDeadline - MANAGED_PROFILE_OFF_WARNING_PERIOD; deadlineState = PROFILE_OFF_DEADLINE_DEFAULT; } private void updateProfileOffAlarm(long profileOffDeadline) { final AlarmManager am = mInjector.getAlarmManager(); final PendingIntent pi = mInjector.pendingIntentGetBroadcast( mContext, REQUEST_PROFILE_OFF_DEADLINE, new Intent(ACTION_PROFILE_OFF_DEADLINE), PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT); if (alarmTime == 0) { Slog.i(LOG_TAG, "Profile off deadline alarm is removed."); am.cancel(pi); if (profileOffDeadline != 0) { Slog.i(LOG_TAG, "Profile off deadline alarm is set."); am.set(AlarmManager.RTC, profileOffDeadline, pi); } else { Slog.i(LOG_TAG, "Profile off deadline alarm is removed."); } Slog.i(LOG_TAG, "Profile off deadline alarm is set."); am.set(AlarmManager.RTC, alarmTime, pi); } private void applyPersonalAppsSuspension( int profileUserId, @PersonalAppsSuspensionReason int suspensionState) { final boolean suspended = getUserData(UserHandle.USER_SYSTEM).mAppsSuspended; final boolean shouldSuspend = suspensionState != PERSONAL_APPS_NOT_SUSPENDED; if (suspended != shouldSuspend) { suspendPersonalAppsInternal(shouldSuspend, UserHandle.USER_SYSTEM); return deadlineState; } if (suspensionState == PERSONAL_APPS_SUSPENDED_PROFILE_TIMEOUT) { sendPersonalAppsSuspendedNotification(profileUserId); } else { clearPersonalAppsSuspendedNotification(); } private void suspendPersonalAppsInternal(int userId, boolean suspended) { if (getUserData(userId).mAppsSuspended == suspended) { return; } private void suspendPersonalAppsInternal(boolean suspended, int userId) { Slog.i(LOG_TAG, String.format("%s personal apps for user %d", suspended ? "Suspending" : "Unsuspending", userId)); mInjector.binderWithCleanCallingIdentity(() -> { try { final String[] appsToSuspend = new PersonalAppsSuspensionHelper( mContext.createContextAsUser(UserHandle.of(userId), 0 /* flags */)) .getPersonalAppsForSuspension(); final String[] appsToSuspend = mInjector.getPersonalAppsForSuspension(userId); final String[] failedPackages = mIPackageManager.setPackagesSuspendedAsUser( appsToSuspend, suspended, null, null, null, PLATFORM_PACKAGE_NAME, userId); if (!ArrayUtils.isEmpty(failedPackages)) { Loading @@ -16024,35 +16057,36 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } private void clearPersonalAppsSuspendedNotification() { mInjector.binderWithCleanCallingIdentity(() -> mInjector.getNotificationManager().cancel( SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED)); } private void updateProfileOffDeadlineNotificationLocked(int profileUserId, @Nullable ActiveAdmin profileOwner, int notificationState) { private void sendPersonalAppsSuspendedNotification(int userId) { final String profileOwnerPackageName; final long maxTimeOffDays; synchronized (getLockObject()) { profileOwnerPackageName = mOwners.getProfileOwnerComponent(userId).getPackageName(); final ActiveAdmin poAdmin = getProfileOwnerAdminLocked(userId); maxTimeOffDays = TimeUnit.MILLISECONDS.toDays(poAdmin.mProfileMaximumTimeOffMillis); if (notificationState == PROFILE_OFF_DEADLINE_DEFAULT) { mInjector.getNotificationManager().cancel(SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED); return; } final String profileOwnerPackageName = profileOwner.info.getPackageName(); final long maxTimeOffDays = TimeUnit.MILLISECONDS.toDays(profileOwner.mProfileMaximumTimeOffMillis); final Intent intent = new Intent(DevicePolicyManager.ACTION_CHECK_POLICY_COMPLIANCE); intent.setPackage(profileOwnerPackageName); final PendingIntent pendingIntent = mInjector.pendingIntentGetActivityAsUser(mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_UPDATE_CURRENT, null /* options */, UserHandle.of(userId)); 0 /* requestCode */, intent, PendingIntent.FLAG_UPDATE_CURRENT, null /* options */, UserHandle.of(profileUserId)); // TODO(b/149075510): Only the first of the notifications should be dismissible. final String title = mContext.getString( notificationState == PROFILE_OFF_DEADLINE_WARNING ? R.string.personal_apps_suspended_tomorrow_title : R.string.personal_apps_suspended_title); final Notification notification = new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN) .setSmallIcon(android.R.drawable.stat_sys_warning) .setOngoing(true) .setContentTitle( mContext.getString( R.string.personal_apps_suspended_title)) .setContentTitle(title) .setContentText(mContext.getString( R.string.personal_apps_suspended_text, maxTimeOffDays)) .setColor(mContext.getColor(R.color.system_notification_accent_color)) Loading Loading @@ -16086,7 +16120,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } mInjector.binderWithCleanCallingIdentity( () -> updatePersonalAppSuspension(userId, mUserManager.isUserUnlocked())); () -> updatePersonalAppsSuspension(userId, mUserManager.isUserUnlocked())); DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_MANAGED_PROFILE_MAXIMUM_TIME_OFF) services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java +10 −5 Original line number Diff line number Diff line Loading @@ -51,6 +51,10 @@ import java.util.Set; public class PersonalAppsSuspensionHelper { private static final String LOG_TAG = DevicePolicyManagerService.LOG_TAG; // Flags to get all packages even if the user is still locked. private static final int PACKAGE_QUERY_FLAGS = PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE; private final Context mContext; private final PackageManager mPackageManager; Loading @@ -67,7 +71,7 @@ public class PersonalAppsSuspensionHelper { */ String[] getPersonalAppsForSuspension() { final List<PackageInfo> installedPackageInfos = mPackageManager.getInstalledPackages(0 /* flags */); mPackageManager.getInstalledPackages(PACKAGE_QUERY_FLAGS); final Set<String> result = new ArraySet<>(); for (final PackageInfo packageInfo : installedPackageInfos) { final ApplicationInfo info = packageInfo.applicationInfo; Loading Loading @@ -97,7 +101,7 @@ public class PersonalAppsSuspensionHelper { final Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_HOME); final List<ResolveInfo> matchingActivities = mPackageManager.queryIntentActivities(intent, 0); mPackageManager.queryIntentActivities(intent, PACKAGE_QUERY_FLAGS); for (final ResolveInfo resolveInfo : matchingActivities) { if (resolveInfo.activityInfo == null || TextUtils.isEmpty(resolveInfo.activityInfo.packageName)) { Loading @@ -107,7 +111,7 @@ public class PersonalAppsSuspensionHelper { final String packageName = resolveInfo.activityInfo.packageName; try { final ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(packageName, 0); mPackageManager.getApplicationInfo(packageName, PACKAGE_QUERY_FLAGS); if (applicationInfo.isSystemApp() || applicationInfo.isUpdatedSystemApp()) { result.add(packageName); } Loading Loading @@ -147,7 +151,8 @@ public class PersonalAppsSuspensionHelper { private String getSettingsPackageName() { final Intent intent = new Intent(Settings.ACTION_SETTINGS); intent.addCategory(Intent.CATEGORY_DEFAULT); final ResolveInfo resolveInfo = mPackageManager.resolveActivity(intent, /* flags= */ 0); final ResolveInfo resolveInfo = mPackageManager.resolveActivity(intent, PACKAGE_QUERY_FLAGS); if (resolveInfo != null) { return resolveInfo.activityInfo.packageName; } Loading @@ -164,7 +169,7 @@ public class PersonalAppsSuspensionHelper { intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER); intentToResolve.setPackage(packageName); final List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(intentToResolve, /* flags= */ 0); mPackageManager.queryIntentActivities(intentToResolve, PACKAGE_QUERY_FLAGS); return resolveInfos != null && !resolveInfos.isEmpty(); } Loading services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java +17 −0 Original line number Diff line number Diff line Loading @@ -124,6 +124,9 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi // Key is a pair of uri and userId private final Map<Pair<Uri, Integer>, ContentObserver> mContentObservers = new ArrayMap<>(); // Used as an override when set to nonzero. private long mCurrentTimeMillis = 0; public MockInjector(MockSystemServices services, DpmMockContext context) { super(context); this.services = services; Loading Loading @@ -470,5 +473,19 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi @Override public void runCryptoSelfTest() {} @Override public String[] getPersonalAppsForSuspension(int userId) { return new String[]{}; } public void setSystemCurrentTimeMillis(long value) { mCurrentTimeMillis = value; } @Override public long systemCurrentTimeMillis() { return mCurrentTimeMillis != 0 ? mCurrentTimeMillis : System.currentTimeMillis(); } } } services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +228 −16 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +113 −79 Original line number Diff line number Diff line Loading @@ -403,11 +403,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private static final long EXPIRATION_GRACE_PERIOD_MS = 5 * MS_PER_DAY; // 5 days, in ms private static final long MANAGED_PROFILE_MAXIMUM_TIME_OFF_THRESHOLD = 3 * MS_PER_DAY; /** When to warn the user about the approaching work profile off deadline: 1 day before */ private static final long MANAGED_PROFILE_OFF_WARNING_PERIOD = 1 * MS_PER_DAY; private static final String ACTION_EXPIRED_PASSWORD_NOTIFICATION = "com.android.server.ACTION_EXPIRED_PASSWORD_NOTIFICATION"; private static final String ACTION_PROFILE_OFF_DEADLINE = @VisibleForTesting static final String ACTION_PROFILE_OFF_DEADLINE = "com.android.server.ACTION_PROFILE_OFF_DEADLINE"; private static final String ATTR_PERMISSION_PROVIDER = "permission-provider"; Loading Loading @@ -649,6 +652,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private static final boolean ENABLE_LOCK_GUARD = true; /** Profile off deadline is not set or more than MANAGED_PROFILE_OFF_WARNING_PERIOD away. */ private static final int PROFILE_OFF_DEADLINE_DEFAULT = 0; /** Profile off deadline is closer than MANAGED_PROFILE_OFF_WARNING_PERIOD. */ private static final int PROFILE_OFF_DEADLINE_WARNING = 1; /** Profile off deadline reached, notify the user that personal apps blocked. */ private static final int PROFILE_OFF_DEADLINE_REACHED = 2; interface Stats { int LOCK_GUARD_GUARD = 0; Loading Loading @@ -926,11 +936,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mUserData.remove(userHandle); } handlePackagesChanged(null /* check all admins */, userHandle); updatePersonalAppsSuspensionOnUserStart(userHandle); } else if (Intent.ACTION_USER_STOPPED.equals(action)) { sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_STOPPED, userHandle); if (isManagedProfile(userHandle)) { Slog.d(LOG_TAG, "Managed profile was stopped"); updatePersonalAppSuspension(userHandle, false /* profileIsOn */); updatePersonalAppsSuspension(userHandle, false /* unlocked */); } } else if (Intent.ACTION_USER_SWITCHED.equals(action)) { sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_SWITCHED, userHandle); Loading @@ -940,7 +951,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } if (isManagedProfile(userHandle)) { Slog.d(LOG_TAG, "Managed profile became unlocked"); updatePersonalAppSuspension(userHandle, true /* profileIsOn */); updatePersonalAppsSuspension(userHandle, true /* unlocked */); } } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { handlePackagesChanged(null /* check all admins */, userHandle); Loading @@ -967,7 +978,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Slog.i(LOG_TAG, "Profile off deadline alarm was triggered"); final int userId = getManagedUserId(UserHandle.USER_SYSTEM); if (userId >= 0) { updatePersonalAppSuspension(userId, mUserManager.isUserUnlocked(userId)); updatePersonalAppsSuspension(userId, mUserManager.isUserUnlocked(userId)); } else { Slog.wtf(LOG_TAG, "Got deadline alarm for nonexistent profile"); } Loading Loading @@ -2486,6 +2497,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public void runCryptoSelfTest() { CryptoTestHelper.runAndLogSelfTest(); } public String[] getPersonalAppsForSuspension(int userId) { return new PersonalAppsSuspensionHelper( mContext.createContextAsUser(UserHandle.of(userId), 0 /* flags */)) .getPersonalAppsForSuspension(); } public long systemCurrentTimeMillis() { return System.currentTimeMillis(); } } /** Loading Loading @@ -4045,10 +4066,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { applyManagedProfileRestrictionIfDeviceOwnerLocked(); } maybeStartSecurityLogMonitorOnActivityManagerReady(); final int userId = getManagedUserId(UserHandle.USER_SYSTEM); if (userId >= 0) { updatePersonalAppSuspension(userId, false /* running */); } break; case SystemService.PHASE_BOOT_COMPLETED: ensureDeviceOwnerUserStarted(); // TODO Consider better place to do this. Loading @@ -4056,6 +4073,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } private void updatePersonalAppsSuspensionOnUserStart(int userHandle) { final int profileUserHandle = getManagedUserId(userHandle); if (profileUserHandle >= 0) { // Given that the parent user has just started, profile should be locked. updatePersonalAppsSuspension(profileUserHandle, false /* unlocked */); } else { suspendPersonalAppsInternal(userHandle, false); } } private void onLockSettingsReady() { getUserData(UserHandle.USER_SYSTEM); loadOwners(); Loading Loading @@ -15893,11 +15920,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } final int suspendedState = suspended ? PERSONAL_APPS_SUSPENDED_EXPLICITLY : PERSONAL_APPS_NOT_SUSPENDED; mInjector.binderWithCleanCallingIdentity( () -> applyPersonalAppsSuspension(callingUserId, suspendedState)); mInjector.binderWithCleanCallingIdentity(() -> updatePersonalAppsSuspension( callingUserId, mUserManager.isUserUnlocked(callingUserId))); DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_PERSONAL_APPS_SUSPENDED) Loading @@ -15907,44 +15931,54 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } /** * Checks whether there is a policy that requires personal apps to be suspended and if so, * applies it. * @param running whether the profile is currently considered running. * Checks whether personal apps should be suspended according to the policy and applies the * change if needed. * * @param unlocked whether the profile is currently running unlocked. */ private void updatePersonalAppSuspension(int profileUserId, boolean running) { final int suspensionState; private void updatePersonalAppsSuspension(int profileUserId, boolean unlocked) { final boolean suspended; synchronized (getLockObject()) { final ActiveAdmin profileOwner = getProfileOwnerAdminLocked(profileUserId); if (profileOwner != null) { final boolean deadlineReached = updateProfileOffDeadlineLocked(profileUserId, profileOwner, running); suspensionState = makeSuspensionReasons( profileOwner.mSuspendPersonalApps, deadlineReached); Slog.d(LOG_TAG, String.format("New personal apps suspension state: %d", suspensionState)); final int deadlineState = updateProfileOffDeadlineLocked(profileUserId, profileOwner, unlocked); suspended = profileOwner.mSuspendPersonalApps || deadlineState == PROFILE_OFF_DEADLINE_REACHED; Slog.d(LOG_TAG, String.format("Personal apps suspended: %b, deadline state: %d", suspended, deadlineState)); updateProfileOffDeadlineNotificationLocked(profileUserId, profileOwner, unlocked ? PROFILE_OFF_DEADLINE_DEFAULT : deadlineState); } else { suspensionState = PERSONAL_APPS_NOT_SUSPENDED; suspended = false; } } applyPersonalAppsSuspension(profileUserId, suspensionState); final int parentUserId = getProfileParentId(profileUserId); suspendPersonalAppsInternal(parentUserId, suspended); } /** * Checks work profile time off policy, scheduling personal apps suspension via alarm if * necessary. * @return whether the apps should be suspended based on maximum time off policy. * @return profile deadline state */ private boolean updateProfileOffDeadlineLocked( private int updateProfileOffDeadlineLocked( int profileUserId, ActiveAdmin profileOwner, boolean unlocked) { final long now = System.currentTimeMillis(); final long now = mInjector.systemCurrentTimeMillis(); if (profileOwner.mProfileOffDeadline != 0 && now > profileOwner.mProfileOffDeadline) { // Profile off deadline is already reached. Slog.i(LOG_TAG, "Profile off deadline has been reached."); return true; return PROFILE_OFF_DEADLINE_REACHED; } boolean shouldSaveSettings = false; if (profileOwner.mProfileOffDeadline != 0 if (profileOwner.mSuspendPersonalApps) { // When explicit suspension is active, deadline shouldn't be set. if (profileOwner.mProfileOffDeadline != 0) { profileOwner.mProfileOffDeadline = 0; shouldSaveSettings = true; } } else if (profileOwner.mProfileOffDeadline != 0 && (profileOwner.mProfileMaximumTimeOffMillis == 0 || unlocked)) { // There is a deadline but either there is no policy or the profile is unlocked -> clear // the deadline. Loading @@ -15960,52 +15994,51 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { shouldSaveSettings = true; } updateProfileOffAlarm(profileOwner.mProfileOffDeadline); if (shouldSaveSettings) { saveSettingsLocked(profileUserId); } return false; final long alarmTime; final int deadlineState; if (profileOwner.mProfileOffDeadline == 0) { alarmTime = 0; deadlineState = PROFILE_OFF_DEADLINE_DEFAULT; } else if (profileOwner.mProfileOffDeadline - now < MANAGED_PROFILE_OFF_WARNING_PERIOD) { // The deadline is close, upon the alarm personal apps should be suspended. alarmTime = profileOwner.mProfileOffDeadline; deadlineState = PROFILE_OFF_DEADLINE_WARNING; } else { // The deadline is quite far, upon the alarm we should warn the user first, so the // alarm is scheduled earlier than the actual deadline. alarmTime = profileOwner.mProfileOffDeadline - MANAGED_PROFILE_OFF_WARNING_PERIOD; deadlineState = PROFILE_OFF_DEADLINE_DEFAULT; } private void updateProfileOffAlarm(long profileOffDeadline) { final AlarmManager am = mInjector.getAlarmManager(); final PendingIntent pi = mInjector.pendingIntentGetBroadcast( mContext, REQUEST_PROFILE_OFF_DEADLINE, new Intent(ACTION_PROFILE_OFF_DEADLINE), PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT); if (alarmTime == 0) { Slog.i(LOG_TAG, "Profile off deadline alarm is removed."); am.cancel(pi); if (profileOffDeadline != 0) { Slog.i(LOG_TAG, "Profile off deadline alarm is set."); am.set(AlarmManager.RTC, profileOffDeadline, pi); } else { Slog.i(LOG_TAG, "Profile off deadline alarm is removed."); } Slog.i(LOG_TAG, "Profile off deadline alarm is set."); am.set(AlarmManager.RTC, alarmTime, pi); } private void applyPersonalAppsSuspension( int profileUserId, @PersonalAppsSuspensionReason int suspensionState) { final boolean suspended = getUserData(UserHandle.USER_SYSTEM).mAppsSuspended; final boolean shouldSuspend = suspensionState != PERSONAL_APPS_NOT_SUSPENDED; if (suspended != shouldSuspend) { suspendPersonalAppsInternal(shouldSuspend, UserHandle.USER_SYSTEM); return deadlineState; } if (suspensionState == PERSONAL_APPS_SUSPENDED_PROFILE_TIMEOUT) { sendPersonalAppsSuspendedNotification(profileUserId); } else { clearPersonalAppsSuspendedNotification(); } private void suspendPersonalAppsInternal(int userId, boolean suspended) { if (getUserData(userId).mAppsSuspended == suspended) { return; } private void suspendPersonalAppsInternal(boolean suspended, int userId) { Slog.i(LOG_TAG, String.format("%s personal apps for user %d", suspended ? "Suspending" : "Unsuspending", userId)); mInjector.binderWithCleanCallingIdentity(() -> { try { final String[] appsToSuspend = new PersonalAppsSuspensionHelper( mContext.createContextAsUser(UserHandle.of(userId), 0 /* flags */)) .getPersonalAppsForSuspension(); final String[] appsToSuspend = mInjector.getPersonalAppsForSuspension(userId); final String[] failedPackages = mIPackageManager.setPackagesSuspendedAsUser( appsToSuspend, suspended, null, null, null, PLATFORM_PACKAGE_NAME, userId); if (!ArrayUtils.isEmpty(failedPackages)) { Loading @@ -16024,35 +16057,36 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } private void clearPersonalAppsSuspendedNotification() { mInjector.binderWithCleanCallingIdentity(() -> mInjector.getNotificationManager().cancel( SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED)); } private void updateProfileOffDeadlineNotificationLocked(int profileUserId, @Nullable ActiveAdmin profileOwner, int notificationState) { private void sendPersonalAppsSuspendedNotification(int userId) { final String profileOwnerPackageName; final long maxTimeOffDays; synchronized (getLockObject()) { profileOwnerPackageName = mOwners.getProfileOwnerComponent(userId).getPackageName(); final ActiveAdmin poAdmin = getProfileOwnerAdminLocked(userId); maxTimeOffDays = TimeUnit.MILLISECONDS.toDays(poAdmin.mProfileMaximumTimeOffMillis); if (notificationState == PROFILE_OFF_DEADLINE_DEFAULT) { mInjector.getNotificationManager().cancel(SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED); return; } final String profileOwnerPackageName = profileOwner.info.getPackageName(); final long maxTimeOffDays = TimeUnit.MILLISECONDS.toDays(profileOwner.mProfileMaximumTimeOffMillis); final Intent intent = new Intent(DevicePolicyManager.ACTION_CHECK_POLICY_COMPLIANCE); intent.setPackage(profileOwnerPackageName); final PendingIntent pendingIntent = mInjector.pendingIntentGetActivityAsUser(mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_UPDATE_CURRENT, null /* options */, UserHandle.of(userId)); 0 /* requestCode */, intent, PendingIntent.FLAG_UPDATE_CURRENT, null /* options */, UserHandle.of(profileUserId)); // TODO(b/149075510): Only the first of the notifications should be dismissible. final String title = mContext.getString( notificationState == PROFILE_OFF_DEADLINE_WARNING ? R.string.personal_apps_suspended_tomorrow_title : R.string.personal_apps_suspended_title); final Notification notification = new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN) .setSmallIcon(android.R.drawable.stat_sys_warning) .setOngoing(true) .setContentTitle( mContext.getString( R.string.personal_apps_suspended_title)) .setContentTitle(title) .setContentText(mContext.getString( R.string.personal_apps_suspended_text, maxTimeOffDays)) .setColor(mContext.getColor(R.color.system_notification_accent_color)) Loading Loading @@ -16086,7 +16120,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } mInjector.binderWithCleanCallingIdentity( () -> updatePersonalAppSuspension(userId, mUserManager.isUserUnlocked())); () -> updatePersonalAppsSuspension(userId, mUserManager.isUserUnlocked())); DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_MANAGED_PROFILE_MAXIMUM_TIME_OFF)
services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java +10 −5 Original line number Diff line number Diff line Loading @@ -51,6 +51,10 @@ import java.util.Set; public class PersonalAppsSuspensionHelper { private static final String LOG_TAG = DevicePolicyManagerService.LOG_TAG; // Flags to get all packages even if the user is still locked. private static final int PACKAGE_QUERY_FLAGS = PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE; private final Context mContext; private final PackageManager mPackageManager; Loading @@ -67,7 +71,7 @@ public class PersonalAppsSuspensionHelper { */ String[] getPersonalAppsForSuspension() { final List<PackageInfo> installedPackageInfos = mPackageManager.getInstalledPackages(0 /* flags */); mPackageManager.getInstalledPackages(PACKAGE_QUERY_FLAGS); final Set<String> result = new ArraySet<>(); for (final PackageInfo packageInfo : installedPackageInfos) { final ApplicationInfo info = packageInfo.applicationInfo; Loading Loading @@ -97,7 +101,7 @@ public class PersonalAppsSuspensionHelper { final Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_HOME); final List<ResolveInfo> matchingActivities = mPackageManager.queryIntentActivities(intent, 0); mPackageManager.queryIntentActivities(intent, PACKAGE_QUERY_FLAGS); for (final ResolveInfo resolveInfo : matchingActivities) { if (resolveInfo.activityInfo == null || TextUtils.isEmpty(resolveInfo.activityInfo.packageName)) { Loading @@ -107,7 +111,7 @@ public class PersonalAppsSuspensionHelper { final String packageName = resolveInfo.activityInfo.packageName; try { final ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(packageName, 0); mPackageManager.getApplicationInfo(packageName, PACKAGE_QUERY_FLAGS); if (applicationInfo.isSystemApp() || applicationInfo.isUpdatedSystemApp()) { result.add(packageName); } Loading Loading @@ -147,7 +151,8 @@ public class PersonalAppsSuspensionHelper { private String getSettingsPackageName() { final Intent intent = new Intent(Settings.ACTION_SETTINGS); intent.addCategory(Intent.CATEGORY_DEFAULT); final ResolveInfo resolveInfo = mPackageManager.resolveActivity(intent, /* flags= */ 0); final ResolveInfo resolveInfo = mPackageManager.resolveActivity(intent, PACKAGE_QUERY_FLAGS); if (resolveInfo != null) { return resolveInfo.activityInfo.packageName; } Loading @@ -164,7 +169,7 @@ public class PersonalAppsSuspensionHelper { intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER); intentToResolve.setPackage(packageName); final List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(intentToResolve, /* flags= */ 0); mPackageManager.queryIntentActivities(intentToResolve, PACKAGE_QUERY_FLAGS); return resolveInfos != null && !resolveInfos.isEmpty(); } Loading
services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java +17 −0 Original line number Diff line number Diff line Loading @@ -124,6 +124,9 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi // Key is a pair of uri and userId private final Map<Pair<Uri, Integer>, ContentObserver> mContentObservers = new ArrayMap<>(); // Used as an override when set to nonzero. private long mCurrentTimeMillis = 0; public MockInjector(MockSystemServices services, DpmMockContext context) { super(context); this.services = services; Loading Loading @@ -470,5 +473,19 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi @Override public void runCryptoSelfTest() {} @Override public String[] getPersonalAppsForSuspension(int userId) { return new String[]{}; } public void setSystemCurrentTimeMillis(long value) { mCurrentTimeMillis = value; } @Override public long systemCurrentTimeMillis() { return mCurrentTimeMillis != 0 ? mCurrentTimeMillis : System.currentTimeMillis(); } } }
services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +228 −16 File changed.Preview size limit exceeded, changes collapsed. Show changes