Merge "Warn the user about impending personal app suspension." into rvc-dev
This commit is contained in:
@@ -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 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;
|
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 =
|
private static final String ACTION_EXPIRED_PASSWORD_NOTIFICATION =
|
||||||
"com.android.server.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";
|
"com.android.server.ACTION_PROFILE_OFF_DEADLINE";
|
||||||
|
|
||||||
private static final String ATTR_PERMISSION_PROVIDER = "permission-provider";
|
private static final String ATTR_PERMISSION_PROVIDER = "permission-provider";
|
||||||
@@ -649,6 +652,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
|
|||||||
|
|
||||||
private static final boolean ENABLE_LOCK_GUARD = true;
|
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 {
|
interface Stats {
|
||||||
int LOCK_GUARD_GUARD = 0;
|
int LOCK_GUARD_GUARD = 0;
|
||||||
|
|
||||||
@@ -926,11 +936,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
|
|||||||
mUserData.remove(userHandle);
|
mUserData.remove(userHandle);
|
||||||
}
|
}
|
||||||
handlePackagesChanged(null /* check all admins */, userHandle);
|
handlePackagesChanged(null /* check all admins */, userHandle);
|
||||||
|
updatePersonalAppsSuspensionOnUserStart(userHandle);
|
||||||
} else if (Intent.ACTION_USER_STOPPED.equals(action)) {
|
} else if (Intent.ACTION_USER_STOPPED.equals(action)) {
|
||||||
sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_STOPPED, userHandle);
|
sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_STOPPED, userHandle);
|
||||||
if (isManagedProfile(userHandle)) {
|
if (isManagedProfile(userHandle)) {
|
||||||
Slog.d(LOG_TAG, "Managed profile was stopped");
|
Slog.d(LOG_TAG, "Managed profile was stopped");
|
||||||
updatePersonalAppSuspension(userHandle, false /* profileIsOn */);
|
updatePersonalAppsSuspension(userHandle, false /* unlocked */);
|
||||||
}
|
}
|
||||||
} else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
|
} else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
|
||||||
sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_SWITCHED, userHandle);
|
sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_SWITCHED, userHandle);
|
||||||
@@ -940,7 +951,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
|
|||||||
}
|
}
|
||||||
if (isManagedProfile(userHandle)) {
|
if (isManagedProfile(userHandle)) {
|
||||||
Slog.d(LOG_TAG, "Managed profile became unlocked");
|
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)) {
|
} else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
|
||||||
handlePackagesChanged(null /* check all admins */, userHandle);
|
handlePackagesChanged(null /* check all admins */, userHandle);
|
||||||
@@ -967,7 +978,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
|
|||||||
Slog.i(LOG_TAG, "Profile off deadline alarm was triggered");
|
Slog.i(LOG_TAG, "Profile off deadline alarm was triggered");
|
||||||
final int userId = getManagedUserId(UserHandle.USER_SYSTEM);
|
final int userId = getManagedUserId(UserHandle.USER_SYSTEM);
|
||||||
if (userId >= 0) {
|
if (userId >= 0) {
|
||||||
updatePersonalAppSuspension(userId, mUserManager.isUserUnlocked(userId));
|
updatePersonalAppsSuspension(userId, mUserManager.isUserUnlocked(userId));
|
||||||
} else {
|
} else {
|
||||||
Slog.wtf(LOG_TAG, "Got deadline alarm for nonexistent profile");
|
Slog.wtf(LOG_TAG, "Got deadline alarm for nonexistent profile");
|
||||||
}
|
}
|
||||||
@@ -2486,6 +2497,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
|
|||||||
public void runCryptoSelfTest() {
|
public void runCryptoSelfTest() {
|
||||||
CryptoTestHelper.runAndLogSelfTest();
|
CryptoTestHelper.runAndLogSelfTest();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String[] getPersonalAppsForSuspension(int userId) {
|
||||||
|
return new PersonalAppsSuspensionHelper(
|
||||||
|
mContext.createContextAsUser(UserHandle.of(userId), 0 /* flags */))
|
||||||
|
.getPersonalAppsForSuspension();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long systemCurrentTimeMillis() {
|
||||||
|
return System.currentTimeMillis();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -4045,10 +4066,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
|
|||||||
applyManagedProfileRestrictionIfDeviceOwnerLocked();
|
applyManagedProfileRestrictionIfDeviceOwnerLocked();
|
||||||
}
|
}
|
||||||
maybeStartSecurityLogMonitorOnActivityManagerReady();
|
maybeStartSecurityLogMonitorOnActivityManagerReady();
|
||||||
final int userId = getManagedUserId(UserHandle.USER_SYSTEM);
|
|
||||||
if (userId >= 0) {
|
|
||||||
updatePersonalAppSuspension(userId, false /* running */);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case SystemService.PHASE_BOOT_COMPLETED:
|
case SystemService.PHASE_BOOT_COMPLETED:
|
||||||
ensureDeviceOwnerUserStarted(); // TODO Consider better place to do this.
|
ensureDeviceOwnerUserStarted(); // TODO Consider better place to do this.
|
||||||
@@ -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() {
|
private void onLockSettingsReady() {
|
||||||
getUserData(UserHandle.USER_SYSTEM);
|
getUserData(UserHandle.USER_SYSTEM);
|
||||||
loadOwners();
|
loadOwners();
|
||||||
@@ -15893,11 +15920,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final int suspendedState = suspended
|
mInjector.binderWithCleanCallingIdentity(() -> updatePersonalAppsSuspension(
|
||||||
? PERSONAL_APPS_SUSPENDED_EXPLICITLY
|
callingUserId, mUserManager.isUserUnlocked(callingUserId)));
|
||||||
: PERSONAL_APPS_NOT_SUSPENDED;
|
|
||||||
mInjector.binderWithCleanCallingIdentity(
|
|
||||||
() -> applyPersonalAppsSuspension(callingUserId, suspendedState));
|
|
||||||
|
|
||||||
DevicePolicyEventLogger
|
DevicePolicyEventLogger
|
||||||
.createEvent(DevicePolicyEnums.SET_PERSONAL_APPS_SUSPENDED)
|
.createEvent(DevicePolicyEnums.SET_PERSONAL_APPS_SUSPENDED)
|
||||||
@@ -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,
|
* Checks whether personal apps should be suspended according to the policy and applies the
|
||||||
* applies it.
|
* change if needed.
|
||||||
* @param running whether the profile is currently considered running.
|
*
|
||||||
|
* @param unlocked whether the profile is currently running unlocked.
|
||||||
*/
|
*/
|
||||||
private void updatePersonalAppSuspension(int profileUserId, boolean running) {
|
private void updatePersonalAppsSuspension(int profileUserId, boolean unlocked) {
|
||||||
final int suspensionState;
|
final boolean suspended;
|
||||||
synchronized (getLockObject()) {
|
synchronized (getLockObject()) {
|
||||||
final ActiveAdmin profileOwner = getProfileOwnerAdminLocked(profileUserId);
|
final ActiveAdmin profileOwner = getProfileOwnerAdminLocked(profileUserId);
|
||||||
if (profileOwner != null) {
|
if (profileOwner != null) {
|
||||||
final boolean deadlineReached =
|
final int deadlineState =
|
||||||
updateProfileOffDeadlineLocked(profileUserId, profileOwner, running);
|
updateProfileOffDeadlineLocked(profileUserId, profileOwner, unlocked);
|
||||||
suspensionState = makeSuspensionReasons(
|
suspended = profileOwner.mSuspendPersonalApps
|
||||||
profileOwner.mSuspendPersonalApps, deadlineReached);
|
|| deadlineState == PROFILE_OFF_DEADLINE_REACHED;
|
||||||
Slog.d(LOG_TAG,
|
Slog.d(LOG_TAG, String.format("Personal apps suspended: %b, deadline state: %d",
|
||||||
String.format("New personal apps suspension state: %d", suspensionState));
|
suspended, deadlineState));
|
||||||
|
updateProfileOffDeadlineNotificationLocked(profileUserId, profileOwner,
|
||||||
|
unlocked ? PROFILE_OFF_DEADLINE_DEFAULT : deadlineState);
|
||||||
} else {
|
} 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
|
* Checks work profile time off policy, scheduling personal apps suspension via alarm if
|
||||||
* necessary.
|
* 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) {
|
int profileUserId, ActiveAdmin profileOwner, boolean unlocked) {
|
||||||
final long now = System.currentTimeMillis();
|
final long now = mInjector.systemCurrentTimeMillis();
|
||||||
if (profileOwner.mProfileOffDeadline != 0 && now > profileOwner.mProfileOffDeadline) {
|
if (profileOwner.mProfileOffDeadline != 0 && now > profileOwner.mProfileOffDeadline) {
|
||||||
// Profile off deadline is already reached.
|
// Profile off deadline is already reached.
|
||||||
Slog.i(LOG_TAG, "Profile off deadline has been reached.");
|
Slog.i(LOG_TAG, "Profile off deadline has been reached.");
|
||||||
return true;
|
return PROFILE_OFF_DEADLINE_REACHED;
|
||||||
}
|
}
|
||||||
boolean shouldSaveSettings = false;
|
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)) {
|
&& (profileOwner.mProfileMaximumTimeOffMillis == 0 || unlocked)) {
|
||||||
// There is a deadline but either there is no policy or the profile is unlocked -> clear
|
// There is a deadline but either there is no policy or the profile is unlocked -> clear
|
||||||
// the deadline.
|
// the deadline.
|
||||||
@@ -15960,52 +15994,51 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
|
|||||||
shouldSaveSettings = true;
|
shouldSaveSettings = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateProfileOffAlarm(profileOwner.mProfileOffDeadline);
|
|
||||||
|
|
||||||
if (shouldSaveSettings) {
|
if (shouldSaveSettings) {
|
||||||
saveSettingsLocked(profileUserId);
|
saveSettingsLocked(profileUserId);
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateProfileOffAlarm(long profileOffDeadline) {
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
final AlarmManager am = mInjector.getAlarmManager();
|
final AlarmManager am = mInjector.getAlarmManager();
|
||||||
final PendingIntent pi = mInjector.pendingIntentGetBroadcast(
|
final PendingIntent pi = mInjector.pendingIntentGetBroadcast(
|
||||||
mContext, REQUEST_PROFILE_OFF_DEADLINE, new Intent(ACTION_PROFILE_OFF_DEADLINE),
|
mContext, REQUEST_PROFILE_OFF_DEADLINE, new Intent(ACTION_PROFILE_OFF_DEADLINE),
|
||||||
PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
|
PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
am.cancel(pi);
|
|
||||||
if (profileOffDeadline != 0) {
|
if (alarmTime == 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 removed.");
|
||||||
}
|
am.cancel(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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (suspensionState == PERSONAL_APPS_SUSPENDED_PROFILE_TIMEOUT) {
|
|
||||||
sendPersonalAppsSuspendedNotification(profileUserId);
|
|
||||||
} else {
|
} else {
|
||||||
clearPersonalAppsSuspendedNotification();
|
Slog.i(LOG_TAG, "Profile off deadline alarm is set.");
|
||||||
|
am.set(AlarmManager.RTC, alarmTime, pi);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return deadlineState;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void suspendPersonalAppsInternal(boolean suspended, int userId) {
|
private void suspendPersonalAppsInternal(int userId, boolean suspended) {
|
||||||
|
if (getUserData(userId).mAppsSuspended == suspended) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Slog.i(LOG_TAG, String.format("%s personal apps for user %d",
|
Slog.i(LOG_TAG, String.format("%s personal apps for user %d",
|
||||||
suspended ? "Suspending" : "Unsuspending", userId));
|
suspended ? "Suspending" : "Unsuspending", userId));
|
||||||
mInjector.binderWithCleanCallingIdentity(() -> {
|
mInjector.binderWithCleanCallingIdentity(() -> {
|
||||||
try {
|
try {
|
||||||
final String[] appsToSuspend =
|
final String[] appsToSuspend = mInjector.getPersonalAppsForSuspension(userId);
|
||||||
new PersonalAppsSuspensionHelper(
|
|
||||||
mContext.createContextAsUser(UserHandle.of(userId), 0 /* flags */))
|
|
||||||
.getPersonalAppsForSuspension();
|
|
||||||
final String[] failedPackages = mIPackageManager.setPackagesSuspendedAsUser(
|
final String[] failedPackages = mIPackageManager.setPackagesSuspendedAsUser(
|
||||||
appsToSuspend, suspended, null, null, null, PLATFORM_PACKAGE_NAME, userId);
|
appsToSuspend, suspended, null, null, null, PLATFORM_PACKAGE_NAME, userId);
|
||||||
if (!ArrayUtils.isEmpty(failedPackages)) {
|
if (!ArrayUtils.isEmpty(failedPackages)) {
|
||||||
@@ -16024,37 +16057,38 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clearPersonalAppsSuspendedNotification() {
|
private void updateProfileOffDeadlineNotificationLocked(int profileUserId,
|
||||||
mInjector.binderWithCleanCallingIdentity(() ->
|
@Nullable ActiveAdmin profileOwner, int notificationState) {
|
||||||
mInjector.getNotificationManager().cancel(
|
|
||||||
SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendPersonalAppsSuspendedNotification(int userId) {
|
if (notificationState == PROFILE_OFF_DEADLINE_DEFAULT) {
|
||||||
final String profileOwnerPackageName;
|
mInjector.getNotificationManager().cancel(SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED);
|
||||||
final long maxTimeOffDays;
|
return;
|
||||||
synchronized (getLockObject()) {
|
|
||||||
profileOwnerPackageName = mOwners.getProfileOwnerComponent(userId).getPackageName();
|
|
||||||
final ActiveAdmin poAdmin = getProfileOwnerAdminLocked(userId);
|
|
||||||
maxTimeOffDays = TimeUnit.MILLISECONDS.toDays(poAdmin.mProfileMaximumTimeOffMillis);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final String profileOwnerPackageName = profileOwner.info.getPackageName();
|
||||||
|
final long maxTimeOffDays =
|
||||||
|
TimeUnit.MILLISECONDS.toDays(profileOwner.mProfileMaximumTimeOffMillis);
|
||||||
|
|
||||||
final Intent intent = new Intent(DevicePolicyManager.ACTION_CHECK_POLICY_COMPLIANCE);
|
final Intent intent = new Intent(DevicePolicyManager.ACTION_CHECK_POLICY_COMPLIANCE);
|
||||||
intent.setPackage(profileOwnerPackageName);
|
intent.setPackage(profileOwnerPackageName);
|
||||||
|
|
||||||
final PendingIntent pendingIntent = mInjector.pendingIntentGetActivityAsUser(mContext,
|
final PendingIntent pendingIntent = mInjector.pendingIntentGetActivityAsUser(mContext,
|
||||||
0 /* requestCode */, intent, PendingIntent.FLAG_UPDATE_CURRENT, null /* options */,
|
0 /* requestCode */, intent, PendingIntent.FLAG_UPDATE_CURRENT,
|
||||||
UserHandle.of(userId));
|
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 =
|
final Notification notification =
|
||||||
new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN)
|
new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN)
|
||||||
.setSmallIcon(android.R.drawable.stat_sys_warning)
|
.setSmallIcon(android.R.drawable.stat_sys_warning)
|
||||||
.setOngoing(true)
|
.setOngoing(true)
|
||||||
.setContentTitle(
|
.setContentTitle(title)
|
||||||
mContext.getString(
|
|
||||||
R.string.personal_apps_suspended_title))
|
|
||||||
.setContentText(mContext.getString(
|
.setContentText(mContext.getString(
|
||||||
R.string.personal_apps_suspended_text, maxTimeOffDays))
|
R.string.personal_apps_suspended_text, maxTimeOffDays))
|
||||||
.setColor(mContext.getColor(R.color.system_notification_accent_color))
|
.setColor(mContext.getColor(R.color.system_notification_accent_color))
|
||||||
.setContentIntent(pendingIntent)
|
.setContentIntent(pendingIntent)
|
||||||
.build();
|
.build();
|
||||||
@@ -16086,7 +16120,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mInjector.binderWithCleanCallingIdentity(
|
mInjector.binderWithCleanCallingIdentity(
|
||||||
() -> updatePersonalAppSuspension(userId, mUserManager.isUserUnlocked()));
|
() -> updatePersonalAppsSuspension(userId, mUserManager.isUserUnlocked()));
|
||||||
|
|
||||||
DevicePolicyEventLogger
|
DevicePolicyEventLogger
|
||||||
.createEvent(DevicePolicyEnums.SET_MANAGED_PROFILE_MAXIMUM_TIME_OFF)
|
.createEvent(DevicePolicyEnums.SET_MANAGED_PROFILE_MAXIMUM_TIME_OFF)
|
||||||
|
|||||||
@@ -51,6 +51,10 @@ import java.util.Set;
|
|||||||
public class PersonalAppsSuspensionHelper {
|
public class PersonalAppsSuspensionHelper {
|
||||||
private static final String LOG_TAG = DevicePolicyManagerService.LOG_TAG;
|
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 Context mContext;
|
||||||
private final PackageManager mPackageManager;
|
private final PackageManager mPackageManager;
|
||||||
|
|
||||||
@@ -67,7 +71,7 @@ public class PersonalAppsSuspensionHelper {
|
|||||||
*/
|
*/
|
||||||
String[] getPersonalAppsForSuspension() {
|
String[] getPersonalAppsForSuspension() {
|
||||||
final List<PackageInfo> installedPackageInfos =
|
final List<PackageInfo> installedPackageInfos =
|
||||||
mPackageManager.getInstalledPackages(0 /* flags */);
|
mPackageManager.getInstalledPackages(PACKAGE_QUERY_FLAGS);
|
||||||
final Set<String> result = new ArraySet<>();
|
final Set<String> result = new ArraySet<>();
|
||||||
for (final PackageInfo packageInfo : installedPackageInfos) {
|
for (final PackageInfo packageInfo : installedPackageInfos) {
|
||||||
final ApplicationInfo info = packageInfo.applicationInfo;
|
final ApplicationInfo info = packageInfo.applicationInfo;
|
||||||
@@ -97,7 +101,7 @@ public class PersonalAppsSuspensionHelper {
|
|||||||
final Intent intent = new Intent(Intent.ACTION_MAIN);
|
final Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||||
intent.addCategory(Intent.CATEGORY_HOME);
|
intent.addCategory(Intent.CATEGORY_HOME);
|
||||||
final List<ResolveInfo> matchingActivities =
|
final List<ResolveInfo> matchingActivities =
|
||||||
mPackageManager.queryIntentActivities(intent, 0);
|
mPackageManager.queryIntentActivities(intent, PACKAGE_QUERY_FLAGS);
|
||||||
for (final ResolveInfo resolveInfo : matchingActivities) {
|
for (final ResolveInfo resolveInfo : matchingActivities) {
|
||||||
if (resolveInfo.activityInfo == null
|
if (resolveInfo.activityInfo == null
|
||||||
|| TextUtils.isEmpty(resolveInfo.activityInfo.packageName)) {
|
|| TextUtils.isEmpty(resolveInfo.activityInfo.packageName)) {
|
||||||
@@ -107,7 +111,7 @@ public class PersonalAppsSuspensionHelper {
|
|||||||
final String packageName = resolveInfo.activityInfo.packageName;
|
final String packageName = resolveInfo.activityInfo.packageName;
|
||||||
try {
|
try {
|
||||||
final ApplicationInfo applicationInfo =
|
final ApplicationInfo applicationInfo =
|
||||||
mPackageManager.getApplicationInfo(packageName, 0);
|
mPackageManager.getApplicationInfo(packageName, PACKAGE_QUERY_FLAGS);
|
||||||
if (applicationInfo.isSystemApp() || applicationInfo.isUpdatedSystemApp()) {
|
if (applicationInfo.isSystemApp() || applicationInfo.isUpdatedSystemApp()) {
|
||||||
result.add(packageName);
|
result.add(packageName);
|
||||||
}
|
}
|
||||||
@@ -147,7 +151,8 @@ public class PersonalAppsSuspensionHelper {
|
|||||||
private String getSettingsPackageName() {
|
private String getSettingsPackageName() {
|
||||||
final Intent intent = new Intent(Settings.ACTION_SETTINGS);
|
final Intent intent = new Intent(Settings.ACTION_SETTINGS);
|
||||||
intent.addCategory(Intent.CATEGORY_DEFAULT);
|
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) {
|
if (resolveInfo != null) {
|
||||||
return resolveInfo.activityInfo.packageName;
|
return resolveInfo.activityInfo.packageName;
|
||||||
}
|
}
|
||||||
@@ -164,7 +169,7 @@ public class PersonalAppsSuspensionHelper {
|
|||||||
intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
|
intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||||
intentToResolve.setPackage(packageName);
|
intentToResolve.setPackage(packageName);
|
||||||
final List<ResolveInfo> resolveInfos =
|
final List<ResolveInfo> resolveInfos =
|
||||||
mPackageManager.queryIntentActivities(intentToResolve, /* flags= */ 0);
|
mPackageManager.queryIntentActivities(intentToResolve, PACKAGE_QUERY_FLAGS);
|
||||||
return resolveInfos != null && !resolveInfos.isEmpty();
|
return resolveInfos != null && !resolveInfos.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -124,6 +124,9 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi
|
|||||||
// Key is a pair of uri and userId
|
// Key is a pair of uri and userId
|
||||||
private final Map<Pair<Uri, Integer>, ContentObserver> mContentObservers = new ArrayMap<>();
|
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) {
|
public MockInjector(MockSystemServices services, DpmMockContext context) {
|
||||||
super(context);
|
super(context);
|
||||||
this.services = services;
|
this.services = services;
|
||||||
@@ -470,5 +473,19 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void runCryptoSelfTest() {}
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ import static org.mockito.Matchers.eq;
|
|||||||
import static org.mockito.Matchers.isNull;
|
import static org.mockito.Matchers.isNull;
|
||||||
import static org.mockito.Mockito.atLeast;
|
import static org.mockito.Mockito.atLeast;
|
||||||
import static org.mockito.Mockito.atMost;
|
import static org.mockito.Mockito.atMost;
|
||||||
|
import static org.mockito.Mockito.clearInvocations;
|
||||||
import static org.mockito.Mockito.doAnswer;
|
import static org.mockito.Mockito.doAnswer;
|
||||||
import static org.mockito.Mockito.doReturn;
|
import static org.mockito.Mockito.doReturn;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
@@ -62,6 +63,7 @@ import android.Manifest.permission;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AppOpsManager;
|
import android.app.AppOpsManager;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
|
import android.app.PendingIntent;
|
||||||
import android.app.admin.DeviceAdminReceiver;
|
import android.app.admin.DeviceAdminReceiver;
|
||||||
import android.app.admin.DevicePolicyManager;
|
import android.app.admin.DevicePolicyManager;
|
||||||
import android.app.admin.DevicePolicyManagerInternal;
|
import android.app.admin.DevicePolicyManagerInternal;
|
||||||
@@ -96,6 +98,7 @@ import android.util.Pair;
|
|||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
import com.android.internal.R;
|
import com.android.internal.R;
|
||||||
|
import com.android.internal.messages.nano.SystemMessageProto;
|
||||||
import com.android.internal.widget.LockscreenCredential;
|
import com.android.internal.widget.LockscreenCredential;
|
||||||
import com.android.server.LocalServices;
|
import com.android.server.LocalServices;
|
||||||
import com.android.server.SystemService;
|
import com.android.server.SystemService;
|
||||||
@@ -103,6 +106,7 @@ import com.android.server.devicepolicy.DevicePolicyManagerService.RestrictionsLi
|
|||||||
|
|
||||||
import org.hamcrest.BaseMatcher;
|
import org.hamcrest.BaseMatcher;
|
||||||
import org.hamcrest.Description;
|
import org.hamcrest.Description;
|
||||||
|
import org.hamcrest.Matcher;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import org.mockito.internal.util.collections.Sets;
|
import org.mockito.internal.util.collections.Sets;
|
||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
@@ -181,6 +185,20 @@ public class DevicePolicyManagerTest extends DpmTestBase {
|
|||||||
"wQ==\n" +
|
"wQ==\n" +
|
||||||
"-----END CERTIFICATE-----\n";
|
"-----END CERTIFICATE-----\n";
|
||||||
|
|
||||||
|
// Constants for testing setManagedProfileMaximumTimeOff:
|
||||||
|
// Profile maximum time off value
|
||||||
|
private static final long PROFILE_OFF_TIMEOUT = TimeUnit.DAYS.toMillis(5);
|
||||||
|
// Synthetic time at the beginning of test.
|
||||||
|
private static final long PROFILE_OFF_START = 1;
|
||||||
|
// Time when warning notification should be posted,
|
||||||
|
private static final long PROFILE_OFF_WARNING_TIME =
|
||||||
|
PROFILE_OFF_START + PROFILE_OFF_TIMEOUT - TimeUnit.DAYS.toMillis(1);
|
||||||
|
// Time when the apps should be suspended
|
||||||
|
private static final long PROFILE_OFF_DEADLINE = PROFILE_OFF_START + PROFILE_OFF_TIMEOUT;
|
||||||
|
// Notification titles for setManagedProfileMaximumTimeOff tests:
|
||||||
|
private static final String PROFILE_OFF_WARNING_TITLE = "suspended_tomorrow";
|
||||||
|
private static final String PROFILE_OFF_SUSPENDED_TITLE = "suspended";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setUp() throws Exception {
|
protected void setUp() throws Exception {
|
||||||
super.setUp();
|
super.setUp();
|
||||||
@@ -1558,20 +1576,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
|
|||||||
dpms.approveCaCert(fourCerts.getList().get(1), userId, true);
|
dpms.approveCaCert(fourCerts.getList().get(1), userId, true);
|
||||||
// a notification should be shown saying that there are two certificates left to approve.
|
// a notification should be shown saying that there are two certificates left to approve.
|
||||||
verify(getServices().notificationManager, timeout(1000))
|
verify(getServices().notificationManager, timeout(1000))
|
||||||
.notifyAsUser(anyString(), anyInt(), argThat(
|
.notifyAsUser(anyString(), anyInt(), argThat(hasTitle(TEST_STRING)), eq(user));
|
||||||
new BaseMatcher<Notification>() {
|
|
||||||
@Override
|
|
||||||
public boolean matches(Object item) {
|
|
||||||
final Notification noti = (Notification) item;
|
|
||||||
return TEST_STRING.equals(
|
|
||||||
noti.extras.getString(Notification.EXTRA_TITLE));
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void describeTo(Description description) {
|
|
||||||
description.appendText(
|
|
||||||
"Notification{title=\"" + TEST_STRING + "\"}");
|
|
||||||
}
|
|
||||||
}), eq(user));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -6272,7 +6277,214 @@ public class DevicePolicyManagerTest extends DpmTestBase {
|
|||||||
assertThat(dpm.getAccountTypesWithManagementDisabled()).isEmpty();
|
assertThat(dpm.getAccountTypesWithManagementDisabled()).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
// admin1 is the outgoing DPC, adminAnotherPakcage is the incoming one.
|
/**
|
||||||
|
* Tests the case when the user doesn't turn the profile on in time, verifies that the user is
|
||||||
|
* warned with a notification and then the apps get suspended.
|
||||||
|
*/
|
||||||
|
public void testMaximumProfileTimeOff_profileOffTimeExceeded() throws Exception {
|
||||||
|
prepareMocksForSetMaximumProfileTimeOff();
|
||||||
|
|
||||||
|
mContext.binder.callingUid = DpmMockContext.CALLER_UID;
|
||||||
|
dpm.setManagedProfileMaximumTimeOff(admin1, PROFILE_OFF_TIMEOUT);
|
||||||
|
|
||||||
|
mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
|
||||||
|
// The profile is running, neither alarm nor notification should be posted.
|
||||||
|
verify(getServices().alarmManager, never())
|
||||||
|
.set(anyInt(), anyLong(), any(PendingIntent.class));
|
||||||
|
verify(getServices().notificationManager, never())
|
||||||
|
.notify(anyInt(), any(Notification.class));
|
||||||
|
// Apps shouldn't be suspended.
|
||||||
|
verifyZeroInteractions(getServices().ipackageManager);
|
||||||
|
clearInvocations(getServices().alarmManager);
|
||||||
|
|
||||||
|
sendUserStoppedBroadcastForProfile();
|
||||||
|
|
||||||
|
// Verify the alarm was scheduled for time when the warning should be shown.
|
||||||
|
verify(getServices().alarmManager, times(1))
|
||||||
|
.set(anyInt(), eq(PROFILE_OFF_WARNING_TIME), any());
|
||||||
|
// But still no notification should be posted at this point.
|
||||||
|
verify(getServices().notificationManager, never())
|
||||||
|
.notify(anyInt(), any(Notification.class));
|
||||||
|
// Apps shouldn't be suspended.
|
||||||
|
verifyZeroInteractions(getServices().ipackageManager);
|
||||||
|
clearInvocations(getServices().alarmManager);
|
||||||
|
|
||||||
|
// Pretend the alarm went off.
|
||||||
|
dpms.mMockInjector.setSystemCurrentTimeMillis(PROFILE_OFF_WARNING_TIME + 10);
|
||||||
|
sendProfileOffDeadlineAlarmBroadcast();
|
||||||
|
|
||||||
|
// Verify the alarm was scheduled for the actual deadline this time.
|
||||||
|
verify(getServices().alarmManager, times(1)).set(anyInt(), eq(PROFILE_OFF_DEADLINE), any());
|
||||||
|
// Now the user should see a warning notification.
|
||||||
|
verify(getServices().notificationManager, times(1))
|
||||||
|
.notify(anyInt(), argThat(hasTitle(PROFILE_OFF_WARNING_TITLE)));
|
||||||
|
// Apps shouldn't be suspended yet.
|
||||||
|
verifyZeroInteractions(getServices().ipackageManager);
|
||||||
|
clearInvocations(getServices().alarmManager);
|
||||||
|
clearInvocations(getServices().notificationManager);
|
||||||
|
|
||||||
|
// Pretend the alarm went off.
|
||||||
|
dpms.mMockInjector.setSystemCurrentTimeMillis(PROFILE_OFF_DEADLINE + 10);
|
||||||
|
sendProfileOffDeadlineAlarmBroadcast();
|
||||||
|
|
||||||
|
// Verify the alarm was not set.
|
||||||
|
verifyZeroInteractions(getServices().alarmManager);
|
||||||
|
// Now the user should see a notification about suspended apps.
|
||||||
|
verify(getServices().notificationManager, times(1))
|
||||||
|
.notify(anyInt(), argThat(hasTitle(PROFILE_OFF_SUSPENDED_TITLE)));
|
||||||
|
// Verify that the apps are suspended.
|
||||||
|
verify(getServices().ipackageManager, times(1)).setPackagesSuspendedAsUser(
|
||||||
|
any(), eq(true), any(), any(), any(), any(), anyInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the case when the user turns the profile back on long before the deadline (> 1 day).
|
||||||
|
*/
|
||||||
|
public void testMaximumProfileTimeOff_turnOnBeforeWarning() throws Exception {
|
||||||
|
prepareMocksForSetMaximumProfileTimeOff();
|
||||||
|
|
||||||
|
mContext.binder.callingUid = DpmMockContext.CALLER_UID;
|
||||||
|
dpm.setManagedProfileMaximumTimeOff(admin1, PROFILE_OFF_TIMEOUT);
|
||||||
|
|
||||||
|
mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
|
||||||
|
sendUserStoppedBroadcastForProfile();
|
||||||
|
clearInvocations(getServices().alarmManager);
|
||||||
|
sendUserUnlockedBroadcastForProfile();
|
||||||
|
|
||||||
|
// Verify that the alarm got discharged.
|
||||||
|
verify(getServices().alarmManager, times(1)).cancel((PendingIntent) null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the case when the user turns the profile back on after the warning notification.
|
||||||
|
*/
|
||||||
|
public void testMaximumProfileTimeOff_turnOnAfterWarning() throws Exception {
|
||||||
|
prepareMocksForSetMaximumProfileTimeOff();
|
||||||
|
|
||||||
|
mContext.binder.callingUid = DpmMockContext.CALLER_UID;
|
||||||
|
dpm.setManagedProfileMaximumTimeOff(admin1, PROFILE_OFF_TIMEOUT);
|
||||||
|
|
||||||
|
mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
|
||||||
|
sendUserStoppedBroadcastForProfile();
|
||||||
|
|
||||||
|
// Pretend the alarm went off.
|
||||||
|
dpms.mMockInjector.setSystemCurrentTimeMillis(PROFILE_OFF_WARNING_TIME + 10);
|
||||||
|
sendProfileOffDeadlineAlarmBroadcast();
|
||||||
|
|
||||||
|
clearInvocations(getServices().alarmManager);
|
||||||
|
clearInvocations(getServices().notificationManager);
|
||||||
|
sendUserUnlockedBroadcastForProfile();
|
||||||
|
|
||||||
|
// Verify that the alarm got discharged.
|
||||||
|
verify(getServices().alarmManager, times(1)).cancel((PendingIntent) null);
|
||||||
|
// Verify that the notification is removed.
|
||||||
|
verify(getServices().notificationManager, times(1))
|
||||||
|
.cancel(eq(SystemMessageProto.SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the case when the user turns the profile back on when the apps are already suspended.
|
||||||
|
*/
|
||||||
|
public void testMaximumProfileTimeOff_turnOnAfterDeadline() throws Exception {
|
||||||
|
prepareMocksForSetMaximumProfileTimeOff();
|
||||||
|
|
||||||
|
mContext.binder.callingUid = DpmMockContext.CALLER_UID;
|
||||||
|
dpm.setManagedProfileMaximumTimeOff(admin1, PROFILE_OFF_TIMEOUT);
|
||||||
|
|
||||||
|
mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
|
||||||
|
sendUserStoppedBroadcastForProfile();
|
||||||
|
|
||||||
|
// Pretend the alarm went off after the deadline.
|
||||||
|
dpms.mMockInjector.setSystemCurrentTimeMillis(PROFILE_OFF_DEADLINE + 10);
|
||||||
|
sendProfileOffDeadlineAlarmBroadcast();
|
||||||
|
|
||||||
|
clearInvocations(getServices().alarmManager);
|
||||||
|
clearInvocations(getServices().notificationManager);
|
||||||
|
clearInvocations(getServices().ipackageManager);
|
||||||
|
|
||||||
|
sendUserUnlockedBroadcastForProfile();
|
||||||
|
|
||||||
|
// Verify that the notification is removed (at this point DPC should show it).
|
||||||
|
verify(getServices().notificationManager, times(1))
|
||||||
|
.cancel(eq(SystemMessageProto.SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED));
|
||||||
|
// Verify that the apps are NOT unsuspeded.
|
||||||
|
verify(getServices().ipackageManager, never()).setPackagesSuspendedAsUser(
|
||||||
|
any(), eq(false), any(), any(), any(), any(), anyInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendUserUnlockedBroadcastForProfile() throws Exception {
|
||||||
|
when(getServices().userManager.isUserUnlocked(eq(DpmMockContext.CALLER_USER_HANDLE)))
|
||||||
|
.thenReturn(true);
|
||||||
|
final Intent unlockedIntent = new Intent(Intent.ACTION_USER_UNLOCKED)
|
||||||
|
.putExtra(Intent.EXTRA_USER_HANDLE, DpmMockContext.CALLER_USER_HANDLE);
|
||||||
|
getServices().injectBroadcast(
|
||||||
|
mServiceContext, unlockedIntent, DpmMockContext.CALLER_USER_HANDLE);
|
||||||
|
flushTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void sendProfileOffDeadlineAlarmBroadcast() throws Exception {
|
||||||
|
final Intent deadlineAlarmIntent =
|
||||||
|
new Intent(DevicePolicyManagerService.ACTION_PROFILE_OFF_DEADLINE);
|
||||||
|
getServices().injectBroadcast(
|
||||||
|
mServiceContext, deadlineAlarmIntent, DpmMockContext.CALLER_USER_HANDLE);
|
||||||
|
flushTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendUserStoppedBroadcastForProfile() throws Exception {
|
||||||
|
when(getServices().userManager.isUserUnlocked(eq(DpmMockContext.CALLER_USER_HANDLE)))
|
||||||
|
.thenReturn(false);
|
||||||
|
final Intent stoppedIntent = new Intent(Intent.ACTION_USER_STOPPED)
|
||||||
|
.putExtra(Intent.EXTRA_USER_HANDLE, DpmMockContext.CALLER_USER_HANDLE);
|
||||||
|
getServices().injectBroadcast(mServiceContext, stoppedIntent,
|
||||||
|
DpmMockContext.CALLER_USER_HANDLE);
|
||||||
|
flushTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void prepareMocksForSetMaximumProfileTimeOff() throws Exception {
|
||||||
|
addManagedProfile(admin1, DpmMockContext.CALLER_UID, admin1);
|
||||||
|
configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE);
|
||||||
|
|
||||||
|
when(getServices().userManager.isUserUnlocked()).thenReturn(true);
|
||||||
|
|
||||||
|
// Pretend our admin handles CHECK_POLICY_COMPLIANCE intent.
|
||||||
|
final Intent intent = new Intent(DevicePolicyManager.ACTION_CHECK_POLICY_COMPLIANCE);
|
||||||
|
intent.setPackage(admin1.getPackageName());
|
||||||
|
|
||||||
|
doReturn(Collections.singletonList(new ResolveInfo()))
|
||||||
|
.when(getServices().packageManager).queryIntentActivitiesAsUser(
|
||||||
|
any(Intent.class), anyInt(), eq(DpmMockContext.CALLER_USER_HANDLE));
|
||||||
|
|
||||||
|
dpms.mMockInjector.setSystemCurrentTimeMillis(PROFILE_OFF_START);
|
||||||
|
// To allow creation of Notification via Notification.Builder
|
||||||
|
mContext.applicationInfo = mRealTestContext.getApplicationInfo();
|
||||||
|
|
||||||
|
// Setup notification titles.
|
||||||
|
when(mServiceContext.resources
|
||||||
|
.getString(R.string.personal_apps_suspended_tomorrow_title))
|
||||||
|
.thenReturn(PROFILE_OFF_WARNING_TITLE);
|
||||||
|
when(mServiceContext.resources
|
||||||
|
.getString(R.string.personal_apps_suspended_title))
|
||||||
|
.thenReturn(PROFILE_OFF_SUSPENDED_TITLE);
|
||||||
|
|
||||||
|
clearInvocations(getServices().ipackageManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Matcher<Notification> hasTitle(String expected) {
|
||||||
|
return new BaseMatcher<Notification>() {
|
||||||
|
@Override
|
||||||
|
public boolean matches(Object item) {
|
||||||
|
final Notification notification = (Notification) item;
|
||||||
|
return expected.equals(notification.extras.getString(Notification.EXTRA_TITLE));
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void describeTo(Description description) {
|
||||||
|
description.appendText("Notification{title=\"" + expected + "\"}");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// admin1 is the outgoing DPC, adminAnotherPackage is the incoming one.
|
||||||
private void assertDeviceOwnershipRevertedWithFakeTransferMetadata() throws Exception {
|
private void assertDeviceOwnershipRevertedWithFakeTransferMetadata() throws Exception {
|
||||||
writeFakeTransferMetadataFile(UserHandle.USER_SYSTEM,
|
writeFakeTransferMetadataFile(UserHandle.USER_SYSTEM,
|
||||||
TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER);
|
TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER);
|
||||||
@@ -6299,7 +6511,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
|
|||||||
mServiceContext.binder.restoreCallingIdentity(ident);
|
mServiceContext.binder.restoreCallingIdentity(ident);
|
||||||
}
|
}
|
||||||
|
|
||||||
// admin1 is the outgoing DPC, adminAnotherPakcage is the incoming one.
|
// admin1 is the outgoing DPC, adminAnotherPackage is the incoming one.
|
||||||
private void assertProfileOwnershipRevertedWithFakeTransferMetadata() throws Exception {
|
private void assertProfileOwnershipRevertedWithFakeTransferMetadata() throws Exception {
|
||||||
writeFakeTransferMetadataFile(DpmMockContext.CALLER_USER_HANDLE,
|
writeFakeTransferMetadataFile(DpmMockContext.CALLER_USER_HANDLE,
|
||||||
TransferOwnershipMetadataManager.ADMIN_TYPE_PROFILE_OWNER);
|
TransferOwnershipMetadataManager.ADMIN_TYPE_PROFILE_OWNER);
|
||||||
|
|||||||
Reference in New Issue
Block a user