ServiceWatcher.java revision b711d57ca4e2c6a1befbfa1a41f4b8094755a93f
1/* 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; 30import android.os.UserHandle; 31import 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; 46 private static final String EXTRA_VERSION = "version"; 47 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 62 private int mCurrentUserId; 63 64 public ServiceWatcher(Context context, String logTag, String action, 65 List<String> initialPackageNames, Runnable newServiceWork, Handler handler, int userId) { 66 mContext = context; 67 mTag = logTag; 68 mAction = action; 69 mPm = mContext.getPackageManager(); 70 mNewServiceWork = newServiceWork; 71 mHandler = handler; 72 mCurrentUserId = userId; 73 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() { 91 synchronized (mLock) { 92 if (!bindBestPackageLocked(null)) return false; 93 } 94 95 mPackageMonitor.register(mContext, null, UserHandle.ALL, true); 96 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 */ 106 private boolean bindBestPackageLocked(String justCheckThisPackage) { 107 Intent intent = new Intent(mAction); 108 if (justCheckThisPackage != null) { 109 intent.setPackage(justCheckThisPackage); 110 } 111 List<ResolveInfo> rInfos = mPm.queryIntentServicesAsUser(new Intent(mAction), 112 PackageManager.GET_META_DATA, mCurrentUserId); 113 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) { 135 version = rInfo.serviceInfo.metaData.getInt(EXTRA_VERSION, 0); 136 } 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) { 149 bindToPackageLocked(bestPackage, bestVersion); 150 return true; 151 } 152 return false; 153 } 154 155 private void unbindLocked() { 156 String pkg; 157 pkg = mPackageName; 158 mPackageName = null; 159 mVersion = Integer.MIN_VALUE; 160 if (pkg != null) { 161 if (D) Log.d(mTag, "unbinding " + pkg); 162 mContext.unbindService(this); 163 } 164 } 165 166 private void bindToPackageLocked(String packageName, int version) { 167 unbindLocked(); 168 Intent intent = new Intent(mAction); 169 intent.setPackage(packageName); 170 mPackageName = packageName; 171 mVersion = version; 172 if (D) Log.d(mTag, "binding " + packageName + " (version " + version + ")"); 173 mContext.bindService(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND 174 | Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_NOT_VISIBLE, mCurrentUserId); 175 } 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) { 201 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); 208 } 209 } 210 211 @Override 212 public void onPackageAdded(String packageName, int uid) { 213 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); 220 } 221 } 222 223 @Override 224 public void onPackageRemoved(String packageName, int uid) { 225 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 } 232 } 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 } 281 282 public void switchUser(int userId) { 283 synchronized (mLock) { 284 unbindLocked(); 285 mCurrentUserId = userId; 286 bindBestPackageLocked(null); 287 } 288 } 289} 290