FileDocCategorySizeDatePackage
ServiceWatcher.javaAPI DocAndroid 5.1 API14385Thu Mar 12 22:22:42 GMT 2015com.android.server

ServiceWatcher

public class ServiceWatcher extends Object implements android.content.ServiceConnection
Find the best Service, and bind to it. Handles run-time package changes.

Fields Summary
private static final boolean
D
public static final String
EXTRA_SERVICE_VERSION
public static final String
EXTRA_SERVICE_IS_MULTIUSER
private final String
mTag
private final android.content.Context
mContext
private final android.content.pm.PackageManager
mPm
private final List
mSignatureSets
private final String
mAction
private final String
mServicePackageName
If mServicePackageName is not null, only this package will be searched for the service that implements mAction. When null, all packages in the system that matches one of the signature in mSignatureSets are searched.
private final Runnable
mNewServiceWork
private final android.os.Handler
mHandler
private Object
mLock
private android.os.IBinder
mBinder
private String
mPackageName
private int
mVersion
private boolean
mIsMultiuser
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 final com.android.internal.content.PackageMonitor
mPackageMonitor
Constructors Summary
public ServiceWatcher(android.content.Context context, String logTag, String action, int overlaySwitchResId, int defaultServicePackageNameResId, int initialPackageNamesResId, Runnable newServiceWork, android.os.Handler handler)

        mContext = context;
        mTag = logTag;
        mAction = action;
        mPm = mContext.getPackageManager();
        mNewServiceWork = newServiceWork;
        mHandler = handler;
        Resources resources = context.getResources();

        // Whether to enable service overlay.
        boolean enableOverlay = resources.getBoolean(overlaySwitchResId);
        ArrayList<String>  initialPackageNames = new ArrayList<String>();
        if (enableOverlay) {
            // A list of package names used to create the signatures.
            String[] pkgs = resources.getStringArray(initialPackageNamesResId);
            if (pkgs != null) initialPackageNames.addAll(Arrays.asList(pkgs));
            mServicePackageName = null;
            if (D) Log.d(mTag, "Overlay enabled, packages=" + Arrays.toString(pkgs));
        } else {
            // The default package name that is searched for service implementation when overlay is
            // disabled.
            String servicePackageName = resources.getString(defaultServicePackageNameResId);
            if (servicePackageName != null) initialPackageNames.add(servicePackageName);
            mServicePackageName = servicePackageName;
            if (D) Log.d(mTag, "Overlay disabled, default package=" + servicePackageName);
        }
        mSignatureSets = getSignatureSets(context, initialPackageNames);
    
Methods Summary
private booleanbindBestPackageLocked(java.lang.String justCheckThisPackage)
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.

        Intent intent = new Intent(mAction);
        if (justCheckThisPackage != null) {
            intent.setPackage(justCheckThisPackage);
        }
        List<ResolveInfo> rInfos = mPm.queryIntentServicesAsUser(intent,
                PackageManager.GET_META_DATA, UserHandle.USER_OWNER);
        int bestVersion = Integer.MIN_VALUE;
        String bestPackage = null;
        boolean bestIsMultiuser = false;
        if (rInfos != null) {
            for (ResolveInfo rInfo : rInfos) {
                String packageName = rInfo.serviceInfo.packageName;

                // check signature
                try {
                    PackageInfo pInfo;
                    pInfo = mPm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
                    if (!isSignatureMatch(pInfo.signatures)) {
                        Log.w(mTag, packageName + " resolves service " + mAction
                                + ", but has wrong signature, ignoring");
                        continue;
                    }
                } catch (NameNotFoundException e) {
                    Log.wtf(mTag, e);
                    continue;
                }

                // check metadata
                int version = Integer.MIN_VALUE;
                boolean isMultiuser = false;
                if (rInfo.serviceInfo.metaData != null) {
                    version = rInfo.serviceInfo.metaData.getInt(
                            EXTRA_SERVICE_VERSION, Integer.MIN_VALUE);
                    isMultiuser = rInfo.serviceInfo.metaData.getBoolean(EXTRA_SERVICE_IS_MULTIUSER);
                }

                if (version > mVersion) {
                    bestVersion = version;
                    bestPackage = packageName;
                    bestIsMultiuser = isMultiuser;
                }
            }

            if (D) {
                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)));
            }
        } else {
            if (D) Log.d(mTag, "Unable to query intent services for action: " + mAction);
        }
        if (bestPackage != null) {
            bindToPackageLocked(bestPackage, bestVersion, bestIsMultiuser);
            return true;
        }
        return false;
    
private voidbindToPackageLocked(java.lang.String packageName, int version, boolean isMultiuser)

        unbindLocked();
        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.OWNER : UserHandle.CURRENT);
    
public java.lang.StringgetBestPackageName()

        synchronized (mLock) {
            return mPackageName;
        }
    
public intgetBestVersion()

        synchronized (mLock) {
            return mVersion;
        }
    
public android.os.IBindergetBinder()

        synchronized (mLock) {
            return mBinder;
        }
    
public static java.util.ArrayListgetSignatureSets(android.content.Context context, java.util.List initialPackageNames)


        
              
        PackageManager pm = context.getPackageManager();
        ArrayList<HashSet<Signature>> sigSets = new ArrayList<HashSet<Signature>>();
        for (int i = 0, size = initialPackageNames.size(); i < size; i++) {
            String pkg = initialPackageNames.get(i);
            try {
                HashSet<Signature> set = new HashSet<Signature>();
                Signature[] sigs = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES).signatures;
                set.addAll(Arrays.asList(sigs));
                sigSets.add(set);
            } catch (NameNotFoundException e) {
                Log.w("ServiceWatcher", pkg + " not found");
            }
        }
        return sigSets;
    
public static booleanisSignatureMatch(android.content.pm.Signature[] signatures, java.util.List sigSets)

        if (signatures == null) return false;

        // build hashset of input to test against
        HashSet<Signature> inputSet = new HashSet<Signature>();
        for (Signature s : signatures) {
            inputSet.add(s);
        }

        // test input against each of the signature sets
        for (HashSet<Signature> referenceSet : sigSets) {
            if (referenceSet.equals(inputSet)) {
                return true;
            }
        }
        return false;
    
private booleanisSignatureMatch(android.content.pm.Signature[] signatures)

        return isSignatureMatch(signatures, mSignatureSets);
    
public voidonServiceConnected(android.content.ComponentName name, android.os.IBinder binder)


    
          
        synchronized (mLock) {
            String packageName = name.getPackageName();
            if (packageName.equals(mPackageName)) {
                if (D) Log.d(mTag, packageName + " connected");
                mBinder = binder;
                if (mHandler !=null && mNewServiceWork != null) {
                    mHandler.post(mNewServiceWork);
                }
            } else {
                Log.w(mTag, "unexpected onServiceConnected: " + packageName);
            }
        }
    
public voidonServiceDisconnected(android.content.ComponentName name)

        synchronized (mLock) {
            String packageName = name.getPackageName();
            if (D) Log.d(mTag, packageName + " disconnected");

            if (packageName.equals(mPackageName)) {
                mBinder = null;
            }
        }
    
public booleanstart()

        synchronized (mLock) {
            if (!bindBestPackageLocked(mServicePackageName)) return false;
        }

        // listen for user change
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
        mContext.registerReceiverAsUser(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                if (Intent.ACTION_USER_SWITCHED.equals(action)) {
                    switchUser();
                }
            }
        }, UserHandle.ALL, intentFilter, null, mHandler);

        // listen for relevant package changes if service overlay is enabled.
        if (mServicePackageName == null) {
            mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
        }

        return true;
    
public voidswitchUser()

        synchronized (mLock) {
            if (!mIsMultiuser) {
                unbindLocked();
                bindBestPackageLocked(mServicePackageName);
            }
        }
    
private voidunbindLocked()

        String pkg;
        pkg = mPackageName;
        mPackageName = null;
        mVersion = Integer.MIN_VALUE;
        mIsMultiuser = false;
        if (pkg != null) {
            if (D) Log.d(mTag, "unbinding " + pkg);
            mContext.unbindService(this);
        }