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);
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();
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);
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);
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);
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();
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);
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);
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);
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();
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);
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);
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;
+ }
}
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);
}
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;
Bundle userRestrictions;
+ // Support text provided by the admin to display to the user.
+ String shortSupportMessage = null;
+ String longSupportMessage = null;
+
ActiveAdmin(DeviceAdminInfo _info) {
info = _info;
}
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,
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);
}
}
+ /**
+ * 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
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;
}
}
+ @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;
+ }
}
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));
+ }
+ }
}