Content Provider Model

Android Platform Transition 1.0

Overview

Zebra is implementing the Android Content Provider (ACP) model, a Google-sanctioned framework for data sharing, as the mechanism for data persistence and data sharing among apps installed on Zebra devices. This model consists of a set of publicly available APIs through which apps can publish data for consumption programmatically by other apps or by the originating app itself.

ACP defines Data Consumers as apps seeking to retrieve information from apps sharing data–called Data Providers–on the device on which they are running. Examples of Zebra publisher apps include StageNow and OEMconfig, as well as Power Manager and other built-in configuration service provider (CSP) modules, which interface directly with Zebra's device-API layer.

image Source: https://developer.android.com/guide/topics/providers/content-provider-basics.html
Click image to enlarge; ESC to exit.

Information for consumption is made available through Data Provider apps. Both app types–Consumers and Providers–use the Zebra Data Provider Interfaces (ZDPIs) to consume data and/or to publish data for consumption.


Consuming Data

The process of retrieving data is the same as that of querying an Android content provider. Android employs content URIs to identify certain data available from a provider.

However, before an app can retrieve data, it must first receive permission to do so. This step must be performed only once for each app, but is required before the app can attempt any read operation.

To acquire read permission:

Add the following statement to the app's AndroidManifest.xml file:

    <uses-permission android:name=”com.zebra.provider.READ”>

Content URIs

Each content URI includes the authority of the content provider represented as a symbolic name along with the package name of a Content Provider or path to a table containing its content. When calling a client method to access a content provider or table, the content URI is passed as an argument.

URIs are broken into four (4) parts as<scheme:>//<Authority>/<Provider name>/<API>

For example, the URI content://oem_info/oem.zebra.secure/build_serial can be broken down as follows:

  • content: is the scheme, which tells Android that the URI points to a content provider.
  • oem_info is the authority name of the content provider.
  • oem.zebra.secure is the content provider name unique within a given authority. Usually a package name.
  • build_serial is the API name unique within a given package name.

Authorization Mechanism

The ZDPI uses Zebra Access Manager to grant or deny apps authorization to access data published by the framework. The Zebra-owned system components and application types listed below are authorized by default.

Pre-Authorized Apps

  • Zebra built-in system apps (e.g. OEMConfig, StageNow, MX components (CSPs))
  • Apps signed with the Zebra Common Key
  • Dual-key apps if one of the keys is the Zebra Common Key
  • Plug-in CSPs (installed after shipment from factory and authorized by key generated by Zebra certificate authority)

Authorization Required

  • Customer-developed apps and services
  • Third-party apps
  • Any app NOT developed by Zebra

When provisioning one or more of the app types listed above, authorization to access the ZDPI can be granted using StageNow and Access Manager, OEMconfig and the Service Access Configuration, or a company's own Enterprise Mobile Management system.

image Click image to enlarge; ESC to exit.

To grant access using Access Manager and StageNow:

Configure the following actions based on the individual requirements.

  1. Service Access Action: “Allow Caller to Call Service”
  2. Service Identifier: ZDPI URI to access (see Data Consumption guide)
  3. Caller Package Name: Package Name of the third-party app
  4. Caller Signature: App certificate of the third-party app

Also See

How to generate a caller signature | Zebra engineering article on the "package signature"


Sample Code

Modify the code samples below to suit individual needs.

Acquire Bluetooth MAC

IMPORTANT: The Bluetooth radio must be enabled on the device to retrieve the Bluetooth MAC address using OEMinfo.

  1. Get the AUTHORITY, PROVIDER and API using the following command:

    String BT_MAC = "content://oem_info/oem.zebra.secure/bt_mac";
    
  2. Get the data (in this case the device's Bluetooth MAC address) by parsing the string using Android cursor query methods implemented in the following Java code:

    
    // Prepare the URI
    public void getData() {
        final Uri myUri = Uri.parse(BT_MAC);
        new UpdateAsync().execute(myUri.toString());
    }
    
    // Always query for data in Async task or background thread
    class UpdateAsync extends AsyncTask<String,Void,Cursor> {
        private String myUri;
    
    @Override
    protected Cursor doInBackground(String... args) {
        myUri = args[0];
    
    
    // Query the content provider
    ContentResolver cr = getContentResolver();
    Cursor cursor = cr.query(Uri.parse(myUri),
                    null, null, null, null);
    
    // Read the cursor
    cursor.moveToFirst();
    String bluetoothMAC = cursor.getString(0);
    Log.i(TAG, "The Bluetooth MAC is : ” + bluetoothMAC);            
    return cursor;
    }
    
    }

Skip to the Callback section to see sample code for setting an app to be notified of changes to OEMinfo data. This Zebra-recommended practice can be helpful for monitoring non-static URIs.


Acquire Serial Number

  1. Get the AUTHORITY, PROVIDER and API using the following command:

    String SERIAL_URI = "content://oem_info/oem.zebra.secure/build_serial";
    
  2. Get the data (in this case the device serial number) by parsing the string using Android cursor query methods implemented in the following Java code:

    
    // Prepare the URI
    public void getData() {
        final Uri myUri = Uri.parse(SERIAL_URI);
        new UpdateAsync().execute(myUri.toString());
    }
    
    // Always query for data in Async task or background thread
    class UpdateAsync extends AsyncTask<String,Void,Cursor> {
        private String myUri;
    
    @Override
    protected Cursor doInBackground(String... args) {
        myUri = args[0];
    
    
    // Query the content provider
    ContentResolver cr = getContentResolver();
    Cursor cursor = cr.query(Uri.parse(myUri),
                    null, null, null, null);
    
    // Read the cursor
    cursor.moveToFirst();
    String serialNumber = cursor.getString(0);
    Log.i(TAG, "Device Serial Number is : ” + serialNumber);            
    return cursor;
    }
    
    }

Skip to the Callback section to see sample code for setting an app to be notified of changes to OEMinfo data. This Zebra-recommended practice can be helpful for monitoring non-static URIs.


Acquire IMEI

  1. Get the AUTHORITY, PROVIDER and API using the following command:

    String IMEI_URI = “content://oem_info/wan/imei”;
    
  2. Get the data (in this case the device IMEI number) by parsing the string using Android cursor query methods implemented in the following Java code:

    
    // Prepare the URI
    public void getData() {
        final Uri myUri = Uri.parse(IMEI_URI);
        new UpdateAsync().execute(myUri.toString());
    }
    
    // Always query for data in Async task or background thread
    class UpdateAsync extends AsyncTask<String,Void,Cursor> {
        private String myUri;
    
    @Override
    protected Cursor doInBackground(String... args) {
        myUri = args[0];
    
    
    // Query the content provider
    ContentResolver cr = getContentResolver();
    Cursor cursor = cr.query(Uri.parse(myUri),
                null, null, null, null);
    
    // Read the cursor
    cursor.moveToFirst();
    String imeiNumber = cursor.getString(0);
    Log.i(TAG, "Device IMEI is : ” + imeiNumber);            
    return cursor;
    }
    
    }

Acquire OS update info

  1. Get the AUTHORITY, PROVIDER and API using the following Java code:

    
    // OS UPDATE URI
    String OSU_URI = “content://oem_info/oem.zebra.osupdate”;
    
    // APIs
    String OSU_STATUS = “status”; 
    // Returns SUCCESS, IN_PROGRESS, FAIL, WAITING_FOR_REBOOT, etc.
    
    String OSU_DETAIL = “detail”;
    // Text representation of the status contains detailed reason
    
    String OSU_TS = “ts”;
    // Returns an Epoch time stamp indicating when the intent is received
    
  2. Get the data (in this case the device OS update info) by parsing the string using Android cursor query methods implemented in the following Java code:

            
            // Prepare the URI
            public void getData() {
                final Uri myUri = Uri.parse(OSU_URI);
                new UpdateAsync().execute(myUri.toString());
            }
    
    
        // Always query for data in Async task or background thread
            class UpdateAsync extends AsyncTask&lt;String,Void,Cursor&gt; {
            private String myUri;
    
        @Override
        protected Cursor doInBackground(String... args) {
            myUri = args[0];
    
            // Query the content provider
            ContentResolver cr = getContentResolver();
            Cursor cursor = cr.query(Uri.parse(myUri),
                            null, null, null, null);
    
            // Read the cursor
            While (cursor.moveToNext()) {
            String status = cursor.getString(
            cursor.getColumnIndex(OSU_STATUS));
    
            String detail = cursor.getString(
            cursor.getColumnIndex(OSU_DETAIL));
    
            String time = cursor.getString(
            cursor.getColumnIndex(OSU_TS));
    
            Log.i(TAG, "STATUS : " + status + “, DETAIL : ” + detail
                + “, TIME : ” + time);
    
        }
        return cursor;
    }
    
    }

Callbacks

Use the following Java code to register an app to be notified when URI data changes. Apps also can receive callbacks for changes to the content using the standard Android content observer methods, but Zebra recommends always registering callbacks for semi-static URI values.

    // Prepare the URI
    Uri myUri = Uri.Parse(MY_URI_STRING);

    // Register with content observer
    ContentResolver cr = getContentResolver();
    cr.registerContentObserver(myUri, true, 
        new ContentObserver(new Handler(getMainLooper())) {

        @Override
        public void onChange(boolean selfChange, Uri uri) {
            super.onChange(selfChange, uri);

            // Content has changed
            Log.d(TAG, "content has changed, uri = " + uri);

            // Reading the data is like above example
            getData();
        }
    });

Limitations

Data acquired through the OEMinfo content provider is subject to the rules and limitations listed below.

  • With the exception of OS Update events, OEMinfo does NOT read system properties that can change at runtime.
  • OEMinfo reads system properties only after being signaled by the BOOT_COMPLETE event.
    • After receiving BOOT_COMPLETE, OEMinfo queries selected system properties and refreshes the content provider. This generally takes a few seconds, but a delay of about one minute is typical before results of an OS Update are published to the ZDPI.
    • If an app queries OEMinfo too soon after reboot, some URIs might return "stale" data, posing a potential issue for non-static values.
  • OEMinfo requires extra time populate the content provider database when device data is wiped after an Enterprise Reset, Factory Reset or other erasure event.
    • Zebra recommends registering apps with a content observer on the URI to receive a callback whenever new data is available to avoid issues relating to stale or missing data due to re-population delays.
  • OEMinfo is “System UID” and platform-signed, and is therefore subject to platform permissions and SELinux restrictions across Android versions and devices.
    • The same set of system properties might not be readable on all devices.
    • System properties might become restricted, removed or added after an OS update.

Also See

Android Developer Docs