blob: 85ca52e17ecbcabb5e1c1b945f74712de7b43bc6 [file] [log] [blame]
/*
* 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.Manifest.permission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.admin.DeviceAdminService;
import android.app.admin.DevicePolicyManager;
import android.app.admin.IDeviceAdminService;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ServiceInfo;
import android.os.Handler;
import android.os.IBinder;
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 com.android.server.appbinding.AppBindingUtils;
import java.io.PrintWriter;
/**
* 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 DevicePolicyConstants mConstants;
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,
mConstants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC,
mConstants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE,
mConstants.DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC,
mConstants.DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC);
}
@Override
protected int getBindFlags() {
return Context.BIND_FOREGROUND_SERVICE;
}
@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,
DevicePolicyConstants constants) {
mService = service;
mInjector = service.mInjector;
mContext = mInjector.mContext;
mHandler = new Handler(BackgroundThread.get().getLooper());
mConstants = constants;
}
/**
* Find a service that handles {@link DevicePolicyManager#ACTION_DEVICE_ADMIN_SERVICE}
* in a given package.
*/
@Nullable
private ServiceInfo findService(@NonNull String packageName, int userId) {
return AppBindingUtils.findService(
packageName,
userId,
DevicePolicyManager.ACTION_DEVICE_ADMIN_SERVICE,
permission.BIND_DEVICE_ADMIN,
DeviceAdminService.class,
mInjector.getIPackageManager(),
new StringBuilder() /* ignore error message */);
}
/**
* 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) {
// Note even when we're already connected to the same service, the binding
// would have died at this point due to a package update. So we disconnect
// anyway and re-connect.
debug("Disconnecting from existing service connection.",
packageName, userId);
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.bind();
}
} 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);
}
}
@GuardedBy("mLock")
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.unbind();
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);
}
pw.println();
}
}
}