blob: 5c7bfab7ec20e3efcc75f7e548178a91e5b79d7a [file] [log] [blame]
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001/*
2 * Copyright (C) 2012 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;
18
Victoria Lease03cdd3d2013-02-01 15:15:54 -080019import android.content.BroadcastReceiver;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070020import android.content.ComponentName;
21import android.content.Context;
22import android.content.Intent;
Victoria Lease03cdd3d2013-02-01 15:15:54 -080023import android.content.IntentFilter;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070024import android.content.ServiceConnection;
25import android.content.pm.PackageInfo;
26import android.content.pm.PackageManager;
27import android.content.pm.PackageManager.NameNotFoundException;
28import android.content.pm.ResolveInfo;
29import android.content.pm.Signature;
Zhentao Sunc5fc9982013-04-17 17:47:53 -070030import android.content.res.Resources;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070031import android.os.Handler;
32import android.os.IBinder;
Victoria Leaseb711d572012-10-02 13:14:11 -070033import android.os.UserHandle;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070034import android.util.Log;
35
36import com.android.internal.content.PackageMonitor;
37
38import java.util.ArrayList;
39import java.util.Arrays;
Zhentao Sunc5fc9982013-04-17 17:47:53 -070040import java.util.Collections;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070041import java.util.HashSet;
42import java.util.List;
43
44/**
45 * Find the best Service, and bind to it.
46 * Handles run-time package changes.
47 */
48public class ServiceWatcher implements ServiceConnection {
49 private static final boolean D = false;
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050050 public static final String EXTRA_SERVICE_VERSION = "serviceVersion";
Victoria Lease03cdd3d2013-02-01 15:15:54 -080051 public static final String EXTRA_SERVICE_IS_MULTIUSER = "serviceIsMultiuser";
Nick Pelly6fa9ad42012-07-16 12:18:23 -070052
53 private final String mTag;
54 private final Context mContext;
55 private final PackageManager mPm;
56 private final List<HashSet<Signature>> mSignatureSets;
57 private final String mAction;
Zhentao Sunc5fc9982013-04-17 17:47:53 -070058
59 /**
60 * If mServicePackageName is not null, only this package will be searched for the service that
61 * implements mAction. When null, all packages in the system that matches one of the signature
62 * in mSignatureSets are searched.
63 */
64 private final String mServicePackageName;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070065 private final Runnable mNewServiceWork;
66 private final Handler mHandler;
67
68 private Object mLock = new Object();
69
70 // all fields below synchronized on mLock
71 private IBinder mBinder; // connected service
72 private String mPackageName; // current best package
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050073 private int mVersion = Integer.MIN_VALUE; // current best version
Victoria Lease03cdd3d2013-02-01 15:15:54 -080074 /**
75 * Whether the currently-connected service is multiuser-aware. This can change at run-time
76 * when switching from one version of a service to another.
77 */
78 private boolean mIsMultiuser = false;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070079
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050080 public static ArrayList<HashSet<Signature>> getSignatureSets(Context context,
81 List<String> initialPackageNames) {
82 PackageManager pm = context.getPackageManager();
83 ArrayList<HashSet<Signature>> sigSets = new ArrayList<HashSet<Signature>>();
84 for (int i = 0, size = initialPackageNames.size(); i < size; i++) {
85 String pkg = initialPackageNames.get(i);
86 try {
87 HashSet<Signature> set = new HashSet<Signature>();
88 Signature[] sigs = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES).signatures;
89 set.addAll(Arrays.asList(sigs));
90 sigSets.add(set);
91 } catch (NameNotFoundException e) {
92 Log.w("ServiceWatcher", pkg + " not found");
93 }
94 }
95 return sigSets;
96 }
97
Nick Pelly6fa9ad42012-07-16 12:18:23 -070098 public ServiceWatcher(Context context, String logTag, String action,
Zhentao Sunc5fc9982013-04-17 17:47:53 -070099 int overlaySwitchResId, int defaultServicePackageNameResId,
100 int initialPackageNamesResId, Runnable newServiceWork,
101 Handler handler) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700102 mContext = context;
103 mTag = logTag;
104 mAction = action;
105 mPm = mContext.getPackageManager();
106 mNewServiceWork = newServiceWork;
107 mHandler = handler;
Zhentao Sunc5fc9982013-04-17 17:47:53 -0700108 Resources resources = context.getResources();
109
110 // Whether to enable service overlay.
111 boolean enableOverlay = resources.getBoolean(overlaySwitchResId);
112 ArrayList<String> initialPackageNames = new ArrayList<String>();
113 if (enableOverlay) {
114 // A list of package names used to create the signatures.
115 String[] pkgs = resources.getStringArray(initialPackageNamesResId);
116 if (pkgs != null) initialPackageNames.addAll(Arrays.asList(pkgs));
117 mServicePackageName = null;
118 if (D) Log.d(mTag, "Overlay enabled, packages=" + Arrays.toString(pkgs));
119 } else {
120 // The default package name that is searched for service implementation when overlay is
121 // disabled.
122 String servicePackageName = resources.getString(defaultServicePackageNameResId);
123 if (servicePackageName != null) initialPackageNames.add(servicePackageName);
124 mServicePackageName = servicePackageName;
125 if (D) Log.d(mTag, "Overlay disabled, default package=" + servicePackageName);
126 }
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500127 mSignatureSets = getSignatureSets(context, initialPackageNames);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700128 }
129
130 public boolean start() {
Victoria Leaseb711d572012-10-02 13:14:11 -0700131 synchronized (mLock) {
Zhentao Sunc5fc9982013-04-17 17:47:53 -0700132 if (!bindBestPackageLocked(mServicePackageName)) return false;
Victoria Leaseb711d572012-10-02 13:14:11 -0700133 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700134
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800135 // listen for user change
136 IntentFilter intentFilter = new IntentFilter();
137 intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
138 mContext.registerReceiverAsUser(new BroadcastReceiver() {
139 @Override
140 public void onReceive(Context context, Intent intent) {
141 String action = intent.getAction();
142 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
143 switchUser();
144 }
145 }
146 }, UserHandle.ALL, intentFilter, null, mHandler);
147
Zhentao Sunc5fc9982013-04-17 17:47:53 -0700148 // listen for relevant package changes if service overlay is enabled.
149 if (mServicePackageName == null) {
150 mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
151 }
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800152
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700153 return true;
154 }
155
156 /**
157 * Searches and binds to the best package, or do nothing
158 * if the best package is already bound.
159 * Only checks the named package, or checks all packages if it
160 * is null.
161 * Return true if a new package was found to bind to.
162 */
Victoria Leaseb711d572012-10-02 13:14:11 -0700163 private boolean bindBestPackageLocked(String justCheckThisPackage) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700164 Intent intent = new Intent(mAction);
165 if (justCheckThisPackage != null) {
166 intent.setPackage(justCheckThisPackage);
167 }
Zhentao Sunc5fc9982013-04-17 17:47:53 -0700168 List<ResolveInfo> rInfos = mPm.queryIntentServicesAsUser(intent,
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800169 PackageManager.GET_META_DATA, UserHandle.USER_OWNER);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700170 int bestVersion = Integer.MIN_VALUE;
171 String bestPackage = null;
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800172 boolean bestIsMultiuser = false;
Zhentao Sunc5fc9982013-04-17 17:47:53 -0700173 if (rInfos != null) {
174 for (ResolveInfo rInfo : rInfos) {
175 String packageName = rInfo.serviceInfo.packageName;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700176
Zhentao Sunc5fc9982013-04-17 17:47:53 -0700177 // check signature
178 try {
179 PackageInfo pInfo;
180 pInfo = mPm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
181 if (!isSignatureMatch(pInfo.signatures)) {
182 Log.w(mTag, packageName + " resolves service " + mAction
183 + ", but has wrong signature, ignoring");
184 continue;
185 }
186 } catch (NameNotFoundException e) {
187 Log.wtf(mTag, e);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700188 continue;
189 }
Zhentao Sunc5fc9982013-04-17 17:47:53 -0700190
191 // check metadata
192 int version = Integer.MIN_VALUE;
193 boolean isMultiuser = false;
194 if (rInfo.serviceInfo.metaData != null) {
195 version = rInfo.serviceInfo.metaData.getInt(
196 EXTRA_SERVICE_VERSION, Integer.MIN_VALUE);
197 isMultiuser = rInfo.serviceInfo.metaData.getBoolean(EXTRA_SERVICE_IS_MULTIUSER);
198 }
199
200 if (version > mVersion) {
201 bestVersion = version;
202 bestPackage = packageName;
203 bestIsMultiuser = isMultiuser;
204 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700205 }
206
Zhentao Sunc5fc9982013-04-17 17:47:53 -0700207 if (D) {
208 Log.d(mTag, String.format("bindBestPackage for %s : %s found %d, %s", mAction,
209 (justCheckThisPackage == null ? ""
210 : "(" + justCheckThisPackage + ") "), rInfos.size(),
211 (bestPackage == null ? "no new best package"
212 : "new best package: " + bestPackage)));
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700213 }
Zhentao Sunc5fc9982013-04-17 17:47:53 -0700214 } else {
215 if (D) Log.d(mTag, "Unable to query intent services for action: " + mAction);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700216 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700217 if (bestPackage != null) {
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800218 bindToPackageLocked(bestPackage, bestVersion, bestIsMultiuser);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700219 return true;
220 }
221 return false;
222 }
223
Victoria Leaseb711d572012-10-02 13:14:11 -0700224 private void unbindLocked() {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700225 String pkg;
Victoria Leaseb711d572012-10-02 13:14:11 -0700226 pkg = mPackageName;
227 mPackageName = null;
228 mVersion = Integer.MIN_VALUE;
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800229 mIsMultiuser = false;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700230 if (pkg != null) {
231 if (D) Log.d(mTag, "unbinding " + pkg);
232 mContext.unbindService(this);
233 }
234 }
235
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800236 private void bindToPackageLocked(String packageName, int version, boolean isMultiuser) {
Victoria Leaseb711d572012-10-02 13:14:11 -0700237 unbindLocked();
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700238 Intent intent = new Intent(mAction);
239 intent.setPackage(packageName);
Victoria Leaseb711d572012-10-02 13:14:11 -0700240 mPackageName = packageName;
241 mVersion = version;
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800242 mIsMultiuser = isMultiuser;
243 if (D) Log.d(mTag, "binding " + packageName + " (version " + version + ") ("
244 + (isMultiuser ? "multi" : "single") + "-user)");
Amith Yamasani27b89e62013-01-16 12:30:11 -0800245 mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800246 | Context.BIND_NOT_VISIBLE, mIsMultiuser ? UserHandle.OWNER : UserHandle.CURRENT);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700247 }
248
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500249 public static boolean isSignatureMatch(Signature[] signatures,
250 List<HashSet<Signature>> sigSets) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700251 if (signatures == null) return false;
252
253 // build hashset of input to test against
254 HashSet<Signature> inputSet = new HashSet<Signature>();
255 for (Signature s : signatures) {
256 inputSet.add(s);
257 }
258
259 // test input against each of the signature sets
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500260 for (HashSet<Signature> referenceSet : sigSets) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700261 if (referenceSet.equals(inputSet)) {
262 return true;
263 }
264 }
265 return false;
266 }
267
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500268 private boolean isSignatureMatch(Signature[] signatures) {
269 return isSignatureMatch(signatures, mSignatureSets);
270 }
271
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700272 private final PackageMonitor mPackageMonitor = new PackageMonitor() {
273 /**
274 * Called when package has been reinstalled
275 */
276 @Override
277 public void onPackageUpdateFinished(String packageName, int uid) {
Victoria Leaseb711d572012-10-02 13:14:11 -0700278 synchronized (mLock) {
279 if (packageName.equals(mPackageName)) {
280 // package updated, make sure to rebind
281 unbindLocked();
282 }
Zhentao Sunc5fc9982013-04-17 17:47:53 -0700283 // Need to check all packages because this method is also called when a
284 // system app is uninstalled and the stock version in reinstalled.
285 bindBestPackageLocked(null);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700286 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700287 }
288
289 @Override
290 public void onPackageAdded(String packageName, int uid) {
Victoria Leaseb711d572012-10-02 13:14:11 -0700291 synchronized (mLock) {
292 if (packageName.equals(mPackageName)) {
293 // package updated, make sure to rebind
294 unbindLocked();
295 }
296 // check the new package is case it is better
Zhentao Sunc5fc9982013-04-17 17:47:53 -0700297 bindBestPackageLocked(null);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700298 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700299 }
300
301 @Override
302 public void onPackageRemoved(String packageName, int uid) {
Victoria Leaseb711d572012-10-02 13:14:11 -0700303 synchronized (mLock) {
304 if (packageName.equals(mPackageName)) {
305 unbindLocked();
306 // the currently bound package was removed,
307 // need to search for a new package
308 bindBestPackageLocked(null);
309 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700310 }
311 }
Zhentao Sunc5fc9982013-04-17 17:47:53 -0700312
313 @Override
314 public boolean onPackageChanged(String packageName, int uid, String[] components) {
315 synchronized (mLock) {
316 if (packageName.equals(mPackageName)) {
317 // service enabled or disabled, make sure to rebind
318 unbindLocked();
319 }
320 // the service might be disabled, need to search for a new
321 // package
322 bindBestPackageLocked(null);
323 }
324 return super.onPackageChanged(packageName, uid, components);
325 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700326 };
327
328 @Override
329 public void onServiceConnected(ComponentName name, IBinder binder) {
330 synchronized (mLock) {
331 String packageName = name.getPackageName();
332 if (packageName.equals(mPackageName)) {
333 if (D) Log.d(mTag, packageName + " connected");
334 mBinder = binder;
335 if (mHandler !=null && mNewServiceWork != null) {
336 mHandler.post(mNewServiceWork);
337 }
338 } else {
339 Log.w(mTag, "unexpected onServiceConnected: " + packageName);
340 }
341 }
342 }
343
344 @Override
345 public void onServiceDisconnected(ComponentName name) {
346 synchronized (mLock) {
347 String packageName = name.getPackageName();
348 if (D) Log.d(mTag, packageName + " disconnected");
349
350 if (packageName.equals(mPackageName)) {
351 mBinder = null;
352 }
353 }
354 }
355
356 public String getBestPackageName() {
357 synchronized (mLock) {
358 return mPackageName;
359 }
360 }
361
362 public int getBestVersion() {
363 synchronized (mLock) {
364 return mVersion;
365 }
366 }
367
368 public IBinder getBinder() {
369 synchronized (mLock) {
370 return mBinder;
371 }
372 }
Victoria Leaseb711d572012-10-02 13:14:11 -0700373
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800374 public void switchUser() {
Victoria Leaseb711d572012-10-02 13:14:11 -0700375 synchronized (mLock) {
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800376 if (!mIsMultiuser) {
377 unbindLocked();
Zhentao Sunc5fc9982013-04-17 17:47:53 -0700378 bindBestPackageLocked(mServicePackageName);
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800379 }
Victoria Leaseb711d572012-10-02 13:14:11 -0700380 }
381 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700382}