blob: 927429506d3d1ab7ec7476827de4665be7c2a17c [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;
40import java.util.HashSet;
41import java.util.List;
42
43/**
44 * Find the best Service, and bind to it.
45 * Handles run-time package changes.
46 */
47public class ServiceWatcher implements ServiceConnection {
48 private static final boolean D = false;
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050049 public static final String EXTRA_SERVICE_VERSION = "serviceVersion";
Victoria Lease03cdd3d2013-02-01 15:15:54 -080050 public static final String EXTRA_SERVICE_IS_MULTIUSER = "serviceIsMultiuser";
Nick Pelly6fa9ad42012-07-16 12:18:23 -070051
52 private final String mTag;
53 private final Context mContext;
54 private final PackageManager mPm;
55 private final List<HashSet<Signature>> mSignatureSets;
56 private final String mAction;
Zhentao Sunc5fc9982013-04-17 17:47:53 -070057
58 /**
59 * If mServicePackageName is not null, only this package will be searched for the service that
60 * implements mAction. When null, all packages in the system that matches one of the signature
61 * in mSignatureSets are searched.
62 */
63 private final String mServicePackageName;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070064 private final Runnable mNewServiceWork;
65 private final Handler mHandler;
66
67 private Object mLock = new Object();
68
69 // all fields below synchronized on mLock
70 private IBinder mBinder; // connected service
71 private String mPackageName; // current best package
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050072 private int mVersion = Integer.MIN_VALUE; // current best version
Victoria Lease03cdd3d2013-02-01 15:15:54 -080073 /**
74 * Whether the currently-connected service is multiuser-aware. This can change at run-time
75 * when switching from one version of a service to another.
76 */
77 private boolean mIsMultiuser = false;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070078
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050079 public static ArrayList<HashSet<Signature>> getSignatureSets(Context context,
80 List<String> initialPackageNames) {
81 PackageManager pm = context.getPackageManager();
82 ArrayList<HashSet<Signature>> sigSets = new ArrayList<HashSet<Signature>>();
83 for (int i = 0, size = initialPackageNames.size(); i < size; i++) {
84 String pkg = initialPackageNames.get(i);
85 try {
86 HashSet<Signature> set = new HashSet<Signature>();
87 Signature[] sigs = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES).signatures;
88 set.addAll(Arrays.asList(sigs));
89 sigSets.add(set);
90 } catch (NameNotFoundException e) {
91 Log.w("ServiceWatcher", pkg + " not found");
92 }
93 }
94 return sigSets;
95 }
96
Nick Pelly6fa9ad42012-07-16 12:18:23 -070097 public ServiceWatcher(Context context, String logTag, String action,
Zhentao Sunc5fc9982013-04-17 17:47:53 -070098 int overlaySwitchResId, int defaultServicePackageNameResId,
99 int initialPackageNamesResId, Runnable newServiceWork,
100 Handler handler) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700101 mContext = context;
102 mTag = logTag;
103 mAction = action;
104 mPm = mContext.getPackageManager();
105 mNewServiceWork = newServiceWork;
106 mHandler = handler;
Zhentao Sunc5fc9982013-04-17 17:47:53 -0700107 Resources resources = context.getResources();
108
109 // Whether to enable service overlay.
110 boolean enableOverlay = resources.getBoolean(overlaySwitchResId);
111 ArrayList<String> initialPackageNames = new ArrayList<String>();
112 if (enableOverlay) {
113 // A list of package names used to create the signatures.
114 String[] pkgs = resources.getStringArray(initialPackageNamesResId);
115 if (pkgs != null) initialPackageNames.addAll(Arrays.asList(pkgs));
116 mServicePackageName = null;
117 if (D) Log.d(mTag, "Overlay enabled, packages=" + Arrays.toString(pkgs));
118 } else {
119 // The default package name that is searched for service implementation when overlay is
120 // disabled.
121 String servicePackageName = resources.getString(defaultServicePackageNameResId);
122 if (servicePackageName != null) initialPackageNames.add(servicePackageName);
123 mServicePackageName = servicePackageName;
124 if (D) Log.d(mTag, "Overlay disabled, default package=" + servicePackageName);
125 }
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500126 mSignatureSets = getSignatureSets(context, initialPackageNames);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700127 }
128
129 public boolean start() {
Victoria Leaseb711d572012-10-02 13:14:11 -0700130 synchronized (mLock) {
Zhentao Sunc5fc9982013-04-17 17:47:53 -0700131 if (!bindBestPackageLocked(mServicePackageName)) return false;
Victoria Leaseb711d572012-10-02 13:14:11 -0700132 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700133
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800134 // listen for user change
135 IntentFilter intentFilter = new IntentFilter();
136 intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
137 mContext.registerReceiverAsUser(new BroadcastReceiver() {
138 @Override
139 public void onReceive(Context context, Intent intent) {
140 String action = intent.getAction();
141 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
142 switchUser();
143 }
144 }
145 }, UserHandle.ALL, intentFilter, null, mHandler);
146
Zhentao Sunc5fc9982013-04-17 17:47:53 -0700147 // listen for relevant package changes if service overlay is enabled.
148 if (mServicePackageName == null) {
149 mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
150 }
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800151
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700152 return true;
153 }
154
155 /**
156 * Searches and binds to the best package, or do nothing
157 * if the best package is already bound.
158 * Only checks the named package, or checks all packages if it
159 * is null.
160 * Return true if a new package was found to bind to.
161 */
Victoria Leaseb711d572012-10-02 13:14:11 -0700162 private boolean bindBestPackageLocked(String justCheckThisPackage) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700163 Intent intent = new Intent(mAction);
164 if (justCheckThisPackage != null) {
165 intent.setPackage(justCheckThisPackage);
166 }
Zhentao Sunc5fc9982013-04-17 17:47:53 -0700167 List<ResolveInfo> rInfos = mPm.queryIntentServicesAsUser(intent,
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800168 PackageManager.GET_META_DATA, UserHandle.USER_OWNER);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700169 int bestVersion = Integer.MIN_VALUE;
170 String bestPackage = null;
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800171 boolean bestIsMultiuser = false;
Zhentao Sunc5fc9982013-04-17 17:47:53 -0700172 if (rInfos != null) {
173 for (ResolveInfo rInfo : rInfos) {
174 String packageName = rInfo.serviceInfo.packageName;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700175
Zhentao Sunc5fc9982013-04-17 17:47:53 -0700176 // check signature
177 try {
178 PackageInfo pInfo;
179 pInfo = mPm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
180 if (!isSignatureMatch(pInfo.signatures)) {
181 Log.w(mTag, packageName + " resolves service " + mAction
182 + ", but has wrong signature, ignoring");
183 continue;
184 }
185 } catch (NameNotFoundException e) {
186 Log.wtf(mTag, e);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700187 continue;
188 }
Zhentao Sunc5fc9982013-04-17 17:47:53 -0700189
190 // check metadata
191 int version = Integer.MIN_VALUE;
192 boolean isMultiuser = false;
193 if (rInfo.serviceInfo.metaData != null) {
194 version = rInfo.serviceInfo.metaData.getInt(
195 EXTRA_SERVICE_VERSION, Integer.MIN_VALUE);
196 isMultiuser = rInfo.serviceInfo.metaData.getBoolean(EXTRA_SERVICE_IS_MULTIUSER);
197 }
198
199 if (version > mVersion) {
200 bestVersion = version;
201 bestPackage = packageName;
202 bestIsMultiuser = isMultiuser;
203 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700204 }
205
Zhentao Sunc5fc9982013-04-17 17:47:53 -0700206 if (D) {
207 Log.d(mTag, String.format("bindBestPackage for %s : %s found %d, %s", mAction,
208 (justCheckThisPackage == null ? ""
209 : "(" + justCheckThisPackage + ") "), rInfos.size(),
210 (bestPackage == null ? "no new best package"
211 : "new best package: " + bestPackage)));
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700212 }
Zhentao Sunc5fc9982013-04-17 17:47:53 -0700213 } else {
214 if (D) Log.d(mTag, "Unable to query intent services for action: " + mAction);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700215 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700216 if (bestPackage != null) {
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800217 bindToPackageLocked(bestPackage, bestVersion, bestIsMultiuser);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700218 return true;
219 }
220 return false;
221 }
222
Victoria Leaseb711d572012-10-02 13:14:11 -0700223 private void unbindLocked() {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700224 String pkg;
Victoria Leaseb711d572012-10-02 13:14:11 -0700225 pkg = mPackageName;
226 mPackageName = null;
227 mVersion = Integer.MIN_VALUE;
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800228 mIsMultiuser = false;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700229 if (pkg != null) {
230 if (D) Log.d(mTag, "unbinding " + pkg);
231 mContext.unbindService(this);
232 }
233 }
234
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800235 private void bindToPackageLocked(String packageName, int version, boolean isMultiuser) {
Victoria Leaseb711d572012-10-02 13:14:11 -0700236 unbindLocked();
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700237 Intent intent = new Intent(mAction);
238 intent.setPackage(packageName);
Victoria Leaseb711d572012-10-02 13:14:11 -0700239 mPackageName = packageName;
240 mVersion = version;
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800241 mIsMultiuser = isMultiuser;
242 if (D) Log.d(mTag, "binding " + packageName + " (version " + version + ") ("
243 + (isMultiuser ? "multi" : "single") + "-user)");
Amith Yamasani27b89e62013-01-16 12:30:11 -0800244 mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800245 | Context.BIND_NOT_VISIBLE, mIsMultiuser ? UserHandle.OWNER : UserHandle.CURRENT);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700246 }
247
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500248 public static boolean isSignatureMatch(Signature[] signatures,
249 List<HashSet<Signature>> sigSets) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700250 if (signatures == null) return false;
251
252 // build hashset of input to test against
253 HashSet<Signature> inputSet = new HashSet<Signature>();
254 for (Signature s : signatures) {
255 inputSet.add(s);
256 }
257
258 // test input against each of the signature sets
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500259 for (HashSet<Signature> referenceSet : sigSets) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700260 if (referenceSet.equals(inputSet)) {
261 return true;
262 }
263 }
264 return false;
265 }
266
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500267 private boolean isSignatureMatch(Signature[] signatures) {
268 return isSignatureMatch(signatures, mSignatureSets);
269 }
270
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700271 private final PackageMonitor mPackageMonitor = new PackageMonitor() {
272 /**
273 * Called when package has been reinstalled
274 */
275 @Override
276 public void onPackageUpdateFinished(String packageName, int uid) {
Victoria Leaseb711d572012-10-02 13:14:11 -0700277 synchronized (mLock) {
278 if (packageName.equals(mPackageName)) {
279 // package updated, make sure to rebind
280 unbindLocked();
281 }
Zhentao Sunc5fc9982013-04-17 17:47:53 -0700282 // Need to check all packages because this method is also called when a
283 // system app is uninstalled and the stock version in reinstalled.
284 bindBestPackageLocked(null);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700285 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700286 }
287
288 @Override
289 public void onPackageAdded(String packageName, int uid) {
Victoria Leaseb711d572012-10-02 13:14:11 -0700290 synchronized (mLock) {
291 if (packageName.equals(mPackageName)) {
292 // package updated, make sure to rebind
293 unbindLocked();
294 }
295 // check the new package is case it is better
Zhentao Sunc5fc9982013-04-17 17:47:53 -0700296 bindBestPackageLocked(null);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700297 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700298 }
299
300 @Override
301 public void onPackageRemoved(String packageName, int uid) {
Victoria Leaseb711d572012-10-02 13:14:11 -0700302 synchronized (mLock) {
303 if (packageName.equals(mPackageName)) {
304 unbindLocked();
305 // the currently bound package was removed,
306 // need to search for a new package
307 bindBestPackageLocked(null);
308 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700309 }
310 }
Zhentao Sunc5fc9982013-04-17 17:47:53 -0700311
312 @Override
313 public boolean onPackageChanged(String packageName, int uid, String[] components) {
314 synchronized (mLock) {
315 if (packageName.equals(mPackageName)) {
316 // service enabled or disabled, make sure to rebind
317 unbindLocked();
318 }
319 // the service might be disabled, need to search for a new
320 // package
321 bindBestPackageLocked(null);
322 }
323 return super.onPackageChanged(packageName, uid, components);
324 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700325 };
326
327 @Override
328 public void onServiceConnected(ComponentName name, IBinder binder) {
329 synchronized (mLock) {
330 String packageName = name.getPackageName();
331 if (packageName.equals(mPackageName)) {
332 if (D) Log.d(mTag, packageName + " connected");
333 mBinder = binder;
334 if (mHandler !=null && mNewServiceWork != null) {
335 mHandler.post(mNewServiceWork);
336 }
337 } else {
338 Log.w(mTag, "unexpected onServiceConnected: " + packageName);
339 }
340 }
341 }
342
343 @Override
344 public void onServiceDisconnected(ComponentName name) {
345 synchronized (mLock) {
346 String packageName = name.getPackageName();
347 if (D) Log.d(mTag, packageName + " disconnected");
348
349 if (packageName.equals(mPackageName)) {
350 mBinder = null;
351 }
352 }
353 }
354
355 public String getBestPackageName() {
356 synchronized (mLock) {
357 return mPackageName;
358 }
359 }
360
361 public int getBestVersion() {
362 synchronized (mLock) {
363 return mVersion;
364 }
365 }
366
367 public IBinder getBinder() {
368 synchronized (mLock) {
369 return mBinder;
370 }
371 }
Victoria Leaseb711d572012-10-02 13:14:11 -0700372
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800373 public void switchUser() {
Victoria Leaseb711d572012-10-02 13:14:11 -0700374 synchronized (mLock) {
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800375 if (!mIsMultiuser) {
376 unbindLocked();
Zhentao Sunc5fc9982013-04-17 17:47:53 -0700377 bindBestPackageLocked(mServicePackageName);
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800378 }
Victoria Leaseb711d572012-10-02 13:14:11 -0700379 }
380 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700381}