blob: 4a428ae58dc3f184045bc7cd0f5521b7b951bf9e [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
19import android.content.ComponentName;
20import android.content.Context;
21import android.content.Intent;
22import android.content.ServiceConnection;
23import android.content.pm.PackageInfo;
24import android.content.pm.PackageManager;
25import android.content.pm.PackageManager.NameNotFoundException;
26import android.content.pm.ResolveInfo;
27import android.content.pm.Signature;
28import android.os.Handler;
29import android.os.IBinder;
Victoria Leaseb711d572012-10-02 13:14:11 -070030import android.os.UserHandle;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070031import android.util.Log;
32
33import com.android.internal.content.PackageMonitor;
34
35import java.util.ArrayList;
36import java.util.Arrays;
37import java.util.HashSet;
38import java.util.List;
39
40/**
41 * Find the best Service, and bind to it.
42 * Handles run-time package changes.
43 */
44public class ServiceWatcher implements ServiceConnection {
45 private static final boolean D = false;
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050046 public static final String EXTRA_SERVICE_VERSION = "serviceVersion";
Nick Pelly6fa9ad42012-07-16 12:18:23 -070047
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;
55
56 private Object mLock = new Object();
57
58 // all fields below synchronized on mLock
59 private IBinder mBinder; // connected service
60 private String mPackageName; // current best package
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050061 private int mVersion = Integer.MIN_VALUE; // current best version
Victoria Leaseb711d572012-10-02 13:14:11 -070062 private int mCurrentUserId;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070063
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050064 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);
70 try {
71 HashSet<Signature> set = new HashSet<Signature>();
72 Signature[] sigs = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES).signatures;
73 set.addAll(Arrays.asList(sigs));
74 sigSets.add(set);
75 } catch (NameNotFoundException e) {
76 Log.w("ServiceWatcher", pkg + " not found");
77 }
78 }
79 return sigSets;
80 }
81
Nick Pelly6fa9ad42012-07-16 12:18:23 -070082 public ServiceWatcher(Context context, String logTag, String action,
Victoria Leaseb711d572012-10-02 13:14:11 -070083 List<String> initialPackageNames, Runnable newServiceWork, Handler handler, int userId) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -070084 mContext = context;
85 mTag = logTag;
86 mAction = action;
87 mPm = mContext.getPackageManager();
88 mNewServiceWork = newServiceWork;
89 mHandler = handler;
Victoria Leaseb711d572012-10-02 13:14:11 -070090 mCurrentUserId = userId;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070091
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050092 mSignatureSets = getSignatureSets(context, initialPackageNames);
Nick Pelly6fa9ad42012-07-16 12:18:23 -070093 }
94
95 public boolean start() {
Victoria Leaseb711d572012-10-02 13:14:11 -070096 synchronized (mLock) {
97 if (!bindBestPackageLocked(null)) return false;
98 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -070099
Victoria Leaseb711d572012-10-02 13:14:11 -0700100 mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700101 return true;
102 }
103
104 /**
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
108 * is null.
109 * Return true if a new package was found to bind to.
110 */
Victoria Leaseb711d572012-10-02 13:14:11 -0700111 private boolean bindBestPackageLocked(String justCheckThisPackage) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700112 Intent intent = new Intent(mAction);
113 if (justCheckThisPackage != null) {
114 intent.setPackage(justCheckThisPackage);
115 }
Victoria Leaseb711d572012-10-02 13:14:11 -0700116 List<ResolveInfo> rInfos = mPm.queryIntentServicesAsUser(new Intent(mAction),
117 PackageManager.GET_META_DATA, mCurrentUserId);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700118 int bestVersion = Integer.MIN_VALUE;
119 String bestPackage = null;
120 for (ResolveInfo rInfo : rInfos) {
121 String packageName = rInfo.serviceInfo.packageName;
122
123 // check signature
124 try {
125 PackageInfo pInfo;
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");
130 continue;
131 }
132 } catch (NameNotFoundException e) {
133 Log.wtf(mTag, e);
134 continue;
135 }
136
137 // check version
138 int version = 0;
139 if (rInfo.serviceInfo.metaData != null) {
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500140 version = rInfo.serviceInfo.metaData.getInt(EXTRA_SERVICE_VERSION, 0);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700141 }
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500142
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700143 if (version > mVersion) {
144 bestVersion = version;
145 bestPackage = packageName;
146 }
147 }
148
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500149 if (D) Log.d(mTag, String.format("bindBestPackage for %s : %s found %d, %s", mAction,
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700150 (justCheckThisPackage == null ? "" : "(" + justCheckThisPackage + ") "),
151 rInfos.size(),
152 (bestPackage == null ? "no new best package" : "new best packge: " + bestPackage)));
153
154 if (bestPackage != null) {
Victoria Leaseb711d572012-10-02 13:14:11 -0700155 bindToPackageLocked(bestPackage, bestVersion);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700156 return true;
157 }
158 return false;
159 }
160
Victoria Leaseb711d572012-10-02 13:14:11 -0700161 private void unbindLocked() {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700162 String pkg;
Victoria Leaseb711d572012-10-02 13:14:11 -0700163 pkg = mPackageName;
164 mPackageName = null;
165 mVersion = Integer.MIN_VALUE;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700166 if (pkg != null) {
167 if (D) Log.d(mTag, "unbinding " + pkg);
168 mContext.unbindService(this);
169 }
170 }
171
Victoria Leaseb711d572012-10-02 13:14:11 -0700172 private void bindToPackageLocked(String packageName, int version) {
173 unbindLocked();
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700174 Intent intent = new Intent(mAction);
175 intent.setPackage(packageName);
Victoria Leaseb711d572012-10-02 13:14:11 -0700176 mPackageName = packageName;
177 mVersion = version;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700178 if (D) Log.d(mTag, "binding " + packageName + " (version " + version + ")");
Amith Yamasani27b89e62013-01-16 12:30:11 -0800179 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));
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700182 }
183
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500184 public static boolean isSignatureMatch(Signature[] signatures,
185 List<HashSet<Signature>> sigSets) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700186 if (signatures == null) return false;
187
188 // build hashset of input to test against
189 HashSet<Signature> inputSet = new HashSet<Signature>();
190 for (Signature s : signatures) {
191 inputSet.add(s);
192 }
193
194 // test input against each of the signature sets
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500195 for (HashSet<Signature> referenceSet : sigSets) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700196 if (referenceSet.equals(inputSet)) {
197 return true;
198 }
199 }
200 return false;
201 }
202
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500203 private boolean isSignatureMatch(Signature[] signatures) {
204 return isSignatureMatch(signatures, mSignatureSets);
205 }
206
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700207 private final PackageMonitor mPackageMonitor = new PackageMonitor() {
208 /**
209 * Called when package has been reinstalled
210 */
211 @Override
212 public void onPackageUpdateFinished(String packageName, int uid) {
Victoria Leaseb711d572012-10-02 13:14:11 -0700213 synchronized (mLock) {
214 if (packageName.equals(mPackageName)) {
215 // package updated, make sure to rebind
216 unbindLocked();
217 }
218 // check the updated package in case it is better
219 bindBestPackageLocked(packageName);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700220 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700221 }
222
223 @Override
224 public void onPackageAdded(String packageName, int uid) {
Victoria Leaseb711d572012-10-02 13:14:11 -0700225 synchronized (mLock) {
226 if (packageName.equals(mPackageName)) {
227 // package updated, make sure to rebind
228 unbindLocked();
229 }
230 // check the new package is case it is better
231 bindBestPackageLocked(packageName);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700232 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700233 }
234
235 @Override
236 public void onPackageRemoved(String packageName, int uid) {
Victoria Leaseb711d572012-10-02 13:14:11 -0700237 synchronized (mLock) {
238 if (packageName.equals(mPackageName)) {
239 unbindLocked();
240 // the currently bound package was removed,
241 // need to search for a new package
242 bindBestPackageLocked(null);
243 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700244 }
245 }
246 };
247
248 @Override
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");
254 mBinder = binder;
255 if (mHandler !=null && mNewServiceWork != null) {
256 mHandler.post(mNewServiceWork);
257 }
258 } else {
259 Log.w(mTag, "unexpected onServiceConnected: " + packageName);
260 }
261 }
262 }
263
264 @Override
265 public void onServiceDisconnected(ComponentName name) {
266 synchronized (mLock) {
267 String packageName = name.getPackageName();
268 if (D) Log.d(mTag, packageName + " disconnected");
269
270 if (packageName.equals(mPackageName)) {
271 mBinder = null;
272 }
273 }
274 }
275
276 public String getBestPackageName() {
277 synchronized (mLock) {
278 return mPackageName;
279 }
280 }
281
282 public int getBestVersion() {
283 synchronized (mLock) {
284 return mVersion;
285 }
286 }
287
288 public IBinder getBinder() {
289 synchronized (mLock) {
290 return mBinder;
291 }
292 }
Victoria Leaseb711d572012-10-02 13:14:11 -0700293
294 public void switchUser(int userId) {
295 synchronized (mLock) {
296 unbindLocked();
297 mCurrentUserId = userId;
298 bindBestPackageLocked(null);
299 }
300 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700301}