summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt4
-rw-r--r--core/java/android/app/admin/DeviceAdminReceiver.java47
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java54
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl2
-rw-r--r--core/res/AndroidManifest.xml4
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java2
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java78
7 files changed, 144 insertions, 47 deletions
diff --git a/api/current.txt b/api/current.txt
index ed0c6a982b70..484ca48bb95b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6322,6 +6322,7 @@ package android.app.admin {
method public void onReceive(android.content.Context, android.content.Intent);
method public void onSecurityLogsAvailable(android.content.Context, android.content.Intent);
method public void onSystemUpdatePending(android.content.Context, android.content.Intent, long);
+ method public void onTransferOwnershipComplete(android.content.Context, android.os.PersistableBundle);
method public void onUserAdded(android.content.Context, android.content.Intent, android.os.UserHandle);
method public void onUserRemoved(android.content.Context, android.content.Intent, android.os.UserHandle);
field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED";
@@ -6339,6 +6340,7 @@ package android.app.admin {
field public static final java.lang.String DEVICE_ADMIN_META_DATA = "android.app.device_admin";
field public static final java.lang.String EXTRA_DISABLE_WARNING = "android.app.extra.DISABLE_WARNING";
field public static final java.lang.String EXTRA_LOCK_TASK_PACKAGE = "android.app.extra.LOCK_TASK_PACKAGE";
+ field public static final java.lang.String EXTRA_TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE = "android.app.extra.TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE";
}
public class DeviceAdminService extends android.app.Service {
@@ -6531,6 +6533,7 @@ package android.app.admin {
method public void setUserIcon(android.content.ComponentName, android.graphics.Bitmap);
method public boolean stopUser(android.content.ComponentName, android.os.UserHandle);
method public boolean switchUser(android.content.ComponentName, android.os.UserHandle);
+ method public void transferOwnership(android.content.ComponentName, android.content.ComponentName, android.os.PersistableBundle);
method public void uninstallAllUserCaCerts(android.content.ComponentName);
method public void uninstallCaCert(android.content.ComponentName, byte[]);
method public void wipeData(int);
@@ -6540,6 +6543,7 @@ package android.app.admin {
field public static final java.lang.String ACTION_DEVICE_ADMIN_SERVICE = "android.app.action.DEVICE_ADMIN_SERVICE";
field public static final java.lang.String ACTION_DEVICE_OWNER_CHANGED = "android.app.action.DEVICE_OWNER_CHANGED";
field public static final java.lang.String ACTION_MANAGED_PROFILE_PROVISIONED = "android.app.action.MANAGED_PROFILE_PROVISIONED";
+ field public static final java.lang.String ACTION_PROFILE_OWNER_CHANGED = "android.app.action.PROFILE_OWNER_CHANGED";
field public static final java.lang.String ACTION_PROVISIONING_SUCCESSFUL = "android.app.action.PROVISIONING_SUCCESSFUL";
field public static final java.lang.String ACTION_PROVISION_MANAGED_DEVICE = "android.app.action.PROVISION_MANAGED_DEVICE";
field public static final java.lang.String ACTION_PROVISION_MANAGED_PROFILE = "android.app.action.PROVISION_MANAGED_PROFILE";
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index 2e697ac0e6bb..aa05b7630c9d 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -29,10 +29,14 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
+import android.os.PersistableBundle;
import android.os.Process;
import android.os.UserHandle;
import android.security.KeyChain;
+import libcore.util.NonNull;
+import libcore.util.Nullable;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -438,6 +442,31 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
// TO DO: describe syntax.
public static final String DEVICE_ADMIN_META_DATA = "android.app.device_admin";
+ /**
+ * Broadcast action: notify the newly transferred administrator that the transfer
+ * from the original administrator was successful.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_TRANSFER_OWNERSHIP_COMPLETE =
+ "android.app.action.TRANSFER_OWNERSHIP_COMPLETE";
+
+ /**
+ * A {@link android.os.Parcelable} extra of type {@link android.os.PersistableBundle} that
+ * allows a mobile device management application to pass data to the management application
+ * instance after owner transfer.
+ *
+ * <p>
+ * If the transfer is successful, the new device owner receives the data in
+ * {@link DeviceAdminReceiver#onTransferOwnershipComplete(Context, PersistableBundle)}.
+ * The bundle is not changed during the ownership transfer.
+ *
+ * @see DevicePolicyManager#transferOwnership(ComponentName, ComponentName, PersistableBundle)
+ */
+ public static final String EXTRA_TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE =
+ "android.app.extra.TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE";
+
private DevicePolicyManager mManager;
private ComponentName mWho;
@@ -860,6 +889,20 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
}
/**
+ * Called on the newly assigned owner (either device owner or profile owner) when the ownership
+ * transfer has completed successfully.
+ *
+ * <p> The {@code bundle} parameter allows the original owner to pass data
+ * to the new one.
+ *
+ * @param context the running context as per {@link #onReceive}
+ * @param bundle the data to be passed to the new owner
+ */
+ public void onTransferOwnershipComplete(@NonNull Context context,
+ @Nullable PersistableBundle bundle) {
+ }
+
+ /**
* Intercept standard device administrator broadcasts. Implementations
* should not override this method; it is better to implement the
* convenience callbacks for each action.
@@ -921,6 +964,10 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
onUserAdded(context, intent, intent.getParcelableExtra(Intent.EXTRA_USER));
} else if (ACTION_USER_REMOVED.equals(action)) {
onUserRemoved(context, intent, intent.getParcelableExtra(Intent.EXTRA_USER));
+ } else if (ACTION_TRANSFER_OWNERSHIP_COMPLETE.equals(action)) {
+ PersistableBundle bundle =
+ intent.getParcelableExtra(EXTRA_TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE);
+ onTransferOwnershipComplete(context, bundle);
}
}
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 76c078ec9c05..ab85fdce6ce8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1124,6 +1124,7 @@ public class DevicePolicyManager {
*
* This broadcast is sent only to the primary user.
* @see #ACTION_PROVISION_MANAGED_DEVICE
+ * @see DevicePolicyManager#transferOwnership(ComponentName, ComponentName, PersistableBundle)
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_DEVICE_OWNER_CHANGED
@@ -1709,6 +1710,16 @@ public class DevicePolicyManager {
public static final int ID_TYPE_MEID = 8;
/**
+ * Broadcast action: sent when the profile owner is set, changed or cleared.
+ *
+ * This broadcast is sent only to the user managed by the new profile owner.
+ * @see DevicePolicyManager#transferOwnership(ComponentName, ComponentName, PersistableBundle)
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_PROFILE_OWNER_CHANGED =
+ "android.app.action.PROFILE_OWNER_CHANGED";
+
+ /**
* Return true if the given administrator component is currently active (enabled) in the system.
*
* @param admin The administrator component to check for.
@@ -8997,41 +9008,34 @@ public class DevicePolicyManager {
}
}
- //TODO STOPSHIP Add link to onTransferComplete callback when implemented.
/**
- * Transfers the current administrator. All policies from the current administrator are
- * migrated to the new administrator. The whole operation is atomic - the transfer is either
- * complete or not done at all.
+ * Changes the current administrator to another one. All policies from the current
+ * administrator are migrated to the new administrator. The whole operation is atomic -
+ * the transfer is either complete or not done at all.
*
- * Depending on the current administrator (device owner, profile owner, corporate owned
- * profile owner), you have the following expected behaviour:
+ * <p>Depending on the current administrator (device owner, profile owner), you have the
+ * following expected behaviour:
* <ul>
* <li>A device owner can only be transferred to a new device owner</li>
* <li>A profile owner can only be transferred to a new profile owner</li>
- * <li>A corporate owned managed profile can have two cases:
- * <ul>
- * <li>If the device owner and profile owner are the same package,
- * both will be transferred.</li>
- * <li>If the device owner and profile owner are different packages,
- * and if this method is called from the profile owner, only the profile owner
- * is transferred. Similarly, if it is called from the device owner, only
- * the device owner is transferred.</li>
- * </ul>
- * </li>
* </ul>
*
- * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
- * @param target Which {@link DeviceAdminReceiver} we want the new administrator to be.
- * @param bundle Parameters - This bundle allows the current administrator to pass data to the
- * new administrator. The parameters will be received in the
- * onTransferComplete callback.
- * @hide
+ * <p>Use the {@code bundle} parameter to pass data to the new administrator. The parameters
+ * will be received in the
+ * {@link DeviceAdminReceiver#onTransferOwnershipComplete(Context, PersistableBundle)} callback.
+ *
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with
+ * @param target which {@link DeviceAdminReceiver} we want the new administrator to be
+ * @param bundle data to be sent to the new administrator
+ * @throws SecurityException if {@code admin} is not a device owner nor a profile owner
+ * @throws IllegalArgumentException if {@code admin} or {@code target} is {@code null},
+ * both are components in the same package or {@code target} is not an active admin
*/
- public void transferOwner(@NonNull ComponentName admin, @NonNull ComponentName target,
+ public void transferOwnership(@NonNull ComponentName admin, @NonNull ComponentName target,
PersistableBundle bundle) {
- throwIfParentInstance("transferOwner");
+ throwIfParentInstance("transferOwnership");
try {
- mService.transferOwner(admin, target, bundle);
+ mService.transferOwnership(admin, target, bundle);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 5916a629f2e6..1d8ddeebcc26 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -387,5 +387,5 @@ interface IDevicePolicyManager {
boolean isLogoutEnabled();
List<String> getDisallowedSystemApps(in ComponentName admin, int userId, String provisioningAction);
- void transferOwner(in ComponentName admin, in ComponentName target, in PersistableBundle bundle);
+ void transferOwnership(in ComponentName admin, in ComponentName target, in PersistableBundle bundle);
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 00e3a4a68f78..060d66465728 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -572,6 +572,10 @@
<protected-broadcast android:name="android.intent.action.GET_RESTRICTION_ENTRIES" />
<protected-broadcast android:name="android.telephony.euicc.action.OTA_STATUS_CHANGED" />
+ <!-- Added in P -->
+ <protected-broadcast android:name="android.app.action.PROFILE_OWNER_CHANGED" />
+ <protected-broadcast android:name="android.app.action.TRANSFER_OWNER_COMPLETE" />
+
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index c1e95ebeddf2..29ac4cec29e1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -64,7 +64,7 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub {
public void setSystemSetting(ComponentName who, String setting, String value){}
- public void transferOwner(ComponentName admin, ComponentName target, PersistableBundle bundle) {}
+ public void transferOwnership(ComponentName admin, ComponentName target, PersistableBundle bundle) {}
public boolean generateKeyPair(ComponentName who, String callerPackage, String algorithm,
ParcelableKeyGenParameterSpec keySpec, int idAttestationFlags,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f0681e9eb32e..3592fc0b082f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -20,6 +20,7 @@ import static android.Manifest.permission.BIND_DEVICE_ADMIN;
import static android.Manifest.permission.MANAGE_CA_CERTIFICATES;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.USER_OP_SUCCESS;
+import static android.app.admin.DeviceAdminReceiver.EXTRA_TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE;
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER;
import static android.app.admin.DevicePolicyManager.CODE_ACCOUNTS_NOT_EMPTY;
import static android.app.admin.DevicePolicyManager.CODE_ADD_MANAGED_PROFILE_DISALLOWED;
@@ -154,7 +155,6 @@ import android.provider.ContactsContract.QuickContact;
import android.provider.ContactsInternal;
import android.provider.Settings;
import android.provider.Settings.Global;
-import android.security.Credentials;
import android.security.IKeyChainAliasCallback;
import android.security.IKeyChainService;
import android.security.KeyChain;
@@ -6506,13 +6506,36 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
- synchronized void sendDeviceOwnerCommand(String action, Bundle extras) {
- Intent intent = new Intent(action);
- intent.setComponent(mOwners.getDeviceOwnerComponent());
+ void sendDeviceOwnerCommand(String action, Bundle extras) {
+ int deviceOwnerUserId;
+ ComponentName deviceOwnerComponent;
+ synchronized (this) {
+ deviceOwnerUserId = mOwners.getDeviceOwnerUserId();
+ deviceOwnerComponent = mOwners.getDeviceOwnerComponent();
+ }
+ sendActiveAdminCommand(action, extras, deviceOwnerUserId,
+ deviceOwnerComponent);
+ }
+
+ private void sendProfileOwnerCommand(String action, Bundle extras, int userHandle) {
+ sendActiveAdminCommand(action, extras, userHandle,
+ mOwners.getProfileOwnerComponent(userHandle));
+ }
+
+ private void sendActiveAdminCommand(String action, Bundle extras,
+ int userHandle, ComponentName receiverComponent) {
+ final Intent intent = new Intent(action);
+ intent.setComponent(receiverComponent);
if (extras != null) {
intent.putExtras(extras);
}
- mContext.sendBroadcastAsUser(intent, UserHandle.of(mOwners.getDeviceOwnerUserId()));
+ mContext.sendBroadcastAsUser(intent, UserHandle.of(userHandle));
+ }
+
+ private void sendOwnerChangedBroadcast(String broadcast, int userId) {
+ final Intent intent = new Intent(broadcast)
+ .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
}
private synchronized String getDeviceOwnerRemoteBugreportUri() {
@@ -6890,10 +6913,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
ident = mInjector.binderClearCallingIdentity();
try {
// TODO Send to system too?
- mContext.sendBroadcastAsUser(
- new Intent(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED)
- .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND),
- UserHandle.of(userId));
+ sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED, userId);
} finally {
mInjector.binderRestoreCallingIdentity(ident);
}
@@ -7044,9 +7064,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
try {
clearDeviceOwnerLocked(admin, deviceOwnerUserId);
removeActiveAdminLocked(deviceOwnerComponent, deviceOwnerUserId);
- Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- mContext.sendBroadcastAsUser(intent, UserHandle.of(deviceOwnerUserId));
+ sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED,
+ deviceOwnerUserId);
} finally {
mInjector.binderRestoreCallingIdentity(ident);
}
@@ -7131,6 +7150,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
ensureUnknownSourcesRestrictionForProfileOwnerLocked(userHandle, admin,
true /* newOwner */);
}
+ sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_PROFILE_OWNER_CHANGED,
+ userHandle);
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
@@ -7159,6 +7180,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
try {
clearProfileOwnerLocked(admin, userId);
removeActiveAdminLocked(who, userId);
+ sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_PROFILE_OWNER_CHANGED,
+ userId);
} finally {
mInjector.binderRestoreCallingIdentity(ident);
}
@@ -11847,9 +11870,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
//TODO: Add callback information to the javadoc once it is completed.
- //TODO: Make transferOwner atomic.
+ //TODO: Make transferOwnership atomic.
@Override
- public void transferOwner(ComponentName admin, ComponentName target, PersistableBundle bundle) {
+ public void transferOwnership(ComponentName admin, ComponentName target, PersistableBundle bundle) {
if (!mHasFeature) {
return;
}
@@ -11877,13 +11900,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final long id = mInjector.binderClearCallingIdentity();
try {
- //STOPSHIP add support for COMP, edge cases when device is rebooted/work mode off,
- //transfer callbacks and broadcast
+ //STOPSHIP add support for COMP, edge cases when device is rebooted/work mode off
synchronized (this) {
if (isProfileOwner(admin, callingUserId)) {
- transferProfileOwnerLocked(admin, target, callingUserId);
+ transferProfileOwnerLocked(admin, target, callingUserId, bundle);
} else if (isDeviceOwner(admin, callingUserId)) {
- transferDeviceOwnerLocked(admin, target, callingUserId);
+ transferDeviceOwnerLocked(admin, target, callingUserId, bundle);
}
}
} finally {
@@ -11895,24 +11917,40 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
* Transfers the profile owner for user with id profileOwnerUserId from admin to target.
*/
private void transferProfileOwnerLocked(ComponentName admin, ComponentName target,
- int profileOwnerUserId) {
+ int profileOwnerUserId, PersistableBundle bundle) {
transferActiveAdminUncheckedLocked(target, admin, profileOwnerUserId);
mOwners.transferProfileOwner(target, profileOwnerUserId);
Slog.i(LOG_TAG, "Profile owner set: " + target + " on user " + profileOwnerUserId);
mOwners.writeProfileOwner(profileOwnerUserId);
mDeviceAdminServiceController.startServiceForOwner(
target.getPackageName(), profileOwnerUserId, "transfer-profile-owner");
+ sendProfileOwnerCommand(DeviceAdminReceiver.ACTION_TRANSFER_OWNERSHIP_COMPLETE,
+ getTransferOwnerAdminExtras(bundle), profileOwnerUserId);
+ sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_PROFILE_OWNER_CHANGED,
+ profileOwnerUserId);
}
/**
* Transfers the device owner for user with id userId from admin to target.
*/
- private void transferDeviceOwnerLocked(ComponentName admin, ComponentName target, int userId) {
+ private void transferDeviceOwnerLocked(ComponentName admin, ComponentName target, int userId,
+ PersistableBundle bundle) {
transferActiveAdminUncheckedLocked(target, admin, userId);
mOwners.transferDeviceOwner(target);
Slog.i(LOG_TAG, "Device owner set: " + target + " on user " + userId);
mOwners.writeDeviceOwner();
mDeviceAdminServiceController.startServiceForOwner(
target.getPackageName(), userId, "transfer-device-owner");
+ sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_TRANSFER_OWNERSHIP_COMPLETE,
+ getTransferOwnerAdminExtras(bundle));
+ sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED, userId);
+ }
+
+ private Bundle getTransferOwnerAdminExtras(PersistableBundle bundle) {
+ Bundle extras = new Bundle();
+ if (bundle != null) {
+ extras.putParcelable(EXTRA_TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE, bundle);
+ }
+ return extras;
}
}