blob: 09e66475c1be0451d3ddb70f03200566d8e43d3e [file] [log] [blame]
John Spurlock7340fc82014-04-24 18:50:12 -04001/**
2 * Copyright (c) 2014, 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
17package com.android.server.notification;
18
19import android.app.ActivityManager;
20import android.app.PendingIntent;
Christopher Tate6597e342015-02-17 12:15:25 -080021import android.content.BroadcastReceiver;
John Spurlock7340fc82014-04-24 18:50:12 -040022import android.content.ComponentName;
23import android.content.ContentResolver;
24import android.content.Context;
25import android.content.Intent;
Christopher Tate6597e342015-02-17 12:15:25 -080026import android.content.IntentFilter;
John Spurlock7340fc82014-04-24 18:50:12 -040027import android.content.ServiceConnection;
28import android.content.pm.ApplicationInfo;
29import android.content.pm.PackageManager;
30import android.content.pm.PackageManager.NameNotFoundException;
31import android.content.pm.ResolveInfo;
32import android.content.pm.ServiceInfo;
33import android.content.pm.UserInfo;
34import android.database.ContentObserver;
35import android.net.Uri;
36import android.os.Build;
37import android.os.Handler;
38import android.os.IBinder;
39import android.os.IInterface;
40import android.os.RemoteException;
41import android.os.UserHandle;
42import android.os.UserManager;
43import android.provider.Settings;
44import android.text.TextUtils;
Ruben Brunkdd18a0b2015-12-04 16:16:31 -080045import android.util.ArrayMap;
John Spurlock7340fc82014-04-24 18:50:12 -040046import android.util.ArraySet;
John Spurlock4db0d982014-08-13 09:19:03 -040047import android.util.Log;
John Spurlock7340fc82014-04-24 18:50:12 -040048import android.util.Slog;
49import android.util.SparseArray;
50
John Spurlock25e2d242014-06-27 13:58:23 -040051import com.android.server.notification.NotificationManagerService.DumpFilter;
52
John Spurlock7340fc82014-04-24 18:50:12 -040053import java.io.PrintWriter;
54import java.util.ArrayList;
John Spurlocke77bb362014-04-26 10:24:59 -040055import java.util.Arrays;
John Spurlock7340fc82014-04-24 18:50:12 -040056import java.util.List;
Ruben Brunkdd18a0b2015-12-04 16:16:31 -080057import java.util.Map.Entry;
Christopher Tate6597e342015-02-17 12:15:25 -080058import java.util.Objects;
John Spurlock7340fc82014-04-24 18:50:12 -040059import java.util.Set;
60
61/**
62 * Manages the lifecycle of application-provided services bound by system server.
63 *
64 * Services managed by this helper must have:
65 * - An associated system settings value with a list of enabled component names.
66 * - A well-known action for services to use in their intent-filter.
67 * - A system permission for services to require in order to ensure system has exclusive binding.
68 * - A settings page for user configuration of enabled services, and associated intent action.
69 * - A remote interface definition (aidl) provided by the service used for communication.
70 */
71abstract public class ManagedServices {
72 protected final String TAG = getClass().getSimpleName();
John Spurlock4db0d982014-08-13 09:19:03 -040073 protected final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
John Spurlock7340fc82014-04-24 18:50:12 -040074
Julia Reynoldsc279b992015-10-30 08:23:51 -040075 protected static final String ENABLED_SERVICES_SEPARATOR = ":";
John Spurlock7340fc82014-04-24 18:50:12 -040076
John Spurlockaf8d6c42014-05-07 17:49:08 -040077 protected final Context mContext;
John Spurlocke77bb362014-04-26 10:24:59 -040078 protected final Object mMutex;
John Spurlock7340fc82014-04-24 18:50:12 -040079 private final UserProfiles mUserProfiles;
80 private final SettingsObserver mSettingsObserver;
81 private final Config mConfig;
Christopher Tate6597e342015-02-17 12:15:25 -080082 private ArraySet<String> mRestored;
John Spurlock7340fc82014-04-24 18:50:12 -040083
84 // contains connections to all connected services, including app services
85 // and system services
86 protected final ArrayList<ManagedServiceInfo> mServices = new ArrayList<ManagedServiceInfo>();
87 // things that will be put into mServices as soon as they're ready
88 private final ArrayList<String> mServicesBinding = new ArrayList<String>();
89 // lists the component names of all enabled (and therefore connected)
90 // app services for current profiles.
91 private ArraySet<ComponentName> mEnabledServicesForCurrentProfiles
92 = new ArraySet<ComponentName>();
93 // Just the packages from mEnabledServicesForCurrentProfiles
94 private ArraySet<String> mEnabledServicesPackageNames = new ArraySet<String>();
Denis Kuznetsov76c02682015-09-17 19:57:00 +020095 // List of packages in restored setting across all mUserProfiles, for quick
96 // filtering upon package updates.
97 private ArraySet<String> mRestoredPackages = new ArraySet<>();
Ruben Brunkdd18a0b2015-12-04 16:16:31 -080098 // State of current service categories
99 private ArrayMap<String, Boolean> mCategoryEnabled = new ArrayMap<>();
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200100
John Spurlock7340fc82014-04-24 18:50:12 -0400101
Christoph Studerb53dfd42014-09-12 14:45:59 +0200102 // Kept to de-dupe user change events (experienced after boot, when we receive a settings and a
103 // user change).
104 private int[] mLastSeenProfileIds;
105
Christopher Tate6597e342015-02-17 12:15:25 -0800106 private final BroadcastReceiver mRestoreReceiver;
107
John Spurlock7340fc82014-04-24 18:50:12 -0400108 public ManagedServices(Context context, Handler handler, Object mutex,
109 UserProfiles userProfiles) {
110 mContext = context;
111 mMutex = mutex;
112 mUserProfiles = userProfiles;
113 mConfig = getConfig();
114 mSettingsObserver = new SettingsObserver(handler);
Christopher Tate6597e342015-02-17 12:15:25 -0800115
116 mRestoreReceiver = new SettingRestoredReceiver();
117 IntentFilter filter = new IntentFilter(Intent.ACTION_SETTING_RESTORED);
118 context.registerReceiver(mRestoreReceiver, filter);
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200119 rebuildRestoredPackages();
Christopher Tate6597e342015-02-17 12:15:25 -0800120 }
121
122 class SettingRestoredReceiver extends BroadcastReceiver {
123 @Override
124 public void onReceive(Context context, Intent intent) {
125 if (Intent.ACTION_SETTING_RESTORED.equals(intent.getAction())) {
126 String element = intent.getStringExtra(Intent.EXTRA_SETTING_NAME);
127 if (Objects.equals(element, mConfig.secureSettingName)) {
128 String prevValue = intent.getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE);
129 String newValue = intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE);
130 settingRestored(element, prevValue, newValue, getSendingUserId());
131 }
132 }
133 }
John Spurlock7340fc82014-04-24 18:50:12 -0400134 }
135
136 abstract protected Config getConfig();
137
138 private String getCaption() {
139 return mConfig.caption;
140 }
141
142 abstract protected IInterface asInterface(IBinder binder);
143
Chris Wren51017d02015-12-15 15:34:46 -0500144 abstract protected boolean checkType(IInterface service);
145
John Spurlock3b98b3f2014-05-01 09:08:48 -0400146 abstract protected void onServiceAdded(ManagedServiceInfo info);
John Spurlock7340fc82014-04-24 18:50:12 -0400147
John Spurlocke77bb362014-04-26 10:24:59 -0400148 protected void onServiceRemovedLocked(ManagedServiceInfo removed) { }
149
John Spurlock7340fc82014-04-24 18:50:12 -0400150 private ManagedServiceInfo newServiceInfo(IInterface service,
151 ComponentName component, int userid, boolean isSystem, ServiceConnection connection,
152 int targetSdkVersion) {
153 return new ManagedServiceInfo(service, component, userid, isSystem, connection,
154 targetSdkVersion);
155 }
156
157 public void onBootPhaseAppsCanStart() {
158 mSettingsObserver.observe();
159 }
160
John Spurlock25e2d242014-06-27 13:58:23 -0400161 public void dump(PrintWriter pw, DumpFilter filter) {
John Spurlocke77bb362014-04-26 10:24:59 -0400162 pw.println(" All " + getCaption() + "s (" + mEnabledServicesForCurrentProfiles.size()
John Spurlock7340fc82014-04-24 18:50:12 -0400163 + ") enabled for current profiles:");
164 for (ComponentName cmpt : mEnabledServicesForCurrentProfiles) {
John Spurlock25e2d242014-06-27 13:58:23 -0400165 if (filter != null && !filter.matches(cmpt)) continue;
John Spurlocke77bb362014-04-26 10:24:59 -0400166 pw.println(" " + cmpt);
John Spurlock7340fc82014-04-24 18:50:12 -0400167 }
168
John Spurlocke77bb362014-04-26 10:24:59 -0400169 pw.println(" Live " + getCaption() + "s (" + mServices.size() + "):");
John Spurlock7340fc82014-04-24 18:50:12 -0400170 for (ManagedServiceInfo info : mServices) {
John Spurlock25e2d242014-06-27 13:58:23 -0400171 if (filter != null && !filter.matches(info.component)) continue;
John Spurlocke77bb362014-04-26 10:24:59 -0400172 pw.println(" " + info.component
John Spurlock7340fc82014-04-24 18:50:12 -0400173 + " (user " + info.userid + "): " + info.service
Chris Wren51017d02015-12-15 15:34:46 -0500174 + (info.isSystem?" SYSTEM":"")
175 + (info.isGuest(this)?" GUEST":""));
John Spurlock7340fc82014-04-24 18:50:12 -0400176 }
177 }
178
Christopher Tate6597e342015-02-17 12:15:25 -0800179 // By convention, restored settings are replicated to another settings
180 // entry, named similarly but with a disambiguation suffix.
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200181 public static String restoredSettingName(Config config) {
Christopher Tate6597e342015-02-17 12:15:25 -0800182 return config.secureSettingName + ":restored";
183 }
184
185 // The OS has done a restore of this service's saved state. We clone it to the
186 // 'restored' reserve, and then once we return and the actual write to settings is
187 // performed, our observer will do the work of maintaining the restored vs live
188 // settings data.
189 public void settingRestored(String element, String oldValue, String newValue, int userid) {
190 if (DEBUG) Slog.d(TAG, "Restored managed service setting: " + element
191 + " ovalue=" + oldValue + " nvalue=" + newValue);
192 if (mConfig.secureSettingName.equals(element)) {
193 if (element != null) {
194 mRestored = null;
195 Settings.Secure.putStringForUser(mContext.getContentResolver(),
196 restoredSettingName(mConfig),
197 newValue,
198 userid);
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200199 updateSettingsAccordingToInstalledServices(userid);
200 rebuildRestoredPackages();
Christopher Tate6597e342015-02-17 12:15:25 -0800201 }
202 }
203 }
204
John Spurlock80774932015-05-07 17:38:50 -0400205 public boolean isComponentEnabledForPackage(String pkg) {
206 return mEnabledServicesPackageNames.contains(pkg);
207 }
208
John Spurlock7340fc82014-04-24 18:50:12 -0400209 public void onPackagesChanged(boolean queryReplace, String[] pkgList) {
John Spurlocke77bb362014-04-26 10:24:59 -0400210 if (DEBUG) Slog.d(TAG, "onPackagesChanged queryReplace=" + queryReplace
211 + " pkgList=" + (pkgList == null ? null : Arrays.asList(pkgList))
212 + " mEnabledServicesPackageNames=" + mEnabledServicesPackageNames);
John Spurlock7340fc82014-04-24 18:50:12 -0400213 boolean anyServicesInvolved = false;
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200214
John Spurlock7340fc82014-04-24 18:50:12 -0400215 if (pkgList != null && (pkgList.length > 0)) {
216 for (String pkgName : pkgList) {
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200217 if (mEnabledServicesPackageNames.contains(pkgName) ||
218 mRestoredPackages.contains(pkgName)) {
John Spurlock7340fc82014-04-24 18:50:12 -0400219 anyServicesInvolved = true;
220 }
221 }
222 }
223
224 if (anyServicesInvolved) {
225 // if we're not replacing a package, clean up orphaned bits
226 if (!queryReplace) {
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200227 updateSettingsAccordingToInstalledServices();
228 rebuildRestoredPackages();
John Spurlock7340fc82014-04-24 18:50:12 -0400229 }
230 // make sure we're still bound to any of our services who may have just upgraded
231 rebindServices();
232 }
233 }
234
John Spurlock1b8b22b2015-05-20 09:47:13 -0400235 public void onUserSwitched(int user) {
236 if (DEBUG) Slog.d(TAG, "onUserSwitched u=" + user);
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200237 rebuildRestoredPackages();
Christoph Studerb53dfd42014-09-12 14:45:59 +0200238 if (Arrays.equals(mLastSeenProfileIds, mUserProfiles.getCurrentProfileIds())) {
239 if (DEBUG) Slog.d(TAG, "Current profile IDs didn't change, skipping rebindServices().");
240 return;
241 }
242 rebindServices();
243 }
244
John Spurlock7340fc82014-04-24 18:50:12 -0400245 public ManagedServiceInfo checkServiceTokenLocked(IInterface service) {
246 checkNotNull(service);
247 final IBinder token = service.asBinder();
248 final int N = mServices.size();
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200249 for (int i = 0; i < N; i++) {
John Spurlock7340fc82014-04-24 18:50:12 -0400250 final ManagedServiceInfo info = mServices.get(i);
251 if (info.service.asBinder() == token) return info;
252 }
253 throw new SecurityException("Disallowed call from unknown " + getCaption() + ": "
254 + service);
255 }
256
257 public void unregisterService(IInterface service, int userid) {
258 checkNotNull(service);
259 // no need to check permissions; if your service binder is in the list,
260 // that's proof that you had permission to add it in the first place
261 unregisterServiceImpl(service, userid);
262 }
263
264 public void registerService(IInterface service, ComponentName component, int userid) {
265 checkNotNull(service);
Christoph Studer3e144d32014-05-22 16:48:40 +0200266 ManagedServiceInfo info = registerServiceImpl(service, component, userid);
267 if (info != null) {
268 onServiceAdded(info);
269 }
John Spurlock7340fc82014-04-24 18:50:12 -0400270 }
271
Chris Wren51017d02015-12-15 15:34:46 -0500272 /**
273 * Add a service to our callbacks. The lifecycle of this service is managed externally,
274 * but unlike a system service, it should not be considered privledged.
275 * */
276 public void registerGuestService(ManagedServiceInfo guest) {
277 checkNotNull(guest.service);
278 checkType(guest.service);
279 if (registerServiceImpl(guest) != null) {
280 onServiceAdded(guest);
281 }
282 }
283
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800284 public void setCategoryState(String category, boolean enabled) {
285 synchronized (mMutex) {
286 final Boolean previous = mCategoryEnabled.put(category, enabled);
287 if (!(previous == null || previous != enabled)) {
288 return;
289 }
290
291 // State changed
292 if (DEBUG) {
293 Slog.d(TAG, ((enabled) ? "Enabling " : "Disabling ") + "category " + category);
294 }
295
296 final int[] userIds = mUserProfiles.getCurrentProfileIds();
297 for (int userId : userIds) {
298 final Set<ComponentName> componentNames = queryPackageForServices(null,
299 userId, category);
300
301 // Disallow services not enabled in Settings
302 final ArraySet<ComponentName> userComponents =
303 loadComponentNamesFromSetting(mConfig.secureSettingName, userId);
304 if (userComponents == null) {
305 componentNames.clear();
306 } else {
307 componentNames.retainAll(userComponents);
308 }
309
310 if (DEBUG) {
311 Slog.d(TAG, "Components for category " + category + ": " + componentNames);
312 }
313 for (ComponentName c : componentNames) {
314 if (enabled) {
315 registerServiceLocked(c, userId);
316 } else {
317 unregisterServiceLocked(c, userId);
318 }
319 }
320 }
321
322 }
323 }
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200324
325 private void rebuildRestoredPackages() {
326 mRestoredPackages.clear();
327 String settingName = restoredSettingName(mConfig);
John Spurlock7340fc82014-04-24 18:50:12 -0400328 int[] userIds = mUserProfiles.getCurrentProfileIds();
329 final int N = userIds.length;
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200330 for (int i = 0; i < N; ++i) {
331 ArraySet<ComponentName> names = loadComponentNamesFromSetting(settingName, userIds[i]);
332 if (names == null)
333 continue;
334 for (ComponentName name: names) {
335 mRestoredPackages.add(name.getPackageName());
336 }
John Spurlock7340fc82014-04-24 18:50:12 -0400337 }
338 }
339
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200340
Julia Reynoldsc279b992015-10-30 08:23:51 -0400341 protected ArraySet<ComponentName> loadComponentNamesFromSetting(String settingName,
342 int userId) {
Christopher Tate6597e342015-02-17 12:15:25 -0800343 final ContentResolver cr = mContext.getContentResolver();
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200344 String settingValue = Settings.Secure.getStringForUser(
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800345 cr,
346 settingName,
347 userId);
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200348 if (TextUtils.isEmpty(settingValue))
349 return null;
350 String[] restored = settingValue.split(ENABLED_SERVICES_SEPARATOR);
351 ArraySet<ComponentName> result = new ArraySet<>(restored.length);
352 for (int i = 0; i < restored.length; i++) {
353 ComponentName value = ComponentName.unflattenFromString(restored[i]);
354 if (null != value) {
355 result.add(value);
Christopher Tate6597e342015-02-17 12:15:25 -0800356 }
357 }
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200358 return result;
359 }
John Spurlock7340fc82014-04-24 18:50:12 -0400360
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200361 private void storeComponentsToSetting(Set<ComponentName> components,
362 String settingName,
363 int userId) {
364 String[] componentNames = null;
365 if (null != components) {
366 componentNames = new String[components.size()];
367 int index = 0;
368 for (ComponentName c: components) {
369 componentNames[index++] = c.flattenToString();
370 }
371 }
372 final String value = (componentNames == null) ? "" :
373 TextUtils.join(ENABLED_SERVICES_SEPARATOR, componentNames);
374 final ContentResolver cr = mContext.getContentResolver();
375 Settings.Secure.putStringForUser(
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800376 cr,
377 settingName,
378 value,
379 userId);
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200380 }
381
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200382 /**
383 * Remove access for any services that no longer exist.
384 */
385 private void updateSettingsAccordingToInstalledServices() {
386 int[] userIds = mUserProfiles.getCurrentProfileIds();
387 final int N = userIds.length;
388 for (int i = 0; i < N; ++i) {
389 updateSettingsAccordingToInstalledServices(userIds[i]);
390 }
391 rebuildRestoredPackages();
392 }
393
Julia Reynoldsc279b992015-10-30 08:23:51 -0400394 protected Set<ComponentName> queryPackageForServices(String packageName, int userId) {
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800395 return queryPackageForServices(packageName, userId, null);
396 }
397
398 protected Set<ComponentName> queryPackageForServices(String packageName, int userId,
399 String category) {
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200400 Set<ComponentName> installed = new ArraySet<>();
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200401 final PackageManager pm = mContext.getPackageManager();
Julia Reynoldsc279b992015-10-30 08:23:51 -0400402 Intent queryIntent = new Intent(mConfig.serviceInterface);
403 if (!TextUtils.isEmpty(packageName)) {
404 queryIntent.setPackage(packageName);
405 }
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800406 if (category != null) {
407 queryIntent.addCategory(category);
408 }
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200409 List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
Julia Reynoldsc279b992015-10-30 08:23:51 -0400410 queryIntent,
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200411 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
412 userId);
413 if (DEBUG)
414 Slog.v(TAG, mConfig.serviceInterface + " services: " + installedServices);
Julia Reynolds50f05142015-10-30 17:03:59 -0400415 if (installedServices != null) {
416 for (int i = 0, count = installedServices.size(); i < count; i++) {
417 ResolveInfo resolveInfo = installedServices.get(i);
418 ServiceInfo info = resolveInfo.serviceInfo;
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200419
Julia Reynolds50f05142015-10-30 17:03:59 -0400420 ComponentName component = new ComponentName(info.packageName, info.name);
421 if (!mConfig.bindPermission.equals(info.permission)) {
422 Slog.w(TAG, "Skipping " + getCaption() + " service "
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800423 + info.packageName + "/" + info.name
424 + ": it does not require the permission "
425 + mConfig.bindPermission);
Julia Reynolds50f05142015-10-30 17:03:59 -0400426 continue;
427 }
428 installed.add(component);
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200429 }
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200430 }
Julia Reynoldsc279b992015-10-30 08:23:51 -0400431 return installed;
432 }
433
434 private void updateSettingsAccordingToInstalledServices(int userId) {
435 boolean restoredChanged = false;
436 boolean currentChanged = false;
437 Set<ComponentName> restored =
438 loadComponentNamesFromSetting(restoredSettingName(mConfig), userId);
439 Set<ComponentName> current =
440 loadComponentNamesFromSetting(mConfig.secureSettingName, userId);
441 // Load all services for all packages.
442 Set<ComponentName> installed = queryPackageForServices(null, userId);
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200443
444 ArraySet<ComponentName> retained = new ArraySet<>();
445
446 for (ComponentName component : installed) {
447 if (null != restored) {
448 boolean wasRestored = restored.remove(component);
449 if (wasRestored) {
450 // Freshly installed package has service that was mentioned in restored setting.
451 if (DEBUG)
452 Slog.v(TAG, "Restoring " + component + " for user " + userId);
453 restoredChanged = true;
454 currentChanged = true;
455 retained.add(component);
John Spurlock7340fc82014-04-24 18:50:12 -0400456 continue;
457 }
John Spurlock7340fc82014-04-24 18:50:12 -0400458 }
459
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200460 if (null != current) {
461 if (current.contains(component))
462 retained.add(component);
John Spurlock7340fc82014-04-24 18:50:12 -0400463 }
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200464 }
465
466 currentChanged |= ((current == null ? 0 : current.size()) != retained.size());
467
468 if (currentChanged) {
469 if (DEBUG) Slog.v(TAG, "List of " + getCaption() + " services was updated " + current);
470 storeComponentsToSetting(retained, mConfig.secureSettingName, userId);
471 }
472
473 if (restoredChanged) {
474 if (DEBUG) Slog.v(TAG,
475 "List of " + getCaption() + " restored services was updated " + restored);
476 storeComponentsToSetting(restored, restoredSettingName(mConfig), userId);
John Spurlock7340fc82014-04-24 18:50:12 -0400477 }
478 }
479
480 /**
481 * Called whenever packages change, the user switches, or the secure setting
482 * is altered. (For example in response to USER_SWITCHED in our broadcast receiver)
483 */
484 private void rebindServices() {
485 if (DEBUG) Slog.d(TAG, "rebindServices");
486 final int[] userIds = mUserProfiles.getCurrentProfileIds();
487 final int nUserIds = userIds.length;
488
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200489 final SparseArray<ArraySet<ComponentName>> componentsByUser = new SparseArray<>();
John Spurlock7340fc82014-04-24 18:50:12 -0400490
491 for (int i = 0; i < nUserIds; ++i) {
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200492 componentsByUser.put(userIds[i],
493 loadComponentNamesFromSetting(mConfig.secureSettingName, userIds[i]));
John Spurlock7340fc82014-04-24 18:50:12 -0400494 }
495
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200496 final ArrayList<ManagedServiceInfo> toRemove = new ArrayList<>();
497 final SparseArray<ArrayList<ComponentName>> toAdd = new SparseArray<>();
John Spurlock7340fc82014-04-24 18:50:12 -0400498
499 synchronized (mMutex) {
Christoph Studer5d423842014-05-23 13:15:54 +0200500 // Unbind automatically bound services, retain system services.
501 for (ManagedServiceInfo service : mServices) {
Chris Wren51017d02015-12-15 15:34:46 -0500502 if (!service.isSystem && !service.isGuest(this)) {
Christoph Studer5d423842014-05-23 13:15:54 +0200503 toRemove.add(service);
504 }
505 }
John Spurlock7340fc82014-04-24 18:50:12 -0400506
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200507 final ArraySet<ComponentName> newEnabled = new ArraySet<>();
508 final ArraySet<String> newPackages = new ArraySet<>();
John Spurlock7340fc82014-04-24 18:50:12 -0400509
510 for (int i = 0; i < nUserIds; ++i) {
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200511 // decode the list of components
512 final ArraySet<ComponentName> userComponents = componentsByUser.get(userIds[i]);
513 if (null == userComponents) {
514 toAdd.put(userIds[i], new ArrayList<ComponentName>());
515 continue;
516 }
517
518 final ArrayList<ComponentName> add = new ArrayList<>(userComponents);
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800519
520 // Remove components from disabled categories so that those services aren't run.
521 for (Entry<String, Boolean> e : mCategoryEnabled.entrySet()) {
522 if (!e.getValue()) {
523 Set<ComponentName> c = queryPackageForServices(null, userIds[i],
524 e.getKey());
525 add.removeAll(c);
526 }
527 }
528
John Spurlock7340fc82014-04-24 18:50:12 -0400529 toAdd.put(userIds[i], add);
530
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200531 newEnabled.addAll(userComponents);
John Spurlock7340fc82014-04-24 18:50:12 -0400532
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200533 for (int j = 0; j < userComponents.size(); j++) {
Chris Wren083094c2015-12-15 16:25:07 -0500534 final ComponentName component = userComponents.valueAt(j);
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200535 newPackages.add(component.getPackageName());
John Spurlock7340fc82014-04-24 18:50:12 -0400536 }
537 }
538 mEnabledServicesForCurrentProfiles = newEnabled;
539 mEnabledServicesPackageNames = newPackages;
540 }
541
542 for (ManagedServiceInfo info : toRemove) {
543 final ComponentName component = info.component;
544 final int oldUser = info.userid;
545 Slog.v(TAG, "disabling " + getCaption() + " for user "
546 + oldUser + ": " + component);
547 unregisterService(component, info.userid);
548 }
549
550 for (int i = 0; i < nUserIds; ++i) {
551 final ArrayList<ComponentName> add = toAdd.get(userIds[i]);
552 final int N = add.size();
553 for (int j = 0; j < N; j++) {
554 final ComponentName component = add.get(j);
555 Slog.v(TAG, "enabling " + getCaption() + " for user " + userIds[i] + ": "
556 + component);
557 registerService(component, userIds[i]);
558 }
559 }
Christoph Studerb53dfd42014-09-12 14:45:59 +0200560
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200561 mLastSeenProfileIds = userIds;
John Spurlock7340fc82014-04-24 18:50:12 -0400562 }
563
564 /**
565 * Version of registerService that takes the name of a service component to bind to.
566 */
567 private void registerService(final ComponentName name, final int userid) {
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800568 synchronized (mMutex) {
569 registerServiceLocked(name, userid);
570 }
571 }
572
573 private void registerServiceLocked(final ComponentName name, final int userid) {
John Spurlock7340fc82014-04-24 18:50:12 -0400574 if (DEBUG) Slog.v(TAG, "registerService: " + name + " u=" + userid);
575
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800576 final String servicesBindingTag = name.toString() + "/" + userid;
577 if (mServicesBinding.contains(servicesBindingTag)) {
578 // stop registering this thing already! we're working on it
579 return;
580 }
581 mServicesBinding.add(servicesBindingTag);
John Spurlock7340fc82014-04-24 18:50:12 -0400582
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800583 final int N = mServices.size();
584 for (int i = N - 1; i >= 0; i--) {
585 final ManagedServiceInfo info = mServices.get(i);
586 if (name.equals(info.component)
587 && info.userid == userid) {
588 // cut old connections
589 if (DEBUG) Slog.v(TAG, " disconnecting old " + getCaption() + ": "
590 + info.service);
591 removeServiceLocked(i);
592 if (info.connection != null) {
593 mContext.unbindService(info.connection);
John Spurlock7340fc82014-04-24 18:50:12 -0400594 }
595 }
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800596 }
John Spurlock7340fc82014-04-24 18:50:12 -0400597
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800598 Intent intent = new Intent(mConfig.serviceInterface);
599 intent.setComponent(name);
John Spurlock7340fc82014-04-24 18:50:12 -0400600
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800601 intent.putExtra(Intent.EXTRA_CLIENT_LABEL, mConfig.clientLabel);
John Spurlock7340fc82014-04-24 18:50:12 -0400602
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800603 final PendingIntent pendingIntent = PendingIntent.getActivity(
604 mContext, 0, new Intent(mConfig.settingsAction), 0);
605 intent.putExtra(Intent.EXTRA_CLIENT_INTENT, pendingIntent);
John Spurlock7340fc82014-04-24 18:50:12 -0400606
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800607 ApplicationInfo appInfo = null;
608 try {
609 appInfo = mContext.getPackageManager().getApplicationInfo(
610 name.getPackageName(), 0);
611 } catch (NameNotFoundException e) {
612 // Ignore if the package doesn't exist we won't be able to bind to the service.
613 }
614 final int targetSdkVersion =
615 appInfo != null ? appInfo.targetSdkVersion : Build.VERSION_CODES.BASE;
John Spurlock7340fc82014-04-24 18:50:12 -0400616
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800617 try {
618 if (DEBUG) Slog.v(TAG, "binding: " + intent);
619 ServiceConnection serviceConnection = new ServiceConnection() {
620 IInterface mService;
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200621
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800622 @Override
623 public void onServiceConnected(ComponentName name, IBinder binder) {
624 boolean added = false;
625 ManagedServiceInfo info = null;
626 synchronized (mMutex) {
627 mServicesBinding.remove(servicesBindingTag);
628 try {
629 mService = asInterface(binder);
630 info = newServiceInfo(mService, name,
631 userid, false /*isSystem*/, this, targetSdkVersion);
632 binder.linkToDeath(info, 0);
633 added = mServices.add(info);
634 } catch (RemoteException e) {
635 // already dead
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200636 }
637 }
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800638 if (added) {
639 onServiceAdded(info);
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200640 }
John Spurlock7340fc82014-04-24 18:50:12 -0400641 }
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800642
643 @Override
644 public void onServiceDisconnected(ComponentName name) {
645 Slog.v(TAG, getCaption() + " connection lost: " + name);
646 }
647 };
648 if (!mContext.bindServiceAsUser(intent,
649 serviceConnection,
650 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
651 new UserHandle(userid))) {
652 mServicesBinding.remove(servicesBindingTag);
653 Slog.w(TAG, "Unable to bind " + getCaption() + " service: " + intent);
John Spurlock7340fc82014-04-24 18:50:12 -0400654 return;
655 }
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800656 } catch (SecurityException ex) {
657 Slog.e(TAG, "Unable to bind " + getCaption() + " service: " + intent, ex);
658 return;
John Spurlock7340fc82014-04-24 18:50:12 -0400659 }
660 }
661
662 /**
663 * Remove a service for the given user by ComponentName
664 */
665 private void unregisterService(ComponentName name, int userid) {
666 synchronized (mMutex) {
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800667 unregisterServiceLocked(name, userid);
668 }
669 }
670
671 private void unregisterServiceLocked(ComponentName name, int userid) {
672 final int N = mServices.size();
673 for (int i = N - 1; i >= 0; i--) {
674 final ManagedServiceInfo info = mServices.get(i);
675 if (name.equals(info.component)
676 && info.userid == userid) {
677 removeServiceLocked(i);
678 if (info.connection != null) {
679 try {
680 mContext.unbindService(info.connection);
681 } catch (IllegalArgumentException ex) {
682 // something happened to the service: we think we have a connection
683 // but it's bogus.
684 Slog.e(TAG, getCaption() + " " + name + " could not be unbound: " + ex);
John Spurlock7340fc82014-04-24 18:50:12 -0400685 }
686 }
687 }
688 }
689 }
690
691 /**
692 * Removes a service from the list but does not unbind
693 *
694 * @return the removed service.
695 */
696 private ManagedServiceInfo removeServiceImpl(IInterface service, final int userid) {
John Spurlocke77bb362014-04-26 10:24:59 -0400697 if (DEBUG) Slog.d(TAG, "removeServiceImpl service=" + service + " u=" + userid);
John Spurlock7340fc82014-04-24 18:50:12 -0400698 ManagedServiceInfo serviceInfo = null;
699 synchronized (mMutex) {
700 final int N = mServices.size();
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200701 for (int i = N - 1; i >= 0; i--) {
John Spurlock7340fc82014-04-24 18:50:12 -0400702 final ManagedServiceInfo info = mServices.get(i);
703 if (info.service.asBinder() == service.asBinder()
704 && info.userid == userid) {
John Spurlocke77bb362014-04-26 10:24:59 -0400705 if (DEBUG) Slog.d(TAG, "Removing active service " + info.component);
706 serviceInfo = removeServiceLocked(i);
John Spurlock7340fc82014-04-24 18:50:12 -0400707 }
708 }
709 }
710 return serviceInfo;
711 }
712
John Spurlocke77bb362014-04-26 10:24:59 -0400713 private ManagedServiceInfo removeServiceLocked(int i) {
714 final ManagedServiceInfo info = mServices.remove(i);
715 onServiceRemovedLocked(info);
716 return info;
717 }
718
John Spurlock7340fc82014-04-24 18:50:12 -0400719 private void checkNotNull(IInterface service) {
720 if (service == null) {
721 throw new IllegalArgumentException(getCaption() + " must not be null");
722 }
723 }
724
Christoph Studer3e144d32014-05-22 16:48:40 +0200725 private ManagedServiceInfo registerServiceImpl(final IInterface service,
John Spurlock7340fc82014-04-24 18:50:12 -0400726 final ComponentName component, final int userid) {
Chris Wren51017d02015-12-15 15:34:46 -0500727 ManagedServiceInfo info = newServiceInfo(service, component, userid,
728 true /*isSystem*/, null /*connection*/, Build.VERSION_CODES.LOLLIPOP);
729 return registerServiceImpl(info);
730 }
731
732 private ManagedServiceInfo registerServiceImpl(ManagedServiceInfo info) {
John Spurlock7340fc82014-04-24 18:50:12 -0400733 synchronized (mMutex) {
734 try {
Chris Wren51017d02015-12-15 15:34:46 -0500735 info.service.asBinder().linkToDeath(info, 0);
John Spurlock7340fc82014-04-24 18:50:12 -0400736 mServices.add(info);
Christoph Studer3e144d32014-05-22 16:48:40 +0200737 return info;
John Spurlock7340fc82014-04-24 18:50:12 -0400738 } catch (RemoteException e) {
739 // already dead
740 }
741 }
Christoph Studer3e144d32014-05-22 16:48:40 +0200742 return null;
John Spurlock7340fc82014-04-24 18:50:12 -0400743 }
744
745 /**
746 * Removes a service from the list and unbinds.
747 */
748 private void unregisterServiceImpl(IInterface service, int userid) {
749 ManagedServiceInfo info = removeServiceImpl(service, userid);
Chris Wren51017d02015-12-15 15:34:46 -0500750 if (info != null && info.connection != null && !info.isGuest(this)) {
John Spurlock7340fc82014-04-24 18:50:12 -0400751 mContext.unbindService(info.connection);
752 }
753 }
754
755 private class SettingsObserver extends ContentObserver {
756 private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(mConfig.secureSettingName);
757
758 private SettingsObserver(Handler handler) {
759 super(handler);
760 }
761
762 private void observe() {
763 ContentResolver resolver = mContext.getContentResolver();
764 resolver.registerContentObserver(mSecureSettingsUri,
765 false, this, UserHandle.USER_ALL);
766 update(null);
767 }
768
769 @Override
770 public void onChange(boolean selfChange, Uri uri) {
771 update(uri);
772 }
773
774 private void update(Uri uri) {
775 if (uri == null || mSecureSettingsUri.equals(uri)) {
Christoph Studerb53dfd42014-09-12 14:45:59 +0200776 if (DEBUG) Slog.d(TAG, "Setting changed: mSecureSettingsUri=" + mSecureSettingsUri +
777 " / uri=" + uri);
John Spurlock7340fc82014-04-24 18:50:12 -0400778 rebindServices();
Denis Kuznetsov76c02682015-09-17 19:57:00 +0200779 rebuildRestoredPackages();
John Spurlock7340fc82014-04-24 18:50:12 -0400780 }
781 }
782 }
783
784 public class ManagedServiceInfo implements IBinder.DeathRecipient {
785 public IInterface service;
786 public ComponentName component;
787 public int userid;
788 public boolean isSystem;
789 public ServiceConnection connection;
790 public int targetSdkVersion;
791
792 public ManagedServiceInfo(IInterface service, ComponentName component,
793 int userid, boolean isSystem, ServiceConnection connection, int targetSdkVersion) {
794 this.service = service;
795 this.component = component;
796 this.userid = userid;
797 this.isSystem = isSystem;
798 this.connection = connection;
799 this.targetSdkVersion = targetSdkVersion;
800 }
801
Chris Wren51017d02015-12-15 15:34:46 -0500802 public boolean isGuest(ManagedServices host) {
803 return ManagedServices.this != host;
804 }
805
John Spurlocke77bb362014-04-26 10:24:59 -0400806 @Override
807 public String toString() {
808 return new StringBuilder("ManagedServiceInfo[")
809 .append("component=").append(component)
810 .append(",userid=").append(userid)
811 .append(",isSystem=").append(isSystem)
812 .append(",targetSdkVersion=").append(targetSdkVersion)
813 .append(",connection=").append(connection == null ? null : "<connection>")
814 .append(",service=").append(service)
815 .append(']').toString();
816 }
817
John Spurlock7340fc82014-04-24 18:50:12 -0400818 public boolean enabledAndUserMatches(int nid) {
819 if (!isEnabledForCurrentProfiles()) {
820 return false;
821 }
822 if (this.userid == UserHandle.USER_ALL) return true;
823 if (nid == UserHandle.USER_ALL || nid == this.userid) return true;
824 return supportsProfiles() && mUserProfiles.isCurrentProfile(nid);
825 }
826
827 public boolean supportsProfiles() {
Dianne Hackborn955d8d62014-10-07 20:17:19 -0700828 return targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP;
John Spurlock7340fc82014-04-24 18:50:12 -0400829 }
830
831 @Override
832 public void binderDied() {
John Spurlocke77bb362014-04-26 10:24:59 -0400833 if (DEBUG) Slog.d(TAG, "binderDied");
John Spurlock7340fc82014-04-24 18:50:12 -0400834 // Remove the service, but don't unbind from the service. The system will bring the
835 // service back up, and the onServiceConnected handler will readd the service with the
836 // new binding. If this isn't a bound service, and is just a registered
837 // service, just removing it from the list is all we need to do anyway.
838 removeServiceImpl(this.service, this.userid);
839 }
840
841 /** convenience method for looking in mEnabledServicesForCurrentProfiles */
842 public boolean isEnabledForCurrentProfiles() {
843 if (this.isSystem) return true;
844 if (this.connection == null) return false;
845 return mEnabledServicesForCurrentProfiles.contains(this.component);
846 }
847 }
848
849 public static class UserProfiles {
850 // Profiles of the current user.
851 private final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
852
853 public void updateCache(Context context) {
854 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
855 if (userManager != null) {
856 int currentUserId = ActivityManager.getCurrentUser();
857 List<UserInfo> profiles = userManager.getProfiles(currentUserId);
858 synchronized (mCurrentProfiles) {
859 mCurrentProfiles.clear();
860 for (UserInfo user : profiles) {
861 mCurrentProfiles.put(user.id, user);
862 }
863 }
864 }
865 }
866
867 public int[] getCurrentProfileIds() {
868 synchronized (mCurrentProfiles) {
869 int[] users = new int[mCurrentProfiles.size()];
870 final int N = mCurrentProfiles.size();
871 for (int i = 0; i < N; ++i) {
872 users[i] = mCurrentProfiles.keyAt(i);
873 }
874 return users;
875 }
876 }
877
878 public boolean isCurrentProfile(int userId) {
879 synchronized (mCurrentProfiles) {
880 return mCurrentProfiles.get(userId) != null;
881 }
882 }
883 }
884
885 protected static class Config {
886 String caption;
887 String serviceInterface;
888 String secureSettingName;
889 String bindPermission;
890 String settingsAction;
891 int clientLabel;
892 }
893}