blob: aea843adbd4814cf8618b97972685402152bf087 [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);
liulvpingbf76ee72016-09-08 09:16:48 +0800393 if (packages != null) {
394 for (String name : packages) {
395 try {
396 PackageInfo packageInfo =
397 mContext.getPackageManager().getPackageInfo(name, 0 /* flags */);
398 if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
399 return true;
400 }
401 } catch (PackageManager.NameNotFoundException e) {
402 return false;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800403 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800404 }
405 }
406 return false;
407 }
408
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -0800409 @VisibleForTesting
410 protected List<ResolveInfo> queryIntentServices(int userId) {
411 final PackageManager pm = mContext.getPackageManager();
Jeff Sharkey9d8a1042015-12-03 17:56:20 -0700412 return pm.queryIntentServicesAsUser(new Intent(mInterfaceName),
Jeff Sharkey8a372a02016-03-16 16:25:45 -0600413 PackageManager.GET_META_DATA | PackageManager.MATCH_DIRECT_BOOT_AWARE
414 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
Jeff Sharkey9d8a1042015-12-03 17:56:20 -0700415 userId);
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -0800416 }
417
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700418 /**
419 * Populate {@link UserServices#services} by scanning installed packages for
420 * given {@link UserHandle}.
Amith Yamasani460a7b42015-02-06 14:41:40 -0800421 * @param changedUids the array of uids that have been affected, as mentioned in the broadcast
422 * or null to assume that everything is affected.
423 * @param userId the user for whom to update the services map.
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700424 */
Amith Yamasani460a7b42015-02-06 14:41:40 -0800425 private void generateServicesMap(int[] changedUids, int userId) {
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800426 if (DEBUG) {
Fyodor Kupolov81446482016-08-24 11:27:49 -0700427 Slog.d(TAG, "generateServicesMap() for " + userId + ", changed UIDs = "
428 + Arrays.toString(changedUids));
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800429 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700430
Fyodor Kupolov81446482016-08-24 11:27:49 -0700431 final ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<>();
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -0800432 final List<ResolveInfo> resolveInfos = queryIntentServices(userId);
Fred Quintana718d8a22009-04-29 17:53:20 -0700433 for (ResolveInfo resolveInfo : resolveInfos) {
434 try {
435 ServiceInfo<V> info = parseServiceInfo(resolveInfo);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800436 if (info == null) {
437 Log.w(TAG, "Unable to load service info " + resolveInfo.toString());
438 continue;
Fred Quintana718d8a22009-04-29 17:53:20 -0700439 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800440 serviceInfos.add(info);
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -0800441 } catch (XmlPullParserException|IOException e) {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800442 Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);
Fred Quintana718d8a22009-04-29 17:53:20 -0700443 }
444 }
445
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800446 synchronized (mServicesLock) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700447 final UserServices<V> user = findOrCreateUserLocked(userId);
448 final boolean firstScan = user.services == null;
449 if (firstScan) {
450 user.services = Maps.newHashMap();
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800451 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700452
Alon Albert3fa51e32010-11-11 09:24:04 -0800453 StringBuilder changes = new StringBuilder();
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800454 boolean changed = false;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800455 for (ServiceInfo<V> info : serviceInfos) {
456 // four cases:
457 // - doesn't exist yet
458 // - add, notify user that it was added
459 // - exists and the UID is the same
460 // - replace, don't notify user
461 // - exists, the UID is different, and the new one is not a system package
462 // - ignore
463 // - exists, the UID is different, and the new one is a system package
464 // - add, notify user that it was added
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700465 Integer previousUid = user.persistentServices.get(info.type);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800466 if (previousUid == null) {
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800467 if (DEBUG) {
468 changes.append(" New service added: ").append(info).append("\n");
469 }
470 changed = true;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700471 user.services.put(info.type, info);
472 user.persistentServices.put(info.type, info.uid);
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800473 if (!(user.mPersistentServicesFileDidNotExist && firstScan)) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700474 notifyListener(info.type, userId, false /* removed */);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800475 }
476 } else if (previousUid == info.uid) {
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800477 if (DEBUG) {
Alon Albert3fa51e32010-11-11 09:24:04 -0800478 changes.append(" Existing service (nop): ").append(info).append("\n");
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800479 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700480 user.services.put(info.type, info);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800481 } else if (inSystemImage(info.uid)
482 || !containsTypeAndUid(serviceInfos, info.type, previousUid)) {
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800483 if (DEBUG) {
484 if (inSystemImage(info.uid)) {
485 changes.append(" System service replacing existing: ").append(info)
486 .append("\n");
487 } else {
488 changes.append(" Existing service replacing a removed service: ")
489 .append(info).append("\n");
490 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800491 }
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800492 changed = true;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700493 user.services.put(info.type, info);
494 user.persistentServices.put(info.type, info.uid);
495 notifyListener(info.type, userId, false /* removed */);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800496 } else {
497 // ignore
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800498 if (DEBUG) {
499 changes.append(" Existing service with new uid ignored: ").append(info)
500 .append("\n");
501 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800502 }
503 }
504
505 ArrayList<V> toBeRemoved = Lists.newArrayList();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700506 for (V v1 : user.persistentServices.keySet()) {
Amith Yamasani460a7b42015-02-06 14:41:40 -0800507 // Remove a persisted service that's not in the currently available services list.
508 // And only if it is in the list of changedUids.
509 if (!containsType(serviceInfos, v1)
510 && containsUid(changedUids, user.persistentServices.get(v1))) {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800511 toBeRemoved.add(v1);
512 }
513 }
514 for (V v1 : toBeRemoved) {
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800515 if (DEBUG) {
516 changes.append(" Service removed: ").append(v1).append("\n");
517 }
518 changed = true;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700519 user.persistentServices.remove(v1);
Amith Yamasani460a7b42015-02-06 14:41:40 -0800520 user.services.remove(v1);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700521 notifyListener(v1, userId, true /* removed */);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800522 }
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800523 if (DEBUG) {
Amith Yamasani460a7b42015-02-06 14:41:40 -0800524 Log.d(TAG, "user.services=");
525 for (V v : user.services.keySet()) {
526 Log.d(TAG, " " + v + " " + user.services.get(v));
527 }
528 Log.d(TAG, "user.persistentServices=");
529 for (V v : user.persistentServices.keySet()) {
530 Log.d(TAG, " " + v + " " + user.persistentServices.get(v));
531 }
532 }
533 if (DEBUG) {
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800534 if (changes.length() > 0) {
Dianne Hackborn4428e172012-08-24 17:43:05 -0700535 Log.d(TAG, "generateServicesMap(" + mInterfaceName + "): " +
536 serviceInfos.size() + " services:\n" + changes);
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800537 } else {
Dianne Hackborn4428e172012-08-24 17:43:05 -0700538 Log.d(TAG, "generateServicesMap(" + mInterfaceName + "): " +
539 serviceInfos.size() + " services unchanged");
540 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800541 }
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800542 if (changed) {
Amith Yamasani37a40c22015-06-17 13:25:42 -0700543 onServicesChangedLocked(userId);
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800544 writePersistentServicesLocked(user, userId);
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800545 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800546 }
547 }
548
Amith Yamasani37a40c22015-06-17 13:25:42 -0700549 protected void onServicesChangedLocked(int userId) {
550 // Feel free to override
551 }
552
Amith Yamasani460a7b42015-02-06 14:41:40 -0800553 /**
554 * Returns true if the list of changed uids is null (wildcard) or the specified uid
555 * is contained in the list of changed uids.
556 */
557 private boolean containsUid(int[] changedUids, int uid) {
558 return changedUids == null || ArrayUtils.contains(changedUids, uid);
559 }
560
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800561 private boolean containsType(ArrayList<ServiceInfo<V>> serviceInfos, V type) {
562 for (int i = 0, N = serviceInfos.size(); i < N; i++) {
563 if (serviceInfos.get(i).type.equals(type)) {
564 return true;
565 }
566 }
567
568 return false;
569 }
570
571 private boolean containsTypeAndUid(ArrayList<ServiceInfo<V>> serviceInfos, V type, int uid) {
572 for (int i = 0, N = serviceInfos.size(); i < N; i++) {
573 final ServiceInfo<V> serviceInfo = serviceInfos.get(i);
574 if (serviceInfo.type.equals(type) && serviceInfo.uid == uid) {
575 return true;
576 }
577 }
578
579 return false;
Fred Quintana718d8a22009-04-29 17:53:20 -0700580 }
581
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -0800582 @VisibleForTesting
583 protected ServiceInfo<V> parseServiceInfo(ResolveInfo service)
Fred Quintana718d8a22009-04-29 17:53:20 -0700584 throws XmlPullParserException, IOException {
585 android.content.pm.ServiceInfo si = service.serviceInfo;
586 ComponentName componentName = new ComponentName(si.packageName, si.name);
587
588 PackageManager pm = mContext.getPackageManager();
589
590 XmlResourceParser parser = null;
591 try {
592 parser = si.loadXmlMetaData(pm, mMetaDataName);
593 if (parser == null) {
594 throw new XmlPullParserException("No " + mMetaDataName + " meta-data");
595 }
596
597 AttributeSet attrs = Xml.asAttributeSet(parser);
598
599 int type;
600 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
601 && type != XmlPullParser.START_TAG) {
602 }
603
604 String nodeName = parser.getName();
605 if (!mAttributesName.equals(nodeName)) {
606 throw new XmlPullParserException(
607 "Meta-data does not start with " + mAttributesName + " tag");
608 }
609
Dianne Hackborn20cb56e2010-03-04 00:58:29 -0800610 V v = parseServiceAttributes(pm.getResourcesForApplication(si.applicationInfo),
611 si.packageName, attrs);
Fred Quintana718d8a22009-04-29 17:53:20 -0700612 if (v == null) {
613 return null;
614 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700615 final android.content.pm.ServiceInfo serviceInfo = service.serviceInfo;
Jeff Sharkey9d8a1042015-12-03 17:56:20 -0700616 return new ServiceInfo<V>(v, serviceInfo, componentName);
Dianne Hackborn20cb56e2010-03-04 00:58:29 -0800617 } catch (NameNotFoundException e) {
618 throw new XmlPullParserException(
619 "Unable to load resources for pacakge " + si.packageName);
Fred Quintana718d8a22009-04-29 17:53:20 -0700620 } finally {
621 if (parser != null) parser.close();
622 }
623 }
624
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800625 /**
626 * Read all sync status back in to the initial engine state.
627 */
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800628 private void readPersistentServicesLocked(InputStream is)
629 throws XmlPullParserException, IOException {
630 XmlPullParser parser = Xml.newPullParser();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100631 parser.setInput(is, StandardCharsets.UTF_8.name());
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800632 int eventType = parser.getEventType();
633 while (eventType != XmlPullParser.START_TAG
634 && eventType != XmlPullParser.END_DOCUMENT) {
635 eventType = parser.next();
636 }
637 String tagName = parser.getName();
638 if ("services".equals(tagName)) {
639 eventType = parser.next();
640 do {
641 if (eventType == XmlPullParser.START_TAG && parser.getDepth() == 2) {
642 tagName = parser.getName();
643 if ("service".equals(tagName)) {
644 V service = mSerializerAndParser.createFromXml(parser);
645 if (service == null) {
646 break;
647 }
648 String uidString = parser.getAttributeValue(null, "uid");
649 final int uid = Integer.parseInt(uidString);
650 final int userId = UserHandle.getUserId(uid);
651 final UserServices<V> user = findOrCreateUserLocked(userId,
652 false /*loadFromFileIfNew*/) ;
653 user.persistentServices.put(service, uid);
654 }
655 }
656 eventType = parser.next();
657 } while (eventType != XmlPullParser.END_DOCUMENT);
658 }
659 }
660
661 private void migrateIfNecessaryLocked() {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800662 if (mSerializerAndParser == null) {
663 return;
664 }
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800665 File systemDir = new File(getDataDirectory(), "system");
666 File syncDir = new File(systemDir, REGISTERED_SERVICES_DIR);
667 AtomicFile oldFile = new AtomicFile(new File(syncDir, mInterfaceName + ".xml"));
668 boolean oldFileExists = oldFile.getBaseFile().exists();
669
670 if (oldFileExists) {
671 File marker = new File(syncDir, mInterfaceName + ".xml.migrated");
672 // if not migrated, perform the migration and add a marker
673 if (!marker.exists()) {
674 if (DEBUG) {
675 Slog.i(TAG, "Marker file " + marker + " does not exist - running migration");
676 }
677 InputStream is = null;
678 try {
679 is = oldFile.openRead();
680 mUserServices.clear();
681 readPersistentServicesLocked(is);
682 } catch (Exception e) {
683 Log.w(TAG, "Error reading persistent services, starting from scratch", e);
684 } finally {
685 IoUtils.closeQuietly(is);
686 }
687 try {
688 for (UserInfo user : getUsers()) {
689 UserServices<V> userServices = mUserServices.get(user.id);
690 if (userServices != null) {
691 if (DEBUG) {
692 Slog.i(TAG, "Migrating u" + user.id + " services "
693 + userServices.persistentServices);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800694 }
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800695 writePersistentServicesLocked(userServices, user.id);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800696 }
697 }
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800698 marker.createNewFile();
699 } catch (Exception e) {
700 Log.w(TAG, "Migration failed", e);
701 }
702 // Migration is complete and we don't need to keep data for all users anymore,
703 // It will be loaded from a new location when requested
704 mUserServices.clear();
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800705 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800706 }
707 }
708
709 /**
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800710 * Writes services of a specified user to the file.
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800711 */
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800712 private void writePersistentServicesLocked(UserServices<V> user, int userId) {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800713 if (mSerializerAndParser == null) {
714 return;
715 }
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800716 AtomicFile atomicFile = createFileForUser(userId);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800717 FileOutputStream fos = null;
718 try {
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800719 fos = atomicFile.startWrite();
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800720 XmlSerializer out = new FastXmlSerializer();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100721 out.setOutput(fos, StandardCharsets.UTF_8.name());
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800722 out.startDocument(null, true);
723 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
724 out.startTag(null, "services");
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800725 for (Map.Entry<V, Integer> service : user.persistentServices.entrySet()) {
726 out.startTag(null, "service");
727 out.attribute(null, "uid", Integer.toString(service.getValue()));
728 mSerializerAndParser.writeAsXml(service.getKey(), out);
729 out.endTag(null, "service");
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800730 }
731 out.endTag(null, "services");
732 out.endDocument();
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800733 atomicFile.finishWrite(fos);
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -0800734 } catch (IOException e1) {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800735 Log.w(TAG, "Error writing accounts", e1);
736 if (fos != null) {
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800737 atomicFile.failWrite(fos);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800738 }
739 }
740 }
741
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -0800742 @VisibleForTesting
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800743 protected void onUserRemoved(int userId) {
Amith Yamasani37a40c22015-06-17 13:25:42 -0700744 synchronized (mServicesLock) {
745 mUserServices.remove(userId);
746 }
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800747 }
748
749 @VisibleForTesting
750 protected List<UserInfo> getUsers() {
751 return UserManager.get(mContext).getUsers(true);
752 }
753
754 @VisibleForTesting
755 protected UserInfo getUser(int userId) {
756 return UserManager.get(mContext).getUserInfo(userId);
757 }
758
759 private AtomicFile createFileForUser(int userId) {
760 File userDir = getUserSystemDirectory(userId);
761 File userFile = new File(userDir, REGISTERED_SERVICES_DIR + "/" + mInterfaceName + ".xml");
762 return new AtomicFile(userFile);
763 }
764
765 @VisibleForTesting
766 protected File getUserSystemDirectory(int userId) {
767 return Environment.getUserSystemDirectory(userId);
768 }
769
770 @VisibleForTesting
771 protected File getDataDirectory() {
772 return Environment.getDataDirectory();
773 }
774
775 @VisibleForTesting
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -0800776 protected Map<V, Integer> getPersistentServices(int userId) {
777 return findOrCreateUserLocked(userId).persistentServices;
778 }
779
Dianne Hackborn20cb56e2010-03-04 00:58:29 -0800780 public abstract V parseServiceAttributes(Resources res,
781 String packageName, AttributeSet attrs);
Fred Quintana718d8a22009-04-29 17:53:20 -0700782}