FileDocCategorySizeDatePackage
ManagedServices.javaAPI DocAndroid 5.1 API26809Thu Mar 12 22:22:42 GMT 2015com.android.server.notification

ManagedServices

public abstract class ManagedServices extends Object
Manages the lifecycle of application-provided services bound by system server. Services managed by this helper must have: - An associated system settings value with a list of enabled component names. - A well-known action for services to use in their intent-filter. - A system permission for services to require in order to ensure system has exclusive binding. - A settings page for user configuration of enabled services, and associated intent action. - A remote interface definition (aidl) provided by the service used for communication.

Fields Summary
protected final String
TAG
protected final boolean
DEBUG
private static final String
ENABLED_SERVICES_SEPARATOR
protected final android.content.Context
mContext
protected final Object
mMutex
private final UserProfiles
mUserProfiles
private final SettingsObserver
mSettingsObserver
private final Config
mConfig
protected final ArrayList
mServices
private final ArrayList
mServicesBinding
private android.util.ArraySet
mEnabledServicesForCurrentProfiles
private android.util.ArraySet
mEnabledServicesPackageNames
private int[]
mLastSeenProfileIds
Constructors Summary
public ManagedServices(android.content.Context context, android.os.Handler handler, Object mutex, UserProfiles userProfiles)


          
              
        mContext = context;
        mMutex = mutex;
        mUserProfiles = userProfiles;
        mConfig = getConfig();
        mSettingsObserver = new SettingsObserver(handler);
    
Methods Summary
protected abstract android.os.IInterfaceasInterface(android.os.IBinder binder)

private voidcheckNotNull(android.os.IInterface service)

        if (service == null) {
            throw new IllegalArgumentException(getCaption() + " must not be null");
        }
    
public com.android.server.notification.ManagedServices$ManagedServiceInfocheckServiceTokenLocked(android.os.IInterface service)

        checkNotNull(service);
        final IBinder token = service.asBinder();
        final int N = mServices.size();
        for (int i=0; i<N; i++) {
            final ManagedServiceInfo info = mServices.get(i);
            if (info.service.asBinder() == token) return info;
        }
        throw new SecurityException("Disallowed call from unknown " + getCaption() + ": "
                + service);
    
private voiddisableNonexistentServices()
Remove access for any services that no longer exist.

        int[] userIds = mUserProfiles.getCurrentProfileIds();
        final int N = userIds.length;
        for (int i = 0 ; i < N; ++i) {
            disableNonexistentServices(userIds[i]);
        }
    
private voiddisableNonexistentServices(int userId)

        String flatIn = Settings.Secure.getStringForUser(
                mContext.getContentResolver(),
                mConfig.secureSettingName,
                userId);
        if (!TextUtils.isEmpty(flatIn)) {
            if (DEBUG) Slog.v(TAG, "flat before: " + flatIn);
            PackageManager pm = mContext.getPackageManager();
            List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
                    new Intent(mConfig.serviceInterface),
                    PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
                    userId);
            if (DEBUG) Slog.v(TAG, mConfig.serviceInterface + " services: " + installedServices);
            Set<ComponentName> installed = new ArraySet<ComponentName>();
            for (int i = 0, count = installedServices.size(); i < count; i++) {
                ResolveInfo resolveInfo = installedServices.get(i);
                ServiceInfo info = resolveInfo.serviceInfo;

                if (!mConfig.bindPermission.equals(info.permission)) {
                    Slog.w(TAG, "Skipping " + getCaption() + " service "
                            + info.packageName + "/" + info.name
                            + ": it does not require the permission "
                            + mConfig.bindPermission);
                    continue;
                }
                installed.add(new ComponentName(info.packageName, info.name));
            }

            String flatOut = "";
            if (!installed.isEmpty()) {
                String[] enabled = flatIn.split(ENABLED_SERVICES_SEPARATOR);
                ArrayList<String> remaining = new ArrayList<String>(enabled.length);
                for (int i = 0; i < enabled.length; i++) {
                    ComponentName enabledComponent = ComponentName.unflattenFromString(enabled[i]);
                    if (installed.contains(enabledComponent)) {
                        remaining.add(enabled[i]);
                    }
                }
                flatOut = TextUtils.join(ENABLED_SERVICES_SEPARATOR, remaining);
            }
            if (DEBUG) Slog.v(TAG, "flat after: " + flatOut);
            if (!flatIn.equals(flatOut)) {
                Settings.Secure.putStringForUser(mContext.getContentResolver(),
                        mConfig.secureSettingName,
                        flatOut, userId);
            }
        }
    
public voiddump(java.io.PrintWriter pw, com.android.server.notification.NotificationManagerService.DumpFilter filter)

        pw.println("    All " + getCaption() + "s (" + mEnabledServicesForCurrentProfiles.size()
                + ") enabled for current profiles:");
        for (ComponentName cmpt : mEnabledServicesForCurrentProfiles) {
            if (filter != null && !filter.matches(cmpt)) continue;
            pw.println("      " + cmpt);
        }

        pw.println("    Live " + getCaption() + "s (" + mServices.size() + "):");
        for (ManagedServiceInfo info : mServices) {
            if (filter != null && !filter.matches(info.component)) continue;
            pw.println("      " + info.component
                    + " (user " + info.userid + "): " + info.service
                    + (info.isSystem?" SYSTEM":""));
        }
    
private java.lang.StringgetCaption()

        return mConfig.caption;
    
protected abstract com.android.server.notification.ManagedServices$ConfiggetConfig()

private com.android.server.notification.ManagedServices$ManagedServiceInfonewServiceInfo(android.os.IInterface service, android.content.ComponentName component, int userid, boolean isSystem, android.content.ServiceConnection connection, int targetSdkVersion)

        return new ManagedServiceInfo(service, component, userid, isSystem, connection,
                targetSdkVersion);
    
public voidonBootPhaseAppsCanStart()

        mSettingsObserver.observe();
    
public voidonPackagesChanged(boolean queryReplace, java.lang.String[] pkgList)

        if (DEBUG) Slog.d(TAG, "onPackagesChanged queryReplace=" + queryReplace
                + " pkgList=" + (pkgList == null ? null : Arrays.asList(pkgList))
                + " mEnabledServicesPackageNames=" + mEnabledServicesPackageNames);
        boolean anyServicesInvolved = false;
        if (pkgList != null && (pkgList.length > 0)) {
            for (String pkgName : pkgList) {
                if (mEnabledServicesPackageNames.contains(pkgName)) {
                    anyServicesInvolved = true;
                }
            }
        }

        if (anyServicesInvolved) {
            // if we're not replacing a package, clean up orphaned bits
            if (!queryReplace) {
                disableNonexistentServices();
            }
            // make sure we're still bound to any of our services who may have just upgraded
            rebindServices();
        }
    
protected abstract voidonServiceAdded(com.android.server.notification.ManagedServices$ManagedServiceInfo info)

protected voidonServiceRemovedLocked(com.android.server.notification.ManagedServices$ManagedServiceInfo removed)

 
public voidonUserSwitched()

        if (DEBUG) Slog.d(TAG, "onUserSwitched");
        if (Arrays.equals(mLastSeenProfileIds, mUserProfiles.getCurrentProfileIds())) {
            if (DEBUG) Slog.d(TAG, "Current profile IDs didn't change, skipping rebindServices().");
            return;
        }
        rebindServices();
    
private voidrebindServices()
Called whenever packages change, the user switches, or the secure setting is altered. (For example in response to USER_SWITCHED in our broadcast receiver)

        if (DEBUG) Slog.d(TAG, "rebindServices");
        final int[] userIds = mUserProfiles.getCurrentProfileIds();
        final int nUserIds = userIds.length;

        final SparseArray<String> flat = new SparseArray<String>();

        for (int i = 0; i < nUserIds; ++i) {
            flat.put(userIds[i], Settings.Secure.getStringForUser(
                    mContext.getContentResolver(),
                    mConfig.secureSettingName,
                    userIds[i]));
        }

        ArrayList<ManagedServiceInfo> toRemove = new ArrayList<ManagedServiceInfo>();
        final SparseArray<ArrayList<ComponentName>> toAdd
                = new SparseArray<ArrayList<ComponentName>>();

        synchronized (mMutex) {
            // Unbind automatically bound services, retain system services.
            for (ManagedServiceInfo service : mServices) {
                if (!service.isSystem) {
                    toRemove.add(service);
                }
            }

            final ArraySet<ComponentName> newEnabled = new ArraySet<ComponentName>();
            final ArraySet<String> newPackages = new ArraySet<String>();

            for (int i = 0; i < nUserIds; ++i) {
                final ArrayList<ComponentName> add = new ArrayList<ComponentName>();
                toAdd.put(userIds[i], add);

                // decode the list of components
                String toDecode = flat.get(userIds[i]);
                if (toDecode != null) {
                    String[] components = toDecode.split(ENABLED_SERVICES_SEPARATOR);
                    for (int j = 0; j < components.length; j++) {
                        final ComponentName component
                                = ComponentName.unflattenFromString(components[j]);
                        if (component != null) {
                            newEnabled.add(component);
                            add.add(component);
                            newPackages.add(component.getPackageName());
                        }
                    }

                }
            }
            mEnabledServicesForCurrentProfiles = newEnabled;
            mEnabledServicesPackageNames = newPackages;
        }

        for (ManagedServiceInfo info : toRemove) {
            final ComponentName component = info.component;
            final int oldUser = info.userid;
            Slog.v(TAG, "disabling " + getCaption() + " for user "
                    + oldUser + ": " + component);
            unregisterService(component, info.userid);
        }

        for (int i = 0; i < nUserIds; ++i) {
            final ArrayList<ComponentName> add = toAdd.get(userIds[i]);
            final int N = add.size();
            for (int j = 0; j < N; j++) {
                final ComponentName component = add.get(j);
                Slog.v(TAG, "enabling " + getCaption() + " for user " + userIds[i] + ": "
                        + component);
                registerService(component, userIds[i]);
            }
        }

        mLastSeenProfileIds = mUserProfiles.getCurrentProfileIds();
    
public voidregisterService(android.os.IInterface service, android.content.ComponentName component, int userid)

        checkNotNull(service);
        ManagedServiceInfo info = registerServiceImpl(service, component, userid);
        if (info != null) {
            onServiceAdded(info);
        }
    
private voidregisterService(android.content.ComponentName name, int userid)
Version of registerService that takes the name of a service component to bind to.

        if (DEBUG) Slog.v(TAG, "registerService: " + name + " u=" + userid);

        synchronized (mMutex) {
            final String servicesBindingTag = name.toString() + "/" + userid;
            if (mServicesBinding.contains(servicesBindingTag)) {
                // stop registering this thing already! we're working on it
                return;
            }
            mServicesBinding.add(servicesBindingTag);

            final int N = mServices.size();
            for (int i=N-1; i>=0; i--) {
                final ManagedServiceInfo info = mServices.get(i);
                if (name.equals(info.component)
                        && info.userid == userid) {
                    // cut old connections
                    if (DEBUG) Slog.v(TAG, "    disconnecting old " + getCaption() + ": "
                            + info.service);
                    removeServiceLocked(i);
                    if (info.connection != null) {
                        mContext.unbindService(info.connection);
                    }
                }
            }

            Intent intent = new Intent(mConfig.serviceInterface);
            intent.setComponent(name);

            intent.putExtra(Intent.EXTRA_CLIENT_LABEL, mConfig.clientLabel);

            final PendingIntent pendingIntent = PendingIntent.getActivity(
                    mContext, 0, new Intent(mConfig.settingsAction), 0);
            intent.putExtra(Intent.EXTRA_CLIENT_INTENT, pendingIntent);

            ApplicationInfo appInfo = null;
            try {
                appInfo = mContext.getPackageManager().getApplicationInfo(
                        name.getPackageName(), 0);
            } catch (NameNotFoundException e) {
                // Ignore if the package doesn't exist we won't be able to bind to the service.
            }
            final int targetSdkVersion =
                    appInfo != null ? appInfo.targetSdkVersion : Build.VERSION_CODES.BASE;

            try {
                if (DEBUG) Slog.v(TAG, "binding: " + intent);
                if (!mContext.bindServiceAsUser(intent,
                        new ServiceConnection() {
                            IInterface mService;

                            @Override
                            public void onServiceConnected(ComponentName name, IBinder binder) {
                                boolean added = false;
                                ManagedServiceInfo info = null;
                                synchronized (mMutex) {
                                    mServicesBinding.remove(servicesBindingTag);
                                    try {
                                        mService = asInterface(binder);
                                        info = newServiceInfo(mService, name,
                                                userid, false /*isSystem*/, this, targetSdkVersion);
                                        binder.linkToDeath(info, 0);
                                        added = mServices.add(info);
                                    } catch (RemoteException e) {
                                        // already dead
                                    }
                                }
                                if (added) {
                                    onServiceAdded(info);
                                }
                            }

                            @Override
                            public void onServiceDisconnected(ComponentName name) {
                                Slog.v(TAG, getCaption() + " connection lost: " + name);
                            }
                        },
                        Context.BIND_AUTO_CREATE,
                        new UserHandle(userid)))
                {
                    mServicesBinding.remove(servicesBindingTag);
                    Slog.w(TAG, "Unable to bind " + getCaption() + " service: " + intent);
                    return;
                }
            } catch (SecurityException ex) {
                Slog.e(TAG, "Unable to bind " + getCaption() + " service: " + intent, ex);
                return;
            }
        }
    
private com.android.server.notification.ManagedServices$ManagedServiceInforegisterServiceImpl(android.os.IInterface service, android.content.ComponentName component, int userid)

        synchronized (mMutex) {
            try {
                ManagedServiceInfo info = newServiceInfo(service, component, userid,
                        true /*isSystem*/, null, Build.VERSION_CODES.LOLLIPOP);
                service.asBinder().linkToDeath(info, 0);
                mServices.add(info);
                return info;
            } catch (RemoteException e) {
                // already dead
            }
        }
        return null;
    
private com.android.server.notification.ManagedServices$ManagedServiceInforemoveServiceImpl(android.os.IInterface service, int userid)
Removes a service from the list but does not unbind

return
the removed service.

        if (DEBUG) Slog.d(TAG, "removeServiceImpl service=" + service + " u=" + userid);
        ManagedServiceInfo serviceInfo = null;
        synchronized (mMutex) {
            final int N = mServices.size();
            for (int i=N-1; i>=0; i--) {
                final ManagedServiceInfo info = mServices.get(i);
                if (info.service.asBinder() == service.asBinder()
                        && info.userid == userid) {
                    if (DEBUG) Slog.d(TAG, "Removing active service " + info.component);
                    serviceInfo = removeServiceLocked(i);
                }
            }
        }
        return serviceInfo;
    
private com.android.server.notification.ManagedServices$ManagedServiceInforemoveServiceLocked(int i)

        final ManagedServiceInfo info = mServices.remove(i);
        onServiceRemovedLocked(info);
        return info;
    
public voidunregisterService(android.os.IInterface service, int userid)

        checkNotNull(service);
        // no need to check permissions; if your service binder is in the list,
        // that's proof that you had permission to add it in the first place
        unregisterServiceImpl(service, userid);
    
private voidunregisterService(android.content.ComponentName name, int userid)
Remove a service for the given user by ComponentName

        synchronized (mMutex) {
            final int N = mServices.size();
            for (int i=N-1; i>=0; i--) {
                final ManagedServiceInfo info = mServices.get(i);
                if (name.equals(info.component)
                        && info.userid == userid) {
                    removeServiceLocked(i);
                    if (info.connection != null) {
                        try {
                            mContext.unbindService(info.connection);
                        } catch (IllegalArgumentException ex) {
                            // something happened to the service: we think we have a connection
                            // but it's bogus.
                            Slog.e(TAG, getCaption() + " " + name + " could not be unbound: " + ex);
                        }
                    }
                }
            }
        }
    
private voidunregisterServiceImpl(android.os.IInterface service, int userid)
Removes a service from the list and unbinds.

        ManagedServiceInfo info = removeServiceImpl(service, userid);
        if (info != null && info.connection != null) {
            mContext.unbindService(info.connection);
        }