blob: 2e7c6d16ea3b44fe51d767f7f6e826cf5413114a [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 + ")");
179 mContext.bindService(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
Victoria Leaseb711d572012-10-02 13:14:11 -0700180 | Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_NOT_VISIBLE, mCurrentUserId);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700181 }
182
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500183 public static boolean isSignatureMatch(Signature[] signatures,
184 List<HashSet<Signature>> sigSets) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700185 if (signatures == null) return false;
186
187 // build hashset of input to test against
188 HashSet<Signature> inputSet = new HashSet<Signature>();
189 for (Signature s : signatures) {
190 inputSet.add(s);
191 }
192
193 // test input against each of the signature sets
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500194 for (HashSet<Signature> referenceSet : sigSets) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700195 if (referenceSet.equals(inputSet)) {
196 return true;
197 }
198 }
199 return false;
200 }
201
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500202 private boolean isSignatureMatch(Signature[] signatures) {
203 return isSignatureMatch(signatures, mSignatureSets);
204 }
205
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700206 private final PackageMonitor mPackageMonitor = new PackageMonitor() {
207 /**
208 * Called when package has been reinstalled
209 */
210 @Override
211 public void onPackageUpdateFinished(String packageName, int uid) {
Victoria Leaseb711d572012-10-02 13:14:11 -0700212 synchronized (mLock) {
213 if (packageName.equals(mPackageName)) {
214 // package updated, make sure to rebind
215 unbindLocked();
216 }
217 // check the updated package in case it is better
218 bindBestPackageLocked(packageName);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700219 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700220 }
221
222 @Override
223 public void onPackageAdded(String packageName, int uid) {
Victoria Leaseb711d572012-10-02 13:14:11 -0700224 synchronized (mLock) {
225 if (packageName.equals(mPackageName)) {
226 // package updated, make sure to rebind
227 unbindLocked();
228 }
229 // check the new package is case it is better
230 bindBestPackageLocked(packageName);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700231 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700232 }
233
234 @Override
235 public void onPackageRemoved(String packageName, int uid) {
Victoria Leaseb711d572012-10-02 13:14:11 -0700236 synchronized (mLock) {
237 if (packageName.equals(mPackageName)) {
238 unbindLocked();
239 // the currently bound package was removed,
240 // need to search for a new package
241 bindBestPackageLocked(null);
242 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700243 }
244 }
245 };
246
247 @Override
248 public void onServiceConnected(ComponentName name, IBinder binder) {
249 synchronized (mLock) {
250 String packageName = name.getPackageName();
251 if (packageName.equals(mPackageName)) {
252 if (D) Log.d(mTag, packageName + " connected");
253 mBinder = binder;
254 if (mHandler !=null && mNewServiceWork != null) {
255 mHandler.post(mNewServiceWork);
256 }
257 } else {
258 Log.w(mTag, "unexpected onServiceConnected: " + packageName);
259 }
260 }
261 }
262
263 @Override
264 public void onServiceDisconnected(ComponentName name) {
265 synchronized (mLock) {
266 String packageName = name.getPackageName();
267 if (D) Log.d(mTag, packageName + " disconnected");
268
269 if (packageName.equals(mPackageName)) {
270 mBinder = null;
271 }
272 }
273 }
274
275 public String getBestPackageName() {
276 synchronized (mLock) {
277 return mPackageName;
278 }
279 }
280
281 public int getBestVersion() {
282 synchronized (mLock) {
283 return mVersion;
284 }
285 }
286
287 public IBinder getBinder() {
288 synchronized (mLock) {
289 return mBinder;
290 }
291 }
Victoria Leaseb711d572012-10-02 13:14:11 -0700292
293 public void switchUser(int userId) {
294 synchronized (mLock) {
295 unbindLocked();
296 mCurrentUserId = userId;
297 bindBestPackageLocked(null);
298 }
299 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700300}