Merge "Persistent connection to DO/PO service." into oc-dev
diff --git a/Android.mk b/Android.mk
index eb649c9..634272b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -102,6 +102,7 @@
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 d9f6a6a..bf35894 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6232,6 +6232,11 @@
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);
@@ -6398,6 +6403,7 @@
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 1c8325c..ec6c609 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6435,6 +6435,11 @@
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);
@@ -6622,6 +6627,7 @@
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 98db0a4..421cccf 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -6251,6 +6251,11 @@
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);
@@ -6426,6 +6431,7 @@
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 0000000..cd0b1bf
--- /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 6d8d5e9..2f0a630 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1515,6 +1515,16 @@
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 0000000..5276ed5
--- /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 0000000..c34c097
--- /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 0000000..97fa9d5
--- /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 6f49324..bfa1b99 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.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;
@@ -194,7 +193,6 @@
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;
@@ -365,6 +363,7 @@
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
@@ -459,7 +458,17 @@
@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);
}
}
@@ -1420,7 +1429,7 @@
}
}
- 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);
@@ -1434,9 +1443,9 @@
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());
@@ -1461,6 +1470,13 @@
}
}
+ // 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);
@@ -1791,6 +1807,8 @@
// 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;
@@ -2943,7 +2961,7 @@
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();
@@ -2990,10 +3008,32 @@
}
}
- 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() {
@@ -5078,7 +5118,7 @@
* @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}.
@@ -6460,6 +6500,9 @@
} 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;
}
@@ -6615,6 +6658,8 @@
}
private void clearDeviceOwnerLocked(ActiveAdmin admin, int userId) {
+ mDeviceAdminServiceController.stopServiceForOwner(userId, "clear-device-owner");
+
if (admin != null) {
admin.disableCamera = false;
admin.userRestrictions = null;
@@ -6692,6 +6737,8 @@
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
+ mDeviceAdminServiceController.startServiceForOwner(
+ who.getPackageName(), userHandle, "set-profile-owner");
return true;
}
}
@@ -6723,6 +6770,8 @@
}
public void clearProfileOwnerLocked(ActiveAdmin admin, int userId) {
+ mDeviceAdminServiceController.stopServiceForOwner(userId, "clear-profile-owner");
+
if (admin != null) {
admin.disableCamera = false;
admin.userRestrictions = null;
@@ -7275,6 +7324,7 @@
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));
@@ -9624,6 +9674,21 @@
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;