Loading services/core/java/com/android/server/ServiceWatcher.java +122 −97 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server; import android.annotation.Nullable; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; Loading @@ -32,13 +33,16 @@ import android.os.Handler; import android.os.IBinder; import android.os.UserHandle; import android.util.Log; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Objects; /** * Find the best Service, and bind to it. Loading @@ -64,17 +68,21 @@ public class ServiceWatcher implements ServiceConnection { private final Runnable mNewServiceWork; private final Handler mHandler; private Object mLock = new Object(); private final Object mLock = new Object(); // all fields below synchronized on mLock private IBinder mBinder; // connected service private String mPackageName; // current best package private int mVersion = Integer.MIN_VALUE; // current best version /** * Whether the currently-connected service is multiuser-aware. This can change at run-time * when switching from one version of a service to another. */ private boolean mIsMultiuser = false; @GuardedBy("mLock") private int mCurrentUserId = UserHandle.USER_SYSTEM; @GuardedBy("mLock") private IBinder mBoundService; @GuardedBy("mLock") private ComponentName mBoundComponent; @GuardedBy("mLock") private String mBoundPackageName; @GuardedBy("mLock") private int mBoundVersion = Integer.MIN_VALUE; @GuardedBy("mLock") private int mBoundUserId = UserHandle.USER_NULL; public static ArrayList<HashSet<Signature>> getSignatureSets(Context context, List<String> initialPackageNames) { Loading @@ -84,7 +92,9 @@ public class ServiceWatcher implements ServiceConnection { String pkg = initialPackageNames.get(i); try { HashSet<Signature> set = new HashSet<Signature>(); Signature[] sigs = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES).signatures; Signature[] sigs = pm.getPackageInfo(pkg, PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE | PackageManager.GET_SIGNATURES).signatures; set.addAll(Arrays.asList(sigs)); sigSets.add(set); } catch (NameNotFoundException e) { Loading Loading @@ -126,20 +136,32 @@ public class ServiceWatcher implements ServiceConnection { mSignatureSets = getSignatureSets(context, initialPackageNames); } /** * Start this watcher, including binding to the current best match and * re-binding to any better matches down the road. * <p> * Note that if there are no matching encryption-aware services, we may not * bind to a real service until after the current user is unlocked. */ public boolean start() { synchronized (mLock) { if (!bindBestPackageLocked(mServicePackageName)) return false; bindBestPackageLocked(mServicePackageName, false); } // listen for user change IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_USER_SWITCHED); intentFilter.addAction(Intent.ACTION_USER_UNLOCKED); mContext.registerReceiverAsUser(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); final String action = intent.getAction(); final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); if (Intent.ACTION_USER_SWITCHED.equals(action)) { switchUser(); switchUser(userId); } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) { unlockUser(userId); } } }, UserHandle.ALL, intentFilter, null, mHandler); Loading @@ -153,30 +175,36 @@ public class ServiceWatcher implements ServiceConnection { } /** * Searches and binds to the best package, or do nothing * if the best package is already bound. * Only checks the named package, or checks all packages if it * is null. * Return true if a new package was found to bind to. * Searches and binds to the best package, or do nothing if the best package * is already bound, unless force rebinding is requested. * * @param justCheckThisPackage Only consider this package, or consider all * packages if it is {@code null}. * @param forceRebind Force a rebinding to the best package if it's already * bound. * @return {@code true} if a valid package was found to bind to. */ private boolean bindBestPackageLocked(String justCheckThisPackage) { private boolean bindBestPackageLocked(String justCheckThisPackage, boolean forceRebind) { Intent intent = new Intent(mAction); if (justCheckThisPackage != null) { intent.setPackage(justCheckThisPackage); } List<ResolveInfo> rInfos = mPm.queryIntentServicesAsUser(intent, PackageManager.GET_META_DATA, UserHandle.USER_SYSTEM); final List<ResolveInfo> rInfos = mPm.queryIntentServicesAsUser(intent, PackageManager.GET_META_DATA | PackageManager.MATCH_DEBUG_TRIAGED_MISSING, mCurrentUserId); int bestVersion = Integer.MIN_VALUE; String bestPackage = null; ComponentName bestComponent = null; boolean bestIsMultiuser = false; if (rInfos != null) { for (ResolveInfo rInfo : rInfos) { String packageName = rInfo.serviceInfo.packageName; final ComponentName component = rInfo.serviceInfo.getComponentName(); final String packageName = component.getPackageName(); // check signature try { PackageInfo pInfo; pInfo = mPm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); pInfo = mPm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES | PackageManager.MATCH_DEBUG_TRIAGED_MISSING); if (!isSignatureMatch(pInfo.signatures)) { Log.w(mTag, packageName + " resolves service " + mAction + ", but has wrong signature, ignoring"); Loading @@ -196,9 +224,9 @@ public class ServiceWatcher implements ServiceConnection { isMultiuser = rInfo.serviceInfo.metaData.getBoolean(EXTRA_SERVICE_IS_MULTIUSER); } if (version > mVersion) { if (version > bestVersion) { bestVersion = version; bestPackage = packageName; bestComponent = component; bestIsMultiuser = isMultiuser; } } Loading @@ -207,42 +235,53 @@ public class ServiceWatcher implements ServiceConnection { Log.d(mTag, String.format("bindBestPackage for %s : %s found %d, %s", mAction, (justCheckThisPackage == null ? "" : "(" + justCheckThisPackage + ") "), rInfos.size(), (bestPackage == null ? "no new best package" : "new best package: " + bestPackage))); (bestComponent == null ? "no new best component" : "new best component: " + bestComponent))); } } else { if (D) Log.d(mTag, "Unable to query intent services for action: " + mAction); } if (bestPackage != null) { bindToPackageLocked(bestPackage, bestVersion, bestIsMultiuser); return true; } if (bestComponent == null) { Slog.w(mTag, "Odd, no component found for service " + mAction); unbindLocked(); return false; } final int userId = bestIsMultiuser ? UserHandle.USER_SYSTEM : mCurrentUserId; final boolean alreadyBound = Objects.equals(bestComponent, mBoundComponent) && bestVersion == mBoundVersion && userId == mBoundUserId; if (forceRebind || !alreadyBound) { unbindLocked(); bindToPackageLocked(bestComponent, bestVersion, userId); } return true; } private void unbindLocked() { String pkg; pkg = mPackageName; mPackageName = null; mVersion = Integer.MIN_VALUE; mIsMultiuser = false; if (pkg != null) { if (D) Log.d(mTag, "unbinding " + pkg); ComponentName component; component = mBoundComponent; mBoundComponent = null; mBoundPackageName = null; mBoundVersion = Integer.MIN_VALUE; mBoundUserId = UserHandle.USER_NULL; if (component != null) { if (D) Log.d(mTag, "unbinding " + component); mContext.unbindService(this); } } private void bindToPackageLocked(String packageName, int version, boolean isMultiuser) { unbindLocked(); private void bindToPackageLocked(ComponentName component, int version, int userId) { Intent intent = new Intent(mAction); intent.setPackage(packageName); mPackageName = packageName; mVersion = version; mIsMultiuser = isMultiuser; if (D) Log.d(mTag, "binding " + packageName + " (version " + version + ") (" + (isMultiuser ? "multi" : "single") + "-user)"); mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE, mIsMultiuser ? UserHandle.SYSTEM : UserHandle.CURRENT); intent.setComponent(component); mBoundComponent = component; mBoundPackageName = component.getPackageName(); mBoundVersion = version; mBoundUserId = userId; if (D) Log.d(mTag, "binding " + component + " (v" + version + ") (u" + userId + ")"); mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE, new UserHandle(userId)); } public static boolean isSignatureMatch(Signature[] signatures, Loading Loading @@ -275,106 +314,92 @@ public class ServiceWatcher implements ServiceConnection { @Override public void onPackageUpdateFinished(String packageName, int uid) { synchronized (mLock) { if (packageName.equals(mPackageName)) { // package updated, make sure to rebind unbindLocked(); } // Need to check all packages because this method is also called when a // system app is uninstalled and the stock version in reinstalled. bindBestPackageLocked(null); final boolean forceRebind = Objects.equals(packageName, mBoundPackageName); bindBestPackageLocked(null, forceRebind); } } @Override public void onPackageAdded(String packageName, int uid) { synchronized (mLock) { if (packageName.equals(mPackageName)) { // package updated, make sure to rebind unbindLocked(); } // check the new package is case it is better bindBestPackageLocked(null); final boolean forceRebind = Objects.equals(packageName, mBoundPackageName); bindBestPackageLocked(null, forceRebind); } } @Override public void onPackageRemoved(String packageName, int uid) { synchronized (mLock) { if (packageName.equals(mPackageName)) { unbindLocked(); // the currently bound package was removed, // need to search for a new package bindBestPackageLocked(null); } final boolean forceRebind = Objects.equals(packageName, mBoundPackageName); bindBestPackageLocked(null, forceRebind); } } @Override public boolean onPackageChanged(String packageName, int uid, String[] components) { synchronized (mLock) { if (packageName.equals(mPackageName)) { // service enabled or disabled, make sure to rebind unbindLocked(); } // the service might be disabled, need to search for a new // package bindBestPackageLocked(null); final boolean forceRebind = Objects.equals(packageName, mBoundPackageName); bindBestPackageLocked(null, forceRebind); } return super.onPackageChanged(packageName, uid, components); } }; @Override public void onServiceConnected(ComponentName name, IBinder binder) { public void onServiceConnected(ComponentName component, IBinder binder) { synchronized (mLock) { String packageName = name.getPackageName(); if (packageName.equals(mPackageName)) { if (D) Log.d(mTag, packageName + " connected"); mBinder = binder; if (component.equals(mBoundComponent)) { if (D) Log.d(mTag, component + " connected"); mBoundService = binder; if (mHandler !=null && mNewServiceWork != null) { mHandler.post(mNewServiceWork); } } else { Log.w(mTag, "unexpected onServiceConnected: " + packageName); Log.w(mTag, "unexpected onServiceConnected: " + component); } } } @Override public void onServiceDisconnected(ComponentName name) { public void onServiceDisconnected(ComponentName component) { synchronized (mLock) { String packageName = name.getPackageName(); if (D) Log.d(mTag, packageName + " disconnected"); if (D) Log.d(mTag, component + " disconnected"); if (packageName.equals(mPackageName)) { mBinder = null; if (component.equals(mBoundComponent)) { mBoundService = null; } } } public String getBestPackageName() { public @Nullable String getBestPackageName() { synchronized (mLock) { return mPackageName; return mBoundPackageName; } } public int getBestVersion() { synchronized (mLock) { return mVersion; return mBoundVersion; } } public IBinder getBinder() { public @Nullable IBinder getBinder() { synchronized (mLock) { return mBinder; return mBoundService; } } public void switchUser() { public void switchUser(int userId) { synchronized (mLock) { if (!mIsMultiuser) { unbindLocked(); bindBestPackageLocked(mServicePackageName); mCurrentUserId = userId; bindBestPackageLocked(mServicePackageName, false); } } public void unlockUser(int userId) { synchronized (mLock) { if (userId == mCurrentUserId) { bindBestPackageLocked(mServicePackageName, false); } } } Loading Loading
services/core/java/com/android/server/ServiceWatcher.java +122 −97 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server; import android.annotation.Nullable; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; Loading @@ -32,13 +33,16 @@ import android.os.Handler; import android.os.IBinder; import android.os.UserHandle; import android.util.Log; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Objects; /** * Find the best Service, and bind to it. Loading @@ -64,17 +68,21 @@ public class ServiceWatcher implements ServiceConnection { private final Runnable mNewServiceWork; private final Handler mHandler; private Object mLock = new Object(); private final Object mLock = new Object(); // all fields below synchronized on mLock private IBinder mBinder; // connected service private String mPackageName; // current best package private int mVersion = Integer.MIN_VALUE; // current best version /** * Whether the currently-connected service is multiuser-aware. This can change at run-time * when switching from one version of a service to another. */ private boolean mIsMultiuser = false; @GuardedBy("mLock") private int mCurrentUserId = UserHandle.USER_SYSTEM; @GuardedBy("mLock") private IBinder mBoundService; @GuardedBy("mLock") private ComponentName mBoundComponent; @GuardedBy("mLock") private String mBoundPackageName; @GuardedBy("mLock") private int mBoundVersion = Integer.MIN_VALUE; @GuardedBy("mLock") private int mBoundUserId = UserHandle.USER_NULL; public static ArrayList<HashSet<Signature>> getSignatureSets(Context context, List<String> initialPackageNames) { Loading @@ -84,7 +92,9 @@ public class ServiceWatcher implements ServiceConnection { String pkg = initialPackageNames.get(i); try { HashSet<Signature> set = new HashSet<Signature>(); Signature[] sigs = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES).signatures; Signature[] sigs = pm.getPackageInfo(pkg, PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE | PackageManager.GET_SIGNATURES).signatures; set.addAll(Arrays.asList(sigs)); sigSets.add(set); } catch (NameNotFoundException e) { Loading Loading @@ -126,20 +136,32 @@ public class ServiceWatcher implements ServiceConnection { mSignatureSets = getSignatureSets(context, initialPackageNames); } /** * Start this watcher, including binding to the current best match and * re-binding to any better matches down the road. * <p> * Note that if there are no matching encryption-aware services, we may not * bind to a real service until after the current user is unlocked. */ public boolean start() { synchronized (mLock) { if (!bindBestPackageLocked(mServicePackageName)) return false; bindBestPackageLocked(mServicePackageName, false); } // listen for user change IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_USER_SWITCHED); intentFilter.addAction(Intent.ACTION_USER_UNLOCKED); mContext.registerReceiverAsUser(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); final String action = intent.getAction(); final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); if (Intent.ACTION_USER_SWITCHED.equals(action)) { switchUser(); switchUser(userId); } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) { unlockUser(userId); } } }, UserHandle.ALL, intentFilter, null, mHandler); Loading @@ -153,30 +175,36 @@ public class ServiceWatcher implements ServiceConnection { } /** * Searches and binds to the best package, or do nothing * if the best package is already bound. * Only checks the named package, or checks all packages if it * is null. * Return true if a new package was found to bind to. * Searches and binds to the best package, or do nothing if the best package * is already bound, unless force rebinding is requested. * * @param justCheckThisPackage Only consider this package, or consider all * packages if it is {@code null}. * @param forceRebind Force a rebinding to the best package if it's already * bound. * @return {@code true} if a valid package was found to bind to. */ private boolean bindBestPackageLocked(String justCheckThisPackage) { private boolean bindBestPackageLocked(String justCheckThisPackage, boolean forceRebind) { Intent intent = new Intent(mAction); if (justCheckThisPackage != null) { intent.setPackage(justCheckThisPackage); } List<ResolveInfo> rInfos = mPm.queryIntentServicesAsUser(intent, PackageManager.GET_META_DATA, UserHandle.USER_SYSTEM); final List<ResolveInfo> rInfos = mPm.queryIntentServicesAsUser(intent, PackageManager.GET_META_DATA | PackageManager.MATCH_DEBUG_TRIAGED_MISSING, mCurrentUserId); int bestVersion = Integer.MIN_VALUE; String bestPackage = null; ComponentName bestComponent = null; boolean bestIsMultiuser = false; if (rInfos != null) { for (ResolveInfo rInfo : rInfos) { String packageName = rInfo.serviceInfo.packageName; final ComponentName component = rInfo.serviceInfo.getComponentName(); final String packageName = component.getPackageName(); // check signature try { PackageInfo pInfo; pInfo = mPm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); pInfo = mPm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES | PackageManager.MATCH_DEBUG_TRIAGED_MISSING); if (!isSignatureMatch(pInfo.signatures)) { Log.w(mTag, packageName + " resolves service " + mAction + ", but has wrong signature, ignoring"); Loading @@ -196,9 +224,9 @@ public class ServiceWatcher implements ServiceConnection { isMultiuser = rInfo.serviceInfo.metaData.getBoolean(EXTRA_SERVICE_IS_MULTIUSER); } if (version > mVersion) { if (version > bestVersion) { bestVersion = version; bestPackage = packageName; bestComponent = component; bestIsMultiuser = isMultiuser; } } Loading @@ -207,42 +235,53 @@ public class ServiceWatcher implements ServiceConnection { Log.d(mTag, String.format("bindBestPackage for %s : %s found %d, %s", mAction, (justCheckThisPackage == null ? "" : "(" + justCheckThisPackage + ") "), rInfos.size(), (bestPackage == null ? "no new best package" : "new best package: " + bestPackage))); (bestComponent == null ? "no new best component" : "new best component: " + bestComponent))); } } else { if (D) Log.d(mTag, "Unable to query intent services for action: " + mAction); } if (bestPackage != null) { bindToPackageLocked(bestPackage, bestVersion, bestIsMultiuser); return true; } if (bestComponent == null) { Slog.w(mTag, "Odd, no component found for service " + mAction); unbindLocked(); return false; } final int userId = bestIsMultiuser ? UserHandle.USER_SYSTEM : mCurrentUserId; final boolean alreadyBound = Objects.equals(bestComponent, mBoundComponent) && bestVersion == mBoundVersion && userId == mBoundUserId; if (forceRebind || !alreadyBound) { unbindLocked(); bindToPackageLocked(bestComponent, bestVersion, userId); } return true; } private void unbindLocked() { String pkg; pkg = mPackageName; mPackageName = null; mVersion = Integer.MIN_VALUE; mIsMultiuser = false; if (pkg != null) { if (D) Log.d(mTag, "unbinding " + pkg); ComponentName component; component = mBoundComponent; mBoundComponent = null; mBoundPackageName = null; mBoundVersion = Integer.MIN_VALUE; mBoundUserId = UserHandle.USER_NULL; if (component != null) { if (D) Log.d(mTag, "unbinding " + component); mContext.unbindService(this); } } private void bindToPackageLocked(String packageName, int version, boolean isMultiuser) { unbindLocked(); private void bindToPackageLocked(ComponentName component, int version, int userId) { Intent intent = new Intent(mAction); intent.setPackage(packageName); mPackageName = packageName; mVersion = version; mIsMultiuser = isMultiuser; if (D) Log.d(mTag, "binding " + packageName + " (version " + version + ") (" + (isMultiuser ? "multi" : "single") + "-user)"); mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE, mIsMultiuser ? UserHandle.SYSTEM : UserHandle.CURRENT); intent.setComponent(component); mBoundComponent = component; mBoundPackageName = component.getPackageName(); mBoundVersion = version; mBoundUserId = userId; if (D) Log.d(mTag, "binding " + component + " (v" + version + ") (u" + userId + ")"); mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE, new UserHandle(userId)); } public static boolean isSignatureMatch(Signature[] signatures, Loading Loading @@ -275,106 +314,92 @@ public class ServiceWatcher implements ServiceConnection { @Override public void onPackageUpdateFinished(String packageName, int uid) { synchronized (mLock) { if (packageName.equals(mPackageName)) { // package updated, make sure to rebind unbindLocked(); } // Need to check all packages because this method is also called when a // system app is uninstalled and the stock version in reinstalled. bindBestPackageLocked(null); final boolean forceRebind = Objects.equals(packageName, mBoundPackageName); bindBestPackageLocked(null, forceRebind); } } @Override public void onPackageAdded(String packageName, int uid) { synchronized (mLock) { if (packageName.equals(mPackageName)) { // package updated, make sure to rebind unbindLocked(); } // check the new package is case it is better bindBestPackageLocked(null); final boolean forceRebind = Objects.equals(packageName, mBoundPackageName); bindBestPackageLocked(null, forceRebind); } } @Override public void onPackageRemoved(String packageName, int uid) { synchronized (mLock) { if (packageName.equals(mPackageName)) { unbindLocked(); // the currently bound package was removed, // need to search for a new package bindBestPackageLocked(null); } final boolean forceRebind = Objects.equals(packageName, mBoundPackageName); bindBestPackageLocked(null, forceRebind); } } @Override public boolean onPackageChanged(String packageName, int uid, String[] components) { synchronized (mLock) { if (packageName.equals(mPackageName)) { // service enabled or disabled, make sure to rebind unbindLocked(); } // the service might be disabled, need to search for a new // package bindBestPackageLocked(null); final boolean forceRebind = Objects.equals(packageName, mBoundPackageName); bindBestPackageLocked(null, forceRebind); } return super.onPackageChanged(packageName, uid, components); } }; @Override public void onServiceConnected(ComponentName name, IBinder binder) { public void onServiceConnected(ComponentName component, IBinder binder) { synchronized (mLock) { String packageName = name.getPackageName(); if (packageName.equals(mPackageName)) { if (D) Log.d(mTag, packageName + " connected"); mBinder = binder; if (component.equals(mBoundComponent)) { if (D) Log.d(mTag, component + " connected"); mBoundService = binder; if (mHandler !=null && mNewServiceWork != null) { mHandler.post(mNewServiceWork); } } else { Log.w(mTag, "unexpected onServiceConnected: " + packageName); Log.w(mTag, "unexpected onServiceConnected: " + component); } } } @Override public void onServiceDisconnected(ComponentName name) { public void onServiceDisconnected(ComponentName component) { synchronized (mLock) { String packageName = name.getPackageName(); if (D) Log.d(mTag, packageName + " disconnected"); if (D) Log.d(mTag, component + " disconnected"); if (packageName.equals(mPackageName)) { mBinder = null; if (component.equals(mBoundComponent)) { mBoundService = null; } } } public String getBestPackageName() { public @Nullable String getBestPackageName() { synchronized (mLock) { return mPackageName; return mBoundPackageName; } } public int getBestVersion() { synchronized (mLock) { return mVersion; return mBoundVersion; } } public IBinder getBinder() { public @Nullable IBinder getBinder() { synchronized (mLock) { return mBinder; return mBoundService; } } public void switchUser() { public void switchUser(int userId) { synchronized (mLock) { if (!mIsMultiuser) { unbindLocked(); bindBestPackageLocked(mServicePackageName); mCurrentUserId = userId; bindBestPackageLocked(mServicePackageName, false); } } public void unlockUser(int userId) { synchronized (mLock) { if (userId == mCurrentUserId) { bindBestPackageLocked(mServicePackageName, false); } } } Loading