Merge "Add Text-to-Speech annotations to import/export menu phone numbers" into lmp-mr1-dev
diff --git a/src/com/android/contacts/common/model/account/ExternalAccountType.java b/src/com/android/contacts/common/model/account/ExternalAccountType.java
index e4cef52..53089b8 100644
--- a/src/com/android/contacts/common/model/account/ExternalAccountType.java
+++ b/src/com/android/contacts/common/model/account/ExternalAccountType.java
@@ -17,9 +17,10 @@
 package com.android.contacts.common.model.account;
 
 import android.content.Context;
-import android.content.pm.PackageInfo;
+import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -48,12 +49,15 @@
 public class ExternalAccountType extends BaseAccountType {
     private static final String TAG = "ExternalAccountType";
 
+    private static final String SYNC_META_DATA = "android.content.SyncAdapter";
+
     /**
      * The metadata name for so-called "contacts.xml".
      *
      * On LMP and later, we also accept the "alternate" name.
      * This is to allow sync adapters to have a contacts.xml without making it visible on older
-     * platforms.
+     * platforms. If you modify this also update the corresponding list in
+     * ContactsProvider/PhotoPriorityResolver
      */
     private static final String[] METADATA_CONTACTS_NAMES = new String[] {
             "android.provider.ALTERNATE_CONTACTS_STRUCTURE",
@@ -114,15 +118,9 @@
         this.resourcePackageName = packageName;
         this.syncAdapterPackageName = packageName;
 
-        final PackageManager pm = context.getPackageManager();
         final XmlResourceParser parser;
         if (injectedMetadata == null) {
-            try {
-                parser = loadContactsXml(context, packageName);
-            } catch (NameNotFoundException e1) {
-                // If the package name is not found, we can't initialize this account type.
-                return;
-            }
+            parser = loadContactsXml(context, packageName);
         } else {
             parser = injectedMetadata;
         }
@@ -181,35 +179,41 @@
     /**
      * Returns the CONTACTS_STRUCTURE metadata (aka "contacts.xml") in the given apk package.
      *
-     * Unfortunately, there's no public way to determine which service defines a sync service for
-     * which account type, so this method looks through all services in the package, and just
-     * returns the first CONTACTS_STRUCTURE metadata defined in any of them.
+     * This method looks through all services in the package that handle sync adapter
+     * intents for the first one that contains CONTACTS_STRUCTURE metadata. We have to look
+     * through all sync adapters in the package in case there are contacts and other sync
+     * adapters (eg, calendar) in the same package.
      *
      * Returns {@code null} if the package has no CONTACTS_STRUCTURE metadata.  In this case
      * the account type *will* be initialized with minimal configuration.
-     *
-     * On the other hand, if the package is not found, it throws a {@link NameNotFoundException},
-     * in which case the account type will *not* be initialized.
      */
-    private XmlResourceParser loadContactsXml(Context context, String resPackageName)
-            throws NameNotFoundException {
+    public static XmlResourceParser loadContactsXml(Context context, String resPackageName) {
         final PackageManager pm = context.getPackageManager();
-        PackageInfo packageInfo = pm.getPackageInfo(resPackageName,
-                PackageManager.GET_SERVICES|PackageManager.GET_META_DATA);
-        for (ServiceInfo serviceInfo : packageInfo.services) {
-            for (String metadataName : METADATA_CONTACTS_NAMES) {
-                final XmlResourceParser parser = serviceInfo.loadXmlMetaData(pm,
-                        metadataName);
-                if (parser != null) {
-                    if (Log.isLoggable(TAG, Log.DEBUG)) {
-                        Log.d(TAG, String.format("Metadata loaded from: %s, %s, %s",
-                                serviceInfo.packageName, serviceInfo.name,
-                                metadataName));
+        final Intent intent = new Intent(SYNC_META_DATA).setPackage(resPackageName);
+        final List<ResolveInfo> intentServices = pm.queryIntentServices(intent,
+                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
+
+        if (intentServices != null) {
+            for (final ResolveInfo resolveInfo : intentServices) {
+                final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+                if (serviceInfo == null) {
+                    continue;
+                }
+                for (String metadataName : METADATA_CONTACTS_NAMES) {
+                    final XmlResourceParser parser = serviceInfo.loadXmlMetaData(
+                            pm, metadataName);
+                    if (parser != null) {
+                        if (Log.isLoggable(TAG, Log.DEBUG)) {
+                            Log.d(TAG, String.format("Metadata loaded from: %s, %s, %s",
+                                    serviceInfo.packageName, serviceInfo.name,
+                                    metadataName));
+                        }
+                        return parser;
                     }
-                    return parser;
                 }
             }
         }
+
         // Package was found, but that doesn't contain the CONTACTS_STRUCTURE metadata.
         return null;
     }
diff --git a/src/com/android/contacts/common/util/LocalizedNameResolver.java b/src/com/android/contacts/common/util/LocalizedNameResolver.java
index 3c21946..92104c4 100644
--- a/src/com/android/contacts/common/util/LocalizedNameResolver.java
+++ b/src/com/android/contacts/common/util/LocalizedNameResolver.java
@@ -19,10 +19,8 @@
 import android.accounts.AccountManager;
 import android.accounts.AuthenticatorDescription;
 import android.content.Context;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ServiceInfo;
 import android.content.res.Resources;
 import android.content.res.Resources.NotFoundException;
 import android.content.res.TypedArray;
@@ -32,6 +30,7 @@
 import android.util.Xml;
 
 import com.android.contacts.common.R;
+import com.android.contacts.common.model.account.ExternalAccountType;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -45,11 +44,6 @@
 public class LocalizedNameResolver  {
     private static final String TAG = "LocalizedNameResolver";
 
-    /**
-     * Meta-data key for the contacts configuration associated with a sync service.
-     */
-    private static final String METADATA_CONTACTS = "android.provider.CONTACTS_STRUCTURE";
-
     private static final String CONTACTS_DATA_KIND = "ContactsDataKind";
 
     /**
@@ -82,20 +76,9 @@
      * reads the picture priority from that file.
      */
     private static String resolveAllContactsNameFromMetaData(Context context, String packageName) {
-        final PackageManager pm = context.getPackageManager();
-        try {
-            PackageInfo pi = pm.getPackageInfo(packageName, PackageManager.GET_SERVICES
-                    | PackageManager.GET_META_DATA);
-            if (pi != null && pi.services != null) {
-                for (ServiceInfo si : pi.services) {
-                    final XmlResourceParser parser = si.loadXmlMetaData(pm, METADATA_CONTACTS);
-                    if (parser != null) {
-                        return loadAllContactsNameFromXml(context, parser, packageName);
-                    }
-                }
-            }
-        } catch (NameNotFoundException e) {
-            Log.w(TAG, "Problem loading \"All Contacts\"-name: " + e.toString());
+        final XmlResourceParser parser = ExternalAccountType.loadContactsXml(context, packageName);
+        if (parser != null) {
+            return loadAllContactsNameFromXml(context, parser, packageName);
         }
         return null;
     }