blob: 6078d8ad453014dc621b68b61ee8dcd418294fe6 [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;
30import android.os.Handler;
31import android.os.IBinder;
Victoria Leaseb711d572012-10-02 13:14:11 -070032import android.os.UserHandle;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070033import android.util.Log;
34
35import com.android.internal.content.PackageMonitor;
36
37import java.util.ArrayList;
38import java.util.Arrays;
39import java.util.HashSet;
40import java.util.List;
41
42/**
43 * Find the best Service, and bind to it.
44 * Handles run-time package changes.
45 */
46public class ServiceWatcher implements ServiceConnection {
47 private static final boolean D = false;
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050048 public static final String EXTRA_SERVICE_VERSION = "serviceVersion";
Victoria Lease03cdd3d2013-02-01 15:15:54 -080049 public static final String EXTRA_SERVICE_IS_MULTIUSER = "serviceIsMultiuser";
Nick Pelly6fa9ad42012-07-16 12:18:23 -070050
51 private final String mTag;
52 private final Context mContext;
53 private final PackageManager mPm;
54 private final List<HashSet<Signature>> mSignatureSets;
55 private final String mAction;
56 private final Runnable mNewServiceWork;
57 private final Handler mHandler;
58
59 private Object mLock = new Object();
60
61 // all fields below synchronized on mLock
62 private IBinder mBinder; // connected service
63 private String mPackageName; // current best package
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050064 private int mVersion = Integer.MIN_VALUE; // current best version
Victoria Lease03cdd3d2013-02-01 15:15:54 -080065 /**
66 * Whether the currently-connected service is multiuser-aware. This can change at run-time
67 * when switching from one version of a service to another.
68 */
69 private boolean mIsMultiuser = false;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070070
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050071 public static ArrayList<HashSet<Signature>> getSignatureSets(Context context,
72 List<String> initialPackageNames) {
73 PackageManager pm = context.getPackageManager();
74 ArrayList<HashSet<Signature>> sigSets = new ArrayList<HashSet<Signature>>();
75 for (int i = 0, size = initialPackageNames.size(); i < size; i++) {
76 String pkg = initialPackageNames.get(i);
77 try {
78 HashSet<Signature> set = new HashSet<Signature>();
79 Signature[] sigs = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES).signatures;
80 set.addAll(Arrays.asList(sigs));
81 sigSets.add(set);
82 } catch (NameNotFoundException e) {
83 Log.w("ServiceWatcher", pkg + " not found");
84 }
85 }
86 return sigSets;
87 }
88
Nick Pelly6fa9ad42012-07-16 12:18:23 -070089 public ServiceWatcher(Context context, String logTag, String action,
Victoria Lease03cdd3d2013-02-01 15:15:54 -080090 List<String> initialPackageNames, Runnable newServiceWork, Handler handler) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -070091 mContext = context;
92 mTag = logTag;
93 mAction = action;
94 mPm = mContext.getPackageManager();
95 mNewServiceWork = newServiceWork;
96 mHandler = handler;
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050097 mSignatureSets = getSignatureSets(context, initialPackageNames);
Nick Pelly6fa9ad42012-07-16 12:18:23 -070098 }
99
100 public boolean start() {
Victoria Leaseb711d572012-10-02 13:14:11 -0700101 synchronized (mLock) {
102 if (!bindBestPackageLocked(null)) return false;
103 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700104
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800105 // listen for user change
106 IntentFilter intentFilter = new IntentFilter();
107 intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
108 mContext.registerReceiverAsUser(new BroadcastReceiver() {
109 @Override
110 public void onReceive(Context context, Intent intent) {
111 String action = intent.getAction();
112 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
113 switchUser();
114 }
115 }
116 }, UserHandle.ALL, intentFilter, null, mHandler);
117
118 // listen for relevant package changes
Victoria Leaseb711d572012-10-02 13:14:11 -0700119 mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800120
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700121 return true;
122 }
123
124 /**
125 * Searches and binds to the best package, or do nothing
126 * if the best package is already bound.
127 * Only checks the named package, or checks all packages if it
128 * is null.
129 * Return true if a new package was found to bind to.
130 */
Victoria Leaseb711d572012-10-02 13:14:11 -0700131 private boolean bindBestPackageLocked(String justCheckThisPackage) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700132 Intent intent = new Intent(mAction);
133 if (justCheckThisPackage != null) {
134 intent.setPackage(justCheckThisPackage);
135 }
Victoria Leaseb711d572012-10-02 13:14:11 -0700136 List<ResolveInfo> rInfos = mPm.queryIntentServicesAsUser(new Intent(mAction),
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800137 PackageManager.GET_META_DATA, UserHandle.USER_OWNER);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700138 int bestVersion = Integer.MIN_VALUE;
139 String bestPackage = null;
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800140 boolean bestIsMultiuser = false;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700141 for (ResolveInfo rInfo : rInfos) {
142 String packageName = rInfo.serviceInfo.packageName;
143
144 // check signature
145 try {
146 PackageInfo pInfo;
147 pInfo = mPm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
148 if (!isSignatureMatch(pInfo.signatures)) {
149 Log.w(mTag, packageName + " resolves service " + mAction +
150 ", but has wrong signature, ignoring");
151 continue;
152 }
153 } catch (NameNotFoundException e) {
154 Log.wtf(mTag, e);
155 continue;
156 }
157
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800158 // check metadata
159 int version = Integer.MIN_VALUE;
160 boolean isMultiuser = false;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700161 if (rInfo.serviceInfo.metaData != null) {
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800162 version = rInfo.serviceInfo.metaData.getInt(EXTRA_SERVICE_VERSION,
163 Integer.MIN_VALUE);
164 isMultiuser = rInfo.serviceInfo.metaData.getBoolean(EXTRA_SERVICE_IS_MULTIUSER);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700165 }
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500166
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700167 if (version > mVersion) {
168 bestVersion = version;
169 bestPackage = packageName;
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800170 bestIsMultiuser = isMultiuser;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700171 }
172 }
173
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500174 if (D) Log.d(mTag, String.format("bindBestPackage for %s : %s found %d, %s", mAction,
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700175 (justCheckThisPackage == null ? "" : "(" + justCheckThisPackage + ") "),
176 rInfos.size(),
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800177 (bestPackage == null ? "no new best package" : "new best package: "
178 + bestPackage)));
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700179
180 if (bestPackage != null) {
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800181 bindToPackageLocked(bestPackage, bestVersion, bestIsMultiuser);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700182 return true;
183 }
184 return false;
185 }
186
Victoria Leaseb711d572012-10-02 13:14:11 -0700187 private void unbindLocked() {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700188 String pkg;
Victoria Leaseb711d572012-10-02 13:14:11 -0700189 pkg = mPackageName;
190 mPackageName = null;
191 mVersion = Integer.MIN_VALUE;
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800192 mIsMultiuser = false;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700193 if (pkg != null) {
194 if (D) Log.d(mTag, "unbinding " + pkg);
195 mContext.unbindService(this);
196 }
197 }
198
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800199 private void bindToPackageLocked(String packageName, int version, boolean isMultiuser) {
Victoria Leaseb711d572012-10-02 13:14:11 -0700200 unbindLocked();
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700201 Intent intent = new Intent(mAction);
202 intent.setPackage(packageName);
Victoria Leaseb711d572012-10-02 13:14:11 -0700203 mPackageName = packageName;
204 mVersion = version;
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800205 mIsMultiuser = isMultiuser;
206 if (D) Log.d(mTag, "binding " + packageName + " (version " + version + ") ("
207 + (isMultiuser ? "multi" : "single") + "-user)");
Amith Yamasani27b89e62013-01-16 12:30:11 -0800208 mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800209 | Context.BIND_NOT_VISIBLE, mIsMultiuser ? UserHandle.OWNER : UserHandle.CURRENT);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700210 }
211
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500212 public static boolean isSignatureMatch(Signature[] signatures,
213 List<HashSet<Signature>> sigSets) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700214 if (signatures == null) return false;
215
216 // build hashset of input to test against
217 HashSet<Signature> inputSet = new HashSet<Signature>();
218 for (Signature s : signatures) {
219 inputSet.add(s);
220 }
221
222 // test input against each of the signature sets
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500223 for (HashSet<Signature> referenceSet : sigSets) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700224 if (referenceSet.equals(inputSet)) {
225 return true;
226 }
227 }
228 return false;
229 }
230
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500231 private boolean isSignatureMatch(Signature[] signatures) {
232 return isSignatureMatch(signatures, mSignatureSets);
233 }
234
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700235 private final PackageMonitor mPackageMonitor = new PackageMonitor() {
236 /**
237 * Called when package has been reinstalled
238 */
239 @Override
240 public void onPackageUpdateFinished(String packageName, int uid) {
Victoria Leaseb711d572012-10-02 13:14:11 -0700241 synchronized (mLock) {
242 if (packageName.equals(mPackageName)) {
243 // package updated, make sure to rebind
244 unbindLocked();
245 }
246 // check the updated package in case it is better
247 bindBestPackageLocked(packageName);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700248 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700249 }
250
251 @Override
252 public void onPackageAdded(String packageName, int uid) {
Victoria Leaseb711d572012-10-02 13:14:11 -0700253 synchronized (mLock) {
254 if (packageName.equals(mPackageName)) {
255 // package updated, make sure to rebind
256 unbindLocked();
257 }
258 // check the new package is case it is better
259 bindBestPackageLocked(packageName);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700260 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700261 }
262
263 @Override
264 public void onPackageRemoved(String packageName, int uid) {
Victoria Leaseb711d572012-10-02 13:14:11 -0700265 synchronized (mLock) {
266 if (packageName.equals(mPackageName)) {
267 unbindLocked();
268 // the currently bound package was removed,
269 // need to search for a new package
270 bindBestPackageLocked(null);
271 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700272 }
273 }
274 };
275
276 @Override
277 public void onServiceConnected(ComponentName name, IBinder binder) {
278 synchronized (mLock) {
279 String packageName = name.getPackageName();
280 if (packageName.equals(mPackageName)) {
281 if (D) Log.d(mTag, packageName + " connected");
282 mBinder = binder;
283 if (mHandler !=null && mNewServiceWork != null) {
284 mHandler.post(mNewServiceWork);
285 }
286 } else {
287 Log.w(mTag, "unexpected onServiceConnected: " + packageName);
288 }
289 }
290 }
291
292 @Override
293 public void onServiceDisconnected(ComponentName name) {
294 synchronized (mLock) {
295 String packageName = name.getPackageName();
296 if (D) Log.d(mTag, packageName + " disconnected");
297
298 if (packageName.equals(mPackageName)) {
299 mBinder = null;
300 }
301 }
302 }
303
304 public String getBestPackageName() {
305 synchronized (mLock) {
306 return mPackageName;
307 }
308 }
309
310 public int getBestVersion() {
311 synchronized (mLock) {
312 return mVersion;
313 }
314 }
315
316 public IBinder getBinder() {
317 synchronized (mLock) {
318 return mBinder;
319 }
320 }
Victoria Leaseb711d572012-10-02 13:14:11 -0700321
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800322 public void switchUser() {
Victoria Leaseb711d572012-10-02 13:14:11 -0700323 synchronized (mLock) {
Victoria Lease03cdd3d2013-02-01 15:15:54 -0800324 if (!mIsMultiuser) {
325 unbindLocked();
326 bindBestPackageLocked(null);
327 }
Victoria Leaseb711d572012-10-02 13:14:11 -0700328 }
329 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700330}