diff options
| -rw-r--r-- | Android.mk | 1 | ||||
| -rw-r--r-- | api/current.txt | 6 | ||||
| -rw-r--r-- | api/system-current.txt | 6 | ||||
| -rw-r--r-- | api/test-current.txt | 6 | ||||
| -rw-r--r-- | core/java/android/app/admin/DeviceAdminService.java | 56 | ||||
| -rw-r--r-- | core/java/android/app/admin/DevicePolicyManager.java | 10 | ||||
| -rw-r--r-- | core/java/android/app/admin/IDeviceAdminService.aidl | 23 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/PersistentConnection.java | 168 | ||||
| -rw-r--r-- | services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java | 211 | ||||
| -rw-r--r-- | services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java | 85 |
10 files changed, 562 insertions, 10 deletions
diff --git a/Android.mk b/Android.mk index 8daab8ff800e..5fb0696e7d7b 100644 --- a/Android.mk +++ b/Android.mk @@ -102,6 +102,7 @@ LOCAL_SRC_FILES += \ core/java/android/app/IUserSwitchObserver.aidl \ core/java/android/app/IWallpaperManager.aidl \ core/java/android/app/IWallpaperManagerCallback.aidl \ + core/java/android/app/admin/IDeviceAdminService.aidl \ core/java/android/app/admin/IDevicePolicyManager.aidl \ core/java/android/app/trust/IStrongAuthTracker.aidl \ core/java/android/app/trust/ITrustManager.aidl \ diff --git a/api/current.txt b/api/current.txt index 6234168a3a99..42b9a08a4e72 100644 --- a/api/current.txt +++ b/api/current.txt @@ -6223,6 +6223,11 @@ package android.app.admin { field public static final java.lang.String EXTRA_LOCK_TASK_PACKAGE = "android.app.extra.LOCK_TASK_PACKAGE"; } + public class DeviceAdminService extends android.app.Service { + ctor public DeviceAdminService(); + method public final android.os.IBinder onBind(android.content.Intent); + } + public class DevicePolicyManager { method public void addCrossProfileIntentFilter(android.content.ComponentName, android.content.IntentFilter, int); method public boolean addCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String); @@ -6389,6 +6394,7 @@ package android.app.admin { method public void wipeData(int); field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN"; field public static final java.lang.String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED = "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED"; + 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_PROVISIONING_SUCCESSFUL = "android.app.action.PROVISIONING_SUCCESSFUL"; diff --git a/api/system-current.txt b/api/system-current.txt index 6a6b279d8f32..5d7a7cfd4ef1 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -6425,6 +6425,11 @@ package android.app.admin { field public static final java.lang.String EXTRA_LOCK_TASK_PACKAGE = "android.app.extra.LOCK_TASK_PACKAGE"; } + public class DeviceAdminService extends android.app.Service { + ctor public DeviceAdminService(); + method public final android.os.IBinder onBind(android.content.Intent); + } + public class DevicePolicyManager { method public void addCrossProfileIntentFilter(android.content.ComponentName, android.content.IntentFilter, int); method public boolean addCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String); @@ -6612,6 +6617,7 @@ package android.app.admin { field public static final java.lang.String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED"; field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN"; field public static final java.lang.String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED = "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED"; + 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_PROVISIONING_SUCCESSFUL = "android.app.action.PROVISIONING_SUCCESSFUL"; diff --git a/api/test-current.txt b/api/test-current.txt index 1f342e6b9409..b58e40c6a20f 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -6242,6 +6242,11 @@ package android.app.admin { field public static final java.lang.String EXTRA_LOCK_TASK_PACKAGE = "android.app.extra.LOCK_TASK_PACKAGE"; } + public class DeviceAdminService extends android.app.Service { + ctor public DeviceAdminService(); + method public final android.os.IBinder onBind(android.content.Intent); + } + public class DevicePolicyManager { method public void addCrossProfileIntentFilter(android.content.ComponentName, android.content.IntentFilter, int); method public boolean addCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String); @@ -6417,6 +6422,7 @@ package android.app.admin { field public static final java.lang.String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED"; field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN"; field public static final java.lang.String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED = "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED"; + 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_PROVISIONING_SUCCESSFUL = "android.app.action.PROVISIONING_SUCCESSFUL"; diff --git a/core/java/android/app/admin/DeviceAdminService.java b/core/java/android/app/admin/DeviceAdminService.java new file mode 100644 index 000000000000..cd0b1bf3d0a8 --- /dev/null +++ b/core/java/android/app/admin/DeviceAdminService.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.app.admin; + +import android.app.Service; +import android.content.ComponentName; +import android.content.Intent; +import android.os.IBinder; + +/** + * Base class for a service that device owner/profile owners can optionally have. + * + * <p>The system searches for it with an intent filter with the + * {@link DevicePolicyManager#ACTION_DEVICE_ADMIN_SERVICE} action, and tries to keep a bound + * connection as long as the hosting user is running, so that the device/profile owner is always + * considered to be in the foreground. + * + * <p>Device/profile owners can use + * {@link android.content.pm.PackageManager#setComponentEnabledSetting(ComponentName, int, int)} + * to disable/enable its own service. For example, when a device/profile owner no longer needs + * to be in the foreground, it can (and should) disable its service. + * + * <p>The service must not be exported. + * + * <p>TODO: Describe how the system handles crashes in DO/PO. + */ +public class DeviceAdminService extends Service { + private final IDeviceAdminServiceImpl mImpl; + + public DeviceAdminService() { + mImpl = new IDeviceAdminServiceImpl(); + } + + @Override + public final IBinder onBind(Intent intent) { + return mImpl.asBinder(); + } + + private class IDeviceAdminServiceImpl extends IDeviceAdminService.Stub { + } + + // So far, we have no methods in this class. +} diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 6d8d5e93c1ee..2f0a630bcd04 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -1515,6 +1515,16 @@ public class DevicePolicyManager { public @interface ProvisioningPreCondition {} /** + * Service action: Action for a service that device owner and profile owner can optionally + * own. If a device owner or a profile owner has such a service, the system tries to keep + * a bound connection to it, in order to keep their process always running. + * The service must not be exported. + */ + @SdkConstant(SdkConstantType.SERVICE_ACTION) + public static final String ACTION_DEVICE_ADMIN_SERVICE + = "android.app.action.DEVICE_ADMIN_SERVICE"; + + /** * Return true if the given administrator component is currently active (enabled) in the system. * * @param admin The administrator component to check for. diff --git a/core/java/android/app/admin/IDeviceAdminService.aidl b/core/java/android/app/admin/IDeviceAdminService.aidl new file mode 100644 index 000000000000..5276ed599226 --- /dev/null +++ b/core/java/android/app/admin/IDeviceAdminService.aidl @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.admin; + +/** + * @hide + */ +interface IDeviceAdminService { +} diff --git a/services/core/java/com/android/server/am/PersistentConnection.java b/services/core/java/com/android/server/am/PersistentConnection.java new file mode 100644 index 000000000000..c34c097cb696 --- /dev/null +++ b/services/core/java/com/android/server/am/PersistentConnection.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.am; + +import android.annotation.NonNull; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Handler; +import android.os.IBinder; +import android.os.UserHandle; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; + +import java.io.PrintWriter; + +/** + * Connects to a given service component on a given user. + * + * - Call {@link #connect()} to create a connection. + * - Call {@link #disconnect()} to disconnect. Make sure to disconnect when the user stops. + * + * Add onConnected/onDisconnected callbacks as needed. + */ +public abstract class PersistentConnection<T> { + private final Object mLock = new Object(); + + private final String mTag; + private final Context mContext; + private final Handler mHandler; + private final int mUserId; + private final ComponentName mComponentName; + + @GuardedBy("mLock") + private boolean mStarted; + + @GuardedBy("mLock") + private boolean mIsConnected; + + @GuardedBy("mLock") + private T mService; + + private final ServiceConnection mServiceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + synchronized (mLock) { + Slog.i(mTag, "Connected: " + mComponentName.flattenToShortString() + + " u" + mUserId); + + mIsConnected = true; + mService = asInterface(service); + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + synchronized (mLock) { + Slog.i(mTag, "Disconnected: " + mComponentName.flattenToShortString() + + " u" + mUserId); + + cleanUpConnectionLocked(); + } + } + }; + + public PersistentConnection(@NonNull String tag, @NonNull Context context, + @NonNull Handler handler, int userId, @NonNull ComponentName componentName) { + mTag = tag; + mContext = context; + mHandler = handler; + mUserId = userId; + mComponentName = componentName; + } + + public final ComponentName getComponentName() { + return mComponentName; + } + + /** + * @return whether connected. + */ + public final boolean isConnected() { + synchronized (mLock) { + return mIsConnected; + } + } + + /** + * @return the service binder interface. + */ + public final T getServiceBinder() { + synchronized (mLock) { + return mService; + } + } + + /** + * Connects to the service. + */ + public final void connect() { + synchronized (mLock) { + if (mStarted) { + return; + } + mStarted = true; + + final Intent service = new Intent().setComponent(mComponentName); + + final boolean success = mContext.bindServiceAsUser(service, mServiceConnection, + Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, + mHandler, UserHandle.of(mUserId)); + + if (!success) { + Slog.e(mTag, "Binding: " + service.getComponent() + " u" + mUserId + + " failed."); + } + } + } + + private void cleanUpConnectionLocked() { + mIsConnected = false; + mService = null; + } + + /** + * Disconnect from the service. + */ + public final void disconnect() { + synchronized (mLock) { + if (!mStarted) { + return; + } + Slog.i(mTag, "Stopping: " + mComponentName.flattenToShortString() + " u" + mUserId); + mStarted = false; + mContext.unbindService(mServiceConnection); + + cleanUpConnectionLocked(); + } + } + + /** Must be implemented by a subclass to convert an {@link IBinder} to a stub. */ + protected abstract T asInterface(IBinder binder); + + public void dump(String prefix, PrintWriter pw) { + synchronized (mLock) { + pw.print(prefix); + pw.print(mComponentName.flattenToShortString()); + pw.print(mStarted ? " [started]" : " [not started]"); + pw.print(mIsConnected ? " [connected]" : " [not connected]"); + pw.println(); + } + } +} diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java new file mode 100644 index 000000000000..97fa9d552142 --- /dev/null +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.devicepolicy; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.admin.DevicePolicyManager; +import android.app.admin.IDeviceAdminService; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ParceledListSlice; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; +import android.util.Slog; +import android.util.SparseArray; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.os.BackgroundThread; +import com.android.server.am.PersistentConnection; + +import java.io.PrintWriter; +import java.util.List; + +/** + * Manages connections to persistent services in owner packages. + */ +public class DeviceAdminServiceController { + static final String TAG = DevicePolicyManagerService.LOG_TAG; + + static final boolean DEBUG = false; // DO NOT MERGE WITH TRUE. + + final Object mLock = new Object(); + final Context mContext; + + private final DevicePolicyManagerService mService; + private final DevicePolicyManagerService.Injector mInjector; + + private final Handler mHandler; // needed? + + static void debug(String format, Object... args) { + if (!DEBUG) { + return; + } + Slog.d(TAG, String.format(format, args)); + } + + private class DevicePolicyServiceConnection + extends PersistentConnection<IDeviceAdminService> { + public DevicePolicyServiceConnection(int userId, @NonNull ComponentName componentName) { + super(TAG, mContext, mHandler, userId, componentName); + } + + @Override + protected IDeviceAdminService asInterface(IBinder binder) { + return IDeviceAdminService.Stub.asInterface(binder); + } + } + + /** + * User-ID -> {@link PersistentConnection}. + */ + @GuardedBy("mLock") + private final SparseArray<DevicePolicyServiceConnection> mConnections = new SparseArray<>(); + + public DeviceAdminServiceController(DevicePolicyManagerService service) { + mService = service; + mInjector = service.mInjector; + mContext = mInjector.mContext; + mHandler = new Handler(BackgroundThread.get().getLooper()); + } + + /** + * Find a service that handles {@link DevicePolicyManager#ACTION_DEVICE_ADMIN_SERVICE} + * in a given package. + */ + @Nullable + private ServiceInfo findService(@NonNull String packageName, int userId) { + final Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_ADMIN_SERVICE); + intent.setPackage(packageName); + + try { + final ParceledListSlice<ResolveInfo> pls = mInjector.getIPackageManager() + .queryIntentServices(intent, null, /* flags=*/ 0, userId); + if (pls == null) { + return null; + } + final List<ResolveInfo> list = pls.getList(); + if (list.size() == 0) { + return null; + } + // Note if multiple services are found, that's an error, even if only one of them + // is exported. + if (list.size() > 1) { + Log.e(TAG, "More than one DeviceAdminService's found in package " + + packageName + + ". They'll all be ignored."); + return null; + } + final ServiceInfo si = list.get(0).serviceInfo; + if (si.exported) { + Log.e(TAG, "DeviceAdminService must not be exported: '" + + si.getComponentName().flattenToShortString() + + "' will be ignored."); + return null; + } + return si; + } catch (RemoteException e) { + } + return null; + } + + /** + * Find a service that handles {@link DevicePolicyManager#ACTION_DEVICE_ADMIN_SERVICE} + * in an owner package and connect to it. + */ + public void startServiceForOwner(@NonNull String packageName, int userId, + @NonNull String actionForLog) { + final long token = mInjector.binderClearCallingIdentity(); + try { + synchronized (mLock) { + final ServiceInfo service = findService(packageName, userId); + if (service == null) { + debug("Owner package %s on u%d has no service.", + packageName, userId); + disconnectServiceOnUserLocked(userId, actionForLog); + return; + } + // See if it's already running. + final PersistentConnection<IDeviceAdminService> existing = + mConnections.get(userId); + if (existing != null) { + if (existing.getComponentName().equals(service.getComponentName())) { + return; + } + disconnectServiceOnUserLocked(userId, actionForLog); + } + + debug("Owner package %s on u%d has service %s for %s", + packageName, userId, + service.getComponentName().flattenToShortString(), actionForLog); + + final DevicePolicyServiceConnection conn = + new DevicePolicyServiceConnection( + userId, service.getComponentName()); + mConnections.put(userId, conn); + conn.connect(); + } + } finally { + mInjector.binderRestoreCallingIdentity(token); + } + } + + /** + * Stop an owner service on a given user. + */ + public void stopServiceForOwner(int userId, @NonNull String actionForLog) { + final long token = mInjector.binderClearCallingIdentity(); + try { + synchronized (mLock) { + disconnectServiceOnUserLocked(userId, actionForLog); + } + } finally { + mInjector.binderRestoreCallingIdentity(token); + } + } + + private void disconnectServiceOnUserLocked(int userId, @NonNull String actionForLog) { + final DevicePolicyServiceConnection conn = mConnections.get(userId); + if (conn != null) { + debug("Stopping service for u%d if already running for %s.", + userId, actionForLog); + conn.disconnect(); + mConnections.remove(userId); + } + } + + public void dump(String prefix, PrintWriter pw) { + synchronized (mLock) { + if (mConnections.size() == 0) { + return; + } + pw.println(); + pw.print(prefix); pw.println("Owner Services:"); + for (int i = 0; i < mConnections.size(); i++) { + final int userId = mConnections.keyAt(i); + pw.print(prefix); pw.print(" "); pw.print("User: "); pw.println(userId); + + final DevicePolicyServiceConnection con = mConnections.valueAt(i); + con.dump(prefix + " ", pw); + } + } + } +} diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index ab86966334c3..30008c965730 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -140,7 +140,6 @@ import android.os.storage.StorageManager; import android.provider.ContactsContract.QuickContact; import android.provider.ContactsInternal; import android.provider.Settings; -import android.security.Credentials; import android.security.IKeyChainAliasCallback; import android.security.IKeyChainService; import android.security.KeyChain; @@ -192,7 +191,6 @@ import java.io.IOException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.text.DateFormat; -import java.text.NumberFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -366,6 +364,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final UserManagerInternal mUserManagerInternal; final TelephonyManager mTelephonyManager; private final LockPatternUtils mLockPatternUtils; + private final DeviceAdminServiceController mDeviceAdminServiceController; /** * Contains (package-user) pairs to remove. An entry (p, u) implies that removal of package p @@ -460,7 +459,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public void onStartUser(int userHandle) { - mService.onStartUser(userHandle); + mService.handleStartUser(userHandle); + } + + @Override + public void onUnlockUser(int userHandle) { + mService.handleUnlockUser(userHandle); + } + + @Override + public void onStopUser(int userHandle) { + mService.handleStopUser(userHandle); } } @@ -1421,7 +1430,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - private void handlePackagesChanged(String packageName, int userHandle) { + private void handlePackagesChanged(@Nullable String packageName, int userHandle) { boolean removedAdmin = false; if (VERBOSE_LOG) Slog.d(LOG_TAG, "Handling package changes for user " + userHandle); DevicePolicyData policy = getUserData(userHandle); @@ -1435,9 +1444,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (packageName == null || packageName.equals(adminPackage)) { if (mIPackageManager.getPackageInfo(adminPackage, 0, userHandle) == null || mIPackageManager.getReceiverInfo(aa.info.getComponent(), - PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, - userHandle) == null) { + PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, + userHandle) == null) { removedAdmin = true; policy.mAdminList.remove(i); policy.mAdminMap.remove(aa.info.getComponent()); @@ -1462,6 +1471,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + // If it's an owner package, we may need to refresh the bound connection. + final ComponentName owner = getOwnerComponent(userHandle); + if ((packageName != null) && (owner != null) + && (owner.getPackageName().equals(packageName))) { + startOwnerService(userHandle, "package-broadcast"); + } + // Persist updates if the removed package was an admin or delegate. if (removedAdmin || removedDelegate) { saveSettingsLocked(policy.mUserHandle); @@ -1792,6 +1808,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Needed when mHasFeature == false, because it controls the certificate warning text. mCertificateMonitor = new CertificateMonitor(this, mInjector, mBackgroundHandler); + mDeviceAdminServiceController = new DeviceAdminServiceController(this); + if (!mHasFeature) { // Skip the rest of the initialization return; @@ -2944,7 +2962,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { loadOwners(); cleanUpOldUsers(); ensureUnknownSourcesRestrictionForProfileOwners(); - onStartUser(UserHandle.USER_SYSTEM); + handleStartUser(UserHandle.USER_SYSTEM); // Register an observer for watching for user setup complete and settings changes. mSetupContentObserver.register(); @@ -2991,10 +3009,32 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - private void onStartUser(int userId) { + void handleStartUser(int userId) { updateScreenCaptureDisabledInWindowManager(userId, getScreenCaptureDisabled(null, userId)); pushUserRestrictions(userId); + + startOwnerService(userId, "start-user"); + } + + void handleUnlockUser(int userId) { + startOwnerService(userId, "unlock-user"); + } + + void handleStopUser(int userId) { + stopOwnerService(userId, "stop-user"); + } + + private void startOwnerService(int userId, String actionForLog) { + final ComponentName owner = getOwnerComponent(userId); + if (owner != null) { + mDeviceAdminServiceController.startServiceForOwner( + owner.getPackageName(), userId, actionForLog); + } + } + + private void stopOwnerService(int userId, String actionForLog) { + mDeviceAdminServiceController.stopServiceForOwner(userId, actionForLog); } private void cleanUpOldUsers() { @@ -5079,7 +5119,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { * @param callerPackage the name of the calling package. Required if {@code who} is * {@code null}. * @param reqPolicy the policy used in the API whose access permission is being checked. - * @param scoppe the delegation scope corresponding to the API being checked. + * @param scope the delegation scope corresponding to the API being checked. * @throws SecurityException if {@code who} is given and is not an owner for {@code reqPolicy}; * or when {@code who} is {@code null} and {@code callerPackage} is not a delegate * of {@code scope}. @@ -6461,6 +6501,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } finally { mInjector.binderRestoreCallingIdentity(ident); } + mDeviceAdminServiceController.startServiceForOwner( + admin.getPackageName(), userId, "set-device-owner"); + Slog.i(LOG_TAG, "Device owner set: " + admin + " on user " + userId); return true; } @@ -6616,6 +6659,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } private void clearDeviceOwnerLocked(ActiveAdmin admin, int userId) { + mDeviceAdminServiceController.stopServiceForOwner(userId, "clear-device-owner"); + if (admin != null) { admin.disableCamera = false; admin.userRestrictions = null; @@ -6693,6 +6738,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } finally { mInjector.binderRestoreCallingIdentity(id); } + mDeviceAdminServiceController.startServiceForOwner( + who.getPackageName(), userHandle, "set-profile-owner"); return true; } } @@ -6724,6 +6771,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } public void clearProfileOwnerLocked(ActiveAdmin admin, int userId) { + mDeviceAdminServiceController.stopServiceForOwner(userId, "clear-profile-owner"); + if (admin != null) { admin.disableCamera = false; admin.userRestrictions = null; @@ -7283,6 +7332,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { synchronized (this) { pw.println("Current Device Policy Manager state:"); mOwners.dump(" ", pw); + mDeviceAdminServiceController.dump(" ", pw); int userCount = mUserData.size(); for (int u = 0; u < userCount; u++) { DevicePolicyData policy = getUserData(mUserData.keyAt(u)); @@ -9632,6 +9682,21 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return null; } + /** + * Return device owner or profile owner set on a given user. + */ + private @Nullable ComponentName getOwnerComponent(int userId) { + synchronized (this) { + if (mOwners.getDeviceOwnerUserId() == userId) { + return mOwners.getDeviceOwnerComponent(); + } + if (mOwners.hasProfileOwner(userId)) { + return mOwners.getProfileOwnerComponent(userId); + } + } + return null; + } + private int checkManagedUserProvisioningPreCondition(int callingUserId) { if (!hasFeatureManagedUsers()) { return CODE_MANAGED_USERS_NOT_SUPPORTED; |