2 * Copyright (C) 2012 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.server;
19 import android.content.ComponentName;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.ServiceConnection;
23 import android.content.pm.PackageInfo;
24 import android.content.pm.PackageManager;
25 import android.content.pm.PackageManager.NameNotFoundException;
26 import android.content.pm.ResolveInfo;
27 import android.content.pm.Signature;
28 import android.os.Handler;
29 import android.os.IBinder;
30 import android.os.UserHandle;
31 import android.util.Log;
33 import com.android.internal.content.PackageMonitor;
35 import java.util.ArrayList;
36 import java.util.Arrays;
37 import java.util.HashSet;
38 import java.util.List;
41 * Find the best Service, and bind to it.
42 * Handles run-time package changes.
44 public class ServiceWatcher implements ServiceConnection {
45 private static final boolean D = false;
46 public static final String EXTRA_SERVICE_VERSION = "serviceVersion";
48 private final String mTag;
49 private final Context mContext;
50 private final PackageManager mPm;
51 private final List<HashSet<Signature>> mSignatureSets;
52 private final String mAction;
53 private final Runnable mNewServiceWork;
54 private final Handler mHandler;
56 private Object mLock = new Object();
58 // all fields below synchronized on mLock
59 private IBinder mBinder; // connected service
60 private String mPackageName; // current best package
61 private int mVersion = Integer.MIN_VALUE; // current best version
62 private int mCurrentUserId;
64 public static ArrayList<HashSet<Signature>> getSignatureSets(Context context,
65 List<String> initialPackageNames) {
66 PackageManager pm = context.getPackageManager();
67 ArrayList<HashSet<Signature>> sigSets = new ArrayList<HashSet<Signature>>();
68 for (int i = 0, size = initialPackageNames.size(); i < size; i++) {
69 String pkg = initialPackageNames.get(i);
71 HashSet<Signature> set = new HashSet<Signature>();
72 Signature[] sigs = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES).signatures;
73 set.addAll(Arrays.asList(sigs));
75 } catch (NameNotFoundException e) {
76 Log.w("ServiceWatcher", pkg + " not found");
82 public ServiceWatcher(Context context, String logTag, String action,
83 List<String> initialPackageNames, Runnable newServiceWork, Handler handler, int userId) {
87 mPm = mContext.getPackageManager();
88 mNewServiceWork = newServiceWork;
90 mCurrentUserId = userId;
92 mSignatureSets = getSignatureSets(context, initialPackageNames);
95 public boolean start() {
96 synchronized (mLock) {
97 if (!bindBestPackageLocked(null)) return false;
100 mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
105 * Searches and binds to the best package, or do nothing
106 * if the best package is already bound.
107 * Only checks the named package, or checks all packages if it
109 * Return true if a new package was found to bind to.
111 private boolean bindBestPackageLocked(String justCheckThisPackage) {
112 Intent intent = new Intent(mAction);
113 if (justCheckThisPackage != null) {
114 intent.setPackage(justCheckThisPackage);
116 List<ResolveInfo> rInfos = mPm.queryIntentServicesAsUser(new Intent(mAction),
117 PackageManager.GET_META_DATA, mCurrentUserId);
118 int bestVersion = Integer.MIN_VALUE;
119 String bestPackage = null;
120 for (ResolveInfo rInfo : rInfos) {
121 String packageName = rInfo.serviceInfo.packageName;
126 pInfo = mPm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
127 if (!isSignatureMatch(pInfo.signatures)) {
128 Log.w(mTag, packageName + " resolves service " + mAction +
129 ", but has wrong signature, ignoring");
132 } catch (NameNotFoundException e) {
139 if (rInfo.serviceInfo.metaData != null) {
140 version = rInfo.serviceInfo.metaData.getInt(EXTRA_SERVICE_VERSION, 0);
143 if (version > mVersion) {
144 bestVersion = version;
145 bestPackage = packageName;
149 if (D) Log.d(mTag, String.format("bindBestPackage for %s : %s found %d, %s", mAction,
150 (justCheckThisPackage == null ? "" : "(" + justCheckThisPackage + ") "),
152 (bestPackage == null ? "no new best package" : "new best packge: " + bestPackage)));
154 if (bestPackage != null) {
155 bindToPackageLocked(bestPackage, bestVersion);
161 private void unbindLocked() {
165 mVersion = Integer.MIN_VALUE;
167 if (D) Log.d(mTag, "unbinding " + pkg);
168 mContext.unbindService(this);
172 private void bindToPackageLocked(String packageName, int version) {
174 Intent intent = new Intent(mAction);
175 intent.setPackage(packageName);
176 mPackageName = packageName;
178 if (D) Log.d(mTag, "binding " + packageName + " (version " + version + ")");
179 mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
180 | Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_NOT_VISIBLE,
181 new UserHandle(mCurrentUserId));
184 public static boolean isSignatureMatch(Signature[] signatures,
185 List<HashSet<Signature>> sigSets) {
186 if (signatures == null) return false;
188 // build hashset of input to test against
189 HashSet<Signature> inputSet = new HashSet<Signature>();
190 for (Signature s : signatures) {
194 // test input against each of the signature sets
195 for (HashSet<Signature> referenceSet : sigSets) {
196 if (referenceSet.equals(inputSet)) {
203 private boolean isSignatureMatch(Signature[] signatures) {
204 return isSignatureMatch(signatures, mSignatureSets);
207 private final PackageMonitor mPackageMonitor = new PackageMonitor() {
209 * Called when package has been reinstalled
212 public void onPackageUpdateFinished(String packageName, int uid) {
213 synchronized (mLock) {
214 if (packageName.equals(mPackageName)) {
215 // package updated, make sure to rebind
218 // check the updated package in case it is better
219 bindBestPackageLocked(packageName);
224 public void onPackageAdded(String packageName, int uid) {
225 synchronized (mLock) {
226 if (packageName.equals(mPackageName)) {
227 // package updated, make sure to rebind
230 // check the new package is case it is better
231 bindBestPackageLocked(packageName);
236 public void onPackageRemoved(String packageName, int uid) {
237 synchronized (mLock) {
238 if (packageName.equals(mPackageName)) {
240 // the currently bound package was removed,
241 // need to search for a new package
242 bindBestPackageLocked(null);
249 public void onServiceConnected(ComponentName name, IBinder binder) {
250 synchronized (mLock) {
251 String packageName = name.getPackageName();
252 if (packageName.equals(mPackageName)) {
253 if (D) Log.d(mTag, packageName + " connected");
255 if (mHandler !=null && mNewServiceWork != null) {
256 mHandler.post(mNewServiceWork);
259 Log.w(mTag, "unexpected onServiceConnected: " + packageName);
265 public void onServiceDisconnected(ComponentName name) {
266 synchronized (mLock) {
267 String packageName = name.getPackageName();
268 if (D) Log.d(mTag, packageName + " disconnected");
270 if (packageName.equals(mPackageName)) {
276 public String getBestPackageName() {
277 synchronized (mLock) {
282 public int getBestVersion() {
283 synchronized (mLock) {
288 public IBinder getBinder() {
289 synchronized (mLock) {
294 public void switchUser(int userId) {
295 synchronized (mLock) {
297 mCurrentUserId = userId;
298 bindBestPackageLocked(null);