/services/java/com/android/server/ServiceWatcher.java

https://github.com/aizuzi/platform_frameworks_base · Java · 382 lines · 285 code · 38 blank · 59 comment · 51 complexity · 8aaaf8601fb2ee4728cbde7aa85900a2 MD5 · raw file

  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. package com.android.server;
  17. import android.content.BroadcastReceiver;
  18. import android.content.ComponentName;
  19. import android.content.Context;
  20. import android.content.Intent;
  21. import android.content.IntentFilter;
  22. import android.content.ServiceConnection;
  23. import android.content.pm.PackageInfo;
  24. import android.content.pm.PackageManager;
  25. import android.content.pm.PackageManager.NameNotFoundException;
  26. import android.content.pm.ResolveInfo;
  27. import android.content.pm.Signature;
  28. import android.content.res.Resources;
  29. import android.os.Handler;
  30. import android.os.IBinder;
  31. import android.os.UserHandle;
  32. import android.util.Log;
  33. import com.android.internal.content.PackageMonitor;
  34. import java.util.ArrayList;
  35. import java.util.Arrays;
  36. import java.util.Collections;
  37. import java.util.HashSet;
  38. import java.util.List;
  39. /**
  40. * Find the best Service, and bind to it.
  41. * Handles run-time package changes.
  42. */
  43. public class ServiceWatcher implements ServiceConnection {
  44. private static final boolean D = false;
  45. public static final String EXTRA_SERVICE_VERSION = "serviceVersion";
  46. public static final String EXTRA_SERVICE_IS_MULTIUSER = "serviceIsMultiuser";
  47. private final String mTag;
  48. private final Context mContext;
  49. private final PackageManager mPm;
  50. private final List<HashSet<Signature>> mSignatureSets;
  51. private final String mAction;
  52. /**
  53. * If mServicePackageName is not null, only this package will be searched for the service that
  54. * implements mAction. When null, all packages in the system that matches one of the signature
  55. * in mSignatureSets are searched.
  56. */
  57. private final String mServicePackageName;
  58. private final Runnable mNewServiceWork;
  59. private final Handler mHandler;
  60. private Object mLock = new Object();
  61. // all fields below synchronized on mLock
  62. private IBinder mBinder; // connected service
  63. private String mPackageName; // current best package
  64. private int mVersion = Integer.MIN_VALUE; // current best version
  65. /**
  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;
  70. public static ArrayList<HashSet<Signature>> getSignatureSets(Context context,
  71. List<String> initialPackageNames) {
  72. PackageManager pm = context.getPackageManager();
  73. ArrayList<HashSet<Signature>> sigSets = new ArrayList<HashSet<Signature>>();
  74. for (int i = 0, size = initialPackageNames.size(); i < size; i++) {
  75. String pkg = initialPackageNames.get(i);
  76. try {
  77. HashSet<Signature> set = new HashSet<Signature>();
  78. Signature[] sigs = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES).signatures;
  79. set.addAll(Arrays.asList(sigs));
  80. sigSets.add(set);
  81. } catch (NameNotFoundException e) {
  82. Log.w("ServiceWatcher", pkg + " not found");
  83. }
  84. }
  85. return sigSets;
  86. }
  87. public ServiceWatcher(Context context, String logTag, String action,
  88. int overlaySwitchResId, int defaultServicePackageNameResId,
  89. int initialPackageNamesResId, Runnable newServiceWork,
  90. Handler handler) {
  91. mContext = context;
  92. mTag = logTag;
  93. mAction = action;
  94. mPm = mContext.getPackageManager();
  95. mNewServiceWork = newServiceWork;
  96. mHandler = handler;
  97. Resources resources = context.getResources();
  98. // Whether to enable service overlay.
  99. boolean enableOverlay = resources.getBoolean(overlaySwitchResId);
  100. ArrayList<String> initialPackageNames = new ArrayList<String>();
  101. if (enableOverlay) {
  102. // A list of package names used to create the signatures.
  103. String[] pkgs = resources.getStringArray(initialPackageNamesResId);
  104. if (pkgs != null) initialPackageNames.addAll(Arrays.asList(pkgs));
  105. mServicePackageName = null;
  106. if (D) Log.d(mTag, "Overlay enabled, packages=" + Arrays.toString(pkgs));
  107. } else {
  108. // The default package name that is searched for service implementation when overlay is
  109. // disabled.
  110. String servicePackageName = resources.getString(defaultServicePackageNameResId);
  111. if (servicePackageName != null) initialPackageNames.add(servicePackageName);
  112. mServicePackageName = servicePackageName;
  113. if (D) Log.d(mTag, "Overlay disabled, default package=" + servicePackageName);
  114. }
  115. mSignatureSets = getSignatureSets(context, initialPackageNames);
  116. }
  117. public boolean start() {
  118. synchronized (mLock) {
  119. if (!bindBestPackageLocked(mServicePackageName)) return false;
  120. }
  121. // listen for user change
  122. IntentFilter intentFilter = new IntentFilter();
  123. intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
  124. mContext.registerReceiverAsUser(new BroadcastReceiver() {
  125. @Override
  126. public void onReceive(Context context, Intent intent) {
  127. String action = intent.getAction();
  128. if (Intent.ACTION_USER_SWITCHED.equals(action)) {
  129. switchUser();
  130. }
  131. }
  132. }, UserHandle.ALL, intentFilter, null, mHandler);
  133. // listen for relevant package changes if service overlay is enabled.
  134. if (mServicePackageName == null) {
  135. mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
  136. }
  137. return true;
  138. }
  139. /**
  140. * Searches and binds to the best package, or do nothing
  141. * if the best package is already bound.
  142. * Only checks the named package, or checks all packages if it
  143. * is null.
  144. * Return true if a new package was found to bind to.
  145. */
  146. private boolean bindBestPackageLocked(String justCheckThisPackage) {
  147. Intent intent = new Intent(mAction);
  148. if (justCheckThisPackage != null) {
  149. intent.setPackage(justCheckThisPackage);
  150. }
  151. List<ResolveInfo> rInfos = mPm.queryIntentServicesAsUser(intent,
  152. PackageManager.GET_META_DATA, UserHandle.USER_OWNER);
  153. int bestVersion = Integer.MIN_VALUE;
  154. String bestPackage = null;
  155. boolean bestIsMultiuser = false;
  156. if (rInfos != null) {
  157. for (ResolveInfo rInfo : rInfos) {
  158. String packageName = rInfo.serviceInfo.packageName;
  159. // check signature
  160. try {
  161. PackageInfo pInfo;
  162. pInfo = mPm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
  163. if (!isSignatureMatch(pInfo.signatures)) {
  164. Log.w(mTag, packageName + " resolves service " + mAction
  165. + ", but has wrong signature, ignoring");
  166. continue;
  167. }
  168. } catch (NameNotFoundException e) {
  169. Log.wtf(mTag, e);
  170. continue;
  171. }
  172. // check metadata
  173. int version = Integer.MIN_VALUE;
  174. boolean isMultiuser = false;
  175. if (rInfo.serviceInfo.metaData != null) {
  176. version = rInfo.serviceInfo.metaData.getInt(
  177. EXTRA_SERVICE_VERSION, Integer.MIN_VALUE);
  178. isMultiuser = rInfo.serviceInfo.metaData.getBoolean(EXTRA_SERVICE_IS_MULTIUSER);
  179. }
  180. if (version > mVersion) {
  181. bestVersion = version;
  182. bestPackage = packageName;
  183. bestIsMultiuser = isMultiuser;
  184. }
  185. }
  186. if (D) {
  187. Log.d(mTag, String.format("bindBestPackage for %s : %s found %d, %s", mAction,
  188. (justCheckThisPackage == null ? ""
  189. : "(" + justCheckThisPackage + ") "), rInfos.size(),
  190. (bestPackage == null ? "no new best package"
  191. : "new best package: " + bestPackage)));
  192. }
  193. } else {
  194. if (D) Log.d(mTag, "Unable to query intent services for action: " + mAction);
  195. }
  196. if (bestPackage != null) {
  197. bindToPackageLocked(bestPackage, bestVersion, bestIsMultiuser);
  198. return true;
  199. }
  200. return false;
  201. }
  202. private void unbindLocked() {
  203. String pkg;
  204. pkg = mPackageName;
  205. mPackageName = null;
  206. mVersion = Integer.MIN_VALUE;
  207. mIsMultiuser = false;
  208. if (pkg != null) {
  209. if (D) Log.d(mTag, "unbinding " + pkg);
  210. mContext.unbindService(this);
  211. }
  212. }
  213. private void bindToPackageLocked(String packageName, int version, boolean isMultiuser) {
  214. unbindLocked();
  215. Intent intent = new Intent(mAction);
  216. intent.setPackage(packageName);
  217. mPackageName = packageName;
  218. mVersion = version;
  219. mIsMultiuser = isMultiuser;
  220. if (D) Log.d(mTag, "binding " + packageName + " (version " + version + ") ("
  221. + (isMultiuser ? "multi" : "single") + "-user)");
  222. mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
  223. | Context.BIND_NOT_VISIBLE, mIsMultiuser ? UserHandle.OWNER : UserHandle.CURRENT);
  224. }
  225. public static boolean isSignatureMatch(Signature[] signatures,
  226. List<HashSet<Signature>> sigSets) {
  227. if (signatures == null) return false;
  228. // build hashset of input to test against
  229. HashSet<Signature> inputSet = new HashSet<Signature>();
  230. for (Signature s : signatures) {
  231. inputSet.add(s);
  232. }
  233. // test input against each of the signature sets
  234. for (HashSet<Signature> referenceSet : sigSets) {
  235. if (referenceSet.equals(inputSet)) {
  236. return true;
  237. }
  238. }
  239. return false;
  240. }
  241. private boolean isSignatureMatch(Signature[] signatures) {
  242. return isSignatureMatch(signatures, mSignatureSets);
  243. }
  244. private final PackageMonitor mPackageMonitor = new PackageMonitor() {
  245. /**
  246. * Called when package has been reinstalled
  247. */
  248. @Override
  249. public void onPackageUpdateFinished(String packageName, int uid) {
  250. synchronized (mLock) {
  251. if (packageName.equals(mPackageName)) {
  252. // package updated, make sure to rebind
  253. unbindLocked();
  254. }
  255. // Need to check all packages because this method is also called when a
  256. // system app is uninstalled and the stock version in reinstalled.
  257. bindBestPackageLocked(null);
  258. }
  259. }
  260. @Override
  261. public void onPackageAdded(String packageName, int uid) {
  262. synchronized (mLock) {
  263. if (packageName.equals(mPackageName)) {
  264. // package updated, make sure to rebind
  265. unbindLocked();
  266. }
  267. // check the new package is case it is better
  268. bindBestPackageLocked(null);
  269. }
  270. }
  271. @Override
  272. public void onPackageRemoved(String packageName, int uid) {
  273. synchronized (mLock) {
  274. if (packageName.equals(mPackageName)) {
  275. unbindLocked();
  276. // the currently bound package was removed,
  277. // need to search for a new package
  278. bindBestPackageLocked(null);
  279. }
  280. }
  281. }
  282. @Override
  283. public boolean onPackageChanged(String packageName, int uid, String[] components) {
  284. synchronized (mLock) {
  285. if (packageName.equals(mPackageName)) {
  286. // service enabled or disabled, make sure to rebind
  287. unbindLocked();
  288. }
  289. // the service might be disabled, need to search for a new
  290. // package
  291. bindBestPackageLocked(null);
  292. }
  293. return super.onPackageChanged(packageName, uid, components);
  294. }
  295. };
  296. @Override
  297. public void onServiceConnected(ComponentName name, IBinder binder) {
  298. synchronized (mLock) {
  299. String packageName = name.getPackageName();
  300. if (packageName.equals(mPackageName)) {
  301. if (D) Log.d(mTag, packageName + " connected");
  302. mBinder = binder;
  303. if (mHandler !=null && mNewServiceWork != null) {
  304. mHandler.post(mNewServiceWork);
  305. }
  306. } else {
  307. Log.w(mTag, "unexpected onServiceConnected: " + packageName);
  308. }
  309. }
  310. }
  311. @Override
  312. public void onServiceDisconnected(ComponentName name) {
  313. synchronized (mLock) {
  314. String packageName = name.getPackageName();
  315. if (D) Log.d(mTag, packageName + " disconnected");
  316. if (packageName.equals(mPackageName)) {
  317. mBinder = null;
  318. }
  319. }
  320. }
  321. public String getBestPackageName() {
  322. synchronized (mLock) {
  323. return mPackageName;
  324. }
  325. }
  326. public int getBestVersion() {
  327. synchronized (mLock) {
  328. return mVersion;
  329. }
  330. }
  331. public IBinder getBinder() {
  332. synchronized (mLock) {
  333. return mBinder;
  334. }
  335. }
  336. public void switchUser() {
  337. synchronized (mLock) {
  338. if (!mIsMultiuser) {
  339. unbindLocked();
  340. bindBestPackageLocked(mServicePackageName);
  341. }
  342. }
  343. }
  344. }