# 调用queryContactsByPhoneNumber接口异常怎么解决

#### 问题现象

开发者在使用Contacts Kit开发联系人相关功能时会遇到以下常见问题：

* 参照官方文档在AGC中已经配置了通讯录读写权限，工程中也配置了通讯录读权限，但是在启动调试或运行应用/服务时，安装HAP出现错误，提示：

  ```
  error: install failed due to grant request permissions failed
  ```

* 根据电话号码使用contact.queryContactsByPhoneNumber查出来的联系人缺少name信息。  

#### 背景知识

Contacts Kit可以帮助开发者轻松实现联系人的增删改查等功能。该Kit提供了一系列API，可以让开发者在应用中快速集成联系人管理功能，详情请参考[@ohos.contact API](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-contact)。  

#### 问题定位

1. 检查ACL受限权限是否申请成功，生成Profile时是否进行勾选对应受限权限。然后检查手动签名是否更新了Profile文件等签名信息。
2. 检查接口使用是否正确，接口返回值是否有name字段。  

#### 分析结论

1. ohos.permission.READ_CONTACTS是受限开放权限，授权方式是用户授权（user_grant）。该问题是由于默认应用等级为normal，只能使用normal等级的权限，如果使用了system_basic、system_core等级或受限开放的权限，将导致报错。
2. [contact.queryContactsByPhoneNumber](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-contact#contactquerycontactsbyphonenumber10-2)接口仅返回联系人信息中的id、key、phoneNumbers属性。
   * 如果要查询联系人的所有信息，建议使用[contact.queryContact](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-contact#contactquerycontact10-3)接口，根据contact.queryContactsByPhoneNumber接口返回的属性key查询。
* contact.queryContact有多个入参不同的同名接口，详情参考[@ohos.contact API](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-contact)。其中入参含有attrs联系人的属性列表的接口需要注意，attrs不传默认查询所有联系人属性，如果传了就需要包含contact.Attribute.ATTR_NAME接口才会返回name。  

#### 修改建议

查询联系人信息有两种方式，一种是使用Picker选择联系人获取信息，另一种是使用[@ohos.contact API](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-contact)查询联系人信息：

* 使用Picker选择联系人： 通过Picker的方式，拉起联系人列表，引导用户完成界面操作，接口本身无需申请权限，因此推荐使用这种方式。

  ```
  // 1.调用联系人接口，拉起联系人列表，用户点击对应的联系人后返回
  if (canIUse('SystemCapability.Applications.Contacts')) {
    contact.selectContacts({
      isMultiSelect: false
    }, (err: BusinessError, data) => {
      if (err) {
        console.error('selectContact callback, errCode:' + err.code + ', errMessage:' + err.message);
        return;
      }
      // 2.完成操作，返回想要的data数据
      console.info(`selectContact callback: success data->${JSON.stringify(data)}`);
    });
  } else {
    console.error('The current device does not support contact queries.');
  }
  ```

* 使用[@ohos.contact API](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-contact)查询联系人信息：
  * Contacts Kit当前能力受限开放，需要申请受限开放权限ohos.permission.READ_CONTACTS或ohos.permission.WRITE_CONTACTS。该权限通常不允许三方应用申请，仅符合指定场景的应用可申请该权限。
    * 申请方式请参考：[申请使用受限权限](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/declare-permissions-in-acl)。
    * ACL提权参考：[申请ACL权限](https://developer.huawei.com/consumer/cn/doc/app/agc-help-apply-acl-0000002394212138)。
  * 使用[@ohos.contact API](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-contact)查询联系人信息：此方式需要关注使用的接口的入参是否含有attrs联系人的属性列表。以contact.queryContact接口为例，attrs不传默认查询所有联系人属性，如果传了就需要包含contact.Attribute.ATTR_NAME接口才会返回name。

    ```
    let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
    const permissions: Array<Permissions> = ['ohos.permission.READ_CONTACTS'];
    abilityAccessCtrl.createAtManager()
      .requestPermissionsFromUser(context, permissions)
      .then((result: PermissionRequestResult) => {
        if (result.authResults[0] !== 0) { // 0 表示请求权限成功，其他任何非零值表示请求失败
          console.error('request contact permissions failed');
          return;
        }
        if (canIUse('SystemCapability.Applications.ContactsData')) {
          contact.queryContactsByPhoneNumber(context, '138xxxxxxxx', {
            attributes: [contact.Attribute.ATTR_NAME, contact.Attribute.ATTR_PHONE]
          }, (err: BusinessError, data) => {
            if (err) {
              console.error(`Failed to query Contacts By PhoneNumber. Code: ${err.code}, message: ${err.message}`);
              return;
            }
            console.info(`Succeeded in querying Contacts By PhoneNumber. data->${JSON.stringify(data)}`);
            if (data.length !== 0) {
              contact.queryContact(context, data[0].key, {
                attributes: [contact.Attribute.ATTR_NAME, contact.Attribute.ATTR_PHONE]
              }, (err: BusinessError, data) => {
                if (err) {
                  console.error(`Failed to query Contact. Code: ${err.code}, message: ${err.message}`);
                  return;
                }
                console.info(`Succeeded in querying Contact. data->${JSON.stringify(data)}`);
              });
            }
          });
        } else {
          console.error('The current device does not support contact queries.');
        }
      })
      .catch((err: BusinessError) => {
        console.error(`Failed to queryContactsByPhoneNumber. Code: ${err.code}, message: ${err.message}`);
      });
    ```

完整示例参考如下：

```
import { BusinessError } from '@kit.BasicServicesKit';
import { contact } from '@kit.ContactsKit';
import { abilityAccessCtrl, common, PermissionRequestResult, Permissions } from '@kit.AbilityKit';

@Entry
@Component
struct ContactExample {
  build() {
    Column({ space: 20 }) {
      Button('使用Picker选择联系人')
        .onClick(() => {
          // 1.调用联系人接口，拉起联系人列表，用户点击对应的联系人后返回
          if (canIUse('SystemCapability.Applications.Contacts')) {
            contact.selectContacts({
              isMultiSelect: false
            }, (err: BusinessError, data) => {
              if (err) {
                console.error('selectContact callback, errCode:' + err.code + ', errMessage:' + err.message);
                return;
              }
              // 2.完成操作，返回想要的data数据
              console.info(`selectContact callback: success data->${JSON.stringify(data)}`);
            });
          } else {
            console.error('The current device does not support contact queries.');
          }
        });

      Button('根据电话号码和attrs查询联系人')
        .onClick(() => {
          let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
          const permissions: Array<Permissions> = ['ohos.permission.READ_CONTACTS'];
          abilityAccessCtrl.createAtManager()
            .requestPermissionsFromUser(context, permissions)
            .then((result: PermissionRequestResult) => {
              if (result.authResults[0] !== 0) { // 0 表示请求权限成功，其他任何非零值表示请求失败
                console.error('request contact permissions failed');
                return;
              }
              if (canIUse('SystemCapability.Applications.ContactsData')) {
                contact.queryContactsByPhoneNumber(context, '138xxxxxxxx', {
                  attributes: [contact.Attribute.ATTR_NAME, contact.Attribute.ATTR_PHONE]
                }, (err: BusinessError, data) => {
                  if (err) {
                    console.error(`Failed to query Contacts By PhoneNumber. Code: ${err.code}, message: ${err.message}`);
                    return;
                  }
                  console.info(`Succeeded in querying Contacts By PhoneNumber. data->${JSON.stringify(data)}`);
                  if (data.length !== 0) {
                    contact.queryContact(context, data[0].key, {
                      attributes: [contact.Attribute.ATTR_NAME, contact.Attribute.ATTR_PHONE]
                    }, (err: BusinessError, data) => {
                      if (err) {
                        console.error(`Failed to query Contact. Code: ${err.code}, message: ${err.message}`);
                        return;
                      }
                      console.info(`Succeeded in querying Contact. data->${JSON.stringify(data)}`);
                    });
                  }
                });
              } else {
                console.error('The current device does not support contact queries.');
              }
            })
            .catch((err: BusinessError) => {
              console.error(`Failed to queryContactsByPhoneNumber. Code: ${err.code}, message: ${err.message}`);
            });
        });
    }
    .height('100%')
    .width('100%')
    .justifyContent(FlexAlign.Center);
  }
}
```

