Consuming Data

Zebra OEMinfo

Overview

OEMinfo defines Data Consumers as apps seeking to retrieve information about the device on which they are running. Examples of such Zebra-owned apps include StageNow and OEMconfig device configuration tools, as well as Power Manager and other built-in configuration service provider (CSP) modules, which interface directly with Zebra's device-API layer.

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


Consuming OEMinfo Data

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

However, before an app can retrieve data from OEMinfo, 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 OEMinfo 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.

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 Mobility Management (EMM) 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"


Targeting Android 11

Enhancements delivered with Android 11 (API level 30) enforce greater protections on app and user data stored externally, building on the scoped storage feature introduced with Android 10 (API level 29). Scoped storage limits apps targeting API 29 (and later) to an app-specific device folder and to media types it creates. This was intended to provide more user control over stored files and reduce the file clutter. Apps targeting Android 10 (API level 29) can opt out of restrictions imposed by Android 11 by setting a flag in the app's manifest. This flag is ignored by apps that target Android 11.

Android 11 also imposes a package visibility filter, which effectively reduces the number of other apps that appear to be installed on the device when queried by your app. See below to grant visibility to your app and prevent it from being filtered.

Grant Visibility

To grant visibility to an app so that it's not subject to the visibility filter, add the following to the app's manifest:


<queries>
    <package android:name="com.zebra.zebracontentprovider" />
</queries>

Learn more about package visibility filtering from the Android developer community.


Sample Code

Modify the code samples below to suit individual needs. For more information or to request additional sample URIs, contact the Zebra Data Services team.

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

Articles by Zebra Engineers