Merge "Add support message for device admins"

This commit is contained in:
Kenny Guy
2016-01-06 16:27:18 +00:00
committed by Android (Google) Code Review
7 changed files with 379 additions and 2 deletions

View File

@@ -5758,6 +5758,7 @@ package android.app.admin {
method public java.lang.String getDeviceOwnerLockScreenInfo();
method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName);
method public int getKeyguardDisabledFeatures(android.content.ComponentName);
method public java.lang.String getLongSupportMessage(android.content.ComponentName);
method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName);
method public long getMaximumTimeToLock(android.content.ComponentName);
method public boolean getPackageSuspended(android.content.ComponentName, java.lang.String);
@@ -5778,6 +5779,7 @@ package android.app.admin {
method public java.util.List<java.lang.String> getPermittedAccessibilityServices(android.content.ComponentName);
method public java.util.List<java.lang.String> getPermittedInputMethods(android.content.ComponentName);
method public boolean getScreenCaptureDisabled(android.content.ComponentName);
method public java.lang.String getShortSupportMessage(android.content.ComponentName);
method public boolean getStorageEncryption(android.content.ComponentName);
method public int getStorageEncryptionStatus();
method public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy();
@@ -5819,6 +5821,7 @@ package android.app.admin {
method public boolean setKeyguardDisabled(android.content.ComponentName, boolean);
method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
method public void setLockTaskPackages(android.content.ComponentName, java.lang.String[]) throws java.lang.SecurityException;
method public void setLongSupportMessage(android.content.ComponentName, java.lang.String);
method public void setMasterVolumeMuted(android.content.ComponentName, boolean);
method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
method public void setMaximumTimeToLock(android.content.ComponentName, long);
@@ -5843,6 +5846,7 @@ package android.app.admin {
method public void setRestrictionsProvider(android.content.ComponentName, android.content.ComponentName);
method public void setScreenCaptureDisabled(android.content.ComponentName, boolean);
method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public void setShortSupportMessage(android.content.ComponentName, java.lang.String);
method public boolean setStatusBarDisabled(android.content.ComponentName, boolean);
method public int setStorageEncryption(android.content.ComponentName, boolean);
method public void setSystemUpdatePolicy(android.content.ComponentName, android.app.admin.SystemUpdatePolicy);

View File

@@ -5886,6 +5886,7 @@ package android.app.admin {
method public java.lang.String getDeviceOwnerLockScreenInfo();
method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName);
method public int getKeyguardDisabledFeatures(android.content.ComponentName);
method public java.lang.String getLongSupportMessage(android.content.ComponentName);
method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName);
method public long getMaximumTimeToLock(android.content.ComponentName);
method public boolean getPackageSuspended(android.content.ComponentName, java.lang.String);
@@ -5910,6 +5911,7 @@ package android.app.admin {
method public android.content.ComponentName getProfileOwner() throws java.lang.IllegalArgumentException;
method public java.lang.String getProfileOwnerNameAsUser(int) throws java.lang.IllegalArgumentException;
method public boolean getScreenCaptureDisabled(android.content.ComponentName);
method public java.lang.String getShortSupportMessage(android.content.ComponentName);
method public boolean getStorageEncryption(android.content.ComponentName);
method public int getStorageEncryptionStatus();
method public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy();
@@ -5953,6 +5955,7 @@ package android.app.admin {
method public boolean setKeyguardDisabled(android.content.ComponentName, boolean);
method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
method public void setLockTaskPackages(android.content.ComponentName, java.lang.String[]) throws java.lang.SecurityException;
method public void setLongSupportMessage(android.content.ComponentName, java.lang.String);
method public void setMasterVolumeMuted(android.content.ComponentName, boolean);
method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
method public void setMaximumTimeToLock(android.content.ComponentName, long);
@@ -5977,6 +5980,7 @@ package android.app.admin {
method public void setRestrictionsProvider(android.content.ComponentName, android.content.ComponentName);
method public void setScreenCaptureDisabled(android.content.ComponentName, boolean);
method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public void setShortSupportMessage(android.content.ComponentName, java.lang.String);
method public boolean setStatusBarDisabled(android.content.ComponentName, boolean);
method public int setStorageEncryption(android.content.ComponentName, boolean);
method public void setSystemUpdatePolicy(android.content.ComponentName, android.app.admin.SystemUpdatePolicy);

View File

@@ -5758,6 +5758,7 @@ package android.app.admin {
method public java.lang.String getDeviceOwnerLockScreenInfo();
method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName);
method public int getKeyguardDisabledFeatures(android.content.ComponentName);
method public java.lang.String getLongSupportMessage(android.content.ComponentName);
method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName);
method public long getMaximumTimeToLock(android.content.ComponentName);
method public boolean getPackageSuspended(android.content.ComponentName, java.lang.String);
@@ -5778,6 +5779,7 @@ package android.app.admin {
method public java.util.List<java.lang.String> getPermittedAccessibilityServices(android.content.ComponentName);
method public java.util.List<java.lang.String> getPermittedInputMethods(android.content.ComponentName);
method public boolean getScreenCaptureDisabled(android.content.ComponentName);
method public java.lang.String getShortSupportMessage(android.content.ComponentName);
method public boolean getStorageEncryption(android.content.ComponentName);
method public int getStorageEncryptionStatus();
method public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy();
@@ -5819,6 +5821,7 @@ package android.app.admin {
method public boolean setKeyguardDisabled(android.content.ComponentName, boolean);
method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
method public void setLockTaskPackages(android.content.ComponentName, java.lang.String[]) throws java.lang.SecurityException;
method public void setLongSupportMessage(android.content.ComponentName, java.lang.String);
method public void setMasterVolumeMuted(android.content.ComponentName, boolean);
method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
method public void setMaximumTimeToLock(android.content.ComponentName, long);
@@ -5843,6 +5846,7 @@ package android.app.admin {
method public void setRestrictionsProvider(android.content.ComponentName, android.content.ComponentName);
method public void setScreenCaptureDisabled(android.content.ComponentName, boolean);
method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public void setShortSupportMessage(android.content.ComponentName, java.lang.String);
method public boolean setStatusBarDisabled(android.content.ComponentName, boolean);
method public int setStorageEncryption(android.content.ComponentName, boolean);
method public void setSystemUpdatePolicy(android.content.ComponentName, android.app.admin.SystemUpdatePolicy);

View File

@@ -4802,4 +4802,129 @@ public class DevicePolicyManager {
Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
}
}
/**
* Called by a device admin to set the short support message. This will
* be displayed to the user in settings screens where funtionality has
* been disabled by the admin.
*
* The message should be limited to a short statement such as
* "This setting is disabled by your administrator. Contact someone@example.com
* for support."
* If the message is longer than 200 characters it may be truncated.
*
* @see #setLongSupportMessage
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param message Short message to be displayed to the user in settings or null to
* clear the existing message.
*/
public void setShortSupportMessage(@NonNull ComponentName admin,
@Nullable String message) {
if (mService != null) {
try {
mService.setShortSupportMessage(admin, message);
} catch (RemoteException e) {
Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
}
}
}
/**
* Called by a device admin to get the short support message.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @return The message set by {@link #setShortSupportMessage(ComponentName, String)}
* or null if no message has been set.
*/
public String getShortSupportMessage(@NonNull ComponentName admin) {
if (mService != null) {
try {
return mService.getShortSupportMessage(admin);
} catch (RemoteException e) {
Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
}
}
return null;
}
/**
* Called by a device admin to set the long support message. This will
* be displayed to the user in the device administators settings screen.
*
* @see #setShortSupportMessage
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param message Long message to be displayed to the user in settings or null to
* clear the existing message.
*/
public void setLongSupportMessage(@NonNull ComponentName admin,
@Nullable String message) {
if (mService != null) {
try {
mService.setLongSupportMessage(admin, message);
} catch (RemoteException e) {
Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
}
}
}
/**
* Called by a device admin to get the long support message.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @return The message set by {@link #setLongSupportMessage(ComponentName, String)}
* or null if no message has been set.
*/
public String getLongSupportMessage(@NonNull ComponentName admin) {
if (mService != null) {
try {
return mService.getLongSupportMessage(admin);
} catch (RemoteException e) {
Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
}
}
return null;
}
/**
* Called by the system to get the short support message.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param userHandle user id the admin is running as.
* @return The message set by {@link #setShortSupportMessage(ComponentName, String)}
*
* @hide
*/
public String getShortSupportMessageForUser(@NonNull ComponentName admin, int userHandle) {
if (mService != null) {
try {
return mService.getShortSupportMessageForUser(admin, userHandle);
} catch (RemoteException e) {
Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
}
}
return null;
}
/**
* Called by the system to get the long support message.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param userHandle user id the admin is running as.
* @return The message set by {@link #setLongSupportMessage(ComponentName, String)}
*
* @hide
*/
public String getLongSupportMessageForUser(@NonNull ComponentName admin, int userHandle) {
if (mService != null) {
try {
return mService.getLongSupportMessageForUser(admin, userHandle);
} catch (RemoteException e) {
Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
}
}
return null;
}
}

View File

@@ -245,4 +245,12 @@ interface IDevicePolicyManager {
boolean isSystemOnlyUser(in ComponentName admin);
String getWifiMacAddress();
void reboot(in ComponentName admin);
void setShortSupportMessage(in ComponentName admin, in String message);
String getShortSupportMessage(in ComponentName admin);
void setLongSupportMessage(in ComponentName admin, in String message);
String getLongSupportMessage(in ComponentName admin);
String getShortSupportMessageForUser(in ComponentName admin, int userHandle);
String getLongSupportMessageForUser(in ComponentName admin, int userHandle);
}

View File

@@ -424,6 +424,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private static final String TAG_PACKAGE_LIST_ITEM = "item";
private static final String TAG_KEEP_UNINSTALLED_PACKAGES = "keep-uninstalled-packages";
private static final String TAG_USER_RESTRICTIONS = "user-restrictions";
private static final String TAG_SHORT_SUPPORT_MESSAGE = "short-support-message";
private static final String TAG_LONG_SUPPORT_MESSAGE = "long-support-message";
final DeviceAdminInfo info;
@@ -509,6 +511,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
Bundle userRestrictions;
// Support text provided by the admin to display to the user.
String shortSupportMessage = null;
String longSupportMessage = null;
ActiveAdmin(DeviceAdminInfo _info) {
info = _info;
}
@@ -688,6 +694,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
UserRestrictionsUtils.writeRestrictions(
out, userRestrictions, TAG_USER_RESTRICTIONS);
}
if (!TextUtils.isEmpty(shortSupportMessage)) {
out.startTag(null, TAG_SHORT_SUPPORT_MESSAGE);
out.text(shortSupportMessage);
out.endTag(null, TAG_SHORT_SUPPORT_MESSAGE);
}
if (!TextUtils.isEmpty(longSupportMessage)) {
out.startTag(null, TAG_LONG_SUPPORT_MESSAGE);
out.text(longSupportMessage);
out.endTag(null, TAG_LONG_SUPPORT_MESSAGE);
}
}
void writePackageListToXml(XmlSerializer out, String outerTag,
@@ -801,6 +817,20 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
keepUninstalledPackages = readPackageList(parser, tag);
} else if (TAG_USER_RESTRICTIONS.equals(tag)) {
UserRestrictionsUtils.readRestrictions(parser, ensureUserRestrictions());
} else if (TAG_SHORT_SUPPORT_MESSAGE.equals(tag)) {
type = parser.next();
if (type == XmlPullParser.TEXT) {
shortSupportMessage = parser.getText();
} else {
Log.w(LOG_TAG, "Missing text when loading short support message");
}
} else if (TAG_LONG_SUPPORT_MESSAGE.equals(tag)) {
type = parser.next();
if (type == XmlPullParser.TEXT) {
longSupportMessage = parser.getText();
} else {
Log.w(LOG_TAG, "Missing text when loading long support message");
}
} else {
Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
XmlUtils.skipCurrentTag(parser);
@@ -1599,6 +1629,23 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
/**
* Find the admin for the component and userId bit of the uid, then check
* the admin's uid matches the uid.
*/
private ActiveAdmin getActiveAdminForUidLocked(ComponentName who, int uid) {
final int userId = UserHandle.getUserId(uid);
final DevicePolicyData policy = getUserData(userId);
ActiveAdmin admin = policy.mAdminMap.get(who);
if (admin == null) {
throw new SecurityException("No active admin " + who);
}
if (admin.getUid() != uid) {
throw new SecurityException("Admin " + who + " is not owned by uid " + uid);
}
return admin;
}
private ActiveAdmin getActiveAdminWithPolicyForUidLocked(ComponentName who, int reqPolicy,
int uid) {
// Try to find an admin which can use reqPolicy
@@ -1610,8 +1657,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
throw new SecurityException("No active admin " + who);
}
if (admin.getUid() != uid) {
throw new SecurityException("Admin " + who + " is not owned by uid "
+ mInjector.binderGetCallingUid());
throw new SecurityException("Admin " + who + " is not owned by uid " + uid);
}
if (isActiveAdminWithPolicyForUserLocked(admin, reqPolicy, userId)) {
return admin;
@@ -7104,4 +7150,99 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
@Override
public void setShortSupportMessage(@NonNull ComponentName who, String message) {
if (!mHasFeature) {
return;
}
Preconditions.checkNotNull(who, "ComponentName is null");
final int userHandle = mInjector.userHandleGetCallingUserId();
synchronized (this) {
ActiveAdmin admin = getActiveAdminForUidLocked(who,
mInjector.binderGetCallingUid());
if (!TextUtils.equals(admin.shortSupportMessage, message)) {
admin.shortSupportMessage = message;
saveSettingsLocked(userHandle);
}
}
}
@Override
public String getShortSupportMessage(@NonNull ComponentName who) {
if (!mHasFeature) {
return null;
}
Preconditions.checkNotNull(who, "ComponentName is null");
synchronized (this) {
ActiveAdmin admin = getActiveAdminForUidLocked(who,
mInjector.binderGetCallingUid());
return admin.shortSupportMessage;
}
}
@Override
public void setLongSupportMessage(@NonNull ComponentName who, String message) {
if (!mHasFeature) {
return;
}
Preconditions.checkNotNull(who, "ComponentName is null");
final int userHandle = mInjector.userHandleGetCallingUserId();
synchronized (this) {
ActiveAdmin admin = getActiveAdminForUidLocked(who,
mInjector.binderGetCallingUid());
if (!TextUtils.equals(admin.longSupportMessage, message)) {
admin.longSupportMessage = message;
saveSettingsLocked(userHandle);
}
}
}
@Override
public String getLongSupportMessage(@NonNull ComponentName who) {
if (!mHasFeature) {
return null;
}
Preconditions.checkNotNull(who, "ComponentName is null");
synchronized (this) {
ActiveAdmin admin = getActiveAdminForUidLocked(who,
mInjector.binderGetCallingUid());
return admin.longSupportMessage;
}
}
@Override
public String getShortSupportMessageForUser(@NonNull ComponentName who, int userHandle) {
if (!mHasFeature) {
return null;
}
Preconditions.checkNotNull(who, "ComponentName is null");
if (!UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID)) {
throw new SecurityException("Only the system can query support message for user");
}
synchronized (this) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
if (admin != null) {
return admin.shortSupportMessage;
}
}
return null;
}
@Override
public String getLongSupportMessageForUser(@NonNull ComponentName who, int userHandle) {
if (!mHasFeature) {
return null;
}
Preconditions.checkNotNull(who, "ComponentName is null");
if (!UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID)) {
throw new SecurityException("Only the system can query support message for user");
}
synchronized (this) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
if (admin != null) {
return admin.longSupportMessage;
}
}
return null;
}
}

View File

@@ -1385,4 +1385,95 @@ public class DevicePolicyManagerTest extends DpmTestBase {
dpm.reboot(admin1);
}
public void testSetGetSupportText() {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
dpm.setActiveAdmin(admin1, true);
dpm.setActiveAdmin(admin2, true);
mContext.callerPermissions.remove(permission.MANAGE_DEVICE_ADMINS);
// Null default support messages.
{
assertNull(dpm.getLongSupportMessage(admin1));
assertNull(dpm.getShortSupportMessage(admin1));
mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
assertNull(dpm.getShortSupportMessageForUser(admin1,
DpmMockContext.CALLER_USER_HANDLE));
assertNull(dpm.getLongSupportMessageForUser(admin1,
DpmMockContext.CALLER_USER_HANDLE));
mMockContext.binder.callingUid = DpmMockContext.CALLER_UID;
}
// Only system can call the per user versions.
{
try {
dpm.getShortSupportMessageForUser(admin1,
DpmMockContext.CALLER_USER_HANDLE);
fail("Only system should be able to call getXXXForUser versions");
} catch (SecurityException expected) {
MoreAsserts.assertContainsRegex("message for user", expected.getMessage());
}
try {
dpm.getLongSupportMessageForUser(admin1,
DpmMockContext.CALLER_USER_HANDLE);
fail("Only system should be able to call getXXXForUser versions");
} catch (SecurityException expected) {
MoreAsserts.assertContainsRegex("message for user", expected.getMessage());
}
}
// Can't set message for admin in another uid.
{
mContext.binder.callingUid = DpmMockContext.CALLER_UID + 1;
try {
dpm.setShortSupportMessage(admin1, "Some text");
fail("Admins should only be able to change their own support text.");
} catch (SecurityException expected) {
MoreAsserts.assertContainsRegex("is not owned by uid", expected.getMessage());
}
mContext.binder.callingUid = DpmMockContext.CALLER_UID;
}
// Set/Get short returns what it sets and other admins text isn't changed.
{
final String supportText = "Some text to test with.";
dpm.setShortSupportMessage(admin1, supportText);
assertEquals(supportText, dpm.getShortSupportMessage(admin1));
assertNull(dpm.getLongSupportMessage(admin1));
assertNull(dpm.getShortSupportMessage(admin2));
mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
assertEquals(supportText, dpm.getShortSupportMessageForUser(admin1,
DpmMockContext.CALLER_USER_HANDLE));
assertNull(dpm.getShortSupportMessageForUser(admin2,
DpmMockContext.CALLER_USER_HANDLE));
assertNull(dpm.getLongSupportMessageForUser(admin1,
DpmMockContext.CALLER_USER_HANDLE));
mMockContext.binder.callingUid = DpmMockContext.CALLER_UID;
dpm.setShortSupportMessage(admin1, null);
assertNull(dpm.getShortSupportMessage(admin1));
}
// Set/Get long returns what it sets and other admins text isn't changed.
{
final String supportText = "Some text to test with.\nWith more text.";
dpm.setLongSupportMessage(admin1, supportText);
assertEquals(supportText, dpm.getLongSupportMessage(admin1));
assertNull(dpm.getShortSupportMessage(admin1));
assertNull(dpm.getLongSupportMessage(admin2));
mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
assertEquals(supportText, dpm.getLongSupportMessageForUser(admin1,
DpmMockContext.CALLER_USER_HANDLE));
assertNull(dpm.getLongSupportMessageForUser(admin2,
DpmMockContext.CALLER_USER_HANDLE));
assertNull(dpm.getShortSupportMessageForUser(admin1,
DpmMockContext.CALLER_USER_HANDLE));
mMockContext.binder.callingUid = DpmMockContext.CALLER_UID;
dpm.setLongSupportMessage(admin1, null);
assertNull(dpm.getLongSupportMessage(admin1));
}
}
}