Overview
ZDM implements a content provider interface, allowing apps to perform simple queries for information about sharing of files and data among apps on the device. The content provider URIs used to query ZDM are treated as query identifiers, which ZDM redirects to ZDMQueryInterface
implemented classes. The ZDMProviderConstants.java
object provides column names and other URI details required to query ZDM. Query results are returned in the COLUMN_QUERY_RESULT
column of the result URI.
Apps calling for access to another app's interface(s) must be explicitly granted permission by the implementing app, and ZDM manages those permissions. When an app attempts to use an interface that the implementing app has chosen to protect using a Delegation Scope, the implementing app must query ZDM to determine whether the calling app has been granted (by the administrator) a Delegation Scope that grants access to that interface. When a given function supported by ZDM or a Zebra app is controlled by a Delegation Scope, it allows only direct use of that function by a genuine instance of an app that has been explicitly granted that Delegation Scope.
When a genuine instance of an app is allowed direct use of a function controlled by a Delegation Scope, that app might choose to allow indirect use of that function by another application through a defined interface such as AIDL or a JavaScript callback. In such cases, the app that's allowed to use the function becomes responsible for ensuring that any use of that by another app is appropriately controlled.
Zebra-app Queries
Use one or more of the URIs in the table below to query Zebra applications on the device.
Action | URI |
---|---|
Confirm that token is valid for Delegation Scope | public static final String AUTHORITY = "com.zebra.devicemanager.zdmcontentprovider"; |
Get the authorization URI showing an app's (published) interfaces | public static final URI AUTHORITY_URI = Uri.parse("content://" + AUTHORITY); |
Acquire a token for access to an app's (published) interfaces | public static final URI ACQUIRE_TOKEN_URI = Uri.withAppendedPath(AUTHORITY_URI, "AcquireToken"); |
Verify that a token is acquired and valid | public static final URI VERIFY_TOKEN_URI = Uri.withAppendedPath(AUTHORITY_URI, "VerifyToken"); |
Verify that permission is granted to acquire token | public static final URI VERIFY_PERMISSION_URI = Uri.withAppendedPath(AUTHORITY_URI, "VerifyAppPermission"); |
Delegate content to be published | public static final URI DELEGATION_CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "Delegation"); |
Access Android System settings interfaces | public static final URI SYSTEM_SETTINGS_UI_URI = Uri.withAppendedPath(AUTHORITY_URI, "SystemSettingsUI"); |
Get signature | public static final String COLUMN_SIGNATURE = "signature"; |
Get package name | public static final String COLUMN_PACKAGE_NAME = "package_name"; |
Get query result | public static final String COLUMN_QUERY_RESULT = "query_result"; |
Get Delegation Scope | public static final String COLUMN_DELEGATION_SCOPE = "delegation_scope"; |
Get access token | public static final String COLUMN_TOKEN = "token"; |
Manifest of Calling Apps
Apps that call for access to protected interfaces of other apps must contain the following lines in their manifest.xml
file:
<uses-permission android:name="com.zebra.devicemanager.provider.READ_PERMISSION"/>
<uses-permission android:name="com.zebra.devicemanager.provider.WRITE_PERMISSION"/>
For Android 11 or later, the tag below also is required to specify the package name of the Content Provider (aka "implementing app"):
<queries>
<package android:name="com.zebra.devicemanager" />
</queries>
Before coding, Zebra recommends understanding guidance from the Android developer community about constructing the query.
Sample Code
The following code snippets illustrate how to use the URIs programmatically to achieve a desired result.
Delegation Query
This query determines whether the app has been granted the specified Delegation Scope. Use this code to verify that the calling app is bound to a service. The service must share the caller details, such as caller package name and signature, and the Delegation Scope defined by it.
Result Codes
- TRUE - The given caller is assigned the given Delegation Scope; the specified app is permitted to call the service**
- FALSE - Caller is NOT assigned the Delegation Scope specified in the query
Example
val cursor: Cursor = appContext.contentResolver.query(DELEGATION_CONTENT_URI, null,
COLUMN_PACKAGE_NAME + "=? AND " + COLUMN_SIGNATURE + "=? AND " + COLUMN_DELIGATION_SCOPE + "=?",
arrayOf("com.example.application", "hfoahdsohsgjhsjv=", "delegation-zebra-datawedge-intent-api"), null)!!
if (cursor != null && cursor.count > 0) {
cursor.moveToFirst()
val result = cursor.getString(cursor.getColumnIndex(COLUMN_QUERY_RESULT)))
}
Verify Token
This code confirms that a token allows calls to a specified service. For example, a receiver of an intent can verify whether the token sent by the caller app is valid. Since the intent receiver knows the Delegation Scope protects that operation, it must send both the token and the associated Delegation Scope.
Result Codes
- TRUE - the received token is valid for the given Delegation Scope
- FALSE - the received token is NOT valid for the given Delegation Scope
- TokenExpiredException - The token is expired
Example
val cursor: Cursor = appContext.contentResolver.query(VERIFY_TOKEN_URI, null, COLUMN_TOKEN + "=? AND " + COLUMN_DELIGATION_SCOPE + "=?", arrayOf("34uuuuuojsnushnjd232434snmnsdms23434", "delegation-zebra-datawedge-intent-api"), null)!!
if (cursor != null && cursor.count > 0) {
cursor.moveToFirst()
result = cursor.getString(cursor.getColumnIndex(COLUMN_QUERY_RESULT)))
}
OPTIONAL: If verification of access is desired, the package name and signature can be included in the ZDM query along with the token and Delegation Scope, as below:
val cursor: Cursor = appContext.contentResolver.query(VERIFY_TOKEN_URI, null, COLUMN_TOKEN + "=? AND " + COLUMN_DELIGATION_SCOPE + "=?" + COLUMN_PACKAGE_NAME + "=?" + COLUMN_SIGNATURE + "=?", arrayOf("34uuuuuojsnushnjd232434snmnsdms23434", "delegation-zebra-datawedge-intent-api", "com.example.app1", "dyghfjbcdh8=wjsmndhsjenfbaueyruefut"), null)!!
if (cursor != null && cursor.count > 0) {
cursor.moveToFirst()
result = cursor.getString(cursor.getColumnIndex(COLUMN_QUERY_RESULT)))
}
Acquire Token
An application that requires a token to call interfaces of another app can obtain it using the ZDM Content Provider. The caller app must send the Delegation Scope name as defined by the implementing app as part of the column value. If the caller app is granted the same Delegation Scope, ZDM generates and returns a token valid for 24 hours. Otherwise, a SecurityException is generated.
Result Codes
- TRUE - caller app granted Delegation Scope and token valid for 24 hours
- SecurityException - caller is not authorized to receive the token
Example
val cursor: Cursor = appContext.contentResolver.query(ACQUIRE_TOKEN_URI, null,
COLUMN_DELIGATION_SCOPE + "=?", arrayOf("delegation-zebra-datawedge-api-access-query"), null)!!
if (cursor != null && cursor.count > 0) {
cursor.moveToFirst()
result = cursor.getString(cursor.getColumnIndex(COLUMN_QUERY_RESULT)))
}
Verify App Permission
Verifies whether authority is granted to an app for accessing a token.
Result Codes
- TRUE - app is authorized to request a token
- FALSE - app is NOT authorized to request a token; token requests will be denied
Example
val cursor: Cursor = appContext.contentResolver.query(VERIFY_PERMISSION_URI, null, COLUMN_PACKAGE_NAME + "=? AND " + COLUMN_PERMISSION + "=?",
arrayOf("com.example.application", "android.permission.PACKAGE_USAGE_STATS"), null)!!
if (cursor != null && cursor.count > 0) {
cursor.moveToFirst()
result = cursor.getString(cursor.getColumnIndex(COLUMN_QUERY_RESULT)))
}
Query System Setting UI status
Determines access to the Android Settings panel UI for the current device user.
Result Codes
- 0 - user has full access to the Android Settings panel
- 1 - user has access only to Display, Volume and About features of the Android Settings panel
- 2 – no access to Android Settings panel
Example
val cursor: Cursor = appContext.contentResolver.query(SYSTEM_SETTINGS_UI_URI, null, null , null, null)!!
if (cursor != null && cursor.count > 0) {
cursor.moveToFirst()
result = cursor.getString(cursor.getColumnIndex(COLUMN_QUERY_RESULT)))
}
Also See
Android Developer Docs
- Android Content Provider Basics | Introduction to ACP concepts
- Creating a content provider | Why and how to share an app's data
- Android Cursor docs | How to interface with a result data set
- Content Observer | Get a callback when data changes
Articles by Zebra Engineers
- How to display serial and IMEI numbers on device | Sample app, instructions, source code
- Save OEM identifiers to a file on the device | Sample app, source code