OSDN Git Service

Allow cross profile call between DO and PO
authorTony Mak <tonymak@google.com>
Mon, 14 Nov 2016 12:53:06 +0000 (12:53 +0000)
committerTony Mak <tonymak@google.com>
Mon, 14 Nov 2016 14:20:11 +0000 (14:20 +0000)
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

api/current.txt
api/system-current.txt
api/test-current.txt
core/java/android/app/ContextImpl.java
core/java/android/app/admin/DevicePolicyManager.java
core/java/android/app/admin/IDevicePolicyManager.aidl
core/java/android/content/Context.java
core/java/android/content/ContextWrapper.java
services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
test-runner/src/android/test/mock/MockContext.java

index 873957b..6096f90 100644 (file)
@@ -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);
index 88f25a8..370afc5 100644 (file)
@@ -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);
index 5d08832..ab16ce9 100644 (file)
@@ -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);
index f1d0e10..c5180fd 100644 (file)
@@ -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;
     }
 
index 1ab809d..5ca39b0 100644 (file)
@@ -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();
+        }
+    }
 }
index 729d12b..a2546c0 100644 (file)
@@ -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);
 }
index 3964e0a..821b0f8 100644 (file)
@@ -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.");
+    }
 }
index edc8d82..7533655 100644 (file)
 
 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();
+    }
 }
index b687e09..44d8257 100644 (file)
@@ -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;
+    }
 }
index fdd971b..190fc35 100644 (file)
@@ -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();
+    }
 }