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