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.Slog;
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 
37 import java.io.PrintWriter;
38 
39 /**
40  * Manages connections to persistent services in owner packages.
41  */
42 public class DeviceAdminServiceController {
43     static final String TAG = DevicePolicyManagerService.LOG_TAG;
44 
45     static final boolean DEBUG = false; // DO NOT MERGE WITH TRUE.
46 
47     final Object mLock = new Object();
48     final Context mContext;
49 
50     private final DevicePolicyManagerService mService;
51     private final DevicePolicyManagerService.Injector mInjector;
52     private final DevicePolicyConstants mConstants;
53 
54     private final Handler mHandler; // needed?
55 
debug(String format, Object... args)56     static void debug(String format, Object... args) {
57         if (!DEBUG) {
58             return;
59         }
60         Slog.d(TAG, String.format(format, args));
61     }
62 
63     private class DevicePolicyServiceConnection
64             extends PersistentConnection<IDeviceAdminService> {
DevicePolicyServiceConnection(int userId, @NonNull ComponentName componentName)65         public DevicePolicyServiceConnection(int userId, @NonNull ComponentName componentName) {
66             super(TAG, mContext, mHandler, userId, componentName,
67                     mConstants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC,
68                     mConstants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE,
69                     mConstants.DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC,
70                     mConstants.DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC);
71         }
72 
73         @Override
getBindFlags()74         protected int getBindFlags() {
75             return Context.BIND_FOREGROUND_SERVICE;
76         }
77 
78         @Override
asInterface(IBinder binder)79         protected IDeviceAdminService asInterface(IBinder binder) {
80             return IDeviceAdminService.Stub.asInterface(binder);
81         }
82     }
83 
84     /**
85      * User-ID -> {@link PersistentConnection}.
86      */
87     @GuardedBy("mLock")
88     private final SparseArray<DevicePolicyServiceConnection> mConnections = new SparseArray<>();
89 
DeviceAdminServiceController(DevicePolicyManagerService service, DevicePolicyConstants constants)90     public DeviceAdminServiceController(DevicePolicyManagerService service,
91             DevicePolicyConstants constants) {
92         mService = service;
93         mInjector = service.mInjector;
94         mContext = mInjector.mContext;
95         mHandler = new Handler(BackgroundThread.get().getLooper());
96         mConstants = constants;
97     }
98 
99     /**
100      * Find a service that handles {@link DevicePolicyManager#ACTION_DEVICE_ADMIN_SERVICE}
101      * in a given package.
102      */
103     @Nullable
findService(@onNull String packageName, int userId)104     private ServiceInfo findService(@NonNull String packageName, int userId) {
105         return AppBindingUtils.findService(
106                 packageName,
107                 userId,
108                 DevicePolicyManager.ACTION_DEVICE_ADMIN_SERVICE,
109                 permission.BIND_DEVICE_ADMIN,
110                 DeviceAdminService.class,
111                 mInjector.getIPackageManager(),
112                 new StringBuilder() /* ignore error message */);
113     }
114 
115     /**
116      * Find a service that handles {@link DevicePolicyManager#ACTION_DEVICE_ADMIN_SERVICE}
117      * in an owner package and connect to it.
118      */
startServiceForOwner(@onNull String packageName, int userId, @NonNull String actionForLog)119     public void startServiceForOwner(@NonNull String packageName, int userId,
120             @NonNull String actionForLog) {
121         final long token = mInjector.binderClearCallingIdentity();
122         try {
123             synchronized (mLock) {
124                 final ServiceInfo service = findService(packageName, userId);
125                 if (service == null) {
126                     debug("Owner package %s on u%d has no service.",
127                             packageName, userId);
128                     disconnectServiceOnUserLocked(userId, actionForLog);
129                     return;
130                 }
131                 // See if it's already running.
132                 final PersistentConnection<IDeviceAdminService> existing =
133                         mConnections.get(userId);
134                 if (existing != null) {
135                     // Note even when we're already connected to the same service, the binding
136                     // would have died at this point due to a package update.  So we disconnect
137                     // anyway and re-connect.
138                     debug("Disconnecting from existing service connection.",
139                             packageName, userId);
140                     disconnectServiceOnUserLocked(userId, actionForLog);
141                 }
142 
143                 debug("Owner package %s on u%d has service %s for %s",
144                         packageName, userId,
145                         service.getComponentName().flattenToShortString(), actionForLog);
146 
147                 final DevicePolicyServiceConnection conn =
148                         new DevicePolicyServiceConnection(
149                                 userId, service.getComponentName());
150                 mConnections.put(userId, conn);
151                 conn.bind();
152             }
153         } finally {
154             mInjector.binderRestoreCallingIdentity(token);
155         }
156     }
157 
158     /**
159      * Stop an owner service on a given user.
160      */
stopServiceForOwner(int userId, @NonNull String actionForLog)161     public void stopServiceForOwner(int userId, @NonNull String actionForLog) {
162         final long token = mInjector.binderClearCallingIdentity();
163         try {
164             synchronized (mLock) {
165                 disconnectServiceOnUserLocked(userId, actionForLog);
166             }
167         } finally {
168             mInjector.binderRestoreCallingIdentity(token);
169         }
170     }
171 
172     @GuardedBy("mLock")
disconnectServiceOnUserLocked(int userId, @NonNull String actionForLog)173     private void disconnectServiceOnUserLocked(int userId, @NonNull String actionForLog) {
174         final DevicePolicyServiceConnection conn = mConnections.get(userId);
175         if (conn != null) {
176             debug("Stopping service for u%d if already running for %s.",
177                     userId, actionForLog);
178             conn.unbind();
179             mConnections.remove(userId);
180         }
181     }
182 
dump(String prefix, PrintWriter pw)183     public void dump(String prefix, PrintWriter pw) {
184         synchronized (mLock) {
185             if (mConnections.size() == 0) {
186                 return;
187             }
188             pw.println();
189             pw.print(prefix); pw.println("Owner Services:");
190             for (int i = 0; i < mConnections.size(); i++) {
191                 final int userId = mConnections.keyAt(i);
192                 pw.print(prefix); pw.print("  "); pw.print("User: "); pw.println(userId);
193 
194                 final DevicePolicyServiceConnection con = mConnections.valueAt(i);
195                 con.dump(prefix + "    ", pw);
196             }
197             pw.println();
198         }
199     }
200 }
201