15789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler/*
25789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler * Copyright (C) 2015 The Android Open Source Project
35789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler *
45789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler * Licensed under the Apache License, Version 2.0 (the "License");
55789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler * you may not use this file except in compliance with the License.
65789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler * You may obtain a copy of the License at
75789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler *
85789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler *      http://www.apache.org/licenses/LICENSE-2.0
95789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler *
105789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler * Unless required by applicable law or agreed to in writing, software
115789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler * distributed under the License is distributed on an "AS IS" BASIS,
125789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler * See the License for the specific language governing permissions and
145789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler * limitations under the License
155789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler */
165789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler
175789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantlerpackage com.android.settingslib.accessibility;
185789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler
195fb3a7f6664e2009ef4931634c720284a33a693eTony Mantlerimport android.accessibilityservice.AccessibilityServiceInfo;
205789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantlerimport android.content.ComponentName;
215789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantlerimport android.content.Context;
225fb3a7f6664e2009ef4931634c720284a33a693eTony Mantlerimport android.content.pm.ResolveInfo;
23c09a02198718761e20e351f4bf0ee9e30a716d11Phil Weaverimport android.content.pm.ServiceInfo;
245789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantlerimport android.content.res.Configuration;
255789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantlerimport android.content.res.Resources;
2619df1284d9a545df044815adf57cf5c160d306fdMuyuan Liimport android.os.UserHandle;
275789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantlerimport android.provider.Settings;
285789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantlerimport android.text.TextUtils;
295fb3a7f6664e2009ef4931634c720284a33a693eTony Mantlerimport android.util.ArraySet;
305fb3a7f6664e2009ef4931634c720284a33a693eTony Mantlerimport android.view.accessibility.AccessibilityManager;
315789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler
32106fe732050f3d75a08c3bc48fdbcf84cac20b41Phil Weaverimport com.android.internal.R;
33106fe732050f3d75a08c3bc48fdbcf84cac20b41Phil Weaver
345789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantlerimport java.util.Collections;
355789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantlerimport java.util.HashSet;
365fb3a7f6664e2009ef4931634c720284a33a693eTony Mantlerimport java.util.List;
375789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantlerimport java.util.Locale;
385789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantlerimport java.util.Set;
395789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler
405789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantlerpublic class AccessibilityUtils {
415789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler    public static final char ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR = ':';
425789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler
435789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler    final static TextUtils.SimpleStringSplitter sStringColonSplitter =
445789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler            new TextUtils.SimpleStringSplitter(ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR);
455789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler
465789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler    /**
4719df1284d9a545df044815adf57cf5c160d306fdMuyuan Li     * @return the set of enabled accessibility services. If there are no services,
4819df1284d9a545df044815adf57cf5c160d306fdMuyuan Li     * it returns the unmodifiable {@link Collections#emptySet()}.
495789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler     */
505789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler    public static Set<ComponentName> getEnabledServicesFromSettings(Context context) {
5119df1284d9a545df044815adf57cf5c160d306fdMuyuan Li        return getEnabledServicesFromSettings(context, UserHandle.myUserId());
5219df1284d9a545df044815adf57cf5c160d306fdMuyuan Li    }
5319df1284d9a545df044815adf57cf5c160d306fdMuyuan Li
54c09a02198718761e20e351f4bf0ee9e30a716d11Phil Weaver    public static boolean hasServiceCrashed(String packageName, String serviceName,
55c09a02198718761e20e351f4bf0ee9e30a716d11Phil Weaver            List<AccessibilityServiceInfo> enabledServiceInfos) {
56c09a02198718761e20e351f4bf0ee9e30a716d11Phil Weaver        for (int i = 0; i < enabledServiceInfos.size(); i++) {
57c09a02198718761e20e351f4bf0ee9e30a716d11Phil Weaver            AccessibilityServiceInfo accessibilityServiceInfo = enabledServiceInfos.get(i);
58c09a02198718761e20e351f4bf0ee9e30a716d11Phil Weaver            final ServiceInfo serviceInfo = enabledServiceInfos.get(i).getResolveInfo().serviceInfo;
59c09a02198718761e20e351f4bf0ee9e30a716d11Phil Weaver            if (TextUtils.equals(serviceInfo.packageName, packageName)
60c09a02198718761e20e351f4bf0ee9e30a716d11Phil Weaver                    && TextUtils.equals(serviceInfo.name, serviceName)) {
61c09a02198718761e20e351f4bf0ee9e30a716d11Phil Weaver                return accessibilityServiceInfo.crashed;
62c09a02198718761e20e351f4bf0ee9e30a716d11Phil Weaver            }
63c09a02198718761e20e351f4bf0ee9e30a716d11Phil Weaver        }
64c09a02198718761e20e351f4bf0ee9e30a716d11Phil Weaver        return false;
65c09a02198718761e20e351f4bf0ee9e30a716d11Phil Weaver    }
66c09a02198718761e20e351f4bf0ee9e30a716d11Phil Weaver
6719df1284d9a545df044815adf57cf5c160d306fdMuyuan Li    /**
6819df1284d9a545df044815adf57cf5c160d306fdMuyuan Li     * @return the set of enabled accessibility services for {@param userId}. If there are no
6919df1284d9a545df044815adf57cf5c160d306fdMuyuan Li     * services, it returns the unmodifiable {@link Collections#emptySet()}.
7019df1284d9a545df044815adf57cf5c160d306fdMuyuan Li     */
7119df1284d9a545df044815adf57cf5c160d306fdMuyuan Li    public static Set<ComponentName> getEnabledServicesFromSettings(Context context, int userId) {
7219df1284d9a545df044815adf57cf5c160d306fdMuyuan Li        final String enabledServicesSetting = Settings.Secure.getStringForUser(
7319df1284d9a545df044815adf57cf5c160d306fdMuyuan Li                context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
7419df1284d9a545df044815adf57cf5c160d306fdMuyuan Li                userId);
755789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler        if (enabledServicesSetting == null) {
765789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler            return Collections.emptySet();
775789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler        }
785789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler
795fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler        final Set<ComponentName> enabledServices = new HashSet<>();
805789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler        final TextUtils.SimpleStringSplitter colonSplitter = sStringColonSplitter;
815789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler        colonSplitter.setString(enabledServicesSetting);
825789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler
835789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler        while (colonSplitter.hasNext()) {
845789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler            final String componentNameString = colonSplitter.next();
855789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler            final ComponentName enabledService = ComponentName.unflattenFromString(
865789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler                    componentNameString);
875789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler            if (enabledService != null) {
885789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler                enabledServices.add(enabledService);
895789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler            }
905789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler        }
915789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler
925789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler        return enabledServices;
935789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler    }
945789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler
955789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler    /**
965789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler     * @return a localized version of the text resource specified by resId
975789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler     */
985789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler    public static CharSequence getTextForLocale(Context context, Locale locale, int resId) {
995789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler        final Resources res = context.getResources();
1005789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler        final Configuration config = new Configuration(res.getConfiguration());
1015789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler        config.setLocale(locale);
1025789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler        final Context langContext = context.createConfigurationContext(config);
1035789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler        return langContext.getText(resId);
1045789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler    }
1055fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler
10619df1284d9a545df044815adf57cf5c160d306fdMuyuan Li    /**
10719df1284d9a545df044815adf57cf5c160d306fdMuyuan Li     * Changes an accessibility component's state.
10819df1284d9a545df044815adf57cf5c160d306fdMuyuan Li     */
1095fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler    public static void setAccessibilityServiceState(Context context, ComponentName toggledService,
1105fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler            boolean enabled) {
11119df1284d9a545df044815adf57cf5c160d306fdMuyuan Li        setAccessibilityServiceState(context, toggledService, enabled, UserHandle.myUserId());
11219df1284d9a545df044815adf57cf5c160d306fdMuyuan Li    }
11319df1284d9a545df044815adf57cf5c160d306fdMuyuan Li
11419df1284d9a545df044815adf57cf5c160d306fdMuyuan Li    /**
11519df1284d9a545df044815adf57cf5c160d306fdMuyuan Li     * Changes an accessibility component's state for {@param userId}.
11619df1284d9a545df044815adf57cf5c160d306fdMuyuan Li     */
11719df1284d9a545df044815adf57cf5c160d306fdMuyuan Li    public static void setAccessibilityServiceState(Context context, ComponentName toggledService,
11819df1284d9a545df044815adf57cf5c160d306fdMuyuan Li            boolean enabled, int userId) {
1195fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler        // Parse the enabled services.
1205fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler        Set<ComponentName> enabledServices = AccessibilityUtils.getEnabledServicesFromSettings(
12119df1284d9a545df044815adf57cf5c160d306fdMuyuan Li                context, userId);
1225fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler
1235fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler        if (enabledServices.isEmpty()) {
1245fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler            enabledServices = new ArraySet<>(1);
1255fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler        }
1265fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler
1275fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler        // Determine enabled services and accessibility state.
1285fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler        boolean accessibilityEnabled = false;
1295fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler        if (enabled) {
1305fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler            enabledServices.add(toggledService);
1315fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler            // Enabling at least one service enables accessibility.
1325fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler            accessibilityEnabled = true;
1335fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler        } else {
1345fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler            enabledServices.remove(toggledService);
1355fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler            // Check how many enabled and installed services are present.
1365fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler            Set<ComponentName> installedServices = getInstalledServices(context);
1375fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler            for (ComponentName enabledService : enabledServices) {
1385fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler                if (installedServices.contains(enabledService)) {
1395fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler                    // Disabling the last service disables accessibility.
1405fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler                    accessibilityEnabled = true;
1415fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler                    break;
1425fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler                }
1435fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler            }
1445fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler        }
1455fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler
1465fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler        // Update the enabled services setting.
1475fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler        StringBuilder enabledServicesBuilder = new StringBuilder();
1485fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler        // Keep the enabled services even if they are not installed since we
1495fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler        // have no way to know whether the application restore process has
1505fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler        // completed. In general the system should be responsible for the
1515fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler        // clean up not settings.
1525fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler        for (ComponentName enabledService : enabledServices) {
1535fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler            enabledServicesBuilder.append(enabledService.flattenToString());
1545fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler            enabledServicesBuilder.append(
1555fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler                    AccessibilityUtils.ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR);
1565fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler        }
1575fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler        final int enabledServicesBuilderLength = enabledServicesBuilder.length();
1585fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler        if (enabledServicesBuilderLength > 0) {
1595fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler            enabledServicesBuilder.deleteCharAt(enabledServicesBuilderLength - 1);
1605fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler        }
16119df1284d9a545df044815adf57cf5c160d306fdMuyuan Li        Settings.Secure.putStringForUser(context.getContentResolver(),
1625fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
16319df1284d9a545df044815adf57cf5c160d306fdMuyuan Li                enabledServicesBuilder.toString(), userId);
1645fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler    }
1655fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler
166106fe732050f3d75a08c3bc48fdbcf84cac20b41Phil Weaver    /**
167106fe732050f3d75a08c3bc48fdbcf84cac20b41Phil Weaver     * Get the name of the service that should be toggled by the accessibility shortcut. Use
168106fe732050f3d75a08c3bc48fdbcf84cac20b41Phil Weaver     * an OEM-configurable default if the setting has never been set.
169106fe732050f3d75a08c3bc48fdbcf84cac20b41Phil Weaver     *
170106fe732050f3d75a08c3bc48fdbcf84cac20b41Phil Weaver     * @param context A valid context
171106fe732050f3d75a08c3bc48fdbcf84cac20b41Phil Weaver     * @param userId The user whose settings should be checked
172106fe732050f3d75a08c3bc48fdbcf84cac20b41Phil Weaver     *
173106fe732050f3d75a08c3bc48fdbcf84cac20b41Phil Weaver     * @return The component name, flattened to a string, of the target service.
174106fe732050f3d75a08c3bc48fdbcf84cac20b41Phil Weaver     */
175106fe732050f3d75a08c3bc48fdbcf84cac20b41Phil Weaver    public static String getShortcutTargetServiceComponentNameString(
176106fe732050f3d75a08c3bc48fdbcf84cac20b41Phil Weaver            Context context, int userId) {
177106fe732050f3d75a08c3bc48fdbcf84cac20b41Phil Weaver        final String currentShortcutServiceId = Settings.Secure.getStringForUser(
178106fe732050f3d75a08c3bc48fdbcf84cac20b41Phil Weaver                context.getContentResolver(), Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
179106fe732050f3d75a08c3bc48fdbcf84cac20b41Phil Weaver                userId);
180106fe732050f3d75a08c3bc48fdbcf84cac20b41Phil Weaver        if (currentShortcutServiceId != null) {
181106fe732050f3d75a08c3bc48fdbcf84cac20b41Phil Weaver            return currentShortcutServiceId;
182106fe732050f3d75a08c3bc48fdbcf84cac20b41Phil Weaver        }
183106fe732050f3d75a08c3bc48fdbcf84cac20b41Phil Weaver        return context.getString(R.string.config_defaultAccessibilityService);
184106fe732050f3d75a08c3bc48fdbcf84cac20b41Phil Weaver    }
185106fe732050f3d75a08c3bc48fdbcf84cac20b41Phil Weaver
186ce687c5e4a0ce27e9190fb5c823ec4649fbc3712Phil Weaver    /**
187ce687c5e4a0ce27e9190fb5c823ec4649fbc3712Phil Weaver     * Check if the accessibility shortcut is enabled for a user
188ce687c5e4a0ce27e9190fb5c823ec4649fbc3712Phil Weaver     *
189ce687c5e4a0ce27e9190fb5c823ec4649fbc3712Phil Weaver     * @param context A valid context
190ce687c5e4a0ce27e9190fb5c823ec4649fbc3712Phil Weaver     * @param userId The user of interest
191ce687c5e4a0ce27e9190fb5c823ec4649fbc3712Phil Weaver     * @return {@code true} if the shortcut is enabled for the user. {@code false} otherwise.
192ce687c5e4a0ce27e9190fb5c823ec4649fbc3712Phil Weaver     *         Note that the shortcut may be enabled, but no action associated with it.
193ce687c5e4a0ce27e9190fb5c823ec4649fbc3712Phil Weaver     */
194ce687c5e4a0ce27e9190fb5c823ec4649fbc3712Phil Weaver    public static boolean isShortcutEnabled(Context context, int userId) {
195ce687c5e4a0ce27e9190fb5c823ec4649fbc3712Phil Weaver        return Settings.Secure.getIntForUser(context.getContentResolver(),
196ce687c5e4a0ce27e9190fb5c823ec4649fbc3712Phil Weaver                Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED, 1, userId) == 1;
197ce687c5e4a0ce27e9190fb5c823ec4649fbc3712Phil Weaver    }
198ce687c5e4a0ce27e9190fb5c823ec4649fbc3712Phil Weaver
1995fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler    private static Set<ComponentName> getInstalledServices(Context context) {
2005fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler        final Set<ComponentName> installedServices = new HashSet<>();
2015fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler        installedServices.clear();
2025fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler
2035fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler        final List<AccessibilityServiceInfo> installedServiceInfos =
2045fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler                AccessibilityManager.getInstance(context)
2055fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler                        .getInstalledAccessibilityServiceList();
2065fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler        if (installedServiceInfos == null) {
2075fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler            return installedServices;
2085fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler        }
2095fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler
2105fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler        for (final AccessibilityServiceInfo info : installedServiceInfos) {
2115fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler            final ResolveInfo resolveInfo = info.getResolveInfo();
2125fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler            final ComponentName installedService = new ComponentName(
2135fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler                    resolveInfo.serviceInfo.packageName,
2145fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler                    resolveInfo.serviceInfo.name);
2155fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler            installedServices.add(installedService);
2165fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler        }
2175fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler        return installedServices;
2185fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler    }
2195fb3a7f6664e2009ef4931634c720284a33a693eTony Mantler
2205789c3e24f0963bfc85477bfbbfe93be88946024Tony Mantler}
221