Allow cross profile call between DO and PO
Allow DO to bind service of PO in managed profile and vice versa. DO and PO must be the same package. Bug: 31895999 Change-Id: I3ce2943aebd1249401d5814757a0ce25b9f85279 Test: cts-tradefed run cts --module DevicePolicyManager --test com.android.cts.devicepolicy.CorpOwnedManagedProfileTest
This commit is contained in:
@@ -5983,6 +5983,7 @@ package android.app.admin {
|
||||
method public boolean addCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
|
||||
method public void addPersistentPreferredActivity(android.content.ComponentName, android.content.IntentFilter, android.content.ComponentName);
|
||||
method public void addUserRestriction(android.content.ComponentName, java.lang.String);
|
||||
method public boolean bindDeviceAdminServiceAsUser(android.content.ComponentName, android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle);
|
||||
method public void clearCrossProfileIntentFilters(android.content.ComponentName);
|
||||
method public void clearDeviceOwnerApp(java.lang.String);
|
||||
method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String);
|
||||
|
||||
@@ -6154,6 +6154,7 @@ package android.app.admin {
|
||||
method public boolean addCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
|
||||
method public void addPersistentPreferredActivity(android.content.ComponentName, android.content.IntentFilter, android.content.ComponentName);
|
||||
method public void addUserRestriction(android.content.ComponentName, java.lang.String);
|
||||
method public boolean bindDeviceAdminServiceAsUser(android.content.ComponentName, android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle);
|
||||
method public void clearCrossProfileIntentFilters(android.content.ComponentName);
|
||||
method public void clearDeviceOwnerApp(java.lang.String);
|
||||
method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String);
|
||||
|
||||
@@ -5999,6 +5999,7 @@ package android.app.admin {
|
||||
method public boolean addCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
|
||||
method public void addPersistentPreferredActivity(android.content.ComponentName, android.content.IntentFilter, android.content.ComponentName);
|
||||
method public void addUserRestriction(android.content.ComponentName, java.lang.String);
|
||||
method public boolean bindDeviceAdminServiceAsUser(android.content.ComponentName, android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle);
|
||||
method public void clearCrossProfileIntentFilters(android.content.ComponentName);
|
||||
method public void clearDeviceOwnerApp(java.lang.String);
|
||||
method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String);
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.app;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentProvider;
|
||||
@@ -1457,8 +1458,22 @@ class ContextImpl extends Context {
|
||||
return bindServiceCommon(service, conn, flags, handler, user);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@Override
|
||||
public IServiceConnection getServiceDispatcher(ServiceConnection conn, Handler handler,
|
||||
int flags) {
|
||||
return mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@Override
|
||||
public IApplicationThread getIApplicationThread() {
|
||||
return mMainThread.getApplicationThread();
|
||||
}
|
||||
|
||||
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
|
||||
handler, UserHandle user) {
|
||||
// Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
|
||||
IServiceConnection sd;
|
||||
if (conn == null) {
|
||||
throw new IllegalArgumentException("connection is null");
|
||||
@@ -2141,7 +2156,8 @@ class ContextImpl extends Context {
|
||||
return mOuterContext;
|
||||
}
|
||||
|
||||
final IBinder getActivityToken() {
|
||||
@Override
|
||||
public IBinder getActivityToken() {
|
||||
return mActivityToken;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,19 +20,21 @@ import android.annotation.ColorInt;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.annotation.SdkConstant;
|
||||
import android.annotation.SdkConstant.SdkConstantType;
|
||||
import android.annotation.SystemApi;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.annotation.WorkerThread;
|
||||
import android.app.Activity;
|
||||
import android.app.admin.NetworkEvent;
|
||||
import android.app.admin.PasswordMetrics;
|
||||
import android.app.IServiceConnection;
|
||||
import android.app.admin.SecurityLog.SecurityEvent;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
@@ -45,10 +47,8 @@ import android.os.PersistableBundle;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteCallback;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.os.ServiceManager.ServiceNotFoundException;
|
||||
import android.provider.ContactsContract.Directory;
|
||||
import android.provider.Settings;
|
||||
import android.security.Credentials;
|
||||
@@ -6713,4 +6713,40 @@ public class DevicePolicyManager {
|
||||
throw re.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by device owner/ profile owner in managed profile to bind the service with each other.
|
||||
* The service must be unexported. Note that the {@link Context} used to obtain this
|
||||
* {@link DevicePolicyManager} instance via {@link Context#getSystemService(Class)} will be used
|
||||
* to bind to the {@link android.app.Service}.
|
||||
* STOPSHIP (b/31952368): Update the javadoc after we policy to control which packages can talk.
|
||||
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
|
||||
* @param serviceIntent Identifies the service to connect to. The Intent must specify either an
|
||||
* explicit component name or a package name to match an
|
||||
* {@link IntentFilter} published by a service.
|
||||
* @param conn Receives information as the service is started and stopped. This must be a
|
||||
* valid {@link ServiceConnection} object; it must not be {@code null}.
|
||||
* @param flags Operation options for the binding operation. See
|
||||
* {@link Context#bindService(Intent, ServiceConnection, int)}.
|
||||
* @param targetUser Which user to bind to.
|
||||
* @return If you have successfully bound to the service, {@code true} is returned;
|
||||
* {@code false} is returned if the connection is not made and you will not
|
||||
* receive the service object.
|
||||
* @see Context#bindService(Intent, ServiceConnection, int)
|
||||
*/
|
||||
public boolean bindDeviceAdminServiceAsUser(
|
||||
@NonNull ComponentName admin, Intent serviceIntent, @NonNull ServiceConnection conn,
|
||||
@Context.BindServiceFlags int flags, @NonNull UserHandle targetUser) {
|
||||
throwIfParentInstance("bindDeviceAdminServiceAsUser");
|
||||
// Keep this in sync with ContextImpl.bindServiceCommon.
|
||||
try {
|
||||
final IServiceConnection sd = mContext.getServiceDispatcher(conn, null, flags);
|
||||
serviceIntent.prepareToLeaveProcess(mContext);
|
||||
return mService.bindDeviceAdminServiceAsUser(admin,
|
||||
mContext.getIApplicationThread(), mContext.getActivityToken(), serviceIntent,
|
||||
sd, flags, targetUser.getIdentifier());
|
||||
} catch (RemoteException re) {
|
||||
throw re.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
package android.app.admin;
|
||||
|
||||
import android.app.admin.NetworkEvent;
|
||||
import android.app.IApplicationThread;
|
||||
import android.app.IServiceConnection;
|
||||
import android.app.admin.SystemUpdatePolicy;
|
||||
import android.app.admin.PasswordMetrics;
|
||||
import android.content.ComponentName;
|
||||
@@ -319,4 +321,8 @@ interface IDevicePolicyManager {
|
||||
void setNetworkLoggingEnabled(in ComponentName admin, boolean enabled);
|
||||
boolean isNetworkLoggingEnabled(in ComponentName admin);
|
||||
List<NetworkEvent> retrieveNetworkLogs(in ComponentName admin, long batchToken);
|
||||
|
||||
boolean bindDeviceAdminServiceAsUser(in ComponentName admin,
|
||||
IApplicationThread caller, IBinder token, in Intent service,
|
||||
IServiceConnection connection, int flags, int targetUserId);
|
||||
}
|
||||
|
||||
@@ -32,6 +32,10 @@ import android.annotation.StyleableRes;
|
||||
import android.annotation.SystemApi;
|
||||
import android.annotation.TestApi;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.IApplicationThread;
|
||||
import android.app.IServiceConnection;
|
||||
import android.app.LoadedApk;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.AssetManager;
|
||||
@@ -4363,4 +4367,27 @@ public abstract class Context {
|
||||
public boolean isCredentialEncryptedStorage() {
|
||||
return isCredentialProtectedStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public IBinder getActivityToken() {
|
||||
throw new RuntimeException("Not implemented. Must override in a subclass.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@Nullable
|
||||
public IServiceConnection getServiceDispatcher(ServiceConnection conn, Handler handler,
|
||||
int flags) {
|
||||
throw new RuntimeException("Not implemented. Must override in a subclass.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public IApplicationThread getIApplicationThread() {
|
||||
throw new RuntimeException("Not implemented. Must override in a subclass.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,10 @@
|
||||
|
||||
package android.content;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.SystemApi;
|
||||
import android.app.IApplicationThread;
|
||||
import android.app.IServiceConnection;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.AssetManager;
|
||||
@@ -857,4 +860,29 @@ public class ContextWrapper extends Context {
|
||||
public boolean isCredentialProtectedStorage() {
|
||||
return mBase.isCredentialProtectedStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
public IBinder getActivityToken() {
|
||||
return mBase.getActivityToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
public IServiceConnection getServiceDispatcher(ServiceConnection conn, Handler handler,
|
||||
int flags) {
|
||||
return mBase.getServiceDispatcher(conn, handler, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
public IApplicationThread getIApplicationThread() {
|
||||
return mBase.getIApplicationThread();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,8 @@ import android.app.ActivityManagerNative;
|
||||
import android.app.AlarmManager;
|
||||
import android.app.AppGlobals;
|
||||
import android.app.IActivityManager;
|
||||
import android.app.IApplicationThread;
|
||||
import android.app.IServiceConnection;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
@@ -137,6 +139,7 @@ import com.android.internal.R;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.internal.statusbar.IStatusBarService;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
import com.android.internal.util.FastXmlSerializer;
|
||||
import com.android.internal.util.JournaledFile;
|
||||
import com.android.internal.util.ParcelableString;
|
||||
@@ -1439,6 +1442,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|
||||
ServiceManager.getService(IpConnectivityLog.SERVICE_NAME));
|
||||
}
|
||||
|
||||
PackageManager getPackageManager() {
|
||||
return mContext.getPackageManager();
|
||||
}
|
||||
|
||||
PowerManagerInternal getPowerManagerInternal() {
|
||||
return LocalServices.getService(PowerManagerInternal.class);
|
||||
}
|
||||
@@ -5907,6 +5914,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
boolean isDeviceOwner(ActiveAdmin admin) {
|
||||
return isDeviceOwner(admin.info.getComponent(), admin.getUserHandle().getIdentifier());
|
||||
}
|
||||
|
||||
public boolean isDeviceOwner(ComponentName who, int userId) {
|
||||
synchronized (this) {
|
||||
return mOwners.hasDeviceOwner()
|
||||
@@ -9428,6 +9439,77 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean bindDeviceAdminServiceAsUser(
|
||||
@NonNull ComponentName admin, @NonNull IApplicationThread caller,
|
||||
@Nullable IBinder activtiyToken, @NonNull Intent serviceIntent,
|
||||
@NonNull IServiceConnection connection, int flags, @UserIdInt int targetUserId) {
|
||||
if (!mHasFeature) {
|
||||
return false;
|
||||
}
|
||||
Preconditions.checkNotNull(admin);
|
||||
Preconditions.checkNotNull(caller);
|
||||
Preconditions.checkNotNull(serviceIntent);
|
||||
Preconditions.checkNotNull(connection);
|
||||
final int callingUserId = mInjector.userHandleGetCallingUserId();
|
||||
Preconditions.checkArgument(callingUserId != targetUserId,
|
||||
"target user id must be different from the calling user id");
|
||||
|
||||
synchronized (this) {
|
||||
final ActiveAdmin callingAdmin = getActiveAdminForCallerLocked(admin,
|
||||
DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
|
||||
// Ensure the target user is valid.
|
||||
if (isDeviceOwner(callingAdmin)) {
|
||||
enforceManagedProfile(targetUserId, "Target user must be a managed profile");
|
||||
} else {
|
||||
// Further lock down to profile owner in managed profile.
|
||||
enforceManagedProfile(callingUserId,
|
||||
"Only support profile owner in managed profile.");
|
||||
if (mOwners.getDeviceOwnerUserId() != targetUserId) {
|
||||
throw new SecurityException("Target user must be a device owner.");
|
||||
}
|
||||
}
|
||||
}
|
||||
final long callingIdentity = mInjector.binderClearCallingIdentity();
|
||||
try {
|
||||
if (!mUserManager.isSameProfileGroup(callingUserId, targetUserId)) {
|
||||
throw new SecurityException(
|
||||
"Can only bind service across users under the same profile group");
|
||||
}
|
||||
final String targetPackage;
|
||||
synchronized (this) {
|
||||
targetPackage = getOwnerPackageNameForUserLocked(targetUserId);
|
||||
}
|
||||
// STOPSHIP(b/31952368): Add policy to control which packages can talk.
|
||||
if (TextUtils.isEmpty(targetPackage) || !targetPackage.equals(admin.getPackageName())) {
|
||||
throw new SecurityException("Device owner and profile owner must be the same " +
|
||||
"package in order to communicate.");
|
||||
}
|
||||
// Validate and sanitize the incoming service intent.
|
||||
final Intent sanitizedIntent =
|
||||
createCrossUserServiceIntent(serviceIntent, targetPackage);
|
||||
if (sanitizedIntent == null) {
|
||||
// Fail, cannot lookup the target service.
|
||||
throw new SecurityException("Invalid intent or failed to look up the service");
|
||||
}
|
||||
// Ask ActivityManager to bind it. Notice that we are binding the service with the
|
||||
// caller app instead of DevicePolicyManagerService.
|
||||
try {
|
||||
return mInjector.getIActivityManager().bindService(
|
||||
caller, activtiyToken, serviceIntent,
|
||||
serviceIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
|
||||
connection, flags, mContext.getOpPackageName(),
|
||||
targetUserId) != 0;
|
||||
} catch (RemoteException ex) {
|
||||
// Same process, should not happen.
|
||||
}
|
||||
} finally {
|
||||
mInjector.binderRestoreCallingIdentity(callingIdentity);
|
||||
}
|
||||
// Fail to bind.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if a given user has any accounts that'll prevent installing a device or profile
|
||||
* owner {@code owner}.
|
||||
@@ -9589,4 +9671,46 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|
||||
? mNetworkLogger.retrieveLogs(batchToken)
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the package name of owner in a given user.
|
||||
*/
|
||||
private String getOwnerPackageNameForUserLocked(int userId) {
|
||||
return getDeviceOwnerUserId() == userId
|
||||
? mOwners.getDeviceOwnerPackageName()
|
||||
: mOwners.getProfileOwnerPackage(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param rawIntent Original service intent specified by caller.
|
||||
* @param expectedPackageName The expected package name in the incoming intent.
|
||||
* @return Intent that have component explicitly set. {@code null} if the incoming intent
|
||||
* or target service is invalid.
|
||||
*/
|
||||
private Intent createCrossUserServiceIntent (
|
||||
@NonNull Intent rawIntent, @NonNull String expectedPackageName) {
|
||||
if (rawIntent.getComponent() == null && rawIntent.getPackage() == null) {
|
||||
Log.e(LOG_TAG, "Service intent must be explicit (with a package name or component): "
|
||||
+ rawIntent);
|
||||
return null;
|
||||
}
|
||||
ResolveInfo info = mInjector.getPackageManager().resolveService(rawIntent, 0);
|
||||
if (info == null || info.serviceInfo == null) {
|
||||
Log.e(LOG_TAG, "Fail to look up the service: " + rawIntent);
|
||||
return null;
|
||||
}
|
||||
if (!expectedPackageName.equals(info.serviceInfo.packageName)) {
|
||||
Log.e(LOG_TAG, "Only allow to bind service in " + expectedPackageName);
|
||||
return null;
|
||||
}
|
||||
if (info.serviceInfo.exported) {
|
||||
Log.e(LOG_TAG, "The service must be unexported.");
|
||||
return null;
|
||||
}
|
||||
// It is the system server to bind the service, it would be extremely dangerous if it
|
||||
// can be exploited to bind any service. Set the component explicitly to make sure we
|
||||
// do not bind anything accidentally.
|
||||
rawIntent.setComponent(info.serviceInfo.getComponentName());
|
||||
return rawIntent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
package android.test.mock;
|
||||
|
||||
import android.annotation.SystemApi;
|
||||
import android.app.IApplicationThread;
|
||||
import android.app.IServiceConnection;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
@@ -759,4 +761,23 @@ public class MockContext extends Context {
|
||||
public boolean isCredentialProtectedStorage() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
@Override
|
||||
public IBinder getActivityToken() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
@Override
|
||||
public IServiceConnection getServiceDispatcher(ServiceConnection conn, Handler handler,
|
||||
int flags) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
@Override
|
||||
public IApplicationThread getIApplicationThread() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user