blob: a1103838280c0826f14c978be2c1ffa117867ab8 [file] [log] [blame]
Fred Quintana718d8a22009-04-29 17:53:20 -07001/*
2 * Copyright (C) 2009 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 android.content.pm;
18
Fred Quintana718d8a22009-04-29 17:53:20 -070019import android.content.BroadcastReceiver;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070020import android.content.ComponentName;
21import android.content.Context;
Fred Quintana718d8a22009-04-29 17:53:20 -070022import android.content.Intent;
23import android.content.IntentFilter;
Dianne Hackborn20cb56e2010-03-04 00:58:29 -080024import android.content.pm.PackageManager.NameNotFoundException;
25import android.content.res.Resources;
Fred Quintana718d8a22009-04-29 17:53:20 -070026import android.content.res.XmlResourceParser;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -080027import android.os.Environment;
28import android.os.Handler;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070029import android.os.UserHandle;
Fyodor Kupolov259e7612015-02-11 14:13:34 -080030import android.os.UserManager;
Dianne Hackborn39606a02012-07-31 17:54:35 -070031import android.util.AtomicFile;
Fred Quintana718d8a22009-04-29 17:53:20 -070032import android.util.AttributeSet;
Fyodor Kupolov81446482016-08-24 11:27:49 -070033import android.util.IntArray;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070034import android.util.Log;
35import android.util.Slog;
36import android.util.SparseArray;
Fred Quintana718d8a22009-04-29 17:53:20 -070037import android.util.Xml;
38
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -080039import com.android.internal.annotations.GuardedBy;
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -080040import com.android.internal.annotations.VisibleForTesting;
Amith Yamasani460a7b42015-02-06 14:41:40 -080041import com.android.internal.util.ArrayUtils;
Dianne Hackborn2269d1572010-02-24 19:54:22 -080042import com.android.internal.util.FastXmlSerializer;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -080043import com.google.android.collect.Lists;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070044import com.google.android.collect.Maps;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -080045
Fred Quintana718d8a22009-04-29 17:53:20 -070046import org.xmlpull.v1.XmlPullParser;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070047import org.xmlpull.v1.XmlPullParserException;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -080048import org.xmlpull.v1.XmlSerializer;
Fred Quintana718d8a22009-04-29 17:53:20 -070049
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070050import java.io.File;
51import java.io.FileDescriptor;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070052import java.io.FileOutputStream;
53import java.io.IOException;
Fyodor Kupolov259e7612015-02-11 14:13:34 -080054import java.io.InputStream;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070055import java.io.PrintWriter;
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +010056import java.nio.charset.StandardCharsets;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070057import java.util.ArrayList;
Fyodor Kupolov81446482016-08-24 11:27:49 -070058import java.util.Arrays;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070059import java.util.Collection;
60import java.util.Collections;
61import java.util.List;
62import java.util.Map;
63
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -080064import libcore.io.IoUtils;
65
Fred Quintana718d8a22009-04-29 17:53:20 -070066/**
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070067 * Cache of registered services. This cache is lazily built by interrogating
68 * {@link PackageManager} on a per-user basis. It's updated as packages are
69 * added, removed and changed. Users are responsible for calling
70 * {@link #invalidateCache(int)} when a user is started, since
71 * {@link PackageManager} broadcasts aren't sent for stopped users.
72 * <p>
73 * The services are referred to by type V and are made available via the
74 * {@link #getServiceInfo} method.
75 *
Fred Quintana718d8a22009-04-29 17:53:20 -070076 * @hide
77 */
78public abstract class RegisteredServicesCache<V> {
79 private static final String TAG = "PackageManager";
Dianne Hackborn40e9f292012-11-27 19:12:23 -080080 private static final boolean DEBUG = false;
Fyodor Kupolov259e7612015-02-11 14:13:34 -080081 protected static final String REGISTERED_SERVICES_DIR = "registered_services";
Fred Quintana718d8a22009-04-29 17:53:20 -070082
83 public final Context mContext;
84 private final String mInterfaceName;
85 private final String mMetaDataName;
86 private final String mAttributesName;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -080087 private final XmlSerializerAndParser<V> mSerializerAndParser;
Fred Quintana718d8a22009-04-29 17:53:20 -070088
Amith Yamasani37a40c22015-06-17 13:25:42 -070089 protected final Object mServicesLock = new Object();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070090
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -080091 @GuardedBy("mServicesLock")
Dianne Hackbornf4bf0ae2013-05-20 18:42:16 -070092 private final SparseArray<UserServices<V>> mUserServices = new SparseArray<UserServices<V>>(2);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070093
94 private static class UserServices<V> {
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -080095 @GuardedBy("mServicesLock")
Fyodor Kupolov259e7612015-02-11 14:13:34 -080096 final Map<V, Integer> persistentServices = Maps.newHashMap();
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -080097 @GuardedBy("mServicesLock")
Fyodor Kupolov259e7612015-02-11 14:13:34 -080098 Map<V, ServiceInfo<V>> services = null;
99 @GuardedBy("mServicesLock")
100 boolean mPersistentServicesFileDidNotExist = true;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700101 }
102
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800103 @GuardedBy("mServicesLock")
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700104 private UserServices<V> findOrCreateUserLocked(int userId) {
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800105 return findOrCreateUserLocked(userId, true);
106 }
107
108 @GuardedBy("mServicesLock")
109 private UserServices<V> findOrCreateUserLocked(int userId, boolean loadFromFileIfNew) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700110 UserServices<V> services = mUserServices.get(userId);
111 if (services == null) {
112 services = new UserServices<V>();
113 mUserServices.put(userId, services);
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800114 if (loadFromFileIfNew && mSerializerAndParser != null) {
115 // Check if user exists and try loading data from file
116 // clear existing data if there was an error during migration
117 UserInfo user = getUser(userId);
118 if (user != null) {
119 AtomicFile file = createFileForUser(user.id);
120 if (file.getBaseFile().exists()) {
121 if (DEBUG) {
122 Slog.i(TAG, String.format("Loading u%s data from %s", user.id, file));
123 }
124 InputStream is = null;
125 try {
126 is = file.openRead();
127 readPersistentServicesLocked(is);
128 } catch (Exception e) {
129 Log.w(TAG, "Error reading persistent services for user " + user.id, e);
130 } finally {
131 IoUtils.closeQuietly(is);
132 }
133 }
134 }
135 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700136 }
137 return services;
138 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -0700139
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800140 // the listener and handler are synchronized on "this" and must be updated together
141 private RegisteredServicesCacheListener<V> mListener;
142 private Handler mHandler;
Fred Quintana718d8a22009-04-29 17:53:20 -0700143
144 public RegisteredServicesCache(Context context, String interfaceName, String metaDataName,
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800145 String attributeName, XmlSerializerAndParser<V> serializerAndParser) {
Fred Quintana718d8a22009-04-29 17:53:20 -0700146 mContext = context;
147 mInterfaceName = interfaceName;
148 mMetaDataName = metaDataName;
149 mAttributesName = attributeName;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800150 mSerializerAndParser = serializerAndParser;
151
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800152 migrateIfNecessaryLocked();
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800153
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800154 IntentFilter intentFilter = new IntentFilter();
155 intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
156 intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
157 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
158 intentFilter.addDataScheme("package");
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700159 mContext.registerReceiverAsUser(mPackageReceiver, UserHandle.ALL, intentFilter, null, null);
160
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800161 // Register for events related to sdcard installation.
162 IntentFilter sdFilter = new IntentFilter();
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800163 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
164 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700165 mContext.registerReceiver(mExternalReceiver, sdFilter);
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800166
167 // Register for user-related events
168 IntentFilter userFilter = new IntentFilter();
169 sdFilter.addAction(Intent.ACTION_USER_REMOVED);
170 mContext.registerReceiver(mUserRemovedReceiver, userFilter);
Fred Quintana718d8a22009-04-29 17:53:20 -0700171 }
172
Christopher Tate0598d702014-03-11 18:16:46 -0700173 private final void handlePackageEvent(Intent intent, int userId) {
174 // Don't regenerate the services map when the package is removed or its
175 // ASEC container unmounted as a step in replacement. The subsequent
176 // _ADDED / _AVAILABLE call will regenerate the map in the final state.
177 final String action = intent.getAction();
178 // it's a new-component action if it isn't some sort of removal
179 final boolean isRemoval = Intent.ACTION_PACKAGE_REMOVED.equals(action)
180 || Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action);
181 // if it's a removal, is it part of an update-in-place step?
182 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
183
184 if (isRemoval && replacing) {
185 // package is going away, but it's the middle of an upgrade: keep the current
186 // state and do nothing here. This clause is intentionally empty.
187 } else {
Amith Yamasani460a7b42015-02-06 14:41:40 -0800188 int[] uids = null;
Christopher Tate0598d702014-03-11 18:16:46 -0700189 // either we're adding/changing, or it's a removal without replacement, so
Amith Yamasani460a7b42015-02-06 14:41:40 -0800190 // we need to update the set of available services
191 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)
192 || Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
193 uids = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
194 } else {
195 int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
196 if (uid > 0) {
197 uids = new int[] { uid };
198 }
199 }
200 generateServicesMap(uids, userId);
Christopher Tate0598d702014-03-11 18:16:46 -0700201 }
202 }
203
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700204 private final BroadcastReceiver mPackageReceiver = new BroadcastReceiver() {
205 @Override
206 public void onReceive(Context context, Intent intent) {
207 final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
208 if (uid != -1) {
Christopher Tate0598d702014-03-11 18:16:46 -0700209 handlePackageEvent(intent, UserHandle.getUserId(uid));
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700210 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800211 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700212 };
213
214 private final BroadcastReceiver mExternalReceiver = new BroadcastReceiver() {
215 @Override
216 public void onReceive(Context context, Intent intent) {
217 // External apps can't coexist with multi-user, so scan owner
Xiaohui Chen98404fd2015-08-17 16:09:02 -0700218 handlePackageEvent(intent, UserHandle.USER_SYSTEM);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700219 }
220 };
221
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800222 private final BroadcastReceiver mUserRemovedReceiver = new BroadcastReceiver() {
223 @Override
224 public void onReceive(Context context, Intent intent) {
225 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
226 if (DEBUG) {
227 Slog.d(TAG, "u" + userId + " removed - cleaning up");
228 }
229 onUserRemoved(userId);
230 }
231 };
232
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700233 public void invalidateCache(int userId) {
234 synchronized (mServicesLock) {
235 final UserServices<V> user = findOrCreateUserLocked(userId);
236 user.services = null;
Amith Yamasani37a40c22015-06-17 13:25:42 -0700237 onServicesChangedLocked(userId);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700238 }
239 }
240
241 public void dump(FileDescriptor fd, PrintWriter fout, String[] args, int userId) {
242 synchronized (mServicesLock) {
243 final UserServices<V> user = findOrCreateUserLocked(userId);
244 if (user.services != null) {
245 fout.println("RegisteredServicesCache: " + user.services.size() + " services");
246 for (ServiceInfo<?> info : user.services.values()) {
247 fout.println(" " + info);
248 }
249 } else {
250 fout.println("RegisteredServicesCache: services not loaded");
251 }
Fred Quintana718d8a22009-04-29 17:53:20 -0700252 }
253 }
254
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800255 public RegisteredServicesCacheListener<V> getListener() {
Fred Quintana718d8a22009-04-29 17:53:20 -0700256 synchronized (this) {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800257 return mListener;
Fred Quintana718d8a22009-04-29 17:53:20 -0700258 }
259 }
260
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800261 public void setListener(RegisteredServicesCacheListener<V> listener, Handler handler) {
262 if (handler == null) {
263 handler = new Handler(mContext.getMainLooper());
Fred Quintana718d8a22009-04-29 17:53:20 -0700264 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800265 synchronized (this) {
266 mHandler = handler;
267 mListener = listener;
268 }
269 }
270
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700271 private void notifyListener(final V type, final int userId, final boolean removed) {
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800272 if (DEBUG) {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800273 Log.d(TAG, "notifyListener: " + type + " is " + (removed ? "removed" : "added"));
274 }
275 RegisteredServicesCacheListener<V> listener;
276 Handler handler;
277 synchronized (this) {
278 listener = mListener;
279 handler = mHandler;
280 }
281 if (listener == null) {
282 return;
283 }
284
285 final RegisteredServicesCacheListener<V> listener2 = listener;
286 handler.post(new Runnable() {
287 public void run() {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700288 listener2.onServiceChanged(type, userId, removed);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800289 }
290 });
Fred Quintana718d8a22009-04-29 17:53:20 -0700291 }
292
293 /**
294 * Value type that describes a Service. The information within can be used
295 * to bind to the service.
296 */
297 public static class ServiceInfo<V> {
298 public final V type;
Jeff Sharkey9d8a1042015-12-03 17:56:20 -0700299 public final ComponentInfo componentInfo;
Fred Quintana718d8a22009-04-29 17:53:20 -0700300 public final ComponentName componentName;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700301 public final int uid;
Fred Quintana718d8a22009-04-29 17:53:20 -0700302
Fred Quintana56285a62010-12-02 14:20:51 -0800303 /** @hide */
Jeff Sharkey9d8a1042015-12-03 17:56:20 -0700304 public ServiceInfo(V type, ComponentInfo componentInfo, ComponentName componentName) {
Fred Quintana718d8a22009-04-29 17:53:20 -0700305 this.type = type;
Jeff Sharkey9d8a1042015-12-03 17:56:20 -0700306 this.componentInfo = componentInfo;
Fred Quintana718d8a22009-04-29 17:53:20 -0700307 this.componentName = componentName;
Jeff Sharkey9d8a1042015-12-03 17:56:20 -0700308 this.uid = (componentInfo != null) ? componentInfo.applicationInfo.uid : -1;
Fred Quintana718d8a22009-04-29 17:53:20 -0700309 }
310
Jeff Hamiltonc42c0dd2009-09-03 09:08:30 -0500311 @Override
Fred Quintana718d8a22009-04-29 17:53:20 -0700312 public String toString() {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800313 return "ServiceInfo: " + type + ", " + componentName + ", uid " + uid;
Fred Quintana718d8a22009-04-29 17:53:20 -0700314 }
315 }
316
317 /**
318 * Accessor for the registered authenticators.
319 * @param type the account type of the authenticator
320 * @return the AuthenticatorInfo that matches the account type or null if none is present
321 */
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700322 public ServiceInfo<V> getServiceInfo(V type, int userId) {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800323 synchronized (mServicesLock) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700324 // Find user and lazily populate cache
325 final UserServices<V> user = findOrCreateUserLocked(userId);
326 if (user.services == null) {
Amith Yamasani460a7b42015-02-06 14:41:40 -0800327 generateServicesMap(null, userId);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700328 }
329 return user.services.get(type);
Fred Quintana718d8a22009-04-29 17:53:20 -0700330 }
Fred Quintana718d8a22009-04-29 17:53:20 -0700331 }
332
333 /**
334 * @return a collection of {@link RegisteredServicesCache.ServiceInfo} objects for all
335 * registered authenticators.
336 */
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700337 public Collection<ServiceInfo<V>> getAllServices(int userId) {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800338 synchronized (mServicesLock) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700339 // Find user and lazily populate cache
340 final UserServices<V> user = findOrCreateUserLocked(userId);
341 if (user.services == null) {
Amith Yamasani460a7b42015-02-06 14:41:40 -0800342 generateServicesMap(null, userId);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700343 }
Jeff Sharkeya706e2f2012-10-16 12:02:42 -0700344 return Collections.unmodifiableCollection(
345 new ArrayList<ServiceInfo<V>>(user.services.values()));
Fred Quintana718d8a22009-04-29 17:53:20 -0700346 }
Fred Quintana718d8a22009-04-29 17:53:20 -0700347 }
348
Fyodor Kupolov81446482016-08-24 11:27:49 -0700349 public void updateServices(int userId) {
350 if (DEBUG) {
351 Slog.d(TAG, "updateServices u" + userId);
352 }
353 List<ServiceInfo<V>> allServices;
354 synchronized (mServicesLock) {
355 final UserServices<V> user = findOrCreateUserLocked(userId);
356 // If services haven't been initialized yet - no updates required
357 if (user.services == null) {
358 return;
359 }
360 allServices = new ArrayList<>(user.services.values());
361 }
362 IntArray updatedUids = null;
363 for (ServiceInfo<V> service : allServices) {
364 int versionCode = service.componentInfo.applicationInfo.versionCode;
365 String pkg = service.componentInfo.packageName;
366 ApplicationInfo newAppInfo = null;
367 try {
368 newAppInfo = mContext.getPackageManager().getApplicationInfoAsUser(pkg, 0, userId);
369 } catch (NameNotFoundException e) {
370 // Package uninstalled - treat as null app info
371 }
372 // If package updated or removed
373 if ((newAppInfo == null) || (newAppInfo.versionCode != versionCode)) {
374 if (DEBUG) {
375 Slog.d(TAG, "Package " + pkg + " uid=" + service.uid
376 + " updated. New appInfo: " + newAppInfo);
377 }
378 if (updatedUids == null) {
379 updatedUids = new IntArray();
380 }
381 updatedUids.add(service.uid);
382 }
383 }
384 if (updatedUids != null && updatedUids.size() > 0) {
385 int[] updatedUidsArray = updatedUids.toArray();
386 generateServicesMap(updatedUidsArray, userId);
387 }
388 }
389
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -0800390 @VisibleForTesting
391 protected boolean inSystemImage(int callerUid) {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800392 String[] packages = mContext.getPackageManager().getPackagesForUid(callerUid);
393 for (String name : packages) {
394 try {
395 PackageInfo packageInfo =
396 mContext.getPackageManager().getPackageInfo(name, 0 /* flags */);
397 if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
398 return true;
399 }
400 } catch (PackageManager.NameNotFoundException e) {
401 return false;
402 }
403 }
404 return false;
405 }
406
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -0800407 @VisibleForTesting
408 protected List<ResolveInfo> queryIntentServices(int userId) {
409 final PackageManager pm = mContext.getPackageManager();
Jeff Sharkey9d8a1042015-12-03 17:56:20 -0700410 return pm.queryIntentServicesAsUser(new Intent(mInterfaceName),
Jeff Sharkey8a372a02016-03-16 16:25:45 -0600411 PackageManager.GET_META_DATA | PackageManager.MATCH_DIRECT_BOOT_AWARE
412 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
Jeff Sharkey9d8a1042015-12-03 17:56:20 -0700413 userId);
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -0800414 }
415
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700416 /**
417 * Populate {@link UserServices#services} by scanning installed packages for
418 * given {@link UserHandle}.
Amith Yamasani460a7b42015-02-06 14:41:40 -0800419 * @param changedUids the array of uids that have been affected, as mentioned in the broadcast
420 * or null to assume that everything is affected.
421 * @param userId the user for whom to update the services map.
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700422 */
Amith Yamasani460a7b42015-02-06 14:41:40 -0800423 private void generateServicesMap(int[] changedUids, int userId) {
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800424 if (DEBUG) {
Fyodor Kupolov81446482016-08-24 11:27:49 -0700425 Slog.d(TAG, "generateServicesMap() for " + userId + ", changed UIDs = "
426 + Arrays.toString(changedUids));
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800427 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700428
Fyodor Kupolov81446482016-08-24 11:27:49 -0700429 final ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<>();
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -0800430 final List<ResolveInfo> resolveInfos = queryIntentServices(userId);
Fred Quintana718d8a22009-04-29 17:53:20 -0700431 for (ResolveInfo resolveInfo : resolveInfos) {
432 try {
433 ServiceInfo<V> info = parseServiceInfo(resolveInfo);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800434 if (info == null) {
435 Log.w(TAG, "Unable to load service info " + resolveInfo.toString());
436 continue;
Fred Quintana718d8a22009-04-29 17:53:20 -0700437 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800438 serviceInfos.add(info);
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -0800439 } catch (XmlPullParserException|IOException e) {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800440 Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);
Fred Quintana718d8a22009-04-29 17:53:20 -0700441 }
442 }
443
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800444 synchronized (mServicesLock) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700445 final UserServices<V> user = findOrCreateUserLocked(userId);
446 final boolean firstScan = user.services == null;
447 if (firstScan) {
448 user.services = Maps.newHashMap();
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800449 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700450
Alon Albert3fa51e32010-11-11 09:24:04 -0800451 StringBuilder changes = new StringBuilder();
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800452 boolean changed = false;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800453 for (ServiceInfo<V> info : serviceInfos) {
454 // four cases:
455 // - doesn't exist yet
456 // - add, notify user that it was added
457 // - exists and the UID is the same
458 // - replace, don't notify user
459 // - exists, the UID is different, and the new one is not a system package
460 // - ignore
461 // - exists, the UID is different, and the new one is a system package
462 // - add, notify user that it was added
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700463 Integer previousUid = user.persistentServices.get(info.type);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800464 if (previousUid == null) {
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800465 if (DEBUG) {
466 changes.append(" New service added: ").append(info).append("\n");
467 }
468 changed = true;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700469 user.services.put(info.type, info);
470 user.persistentServices.put(info.type, info.uid);
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800471 if (!(user.mPersistentServicesFileDidNotExist && firstScan)) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700472 notifyListener(info.type, userId, false /* removed */);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800473 }
474 } else if (previousUid == info.uid) {
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800475 if (DEBUG) {
Alon Albert3fa51e32010-11-11 09:24:04 -0800476 changes.append(" Existing service (nop): ").append(info).append("\n");
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800477 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700478 user.services.put(info.type, info);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800479 } else if (inSystemImage(info.uid)
480 || !containsTypeAndUid(serviceInfos, info.type, previousUid)) {
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800481 if (DEBUG) {
482 if (inSystemImage(info.uid)) {
483 changes.append(" System service replacing existing: ").append(info)
484 .append("\n");
485 } else {
486 changes.append(" Existing service replacing a removed service: ")
487 .append(info).append("\n");
488 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800489 }
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800490 changed = true;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700491 user.services.put(info.type, info);
492 user.persistentServices.put(info.type, info.uid);
493 notifyListener(info.type, userId, false /* removed */);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800494 } else {
495 // ignore
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800496 if (DEBUG) {
497 changes.append(" Existing service with new uid ignored: ").append(info)
498 .append("\n");
499 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800500 }
501 }
502
503 ArrayList<V> toBeRemoved = Lists.newArrayList();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700504 for (V v1 : user.persistentServices.keySet()) {
Amith Yamasani460a7b42015-02-06 14:41:40 -0800505 // Remove a persisted service that's not in the currently available services list.
506 // And only if it is in the list of changedUids.
507 if (!containsType(serviceInfos, v1)
508 && containsUid(changedUids, user.persistentServices.get(v1))) {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800509 toBeRemoved.add(v1);
510 }
511 }
512 for (V v1 : toBeRemoved) {
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800513 if (DEBUG) {
514 changes.append(" Service removed: ").append(v1).append("\n");
515 }
516 changed = true;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700517 user.persistentServices.remove(v1);
Amith Yamasani460a7b42015-02-06 14:41:40 -0800518 user.services.remove(v1);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700519 notifyListener(v1, userId, true /* removed */);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800520 }
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800521 if (DEBUG) {
Amith Yamasani460a7b42015-02-06 14:41:40 -0800522 Log.d(TAG, "user.services=");
523 for (V v : user.services.keySet()) {
524 Log.d(TAG, " " + v + " " + user.services.get(v));
525 }
526 Log.d(TAG, "user.persistentServices=");
527 for (V v : user.persistentServices.keySet()) {
528 Log.d(TAG, " " + v + " " + user.persistentServices.get(v));
529 }
530 }
531 if (DEBUG) {
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800532 if (changes.length() > 0) {
Dianne Hackborn4428e172012-08-24 17:43:05 -0700533 Log.d(TAG, "generateServicesMap(" + mInterfaceName + "): " +
534 serviceInfos.size() + " services:\n" + changes);
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800535 } else {
Dianne Hackborn4428e172012-08-24 17:43:05 -0700536 Log.d(TAG, "generateServicesMap(" + mInterfaceName + "): " +
537 serviceInfos.size() + " services unchanged");
538 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800539 }
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800540 if (changed) {
Amith Yamasani37a40c22015-06-17 13:25:42 -0700541 onServicesChangedLocked(userId);
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800542 writePersistentServicesLocked(user, userId);
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800543 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800544 }
545 }
546
Amith Yamasani37a40c22015-06-17 13:25:42 -0700547 protected void onServicesChangedLocked(int userId) {
548 // Feel free to override
549 }
550
Amith Yamasani460a7b42015-02-06 14:41:40 -0800551 /**
552 * Returns true if the list of changed uids is null (wildcard) or the specified uid
553 * is contained in the list of changed uids.
554 */
555 private boolean containsUid(int[] changedUids, int uid) {
556 return changedUids == null || ArrayUtils.contains(changedUids, uid);
557 }
558
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800559 private boolean containsType(ArrayList<ServiceInfo<V>> serviceInfos, V type) {
560 for (int i = 0, N = serviceInfos.size(); i < N; i++) {
561 if (serviceInfos.get(i).type.equals(type)) {
562 return true;
563 }
564 }
565
566 return false;
567 }
568
569 private boolean containsTypeAndUid(ArrayList<ServiceInfo<V>> serviceInfos, V type, int uid) {
570 for (int i = 0, N = serviceInfos.size(); i < N; i++) {
571 final ServiceInfo<V> serviceInfo = serviceInfos.get(i);
572 if (serviceInfo.type.equals(type) && serviceInfo.uid == uid) {
573 return true;
574 }
575 }
576
577 return false;
Fred Quintana718d8a22009-04-29 17:53:20 -0700578 }
579
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -0800580 @VisibleForTesting
581 protected ServiceInfo<V> parseServiceInfo(ResolveInfo service)
Fred Quintana718d8a22009-04-29 17:53:20 -0700582 throws XmlPullParserException, IOException {
583 android.content.pm.ServiceInfo si = service.serviceInfo;
584 ComponentName componentName = new ComponentName(si.packageName, si.name);
585
586 PackageManager pm = mContext.getPackageManager();
587
588 XmlResourceParser parser = null;
589 try {
590 parser = si.loadXmlMetaData(pm, mMetaDataName);
591 if (parser == null) {
592 throw new XmlPullParserException("No " + mMetaDataName + " meta-data");
593 }
594
595 AttributeSet attrs = Xml.asAttributeSet(parser);
596
597 int type;
598 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
599 && type != XmlPullParser.START_TAG) {
600 }
601
602 String nodeName = parser.getName();
603 if (!mAttributesName.equals(nodeName)) {
604 throw new XmlPullParserException(
605 "Meta-data does not start with " + mAttributesName + " tag");
606 }
607
Dianne Hackborn20cb56e2010-03-04 00:58:29 -0800608 V v = parseServiceAttributes(pm.getResourcesForApplication(si.applicationInfo),
609 si.packageName, attrs);
Fred Quintana718d8a22009-04-29 17:53:20 -0700610 if (v == null) {
611 return null;
612 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700613 final android.content.pm.ServiceInfo serviceInfo = service.serviceInfo;
Jeff Sharkey9d8a1042015-12-03 17:56:20 -0700614 return new ServiceInfo<V>(v, serviceInfo, componentName);
Dianne Hackborn20cb56e2010-03-04 00:58:29 -0800615 } catch (NameNotFoundException e) {
616 throw new XmlPullParserException(
617 "Unable to load resources for pacakge " + si.packageName);
Fred Quintana718d8a22009-04-29 17:53:20 -0700618 } finally {
619 if (parser != null) parser.close();
620 }
621 }
622
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800623 /**
624 * Read all sync status back in to the initial engine state.
625 */
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800626 private void readPersistentServicesLocked(InputStream is)
627 throws XmlPullParserException, IOException {
628 XmlPullParser parser = Xml.newPullParser();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100629 parser.setInput(is, StandardCharsets.UTF_8.name());
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800630 int eventType = parser.getEventType();
631 while (eventType != XmlPullParser.START_TAG
632 && eventType != XmlPullParser.END_DOCUMENT) {
633 eventType = parser.next();
634 }
635 String tagName = parser.getName();
636 if ("services".equals(tagName)) {
637 eventType = parser.next();
638 do {
639 if (eventType == XmlPullParser.START_TAG && parser.getDepth() == 2) {
640 tagName = parser.getName();
641 if ("service".equals(tagName)) {
642 V service = mSerializerAndParser.createFromXml(parser);
643 if (service == null) {
644 break;
645 }
646 String uidString = parser.getAttributeValue(null, "uid");
647 final int uid = Integer.parseInt(uidString);
648 final int userId = UserHandle.getUserId(uid);
649 final UserServices<V> user = findOrCreateUserLocked(userId,
650 false /*loadFromFileIfNew*/) ;
651 user.persistentServices.put(service, uid);
652 }
653 }
654 eventType = parser.next();
655 } while (eventType != XmlPullParser.END_DOCUMENT);
656 }
657 }
658
659 private void migrateIfNecessaryLocked() {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800660 if (mSerializerAndParser == null) {
661 return;
662 }
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800663 File systemDir = new File(getDataDirectory(), "system");
664 File syncDir = new File(systemDir, REGISTERED_SERVICES_DIR);
665 AtomicFile oldFile = new AtomicFile(new File(syncDir, mInterfaceName + ".xml"));
666 boolean oldFileExists = oldFile.getBaseFile().exists();
667
668 if (oldFileExists) {
669 File marker = new File(syncDir, mInterfaceName + ".xml.migrated");
670 // if not migrated, perform the migration and add a marker
671 if (!marker.exists()) {
672 if (DEBUG) {
673 Slog.i(TAG, "Marker file " + marker + " does not exist - running migration");
674 }
675 InputStream is = null;
676 try {
677 is = oldFile.openRead();
678 mUserServices.clear();
679 readPersistentServicesLocked(is);
680 } catch (Exception e) {
681 Log.w(TAG, "Error reading persistent services, starting from scratch", e);
682 } finally {
683 IoUtils.closeQuietly(is);
684 }
685 try {
686 for (UserInfo user : getUsers()) {
687 UserServices<V> userServices = mUserServices.get(user.id);
688 if (userServices != null) {
689 if (DEBUG) {
690 Slog.i(TAG, "Migrating u" + user.id + " services "
691 + userServices.persistentServices);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800692 }
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800693 writePersistentServicesLocked(userServices, user.id);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800694 }
695 }
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800696 marker.createNewFile();
697 } catch (Exception e) {
698 Log.w(TAG, "Migration failed", e);
699 }
700 // Migration is complete and we don't need to keep data for all users anymore,
701 // It will be loaded from a new location when requested
702 mUserServices.clear();
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800703 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800704 }
705 }
706
707 /**
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800708 * Writes services of a specified user to the file.
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800709 */
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800710 private void writePersistentServicesLocked(UserServices<V> user, int userId) {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800711 if (mSerializerAndParser == null) {
712 return;
713 }
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800714 AtomicFile atomicFile = createFileForUser(userId);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800715 FileOutputStream fos = null;
716 try {
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800717 fos = atomicFile.startWrite();
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800718 XmlSerializer out = new FastXmlSerializer();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100719 out.setOutput(fos, StandardCharsets.UTF_8.name());
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800720 out.startDocument(null, true);
721 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
722 out.startTag(null, "services");
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800723 for (Map.Entry<V, Integer> service : user.persistentServices.entrySet()) {
724 out.startTag(null, "service");
725 out.attribute(null, "uid", Integer.toString(service.getValue()));
726 mSerializerAndParser.writeAsXml(service.getKey(), out);
727 out.endTag(null, "service");
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800728 }
729 out.endTag(null, "services");
730 out.endDocument();
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800731 atomicFile.finishWrite(fos);
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -0800732 } catch (IOException e1) {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800733 Log.w(TAG, "Error writing accounts", e1);
734 if (fos != null) {
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800735 atomicFile.failWrite(fos);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800736 }
737 }
738 }
739
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -0800740 @VisibleForTesting
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800741 protected void onUserRemoved(int userId) {
Amith Yamasani37a40c22015-06-17 13:25:42 -0700742 synchronized (mServicesLock) {
743 mUserServices.remove(userId);
744 }
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800745 }
746
747 @VisibleForTesting
748 protected List<UserInfo> getUsers() {
749 return UserManager.get(mContext).getUsers(true);
750 }
751
752 @VisibleForTesting
753 protected UserInfo getUser(int userId) {
754 return UserManager.get(mContext).getUserInfo(userId);
755 }
756
757 private AtomicFile createFileForUser(int userId) {
758 File userDir = getUserSystemDirectory(userId);
759 File userFile = new File(userDir, REGISTERED_SERVICES_DIR + "/" + mInterfaceName + ".xml");
760 return new AtomicFile(userFile);
761 }
762
763 @VisibleForTesting
764 protected File getUserSystemDirectory(int userId) {
765 return Environment.getUserSystemDirectory(userId);
766 }
767
768 @VisibleForTesting
769 protected File getDataDirectory() {
770 return Environment.getDataDirectory();
771 }
772
773 @VisibleForTesting
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -0800774 protected Map<V, Integer> getPersistentServices(int userId) {
775 return findOrCreateUserLocked(userId).persistentServices;
776 }
777
Dianne Hackborn20cb56e2010-03-04 00:58:29 -0800778 public abstract V parseServiceAttributes(Resources res,
779 String packageName, AttributeSet attrs);
Fred Quintana718d8a22009-04-29 17:53:20 -0700780}