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.DevicePolicyManager;
22 import android.app.admin.IDeviceAdminService;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.pm.ParceledListSlice;
27 import android.content.pm.ResolveInfo;
28 import android.content.pm.ServiceInfo;
29 import android.os.Handler;
30 import android.os.IBinder;
31 import android.os.RemoteException;
32 import android.util.Log;
33 import android.util.Slog;
34 import android.util.SparseArray;
35 
36 import com.android.internal.annotations.GuardedBy;
37 import com.android.internal.os.BackgroundThread;
38 import com.android.server.am.PersistentConnection;
39 
40 import java.io.PrintWriter;
41 import java.util.List;
42 
43 /**
44  * Manages connections to persistent services in owner packages.
45  */
46 public class DeviceAdminServiceController {
47     static final String TAG = DevicePolicyManagerService.LOG_TAG;
48 
49     static final boolean DEBUG = false; // DO NOT MERGE WITH TRUE.
50 
51     final Object mLock = new Object();
52     final Context mContext;
53 
54     private final DevicePolicyManagerService mService;
55     private final DevicePolicyManagerService.Injector mInjector;
56     private final DevicePolicyConstants mConstants;
57 
58     private final Handler mHandler; // needed?
59 
debug(String format, Object... args)60     static void debug(String format, Object... args) {
61         if (!DEBUG) {
62             return;
63         }
64         Slog.d(TAG, String.format(format, args));
65     }
66 
67     private class DevicePolicyServiceConnection
68             extends PersistentConnection<IDeviceAdminService> {
DevicePolicyServiceConnection(int userId, @NonNull ComponentName componentName)69         public DevicePolicyServiceConnection(int userId, @NonNull ComponentName componentName) {
70             super(TAG, mContext, mHandler, userId, componentName,
71                     mConstants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC,
72                     mConstants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE,
73                     mConstants.DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC);
74         }
75 
76         @Override
asInterface(IBinder binder)77         protected IDeviceAdminService asInterface(IBinder binder) {
78             return IDeviceAdminService.Stub.asInterface(binder);
79         }
80     }
81 
82     /**
83      * User-ID -> {@link PersistentConnection}.
84      */
85     @GuardedBy("mLock")
86     private final SparseArray<DevicePolicyServiceConnection> mConnections = new SparseArray<>();
87 
DeviceAdminServiceController(DevicePolicyManagerService service, DevicePolicyConstants constants)88     public DeviceAdminServiceController(DevicePolicyManagerService service,
89             DevicePolicyConstants constants) {
90         mService = service;
91         mInjector = service.mInjector;
92         mContext = mInjector.mContext;
93         mHandler = new Handler(BackgroundThread.get().getLooper());
94         mConstants = constants;
95     }
96 
97     /**
98      * Find a service that handles {@link DevicePolicyManager#ACTION_DEVICE_ADMIN_SERVICE}
99      * in a given package.
100      */
101     @Nullable
findService(@onNull String packageName, int userId)102     private ServiceInfo findService(@NonNull String packageName, int userId) {
103         final Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_ADMIN_SERVICE);
104         intent.setPackage(packageName);
105 
106         try {
107             final ParceledListSlice<ResolveInfo> pls = mInjector.getIPackageManager()
108                     .queryIntentServices(intent, null, /* flags=*/ 0, userId);
109             if (pls == null) {
110                 return null;
111             }
112             final List<ResolveInfo> list = pls.getList();
113             if (list.size() == 0) {
114                 return null;
115             }
116             // Note if multiple services are found, that's an error, even if only one of them
117             // is exported.
118             if (list.size() > 1) {
119                 Log.e(TAG, "More than one DeviceAdminService's found in package "
120                         + packageName
121                         + ".  They'll all be ignored.");
122                 return null;
123             }
124             final ServiceInfo si = list.get(0).serviceInfo;
125 
126             if (!permission.BIND_DEVICE_ADMIN.equals(si.permission)) {
127                 Log.e(TAG, "DeviceAdminService "
128                         + si.getComponentName().flattenToShortString()
129                         + " must be protected with " + permission.BIND_DEVICE_ADMIN
130                         + ".");
131                 return null;
132             }
133             return si;
134         } catch (RemoteException e) {
135         }
136         return null;
137     }
138 
139     /**
140      * Find a service that handles {@link DevicePolicyManager#ACTION_DEVICE_ADMIN_SERVICE}
141      * in an owner package and connect to it.
142      */
startServiceForOwner(@onNull String packageName, int userId, @NonNull String actionForLog)143     public void startServiceForOwner(@NonNull String packageName, int userId,
144             @NonNull String actionForLog) {
145         final long token = mInjector.binderClearCallingIdentity();
146         try {
147             synchronized (mLock) {
148                 final ServiceInfo service = findService(packageName, userId);
149                 if (service == null) {
150                     debug("Owner package %s on u%d has no service.",
151                             packageName, userId);
152                     disconnectServiceOnUserLocked(userId, actionForLog);
153                     return;
154                 }
155                 // See if it's already running.
156                 final PersistentConnection<IDeviceAdminService> existing =
157                         mConnections.get(userId);
158                 if (existing != null) {
159                     // Note even when we're already connected to the same service, the binding
160                     // would have died at this point due to a package update.  So we disconnect
161                     // anyway and re-connect.
162                     debug("Disconnecting from existing service connection.",
163                             packageName, userId);
164                     disconnectServiceOnUserLocked(userId, actionForLog);
165                 }
166 
167                 debug("Owner package %s on u%d has service %s for %s",
168                         packageName, userId,
169                         service.getComponentName().flattenToShortString(), actionForLog);
170 
171                 final DevicePolicyServiceConnection conn =
172                         new DevicePolicyServiceConnection(
173                                 userId, service.getComponentName());
174                 mConnections.put(userId, conn);
175                 conn.bind();
176             }
177         } finally {
178             mInjector.binderRestoreCallingIdentity(token);
179         }
180     }
181 
182     /**
183      * Stop an owner service on a given user.
184      */
stopServiceForOwner(int userId, @NonNull String actionForLog)185     public void stopServiceForOwner(int userId, @NonNull String actionForLog) {
186         final long token = mInjector.binderClearCallingIdentity();
187         try {
188             synchronized (mLock) {
189                 disconnectServiceOnUserLocked(userId, actionForLog);
190             }
191         } finally {
192             mInjector.binderRestoreCallingIdentity(token);
193         }
194     }
195 
196     @GuardedBy("mLock")
disconnectServiceOnUserLocked(int userId, @NonNull String actionForLog)197     private void disconnectServiceOnUserLocked(int userId, @NonNull String actionForLog) {
198         final DevicePolicyServiceConnection conn = mConnections.get(userId);
199         if (conn != null) {
200             debug("Stopping service for u%d if already running for %s.",
201                     userId, actionForLog);
202             conn.unbind();
203             mConnections.remove(userId);
204         }
205     }
206 
dump(String prefix, PrintWriter pw)207     public void dump(String prefix, PrintWriter pw) {
208         synchronized (mLock) {
209             if (mConnections.size() == 0) {
210                 return;
211             }
212             pw.println();
213             pw.print(prefix); pw.println("Owner Services:");
214             for (int i = 0; i < mConnections.size(); i++) {
215                 final int userId = mConnections.keyAt(i);
216                 pw.print(prefix); pw.print("  "); pw.print("User: "); pw.println(userId);
217 
218                 final DevicePolicyServiceConnection con = mConnections.valueAt(i);
219                 con.dump(prefix + "    ", pw);
220             }
221             pw.println();
222         }
223     }
224 }
225