Android 17 ships a new Contact Picker that changes how apps access contact data. Instead of requesting broad READ_CONTACTS permission, apps now specify exactly which fields they need and users select which contacts to share.
What the Contact Picker Actually Does
The Contact Picker is a standard, browsable interface for sharing contacts. It preserves privacy by design: users choose specific contacts and grant access only to the fields an app has requested.
This contrasts with the traditional READ_CONTACTS permission, which grants full access to all contact data—names, phone numbers, emails—after a one-time authorization. That permission carries a "dangerous" protection level under Android's classification system.
With the new picker, users see which data fields an app needs and select specific contacts to share. The app reads only what was selected.
| Feature | READ_CONTACTS | Contact Picker |
|---|---|---|
| Access scope | All contacts, all fields | Selected contacts, requested fields only |
| Authorization | One-time runtime grant | Per-session user selection |
| Data returned | Full database read | Specific URIs |
| User control | Binary (grant or deny) | Granular, per-contact |
| Background access | Yes | No |
How to Implement the Contact Picker
The implementation uses ACTION_PICK_CONTACTS with Intent.EXTRA_REQUESTED_DATA_FIELDS to declare which MIME types your app needs. Multiple data fields can be requested simultaneously through an ArrayList of MIME types defined in ContactsContract.CommonDataKinds.
Key features built into the picker include search, profile switching, and multi-select support. The EXTRA_ALLOW_MULTIPLE flag enables selecting multiple contacts, while EXTRA_PICK_CONTACTS_SELECTION_LIMIT restricts the maximum number that can be chosen. To filter results to only contacts containing all requested data fields, set EXTRA_PICK_CONTACTS_MATCH_ALL_DATA_FIELDS to true.
Our Analysis: Why This Privacy Model Matters
The old permission model forced users into a binary choice: grant broad access or skip the feature entirely. READ_CONTACTS lets apps read the entire contact database in the background, long after initial authorization.
The Contact Picker flips this to a demand model. Apps request specific fields for specific contacts. Users see exactly what's being asked and can decline without losing the app—they just can't complete that particular action.
For users, this is a meaningful privacy improvement. They retain control over each data-sharing interaction rather than making a one-time decision that persists indefinitely.
For developers, the tradeoff is implementation complexity. The picker requires handling asynchronous results via ActivityResultLauncher and parsing returned URIs through a ContentResolver query. But the privacy benefit for users is substantial, and apps targeting Android 17 and above should treat Contact Picker as the default approach for any contact access need.
There's also a practical upside: privacy-conscious users are more likely to engage with features built this way. A messaging app asking for READ_CONTACTS access often triggers suspicion. Asking to "pick a contact" feels different.
Practical Example: Building a Contact Sharing Feature
Suppose you're building a messaging app that needs to let users share contact details. You declare that you need phone numbers and email addresses using the appropriate MIME types. Then you launch the Contact Picker with multi-select enabled.
The user searches their contacts, selects specific people, and confirms. Your app receives only those selected contacts and only the fields you requested. No background access, no database-wide read. If the user cancels, nothing happens.
For backward compatibility, Android 17 automatically upgrades existing ACTION_PICK_CONTACTS calls to the new picker interface. To test this behavior on older SDK versions, add the EXTRA_USE_SYSTEM_CONTACTS_PICKER flag.
Who Should Care
Developers building new contact features on Android 17+:** Contact Picker should be your default implementation.
Developers maintaining existing apps with READ_CONTACTS:** Consider migrating contact-access flows to the picker, especially for features where users are selecting specific people rather than needing bulk access.
Users concerned about app permissions:** Contact Picker gives you visibility into exactly what data an app receives and when.
Bottom Line
Android 17's Contact Picker is the right default for any app that needs contact data. Implement it using ActivityResultContracts with the appropriate MIME type declarations. For apps already using ACTION_PICK_CONTACTS, Android 17 automatically upgrades those calls to the new picker interface.
Key takeaways
- Contact Picker replaces broad READ_CONTACTS permission with specific, user-controlled data sharing
- Apps declare which fields they need; users choose which contacts to share
- Multi-select, search, and profile switching are built into the picker
- Android 17 automatically upgrades existing ACTION_PICK_CONTACTS calls
- Implementation requires handling async results and URI parsing, but privacy benefits justify the complexity
Frequently asked questions
What is the difference between READ_CONTACTS and the Contact Picker?
READ_CONTACTS grants full access to all contact data after one-time authorization. Contact Picker lets apps request specific fields for selected contacts, with users choosing what to share each time.
How do apps declare which contact fields they need?
Apps use Intent.EXTRA_REQUESTED_DATA_FIELDS with an ArrayList of MIME types defined in ContactsContract.CommonDataKinds—such as phone numbers and email addresses.
Does Contact Picker support multi-select?
Yes. Set EXTRA_ALLOW_MULTIPLE to true and optionally use EXTRA_PICK_CONTACTS_SELECTION_LIMIT to restrict how many contacts users can select.
How do you handle backward compatibility?
Android 17 and higher automatically upgrade existing ACTION_PICK_CONTACTS calls to the Contact Picker interface. For older SDKs, implement fallback logic or continue using the traditional approach.
How do you read data from the Contact Picker result?
Use the ContentResolver to query the returned URI with a projection specifying the columns you want—typically LOOKUP_KEY, MIMETYPE, and DATA1 fields.