blob: 5598b0ad38213da9aacb33504f79979ee8f577d3 [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 Hamilton20de1602012-10-05 02:32:52 -050046 private static final String EXTRA_VERSION = "version";
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
61 private int mVersion; // current best version
Victoria Leaseb711d572012-10-02 13:14:11 -070062 private int mCurrentUserId;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070063
64 public ServiceWatcher(Context context, String logTag, String action,
Victoria Leaseb711d572012-10-02 13:14:11 -070065 List<String> initialPackageNames, Runnable newServiceWork, Handler handler, int userId) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -070066 mContext = context;
67 mTag = logTag;
68 mAction = action;
69 mPm = mContext.getPackageManager();
70 mNewServiceWork = newServiceWork;
71 mHandler = handler;
Victoria Leaseb711d572012-10-02 13:14:11 -070072 mCurrentUserId = userId;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070073
74 mSignatureSets = new ArrayList<HashSet<Signature>>();
75 for (int i=0; i < initialPackageNames.size(); i++) {
76 String pkg = initialPackageNames.get(i);
77 HashSet<Signature> set = new HashSet<Signature>();
78 try {
79 Signature[] sigs =
80 mPm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES).signatures;
81 set.addAll(Arrays.asList(sigs));
82 mSignatureSets.add(set);
83 } catch (NameNotFoundException e) {
84 Log.w(logTag, pkg + " not found");
85 }
86 }
87
88 }
89
90 public boolean start() {
Victoria Leaseb711d572012-10-02 13:14:11 -070091 synchronized (mLock) {
92 if (!bindBestPackageLocked(null)) return false;
93 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -070094
Victoria Leaseb711d572012-10-02 13:14:11 -070095 mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
Nick Pelly6fa9ad42012-07-16 12:18:23 -070096 return true;
97 }
98
99 /**
100 * Searches and binds to the best package, or do nothing
101 * if the best package is already bound.
102 * Only checks the named package, or checks all packages if it
103 * is null.
104 * Return true if a new package was found to bind to.
105 */
Victoria Leaseb711d572012-10-02 13:14:11 -0700106 private boolean bindBestPackageLocked(String justCheckThisPackage) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700107 Intent intent = new Intent(mAction);
108 if (justCheckThisPackage != null) {
109 intent.setPackage(justCheckThisPackage);
110 }
Victoria Leaseb711d572012-10-02 13:14:11 -0700111 List<ResolveInfo> rInfos = mPm.queryIntentServicesAsUser(new Intent(mAction),
112 PackageManager.GET_META_DATA, mCurrentUserId);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700113 int bestVersion = Integer.MIN_VALUE;
114 String bestPackage = null;
115 for (ResolveInfo rInfo : rInfos) {
116 String packageName = rInfo.serviceInfo.packageName;
117
118 // check signature
119 try {
120 PackageInfo pInfo;
121 pInfo = mPm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
122 if (!isSignatureMatch(pInfo.signatures)) {
123 Log.w(mTag, packageName + " resolves service " + mAction +
124 ", but has wrong signature, ignoring");
125 continue;
126 }
127 } catch (NameNotFoundException e) {
128 Log.wtf(mTag, e);
129 continue;
130 }
131
132 // check version
133 int version = 0;
134 if (rInfo.serviceInfo.metaData != null) {
Jeff Hamilton20de1602012-10-05 02:32:52 -0500135 version = rInfo.serviceInfo.metaData.getInt(EXTRA_VERSION, 0);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700136 }
137 if (version > mVersion) {
138 bestVersion = version;
139 bestPackage = packageName;
140 }
141 }
142
143 if (D) Log.d(mTag, String.format("bindBestPackage %s found %d, %s",
144 (justCheckThisPackage == null ? "" : "(" + justCheckThisPackage + ") "),
145 rInfos.size(),
146 (bestPackage == null ? "no new best package" : "new best packge: " + bestPackage)));
147
148 if (bestPackage != null) {
Victoria Leaseb711d572012-10-02 13:14:11 -0700149 bindToPackageLocked(bestPackage, bestVersion);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700150 return true;
151 }
152 return false;
153 }
154
Victoria Leaseb711d572012-10-02 13:14:11 -0700155 private void unbindLocked() {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700156 String pkg;
Victoria Leaseb711d572012-10-02 13:14:11 -0700157 pkg = mPackageName;
158 mPackageName = null;
159 mVersion = Integer.MIN_VALUE;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700160 if (pkg != null) {
161 if (D) Log.d(mTag, "unbinding " + pkg);
162 mContext.unbindService(this);
163 }
164 }
165
Victoria Leaseb711d572012-10-02 13:14:11 -0700166 private void bindToPackageLocked(String packageName, int version) {
167 unbindLocked();
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700168 Intent intent = new Intent(mAction);
169 intent.setPackage(packageName);
Victoria Leaseb711d572012-10-02 13:14:11 -0700170 mPackageName = packageName;
171 mVersion = version;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700172 if (D) Log.d(mTag, "binding " + packageName + " (version " + version + ")");
173 mContext.bindService(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
Victoria Leaseb711d572012-10-02 13:14:11 -0700174 | Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_NOT_VISIBLE, mCurrentUserId);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700175 }
176
177 private boolean isSignatureMatch(Signature[] signatures) {
178 if (signatures == null) return false;
179
180 // build hashset of input to test against
181 HashSet<Signature> inputSet = new HashSet<Signature>();
182 for (Signature s : signatures) {
183 inputSet.add(s);
184 }
185
186 // test input against each of the signature sets
187 for (HashSet<Signature> referenceSet : mSignatureSets) {
188 if (referenceSet.equals(inputSet)) {
189 return true;
190 }
191 }
192 return false;
193 }
194
195 private final PackageMonitor mPackageMonitor = new PackageMonitor() {
196 /**
197 * Called when package has been reinstalled
198 */
199 @Override
200 public void onPackageUpdateFinished(String packageName, int uid) {
Victoria Leaseb711d572012-10-02 13:14:11 -0700201 synchronized (mLock) {
202 if (packageName.equals(mPackageName)) {
203 // package updated, make sure to rebind
204 unbindLocked();
205 }
206 // check the updated package in case it is better
207 bindBestPackageLocked(packageName);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700208 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700209 }
210
211 @Override
212 public void onPackageAdded(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 new package is 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 onPackageRemoved(String packageName, int uid) {
Victoria Leaseb711d572012-10-02 13:14:11 -0700225 synchronized (mLock) {
226 if (packageName.equals(mPackageName)) {
227 unbindLocked();
228 // the currently bound package was removed,
229 // need to search for a new package
230 bindBestPackageLocked(null);
231 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700232 }
233 }
234 };
235
236 @Override
237 public void onServiceConnected(ComponentName name, IBinder binder) {
238 synchronized (mLock) {
239 String packageName = name.getPackageName();
240 if (packageName.equals(mPackageName)) {
241 if (D) Log.d(mTag, packageName + " connected");
242 mBinder = binder;
243 if (mHandler !=null && mNewServiceWork != null) {
244 mHandler.post(mNewServiceWork);
245 }
246 } else {
247 Log.w(mTag, "unexpected onServiceConnected: " + packageName);
248 }
249 }
250 }
251
252 @Override
253 public void onServiceDisconnected(ComponentName name) {
254 synchronized (mLock) {
255 String packageName = name.getPackageName();
256 if (D) Log.d(mTag, packageName + " disconnected");
257
258 if (packageName.equals(mPackageName)) {
259 mBinder = null;
260 }
261 }
262 }
263
264 public String getBestPackageName() {
265 synchronized (mLock) {
266 return mPackageName;
267 }
268 }
269
270 public int getBestVersion() {
271 synchronized (mLock) {
272 return mVersion;
273 }
274 }
275
276 public IBinder getBinder() {
277 synchronized (mLock) {
278 return mBinder;
279 }
280 }
Victoria Leaseb711d572012-10-02 13:14:11 -0700281
282 public void switchUser(int userId) {
283 synchronized (mLock) {
284 unbindLocked();
285 mCurrentUserId = userId;
286 bindBestPackageLocked(null);
287 }
288 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700289}