1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.server.devicepolicy; 17 18 import android.Manifest.permission; 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.app.admin.DeviceAdminService; 22 import android.app.admin.DevicePolicyManager; 23 import android.app.admin.IDeviceAdminService; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.pm.ServiceInfo; 27 import android.os.Handler; 28 import android.os.IBinder; 29 import android.util.IndentingPrintWriter; 30 import android.util.SparseArray; 31 32 import com.android.internal.annotations.GuardedBy; 33 import com.android.internal.os.BackgroundThread; 34 import com.android.server.am.PersistentConnection; 35 import com.android.server.appbinding.AppBindingUtils; 36 import com.android.server.utils.Slogf; 37 38 import java.util.HashMap; 39 import java.util.Map; 40 41 /** 42 * Manages connections to persistent services in admin packages. 43 */ 44 public class DeviceAdminServiceController { 45 static final String TAG = DevicePolicyManagerService.LOG_TAG; 46 47 static final boolean DEBUG = false; // DO NOT MERGE WITH TRUE. 48 49 final Object mLock = new Object(); 50 final Context mContext; 51 52 private final DevicePolicyManagerService.Injector mInjector; 53 private final DevicePolicyConstants mConstants; 54 55 private final Handler mHandler; // needed? 56 57 private class DevicePolicyServiceConnection 58 extends PersistentConnection<IDeviceAdminService> { DevicePolicyServiceConnection(int userId, @NonNull ComponentName componentName)59 public DevicePolicyServiceConnection(int userId, @NonNull ComponentName componentName) { 60 super(TAG, mContext, mHandler, userId, componentName, 61 mConstants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC, 62 mConstants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE, 63 mConstants.DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC, 64 mConstants.DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC); 65 } 66 67 @Override getBindFlags()68 protected int getBindFlags() { 69 return Context.BIND_FOREGROUND_SERVICE; 70 } 71 72 @Override asInterface(IBinder binder)73 protected IDeviceAdminService asInterface(IBinder binder) { 74 return IDeviceAdminService.Stub.asInterface(binder); 75 } 76 } 77 78 /** 79 * User-ID -> {@link PersistentConnection}. 80 */ 81 @GuardedBy("mLock") 82 private final SparseArray<Map<String, DevicePolicyServiceConnection>> mConnections = 83 new SparseArray<>(); 84 DeviceAdminServiceController(DevicePolicyManagerService service, DevicePolicyConstants constants)85 public DeviceAdminServiceController(DevicePolicyManagerService service, 86 DevicePolicyConstants constants) { 87 mInjector = service.mInjector; 88 mContext = mInjector.mContext; 89 mHandler = new Handler(BackgroundThread.get().getLooper()); 90 mConstants = constants; 91 } 92 93 /** 94 * Find a service that handles {@link DevicePolicyManager#ACTION_DEVICE_ADMIN_SERVICE} 95 * in a given package. 96 */ 97 @Nullable findService(@onNull String packageName, int userId)98 private ServiceInfo findService(@NonNull String packageName, int userId) { 99 return AppBindingUtils.findService( 100 packageName, 101 userId, 102 DevicePolicyManager.ACTION_DEVICE_ADMIN_SERVICE, 103 permission.BIND_DEVICE_ADMIN, 104 DeviceAdminService.class, 105 mInjector.getIPackageManager(), 106 new StringBuilder() /* ignore error message */); 107 } 108 109 /** 110 * Find a service that handles {@link DevicePolicyManager#ACTION_DEVICE_ADMIN_SERVICE} 111 * in an admin package and connect to it. 112 */ startServiceForAdmin(@onNull String packageName, int userId, @NonNull String actionForLog)113 public void startServiceForAdmin(@NonNull String packageName, int userId, 114 @NonNull String actionForLog) { 115 final long token = mInjector.binderClearCallingIdentity(); 116 try { 117 synchronized (mLock) { 118 final ServiceInfo service = findService(packageName, userId); 119 if (service == null) { 120 if (DEBUG) { 121 Slogf.d(TAG, "Admin package %s on u%d has no service.", packageName, 122 userId); 123 } 124 disconnectServiceOnUserLocked(packageName, userId, actionForLog); 125 return; 126 } 127 // See if it's already running. 128 final PersistentConnection<IDeviceAdminService> existing = 129 mConnections.contains(userId) 130 ? mConnections.get(userId).get(packageName) : null; 131 if (existing != null) { 132 // Note even when we're already connected to the same service, the binding 133 // would have died at this point due to a package update. So we disconnect 134 // anyway and re-connect. 135 if (DEBUG) { 136 Slogf.d("Disconnecting from existing service connection.", packageName, 137 userId); 138 } 139 disconnectServiceOnUserLocked(packageName, userId, actionForLog); 140 } 141 142 if (DEBUG) { 143 Slogf.d("Admin package %s on u%d has service %s for %s", packageName, userId, 144 service.getComponentName().flattenToShortString(), actionForLog); 145 } 146 147 final DevicePolicyServiceConnection conn = 148 new DevicePolicyServiceConnection( 149 userId, service.getComponentName()); 150 if (!mConnections.contains(userId)) { 151 mConnections.put(userId, new HashMap<>()); 152 } 153 mConnections.get(userId).put(packageName, conn); 154 conn.bind(); 155 } 156 } finally { 157 mInjector.binderRestoreCallingIdentity(token); 158 } 159 } 160 161 /** 162 * Stop an admin service on a given user. 163 */ stopServiceForAdmin( @onNull String packageName, int userId, @NonNull String actionForLog)164 public void stopServiceForAdmin( 165 @NonNull String packageName, int userId, @NonNull String actionForLog) { 166 final long token = mInjector.binderClearCallingIdentity(); 167 try { 168 synchronized (mLock) { 169 disconnectServiceOnUserLocked(packageName, userId, actionForLog); 170 } 171 } finally { 172 mInjector.binderRestoreCallingIdentity(token); 173 } 174 } 175 176 /** 177 * Stop all admin services on a given user. 178 */ stopServicesForUser(int userId, @NonNull String actionForLog)179 public void stopServicesForUser(int userId, @NonNull String actionForLog) { 180 final long token = mInjector.binderClearCallingIdentity(); 181 try { 182 synchronized (mLock) { 183 disconnectServiceOnUserLocked(userId, actionForLog); 184 } 185 } finally { 186 mInjector.binderRestoreCallingIdentity(token); 187 } 188 } 189 190 @GuardedBy("mLock") disconnectServiceOnUserLocked( @onNull String packageName, int userId, @NonNull String actionForLog)191 private void disconnectServiceOnUserLocked( 192 @NonNull String packageName, int userId, @NonNull String actionForLog) { 193 final DevicePolicyServiceConnection conn = mConnections.contains(userId) 194 ? mConnections.get(userId).get(packageName) : null; 195 if (conn != null) { 196 if (DEBUG) { 197 Slogf.d(TAG, "Stopping service for package %s on u%d if already running for %s.", 198 packageName, 199 userId, 200 actionForLog); 201 } 202 conn.unbind(); 203 mConnections.get(userId).remove(packageName); 204 if (mConnections.get(userId).isEmpty()) { 205 mConnections.remove(userId); 206 } 207 } 208 } 209 210 @GuardedBy("mLock") disconnectServiceOnUserLocked(int userId, @NonNull String actionForLog)211 private void disconnectServiceOnUserLocked(int userId, @NonNull String actionForLog) { 212 if (!mConnections.contains(userId)) { 213 return; 214 } 215 for (String packageName : mConnections.get(userId).keySet()) { 216 DevicePolicyServiceConnection conn = mConnections.get(userId).get(packageName); 217 if (DEBUG) { 218 Slogf.d(TAG, 219 "Stopping service for package %s on u%d if already running for %s.", 220 packageName, 221 userId, 222 actionForLog); 223 } 224 conn.unbind(); 225 } 226 mConnections.remove(userId); 227 } 228 229 /** dump content */ dump(IndentingPrintWriter pw)230 public void dump(IndentingPrintWriter pw) { 231 synchronized (mLock) { 232 if (mConnections.size() == 0) { 233 return; 234 } 235 pw.println("Admin Services:"); 236 pw.increaseIndent(); 237 for (int i = 0; i < mConnections.size(); i++) { 238 final int userId = mConnections.keyAt(i); 239 pw.print("User: "); 240 pw.println(userId); 241 for (String packageName : mConnections.get(userId).keySet()) { 242 pw.increaseIndent(); 243 pw.print("Package: "); 244 pw.println(packageName); 245 246 final DevicePolicyServiceConnection con = mConnections.valueAt(i) 247 .get(packageName); 248 pw.increaseIndent(); 249 con.dump("", pw); 250 pw.decreaseIndent(); 251 pw.decreaseIndent(); 252 } 253 } 254 pw.decreaseIndent(); 255 } 256 } 257 } 258