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