├── .github └── dependabot.yml ├── .gitignore ├── .idea └── vcs.xml ├── .project ├── DeviceIdentifiersWrapper ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── zebra │ │ └── deviceidentifierswrapper │ │ ├── DICommandBase.java │ │ ├── DICommandBaseSettings.java │ │ ├── DIHelper.java │ │ ├── DIProfileManagerCommand.java │ │ ├── EMessageType.java │ │ ├── ExecutorTask.java │ │ ├── IDIResultCallbacks.java │ │ └── RetrieveOEMInfoTask.java │ └── res │ └── values │ └── strings.xml ├── LICENSE ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | 7 | version: 2 8 | updates: 9 | - package-ecosystem: "" # See documentation for possible values 10 | directory: "/" # Location of package manifests 11 | schedule: 12 | interval: "weekly" 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | *.aab 5 | 6 | # Files for the ART/Dalvik VM 7 | *.dex 8 | 9 | # Java class files 10 | *.class 11 | 12 | # Generated files 13 | bin/ 14 | gen/ 15 | out/ 16 | release/ 17 | 18 | # Gradle files 19 | .gradle/ 20 | build/ 21 | 22 | # Local configuration file (sdk path, etc) 23 | local.properties 24 | 25 | # Proguard folder generated by Eclipse 26 | proguard/ 27 | 28 | # Log Files 29 | *.log 30 | 31 | # Android Studio Navigation editor temp files 32 | .navigation/ 33 | 34 | # Android Studio captures folder 35 | captures/ 36 | 37 | # IntelliJ 38 | *.iml 39 | .idea/workspace.xml 40 | .idea/tasks.xml 41 | .idea/gradle.xml 42 | .idea/assetWizardSettings.xml 43 | .idea/dictionaries 44 | .idea/libraries 45 | # Android Studio 3 in .gitignore file. 46 | .idea/caches 47 | .idea/modules.xml 48 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you 49 | .idea/navEditor.xml 50 | 51 | # Keystore files 52 | # Uncomment the following lines if you do not want to check your keystore files in. 53 | #*.jks 54 | #*.keystore 55 | 56 | # External native build folder generated in Android Studio 2.2 and later 57 | .externalNativeBuild 58 | 59 | # Google Services (e.g. APIs or Firebase) 60 | # google-services.json 61 | 62 | # Freeline 63 | freeline.py 64 | freeline/ 65 | freeline_project_description.json 66 | 67 | # fastlane 68 | fastlane/report.xml 69 | fastlane/Preview.html 70 | fastlane/screenshots 71 | fastlane/test_output 72 | fastlane/readme.md 73 | 74 | # Version control 75 | vcs.xml 76 | 77 | # lint 78 | lint/intermediates/ 79 | lint/generated/ 80 | lint/outputs/ 81 | lint/tmp/ 82 | # lint/reports/ 83 | .idea/ 84 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | EMDK-DeviceIdentifiers-Sample 4 | Project EMDK-DeviceIdentifiers-Sample created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.buildship.core.gradleprojectbuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.buildship.core.gradleprojectnature 16 | 17 | 18 | -------------------------------------------------------------------------------- /DeviceIdentifiersWrapper/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /DeviceIdentifiersWrapper/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | } 4 | 5 | android { 6 | compileSdkVersion 34 7 | 8 | defaultConfig { 9 | minSdkVersion 19 10 | targetSdkVersion 34 11 | versionCode 120 12 | versionName "0.12.0" 13 | 14 | testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' 15 | 16 | } 17 | 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | compileOptions { 25 | sourceCompatibility JavaVersion.VERSION_1_8 26 | targetCompatibility JavaVersion.VERSION_1_8 27 | } 28 | 29 | } 30 | 31 | dependencies { 32 | implementation fileTree(dir: 'libs', include: ['*.jar']) 33 | implementation 'androidx.appcompat:appcompat:1.0.0' 34 | testImplementation 'junit:junit:4.13' 35 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 36 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 37 | compileOnly 'com.symbol:emdk:9.1.1' 38 | } 39 | -------------------------------------------------------------------------------- /DeviceIdentifiersWrapper/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /DeviceIdentifiersWrapper/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /DeviceIdentifiersWrapper/src/main/java/com/zebra/deviceidentifierswrapper/DICommandBase.java: -------------------------------------------------------------------------------- 1 | package com.zebra.deviceidentifierswrapper; 2 | 3 | import android.content.Context; 4 | import android.os.Handler; 5 | import android.os.Parcel; 6 | import android.os.ResultReceiver; 7 | 8 | /** 9 | * Created by Trudu Laurent on 2020/08/11. 10 | */ 11 | 12 | abstract class DICommandBase { 13 | /* 14 | A TAG if we want to log something 15 | */ 16 | protected static String TAG = "DIWrapperMX"; 17 | 18 | /* 19 | A context to work with intents 20 | */ 21 | protected Context mContext = null; 22 | 23 | protected DICommandBaseSettings mSettings = null; 24 | 25 | /* 26 | A handler that will be used by the derived 27 | class to prevent waiting to loong for DW in case 28 | of problem 29 | */ 30 | protected Handler mTimeOutHandler; 31 | 32 | /* 33 | What will be done at the end of the TimeOut 34 | */ 35 | protected Runnable mTimeOutRunnable = new Runnable() { 36 | @Override 37 | public void run() { 38 | onTimeOut(mSettings); 39 | } 40 | }; 41 | 42 | 43 | public DICommandBase(Context aContext) 44 | { 45 | mContext = aContext; 46 | mTimeOutHandler = new Handler(mContext.getMainLooper()); 47 | } 48 | 49 | 50 | protected void execute(DICommandBaseSettings settings) 51 | { 52 | mSettings = settings; 53 | /* 54 | Start time out mechanism 55 | Enabled by default in DWProfileBaseSettings 56 | */ 57 | if(settings.mEnableTimeOutMechanism) { 58 | mTimeOutHandler.postDelayed(mTimeOutRunnable, 59 | mSettings.mTimeOutMS); 60 | } 61 | } 62 | 63 | protected void onTimeOut(DICommandBaseSettings settings) 64 | { 65 | cleanAll(); 66 | } 67 | 68 | protected void cleanAll() 69 | { 70 | if(mTimeOutHandler != null) 71 | { 72 | mTimeOutHandler.removeCallbacks(mTimeOutRunnable); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /DeviceIdentifiersWrapper/src/main/java/com/zebra/deviceidentifierswrapper/DICommandBaseSettings.java: -------------------------------------------------------------------------------- 1 | package com.zebra.deviceidentifierswrapper; 2 | 3 | class DICommandBaseSettings 4 | { 5 | /* 6 | Use this to track the source of the intent 7 | */ 8 | public String mCommandId = ""; 9 | 10 | /* 11 | Some method return only errors (StartScan, StopScan) 12 | We do not need a time out for them 13 | */ 14 | public boolean mEnableTimeOutMechanism = true; 15 | 16 | /* 17 | A time out, in case we don't receive an answer 18 | from PrintConnect 19 | */ 20 | public long mTimeOutMS = 5000; 21 | } 22 | -------------------------------------------------------------------------------- /DeviceIdentifiersWrapper/src/main/java/com/zebra/deviceidentifierswrapper/DIHelper.java: -------------------------------------------------------------------------------- 1 | package com.zebra.deviceidentifierswrapper; 2 | 3 | import android.Manifest.permission; 4 | import android.annotation.SuppressLint; 5 | import android.bluetooth.BluetoothAdapter; 6 | import android.content.Context; 7 | import android.content.pm.PackageInfo; 8 | import android.content.pm.PackageManager; 9 | import android.content.pm.Signature; 10 | import android.database.Cursor; 11 | import android.net.Uri; 12 | import android.os.AsyncTask; 13 | import android.os.Build; 14 | import android.telephony.TelephonyManager; 15 | import android.util.Log; 16 | 17 | import androidx.core.content.ContextCompat; 18 | import java.util.Base64; 19 | 20 | public class DIHelper { 21 | 22 | // Placeholder for custom certificate 23 | // Otherwise, the app will use the first certificate found with the method: 24 | // final Signature[] arrSignatures = packageInfo.signingInfo.getApkContentsSigners(); 25 | // TODO: Put your custom certificate in the apkCertificate member for MX AccessMgr registering (only if necessary and if you know what you are doing) 26 | public static Signature apkCertificate = null; 27 | 28 | protected static String sIMEI = null; 29 | protected static String sSerialNumber = null; 30 | protected static String sBtMacAddress = null; 31 | protected static String sProductModel = null; 32 | protected static String sIdentityDeviceID = null; 33 | protected static String sWifiMac = null; 34 | protected static String sWifiAPMac = null; 35 | protected static String sWifiSSID= null; 36 | protected static String sEthernetMac = null; 37 | 38 | 39 | public static final long SEC_IN_MS = 1000; 40 | public static final long MIN_IN_MS = SEC_IN_MS * 60; 41 | public static long MAX_EMDK_TIMEOUT_IN_MS = 10 * MIN_IN_MS; // 10 minutes 42 | public static long WAIT_PERIOD_BEFORE_RETRY_EMDK_RETRIEVAL_IN_MS = 2 * SEC_IN_MS; // 2 seconds 43 | 44 | public static void resetCachedValues() 45 | { 46 | sIMEI = null; 47 | sSerialNumber = null; 48 | } 49 | 50 | // This method will return the serial number in the string passed through the onSuccess method 51 | public static void getSerialNumber(Context context, IDIResultCallbacks callbackInterface) 52 | { 53 | if(sSerialNumber != null) 54 | { 55 | if(callbackInterface != null) 56 | { 57 | callbackInterface.onDebugStatus("Serial number already in cache."); 58 | } 59 | callbackInterface.onSuccess(sSerialNumber); 60 | return; 61 | } 62 | if (android.os.Build.VERSION.SDK_INT < 29) { 63 | returnSerialUsingAndroidAPIs(context, callbackInterface); 64 | } else { 65 | returnSerialUsingZebraAPIs(context, callbackInterface); 66 | } 67 | } 68 | 69 | @SuppressLint({"MissingPermission", "ObsoleteSdkInt", "HardwareIds"}) 70 | private static void returnSerialUsingAndroidAPIs(Context context, IDIResultCallbacks callbackInterface) { 71 | if (android.os.Build.VERSION.SDK_INT < 26) { 72 | sSerialNumber = Build.SERIAL; 73 | callbackInterface.onSuccess(Build.SERIAL); 74 | } else { 75 | if (ContextCompat.checkSelfPermission(context, permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) { 76 | sSerialNumber = Build.getSerial(); 77 | callbackInterface.onSuccess(Build.getSerial()); 78 | } else { 79 | callbackInterface.onError("Please grant READ_PHONE_STATE permission"); 80 | } 81 | } 82 | } 83 | 84 | private static void returnSerialUsingZebraAPIs(Context context, final IDIResultCallbacks callbackInterface) { 85 | IDIResultCallbacks tempCallbackInterface = new IDIResultCallbacks() { 86 | @Override 87 | public void onSuccess(String message) { 88 | sSerialNumber = message; 89 | callbackInterface.onSuccess(message); 90 | } 91 | 92 | @Override 93 | public void onError(String message) { 94 | callbackInterface.onError(message); 95 | } 96 | 97 | @Override 98 | public void onDebugStatus(String message) { 99 | callbackInterface.onDebugStatus(message); 100 | } 101 | }; 102 | 103 | new RetrieveOEMInfoTask() 104 | .executeAsync(context, Uri.parse("content://oem_info/oem.zebra.secure/build_serial"), 105 | tempCallbackInterface); 106 | } 107 | 108 | // This method will return the imei number in the string passed through the onSuccess method 109 | public static void getIMEINumber(Context context, IDIResultCallbacks callbackInterface) 110 | { 111 | if(sIMEI != null) 112 | { 113 | if(callbackInterface != null) 114 | { 115 | callbackInterface.onDebugStatus("IMEI number already in cache."); 116 | } 117 | callbackInterface.onSuccess(sIMEI); 118 | return; 119 | } 120 | if (android.os.Build.VERSION.SDK_INT < 29) { 121 | returnImeiUsingAndroidAPIs(context, callbackInterface); 122 | } else { 123 | returnImeiUsingZebraAPIs(context, callbackInterface); 124 | } 125 | } 126 | 127 | @SuppressLint({"MissingPermission", "ObsoleteSdkInt", "HardwareIds" }) 128 | private static void returnImeiUsingAndroidAPIs(Context context, IDIResultCallbacks callbackInterface) { 129 | TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 130 | if (android.os.Build.VERSION.SDK_INT < 26) {String imei = telephonyManager.getDeviceId(); 131 | if (imei != null && !imei.isEmpty()) { 132 | sIMEI = imei; 133 | callbackInterface.onSuccess(imei); 134 | } else { 135 | callbackInterface.onError("Could not get IMEI number"); 136 | } 137 | } else { 138 | if (ContextCompat.checkSelfPermission(context, permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) { 139 | String imei = telephonyManager.getImei(); 140 | if (imei != null && !imei.isEmpty()) { 141 | sIMEI = imei; 142 | callbackInterface.onSuccess(imei); 143 | } else { 144 | callbackInterface.onError("Could not get IMEI number"); 145 | } 146 | } else { 147 | callbackInterface.onError("Please grant READ_PHONE_STATE permission"); 148 | } 149 | } 150 | } 151 | 152 | private static void returnImeiUsingZebraAPIs(Context context, final IDIResultCallbacks callbackInterface) { 153 | IDIResultCallbacks tempCallbackInterface = new IDIResultCallbacks() { 154 | @Override 155 | public void onSuccess(String message) { 156 | sIMEI = message; 157 | callbackInterface.onSuccess(message); 158 | } 159 | 160 | @Override 161 | public void onError(String message) { 162 | callbackInterface.onError(message); 163 | } 164 | 165 | @Override 166 | public void onDebugStatus(String message) { 167 | callbackInterface.onDebugStatus(message); 168 | } 169 | }; 170 | 171 | new RetrieveOEMInfoTask().executeAsync(context, Uri.parse("content://oem_info/wan/imei"), 172 | tempCallbackInterface); 173 | } 174 | 175 | public static void getBtMacAddress(Context context, IDIResultCallbacks callbackInterface) 176 | { 177 | if(sBtMacAddress != null) 178 | { 179 | if(callbackInterface != null) 180 | { 181 | callbackInterface.onDebugStatus("BT Mac address already in cache."); 182 | } 183 | callbackInterface.onSuccess(sBtMacAddress); 184 | return; 185 | } 186 | if (android.os.Build.VERSION.SDK_INT < 23) { 187 | returnBtMacAddressUsingAndroidAPIs(context, callbackInterface); 188 | } else { 189 | returnBtMacAddressUsingZebraAPIs(context, callbackInterface); 190 | } 191 | } 192 | 193 | private static void returnBtMacAddressUsingZebraAPIs(Context context, IDIResultCallbacks callbackInterface) { 194 | IDIResultCallbacks tempCallbackInterface = new IDIResultCallbacks() { 195 | @Override 196 | public void onSuccess(String message) { 197 | sBtMacAddress = message; 198 | callbackInterface.onSuccess(message); 199 | } 200 | 201 | @Override 202 | public void onError(String message) { 203 | callbackInterface.onError(message); 204 | } 205 | 206 | @Override 207 | public void onDebugStatus(String message) { 208 | callbackInterface.onDebugStatus(message); 209 | } 210 | }; 211 | 212 | new RetrieveOEMInfoTask().executeAsync(context, Uri.parse("content://oem_info/oem.zebra.secure/bt_mac"), 213 | tempCallbackInterface); 214 | } 215 | 216 | private static void returnBtMacAddressUsingAndroidAPIs(Context context, IDIResultCallbacks callbackInterface) { 217 | BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 218 | String macAddress = mBluetoothAdapter.getAddress(); 219 | if(callbackInterface != null) 220 | { 221 | callbackInterface.onSuccess(macAddress); 222 | } 223 | } 224 | 225 | public static void getProductModel(Context context, IDIResultCallbacks callbackInterface) 226 | { 227 | if(sProductModel != null) 228 | { 229 | if(callbackInterface != null) 230 | { 231 | callbackInterface.onDebugStatus("Product Model already in cache."); 232 | } 233 | callbackInterface.onSuccess(sProductModel); 234 | return; 235 | } 236 | 237 | IDIResultCallbacks tempCallbackInterface = new IDIResultCallbacks() { 238 | @Override 239 | public void onSuccess(String message) { 240 | sProductModel = message; 241 | callbackInterface.onSuccess(message); 242 | } 243 | 244 | @Override 245 | public void onError(String message) { 246 | callbackInterface.onError(message); 247 | } 248 | 249 | @Override 250 | public void onDebugStatus(String message) { 251 | callbackInterface.onDebugStatus(message); 252 | } 253 | }; 254 | 255 | new RetrieveOEMInfoTask().executeAsync(context, Uri.parse("content://oem_info/oem.zebra.secure/ro_product_model"), 256 | tempCallbackInterface); 257 | 258 | } 259 | 260 | public static void getIdentityDeviceID(Context context, IDIResultCallbacks callbackInterface) 261 | { 262 | if(sIdentityDeviceID != null) 263 | { 264 | if(callbackInterface != null) 265 | { 266 | callbackInterface.onDebugStatus("IdentityDeviceID already in cache."); 267 | } 268 | callbackInterface.onSuccess(sIdentityDeviceID); 269 | return; 270 | } 271 | 272 | IDIResultCallbacks tempCallbackInterface = new IDIResultCallbacks() { 273 | @Override 274 | public void onSuccess(String message) { 275 | sIdentityDeviceID = message; 276 | callbackInterface.onSuccess(message); 277 | } 278 | 279 | @Override 280 | public void onError(String message) { 281 | callbackInterface.onError(message); 282 | } 283 | 284 | @Override 285 | public void onDebugStatus(String message) { 286 | callbackInterface.onDebugStatus(message); 287 | } 288 | }; 289 | 290 | new RetrieveOEMInfoTask().executeAsync(context, Uri.parse("content://oem_info/oem.zebra.secure/identity_device_id"), 291 | tempCallbackInterface); 292 | 293 | } 294 | 295 | public static void getWifiMacAddress(Context context, IDIResultCallbacks callbackInterface) 296 | { 297 | if(sWifiMac != null) 298 | { 299 | if(callbackInterface != null) 300 | { 301 | callbackInterface.onDebugStatus("Wifi Mac Address already in cache."); 302 | } 303 | callbackInterface.onSuccess(sWifiMac); 304 | return; 305 | } 306 | 307 | IDIResultCallbacks tempCallbackInterface = new IDIResultCallbacks() { 308 | @Override 309 | public void onSuccess(String message) { 310 | sWifiMac = message; 311 | callbackInterface.onSuccess(message); 312 | } 313 | 314 | @Override 315 | public void onError(String message) { 316 | callbackInterface.onError(message); 317 | } 318 | 319 | @Override 320 | public void onDebugStatus(String message) { 321 | callbackInterface.onDebugStatus(message); 322 | } 323 | }; 324 | 325 | new RetrieveOEMInfoTask().executeAsync(context, Uri.parse("content://oem_info/oem.zebra.secure/wifi_mac"), 326 | tempCallbackInterface); 327 | 328 | } 329 | 330 | public static void getWifiAPMacAddress(Context context, IDIResultCallbacks callbackInterface) 331 | { 332 | if(sWifiAPMac != null) 333 | { 334 | if(callbackInterface != null) 335 | { 336 | callbackInterface.onDebugStatus("Wifi AP Mac Address already in cache."); 337 | } 338 | callbackInterface.onSuccess(sWifiAPMac); 339 | return; 340 | } 341 | 342 | IDIResultCallbacks tempCallbackInterface = new IDIResultCallbacks() { 343 | @Override 344 | public void onSuccess(String message) { 345 | sWifiAPMac = message; 346 | callbackInterface.onSuccess(message); 347 | } 348 | 349 | @Override 350 | public void onError(String message) { 351 | callbackInterface.onError(message); 352 | } 353 | 354 | @Override 355 | public void onDebugStatus(String message) { 356 | callbackInterface.onDebugStatus(message); 357 | } 358 | }; 359 | 360 | new RetrieveOEMInfoTask().executeAsync(context, Uri.parse("content://oem_info/oem.zebra.secure/wifi_ap_mac"), 361 | tempCallbackInterface); 362 | } 363 | 364 | public static void getWifiSSID(Context context, IDIResultCallbacks callbackInterface) 365 | { 366 | if(sWifiSSID != null) 367 | { 368 | if(callbackInterface != null) 369 | { 370 | callbackInterface.onDebugStatus("Wifi SSID already in cache."); 371 | } 372 | callbackInterface.onSuccess(sWifiSSID); 373 | return; 374 | } 375 | 376 | IDIResultCallbacks tempCallbackInterface = new IDIResultCallbacks() { 377 | @Override 378 | public void onSuccess(String message) { 379 | sWifiSSID = message; 380 | callbackInterface.onSuccess(message); 381 | } 382 | 383 | @Override 384 | public void onError(String message) { 385 | callbackInterface.onError(message); 386 | } 387 | 388 | @Override 389 | public void onDebugStatus(String message) { 390 | callbackInterface.onDebugStatus(message); 391 | } 392 | }; 393 | 394 | new RetrieveOEMInfoTask().executeAsync(context, Uri.parse("content://oem_info/oem.zebra.secure/wifi_ssid"), 395 | tempCallbackInterface); 396 | } 397 | 398 | public static void getEthernetMacAddress(Context context, IDIResultCallbacks callbackInterface) 399 | { 400 | if(sEthernetMac != null) 401 | { 402 | if(callbackInterface != null) 403 | { 404 | callbackInterface.onDebugStatus("Ethernet Mac Address already in cache."); 405 | } 406 | callbackInterface.onSuccess(sEthernetMac); 407 | return; 408 | } 409 | 410 | IDIResultCallbacks tempCallbackInterface = new IDIResultCallbacks() { 411 | @Override 412 | public void onSuccess(String message) { 413 | sEthernetMac = message; 414 | callbackInterface.onSuccess(message); 415 | } 416 | 417 | @Override 418 | public void onError(String message) { 419 | callbackInterface.onError(message); 420 | } 421 | 422 | @Override 423 | public void onDebugStatus(String message) { 424 | callbackInterface.onDebugStatus(message); 425 | } 426 | }; 427 | 428 | new RetrieveOEMInfoTask().executeAsync(context, Uri.parse("content://oem_info/oem.zebra.secure/ethernet_mac"), 429 | tempCallbackInterface); 430 | } 431 | 432 | 433 | } 434 | -------------------------------------------------------------------------------- /DeviceIdentifiersWrapper/src/main/java/com/zebra/deviceidentifierswrapper/DIProfileManagerCommand.java: -------------------------------------------------------------------------------- 1 | package com.zebra.deviceidentifierswrapper; 2 | 3 | import android.content.Context; 4 | import android.net.Uri; 5 | import android.os.Build; 6 | import android.text.TextUtils; 7 | import android.util.Log; 8 | import android.util.Xml; 9 | 10 | import com.symbol.emdk.EMDKBase; 11 | import com.symbol.emdk.EMDKException; 12 | import com.symbol.emdk.EMDKManager; 13 | import com.symbol.emdk.EMDKResults; 14 | import com.symbol.emdk.ProfileManager; 15 | 16 | import org.xmlpull.v1.XmlPullParser; 17 | import org.xmlpull.v1.XmlPullParserException; 18 | 19 | import java.io.StringReader; 20 | import java.util.ArrayList; 21 | import java.util.Date; 22 | 23 | class DIProfileManagerCommand extends DICommandBase { 24 | public class ErrorHolder 25 | { 26 | // Provides the error type for characteristic-error 27 | public String sErrorType = ""; 28 | 29 | // Provides the parm name for parm-error 30 | public String sParmName = ""; 31 | 32 | // Provides error description 33 | public String sErrorDescription = ""; 34 | } 35 | 36 | // Membres 37 | private String TAG = "DIWrapperMX"; 38 | 39 | // Callback interface to get the hand back when the profile has been executed 40 | private IDIResultCallbacks idiProfileManagerCommandResult = null; 41 | 42 | // Profile content in XML 43 | private String msProfileData = ""; 44 | 45 | // Profile name to execute 46 | private String msProfileName = ""; 47 | 48 | //Declare a variable to store ProfileManager object 49 | private ProfileManager mProfileManager = null; 50 | 51 | //Declare a variable to store EMDKManager object 52 | private EMDKManager mEMDKManager = null; 53 | 54 | // An ArrayList that will contains errors if we find some 55 | private ArrayList mErrors = new ArrayList<>(); 56 | 57 | // Provides full error description string 58 | public String msErrorString = ""; 59 | 60 | // To prevent multiple initializations at the same time 61 | private boolean bInitializing = false; 62 | 63 | // Status Listener implementation (ensure that we retrieve the profile manager asynchronously 64 | EMDKManager.StatusListener mStatusListener = new EMDKManager.StatusListener() { 65 | @Override 66 | public void onStatus(EMDKManager.StatusData statusData, EMDKBase emdkBase) { 67 | if(statusData.getResult() == EMDKResults.STATUS_CODE.SUCCESS) 68 | { 69 | logMessage("Profile manager retrieved.", EMessageType.DEBUG); 70 | onProfileManagerInitialized((ProfileManager)emdkBase); 71 | } 72 | else 73 | { 74 | String errorMessage = "Error when trying to retrieve ProfileManager: " + getResultCode(statusData.getResult()); 75 | logMessage(errorMessage, EMessageType.ERROR); 76 | } 77 | } 78 | }; 79 | 80 | // EMDKListener implementation 81 | EMDKManager.EMDKListener mEMDKListener = new EMDKManager.EMDKListener() { 82 | @Override 83 | public void onOpened(EMDKManager emdkManager) { 84 | logMessage("EMDKManager.EMDKListener.onOpened", EMessageType.DEBUG); 85 | onEMDKManagerRetrieved(emdkManager); 86 | } 87 | 88 | @Override 89 | public void onClosed() { 90 | logMessage("EMDKManager.EMDKListener.onClosed", EMessageType.DEBUG); 91 | onEMDKManagerClosed(); 92 | } 93 | }; 94 | 95 | 96 | public DIProfileManagerCommand(Context aContext) { 97 | super(aContext); 98 | mSettings = new DICommandBaseSettings(){{ 99 | mTimeOutMS = 20000; 100 | mEnableTimeOutMechanism = true; 101 | mCommandId = "DWProfileManagerCommand"; 102 | }}; 103 | } 104 | 105 | @Override 106 | protected void onTimeOut(DICommandBaseSettings settings) { 107 | super.onTimeOut(settings); 108 | onEMDKManagerClosed(); 109 | } 110 | 111 | public void execute(String mxProfile, String mxProfileName, IDIResultCallbacks resutCallback) 112 | { 113 | // Let's start the timeout mechanism 114 | super.execute(mSettings); 115 | idiProfileManagerCommandResult = resutCallback; 116 | msProfileData = mxProfile; 117 | msProfileName = mxProfileName; 118 | initializeEMDK(); 119 | } 120 | 121 | private void initializeEMDK() 122 | { 123 | if(bInitializing) 124 | return; 125 | bInitializing = true; 126 | if(mEMDKManager == null) 127 | { 128 | EMDKResults results = null; 129 | try 130 | { 131 | //The EMDKManager object will be created and returned in the callback. 132 | results = EMDKManager.getEMDKManager(mContext.getApplicationContext(), mEMDKListener); 133 | } 134 | catch(Exception e) 135 | { 136 | logMessage("Error while requesting EMDKManager.\n" + e.getLocalizedMessage(), EMessageType.ERROR); 137 | e.printStackTrace(); 138 | waitForEMDK(); 139 | return; 140 | } 141 | 142 | //Check the return status of EMDKManager object creation. 143 | if(results.statusCode == EMDKResults.STATUS_CODE.SUCCESS) { 144 | logMessage("EMDKManager request command issued with success", EMessageType.DEBUG); 145 | }else { 146 | logMessage("EMDKManager request command error", EMessageType.ERROR); 147 | waitForEMDK(); 148 | } 149 | } 150 | else 151 | { 152 | onEMDKManagerRetrieved(mEMDKManager); 153 | } 154 | } 155 | 156 | private void waitForEMDK() 157 | { 158 | logMessage("EMDKManager error, this could be a BOOT_COMPLETED issue.", EMessageType.DEBUG); 159 | logMessage("Starting watcher thread to wait for EMDK initialization.", EMessageType.DEBUG); 160 | Thread t = new Thread(new Runnable() { 161 | @Override 162 | public void run() { 163 | long startDate = new Date().getTime(); 164 | long delta = 0; 165 | while(mEMDKManager == null || delta < DIHelper.MAX_EMDK_TIMEOUT_IN_MS ) { 166 | // Try to initialize EMDK 167 | logMessage("Calling EMDK Initialization method", EMessageType.DEBUG); 168 | initializeEMDK(); 169 | try { 170 | logMessage("Waiting " + DIHelper.WAIT_PERIOD_BEFORE_RETRY_EMDK_RETRIEVAL_IN_MS + " milliseconds before retrying.", EMessageType.DEBUG); 171 | Thread.sleep(DIHelper.WAIT_PERIOD_BEFORE_RETRY_EMDK_RETRIEVAL_IN_MS); 172 | } catch (InterruptedException e) { 173 | e.printStackTrace(); 174 | } 175 | delta = new Date().getTime() - startDate; 176 | logMessage("Delta in ms since first EMDK retrieval try: " + delta + "ms stops at " + DIHelper.MAX_EMDK_TIMEOUT_IN_MS + "ms", EMessageType.DEBUG); 177 | } 178 | bInitializing = false; 179 | logMessage("Could not retrieve EMDK Manager after waiting " + DIHelper.WAIT_PERIOD_BEFORE_RETRY_EMDK_RETRIEVAL_IN_MS/DIHelper.SEC_IN_MS + " seconds. Please contact your administrator or check logcat for any EMDK related error.", EMessageType.ERROR); 180 | } 181 | }); 182 | t.setPriority(Thread.MIN_PRIORITY); 183 | t.start(); 184 | } 185 | 186 | private void onEMDKManagerRetrieved(EMDKManager emdkManager) 187 | { 188 | mEMDKManager = emdkManager; 189 | logMessage("EMDK Manager retrieved.", EMessageType.DEBUG); 190 | if(mProfileManager == null) 191 | { 192 | try { 193 | logMessage("Requesting profile manager.", EMessageType.DEBUG); 194 | logMessage("Current API version: " + android.os.Build.VERSION.SDK_INT, EMessageType.VERBOSE); 195 | if(android.os.Build.VERSION.SDK_INT < 33) { 196 | logMessage("Requesting profile manager Asynchonously", EMessageType.DEBUG); 197 | emdkManager.getInstanceAsync(EMDKManager.FEATURE_TYPE.PROFILE, mStatusListener); 198 | } 199 | else 200 | { 201 | logMessage("Requesting profile manager synchronized", EMessageType.DEBUG); 202 | ProfileManager profileManager = (ProfileManager) emdkManager.getInstance(EMDKManager.FEATURE_TYPE.PROFILE); 203 | if(profileManager != null) 204 | { 205 | onProfileManagerInitialized(profileManager); 206 | } 207 | } 208 | } catch (EMDKException e) { 209 | logMessage("Error when trying to retrieve profile manager: " + e.getMessage(), EMessageType.ERROR); 210 | } 211 | } 212 | else 213 | { 214 | logMessage("EMDK Manager already initialized.", EMessageType.DEBUG); 215 | onProfileManagerInitialized(mProfileManager); 216 | } 217 | } 218 | 219 | private void onEMDKManagerClosed() 220 | { 221 | releaseManagers(); 222 | } 223 | 224 | private void releaseManagers() 225 | { 226 | if(mProfileManager != null) 227 | { 228 | mProfileManager = null; 229 | logMessage("Profile Manager reseted.", EMessageType.DEBUG); 230 | } 231 | 232 | //This callback will be issued when the EMDK closes unexpectedly. 233 | if (mEMDKManager != null) { 234 | mEMDKManager.release(); 235 | logMessage("EMDKManager released.", EMessageType.DEBUG); 236 | mEMDKManager = null; 237 | logMessage("EMDKManager reseted.", EMessageType.DEBUG); 238 | } 239 | } 240 | 241 | private void onProfileManagerInitialized(ProfileManager profileManager) 242 | { 243 | mProfileManager = profileManager; 244 | bInitializing = false; 245 | logMessage("Processing MX Content", EMessageType.DEBUG); 246 | processMXContent(); 247 | } 248 | 249 | private void onProfileExecutedWithSuccess() 250 | { 251 | releaseManagers(); 252 | if(idiProfileManagerCommandResult != null) 253 | { 254 | idiProfileManagerCommandResult.onSuccess("Success applying profile:" + msProfileName + "\nProfileData:" + msProfileData); 255 | } 256 | 257 | } 258 | 259 | private void onProfileExecutedError(String message) 260 | { 261 | releaseManagers(); 262 | if(idiProfileManagerCommandResult != null) 263 | { 264 | idiProfileManagerCommandResult.onError("Error on profile: " + msProfileName + "\nError:" + message + "\nProfileData:" + msProfileData); 265 | } 266 | 267 | } 268 | 269 | private void onProfileExecutedStatusChanged(String message) 270 | { 271 | if(idiProfileManagerCommandResult != null) 272 | { 273 | idiProfileManagerCommandResult.onDebugStatus(message); 274 | } 275 | } 276 | 277 | private void processMXContent() 278 | { 279 | String[] params = new String[1]; 280 | params[0] = msProfileData; 281 | 282 | logMessage("Processing profile :" + msProfileData, EMessageType.VERBOSE); 283 | EMDKResults results = mProfileManager.processProfile(msProfileName, ProfileManager.PROFILE_FLAG.SET, params); 284 | 285 | //Check the return status of processProfile 286 | if(results.statusCode == EMDKResults.STATUS_CODE.CHECK_XML) { 287 | 288 | // Get XML response as a String 289 | String statusXMLResponse = results.getStatusString(); 290 | 291 | try { 292 | // Empty Error Holder Array List if it already exists 293 | mErrors.clear(); 294 | 295 | // Create instance of XML Pull Parser to parse the response 296 | XmlPullParser parser = Xml.newPullParser(); 297 | // Provide the string response to the String Reader that reads 298 | // for the parser 299 | parser.setInput(new StringReader(statusXMLResponse)); 300 | // Call method to parse the response 301 | parseXML(parser); 302 | 303 | if ( mErrors.size() == 0 ) { 304 | 305 | logMessage("Profile executed with success: " + msProfileName, EMessageType.SUCCESS); 306 | onProfileExecutedWithSuccess(); 307 | } 308 | else { 309 | String errorMessage = ""; 310 | for(ErrorHolder error : mErrors) 311 | { 312 | errorMessage += "Profile processing error.\t" + "Type:" + error.sErrorType + "\tParamName:" + error.sParmName + "\tDescription:" + error.sErrorDescription; 313 | } 314 | logMessage(errorMessage, EMessageType.ERROR); 315 | onProfileExecutedError(errorMessage); 316 | return; 317 | } 318 | 319 | } catch (XmlPullParserException e) { 320 | String errorMessage = "Error while trying to parse ProfileManager XML Response: " + e.getLocalizedMessage(); 321 | logMessage(errorMessage, EMessageType.ERROR); 322 | onProfileExecutedError(errorMessage); 323 | return; 324 | } 325 | } 326 | else if(results.statusCode == EMDKResults.STATUS_CODE.SUCCESS) 327 | { 328 | logMessage("Profile executed with success: " + msProfileName, EMessageType.DEBUG); 329 | onProfileExecutedWithSuccess(); 330 | return; 331 | } 332 | else 333 | { 334 | String errorMessage = "Profile update failed." + getResultCode(results.statusCode) + "\nProfil:\n" + msProfileName; 335 | logMessage(errorMessage, EMessageType.ERROR); 336 | onProfileExecutedError(errorMessage); 337 | return; 338 | } 339 | } 340 | 341 | // Method to parse the XML response using XML Pull Parser 342 | private void parseXML(XmlPullParser myParser) { 343 | int event; 344 | try { 345 | // Retrieve error details if parm-error/characteristic-error in the response XML 346 | event = myParser.getEventType(); 347 | // An object that will store a temporary error holder if an error characteristic is found 348 | ErrorHolder tempErrorHolder = null; 349 | //logMessage("XML document", EMessageType.VERBOSE); 350 | while (event != XmlPullParser.END_DOCUMENT) { 351 | String name = myParser.getName(); 352 | switch (event) { 353 | case XmlPullParser.START_TAG: 354 | //logMessage("XML Element:<" + myParser.getText()+">", EMessageType.VERBOSE); 355 | if (name.equals("characteristic-error")) 356 | { 357 | if(tempErrorHolder == null) 358 | tempErrorHolder = new ErrorHolder(); 359 | tempErrorHolder.sErrorType = myParser.getAttributeValue(null, "type"); 360 | if(tempErrorHolder.sParmName != null && TextUtils.isEmpty(tempErrorHolder.sParmName) == false) 361 | { 362 | msErrorString += "Nom: " + tempErrorHolder.sParmName + "\nType: " + tempErrorHolder.sErrorType + "\nDescription: " + tempErrorHolder.sErrorDescription + ")"; 363 | mErrors.add(tempErrorHolder); 364 | tempErrorHolder = null; 365 | } 366 | } 367 | else if (name.equals("parm-error")) 368 | { 369 | if(tempErrorHolder == null) 370 | tempErrorHolder = new ErrorHolder(); 371 | tempErrorHolder.sParmName = myParser.getAttributeValue(null, "name"); 372 | tempErrorHolder.sErrorDescription = myParser.getAttributeValue(null, "desc"); 373 | if(tempErrorHolder.sErrorType != null && TextUtils.isEmpty(tempErrorHolder.sErrorType) == false) 374 | { 375 | msErrorString += "Nom: " + tempErrorHolder.sParmName + "\nType: " + tempErrorHolder.sErrorType + "\nDescription: " + tempErrorHolder.sErrorDescription + ")"; 376 | mErrors.add(tempErrorHolder); 377 | tempErrorHolder = null; 378 | } 379 | } 380 | break; 381 | case XmlPullParser.END_TAG: 382 | //logMessage("XML Element:", EMessageType.VERBOSE); 383 | break; 384 | } 385 | event = myParser.next(); 386 | } 387 | 388 | } catch (Exception e) { 389 | e.printStackTrace(); 390 | } 391 | } 392 | 393 | private void logMessage(String message, EMessageType messageType) 394 | { 395 | switch(messageType) 396 | { 397 | case ERROR: 398 | Log.e(TAG, message); 399 | onProfileExecutedStatusChanged("ERROR:" + message); 400 | break; 401 | case SUCCESS: 402 | Log.v(TAG, message); 403 | onProfileExecutedStatusChanged("SUCCESS:" + message); 404 | break; 405 | case VERBOSE: 406 | Log.v(TAG, message); 407 | onProfileExecutedStatusChanged("VERBOSE:" + message); 408 | break; 409 | case WARNING: 410 | Log.w(TAG, message); 411 | onProfileExecutedStatusChanged("WARNING:" + message); 412 | break; 413 | case DEBUG: 414 | Log.d(TAG,message); 415 | onProfileExecutedStatusChanged("DEBUG:" + message); 416 | } 417 | } 418 | 419 | private String getResultCode(EMDKResults.STATUS_CODE aStatusCode) 420 | { 421 | switch (aStatusCode) 422 | { 423 | case FAILURE: 424 | return "FAILURE"; 425 | case NULL_POINTER: 426 | return "NULL_POINTER"; 427 | case EMPTY_PROFILENAME: 428 | return "EMPTY_PROFILENAME"; 429 | case EMDK_NOT_OPENED: 430 | return "EMDK_NOT_OPENED"; 431 | case CHECK_XML: 432 | return "CHECK_XML"; 433 | case PREVIOUS_REQUEST_IN_PROGRESS: 434 | return "PREVIOUS_REQUEST_IN_PROGRESS"; 435 | case PROCESSING: 436 | return "PROCESSING"; 437 | case NO_DATA_LISTENER: 438 | return "NO_DATA_LISTENER"; 439 | case FEATURE_NOT_READY_TO_USE: 440 | return "FEATURE_NOT_READY_TO_USE"; 441 | case FEATURE_NOT_SUPPORTED: 442 | return "FEATURE_NOT_SUPPORTED"; 443 | case UNKNOWN: 444 | default: 445 | return "UNKNOWN"; 446 | } 447 | } 448 | 449 | } 450 | -------------------------------------------------------------------------------- /DeviceIdentifiersWrapper/src/main/java/com/zebra/deviceidentifierswrapper/EMessageType.java: -------------------------------------------------------------------------------- 1 | package com.zebra.deviceidentifierswrapper; 2 | 3 | enum EMessageType { 4 | VERBOSE, 5 | WARNING, 6 | ERROR, 7 | SUCCESS, 8 | DEBUG 9 | } 10 | -------------------------------------------------------------------------------- /DeviceIdentifiersWrapper/src/main/java/com/zebra/deviceidentifierswrapper/ExecutorTask.java: -------------------------------------------------------------------------------- 1 | package com.zebra.deviceidentifierswrapper; 2 | 3 | // Inspiration from Vitality answer : https://stackoverflow.com/a/68395429 4 | 5 | import android.os.Handler; 6 | import android.os.Looper; 7 | import android.util.Log; 8 | 9 | import java.util.concurrent.Executor; 10 | import java.util.concurrent.LinkedBlockingQueue; 11 | import java.util.concurrent.ThreadPoolExecutor; 12 | import java.util.concurrent.TimeUnit; 13 | 14 | public abstract class ExecutorTask { 15 | public static final String TAG = "ExecutorTask"; 16 | 17 | private static final Executor THREAD_POOL_EXECUTOR = 18 | new ThreadPoolExecutor(5, 128, 1, 19 | TimeUnit.SECONDS, new LinkedBlockingQueue()); 20 | 21 | private final Handler mHandler = new Handler(Looper.getMainLooper()); 22 | private boolean mIsInterrupted = false; 23 | 24 | protected void onPreExecute(){} 25 | protected abstract Result doInBackground(Params... params); 26 | protected void onPostExecute(Result result){} 27 | 28 | protected void onProgressUpdate(Progress... values) { 29 | } 30 | protected void onCancelled() {} 31 | 32 | @SafeVarargs 33 | public final void executeAsync(Params... params) { 34 | THREAD_POOL_EXECUTOR.execute(() -> { 35 | try { 36 | checkInterrupted(); 37 | mHandler.post(this::onPreExecute); 38 | 39 | checkInterrupted(); 40 | final Result results = doInBackground(params); 41 | 42 | checkInterrupted(); 43 | mHandler.post(new Runnable() { 44 | @Override 45 | public void run() { 46 | onPostExecute(results); 47 | } 48 | }); 49 | } catch (InterruptedException ex) { 50 | mHandler.post(this::onCancelled); 51 | } catch (Exception ex) { 52 | Log.e(TAG, "executeAsync: " + ex.getMessage() + "\n" + ex.getStackTrace()); 53 | 54 | } 55 | }); 56 | } 57 | 58 | private void checkInterrupted() throws InterruptedException { 59 | if (isInterrupted()){ 60 | throw new InterruptedException(); 61 | } 62 | } 63 | 64 | public void cancel(boolean mayInterruptIfRunning){ 65 | setInterrupted(mayInterruptIfRunning); 66 | } 67 | 68 | public boolean isInterrupted() { 69 | return mIsInterrupted; 70 | } 71 | 72 | public void setInterrupted(boolean interrupted) { 73 | mIsInterrupted = interrupted; 74 | } 75 | } -------------------------------------------------------------------------------- /DeviceIdentifiersWrapper/src/main/java/com/zebra/deviceidentifierswrapper/IDIResultCallbacks.java: -------------------------------------------------------------------------------- 1 | package com.zebra.deviceidentifierswrapper; 2 | 3 | public interface IDIResultCallbacks { 4 | void onSuccess(final String message); 5 | void onError(final String message); 6 | void onDebugStatus(final String message); 7 | } 8 | -------------------------------------------------------------------------------- /DeviceIdentifiersWrapper/src/main/java/com/zebra/deviceidentifierswrapper/RetrieveOEMInfoTask.java: -------------------------------------------------------------------------------- 1 | package com.zebra.deviceidentifierswrapper; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.content.pm.PackageInfo; 6 | import android.content.pm.PackageManager; 7 | import android.content.pm.Signature; 8 | import android.database.Cursor; 9 | import android.net.Uri; 10 | import android.os.AsyncTask; 11 | 12 | import java.util.Base64; 13 | 14 | /** 15 | * Device Identifiers Sample 16 | * 17 | * Original Device Identifier Sample Code: 18 | * - Darryn Campbell 19 | * - https://github.com/darryncampbell/EMDK-DeviceIdentifiers-Sample 20 | * 21 | * Wrapper Code: 22 | * - Trudu Laurent 23 | * - https://github.com/ltrudu/DeviceIdentifiersWrapper-Sample 24 | * 25 | * (c) Zebra 2020 26 | */ 27 | 28 | class RetrieveOEMInfoTask extends ExecutorTask { 29 | 30 | @Override 31 | protected Boolean doInBackground(Object... objects) { 32 | Context context = (Context) objects[0]; 33 | Uri uri = (Uri) objects[1]; 34 | IDIResultCallbacks idiResultCallbacks = (IDIResultCallbacks) objects[2]; 35 | RetrieveOEMInfo(context, uri, idiResultCallbacks); 36 | return true; 37 | } 38 | 39 | private static void RetrieveOEMInfo(final Context context, final Uri uri, final IDIResultCallbacks callbackInterface) { 40 | // For clarity, this code calls ContentResolver.query() on the UI thread but production code should perform queries asynchronously. 41 | // See https://developer.android.com/guide/topics/providers/content-provider-basics.html for more information 42 | Cursor cursor = context.getContentResolver().query(uri, null, null, null, null); 43 | if (cursor == null || cursor.getCount() < 1) 44 | { 45 | if(callbackInterface != null) 46 | { 47 | callbackInterface.onDebugStatus("App not registered to call OEM Service:" + uri.toString()+"\nRegistering current application using profile manger, this may take a couple of seconds..."); 48 | } 49 | // Let's register the application 50 | registerCurrentApplication(context, uri, new IDIResultCallbacks() { 51 | @Override 52 | public void onSuccess(String message) { 53 | // The app has been registered 54 | // Let's try again to get the identifier 55 | Cursor cursor2 = context.getContentResolver().query(uri, null, null, null, null); 56 | if (cursor2 == null || cursor2.getCount() < 1) { 57 | if(callbackInterface != null) 58 | { 59 | callbackInterface.onError("Fail to register the app for OEM Service call:" + uri + "\nIt's time to debug this app ;)"); 60 | return; 61 | } 62 | } 63 | getURIValue(cursor2, uri, callbackInterface); 64 | return; 65 | } 66 | 67 | @Override 68 | public void onError(String message) { 69 | if(callbackInterface != null) 70 | { 71 | callbackInterface.onError(message); 72 | return; 73 | } 74 | } 75 | 76 | @Override 77 | public void onDebugStatus(String message) { 78 | if(callbackInterface != null) 79 | { 80 | callbackInterface.onDebugStatus(message); 81 | } 82 | } 83 | }); 84 | } 85 | else 86 | { 87 | // We have the right to call this service, and we obtained some data to parse... 88 | getURIValue(cursor, uri, callbackInterface); 89 | } 90 | } 91 | 92 | private static void registerCurrentApplication(Context context, Uri serviceIdentifier, IDIResultCallbacks callbackInterface) 93 | { 94 | String profileName = "AccessMgr-1"; 95 | String profileData = ""; 96 | try { 97 | PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNING_CERTIFICATES); 98 | String path = context.getApplicationInfo().sourceDir; 99 | final String strName = packageInfo.applicationInfo.loadLabel(context.getPackageManager()).toString(); 100 | final String strVendor = packageInfo.packageName; 101 | Signature sig = DIHelper.apkCertificate; 102 | 103 | // Let's check if we have a custom certificate 104 | if(sig == null) 105 | { 106 | // Nope, we will get the first apk signing certificate that we find 107 | // You can copy/paste this snippet if you want to provide your own 108 | // certificate 109 | // TODO: use the following code snippet to extract your custom certificate if necessary 110 | Signature[] arrSignatures = null; 111 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) { 112 | arrSignatures = packageInfo.signingInfo.getApkContentsSigners(); 113 | } 114 | if(arrSignatures == null || arrSignatures.length == 0) 115 | { 116 | if(callbackInterface != null) 117 | { 118 | callbackInterface.onError("Error : Package has no signing certificates... how's that possible ?"); 119 | return; 120 | } 121 | } 122 | sig = arrSignatures[0]; 123 | } 124 | 125 | /* 126 | * Get the X.509 certificate. 127 | */ 128 | final byte[] rawCert = sig.toByteArray(); 129 | 130 | // Get the certificate as a base64 string 131 | String encoded = null; 132 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { 133 | encoded = Base64.getEncoder().encodeToString(rawCert); 134 | } 135 | 136 | profileData = 137 | "" + 138 | "" + 139 | "" + 140 | "" + 141 | "" + 142 | "" + 143 | "" + 144 | "" + 145 | "" + 146 | ""+ 147 | ""; 148 | DIProfileManagerCommand profileManagerCommand = new DIProfileManagerCommand(context); 149 | profileManagerCommand.execute(profileData, profileName, callbackInterface); 150 | //} 151 | } catch (Exception e) { 152 | e.printStackTrace(); 153 | if(callbackInterface != null) 154 | { 155 | callbackInterface.onError("Error on profile: " + profileName + "\nError:" + e.getLocalizedMessage() + "\nProfileData:" + profileData); 156 | } 157 | } 158 | } 159 | 160 | 161 | private static void getURIValue(Cursor cursor, Uri uri, IDIResultCallbacks resultCallbacks) 162 | { 163 | while (cursor.moveToNext()) { 164 | if (cursor.getColumnCount() == 0) 165 | { 166 | // No data in the cursor. I have seen this happen on non-WAN devices 167 | String errorMsg = "Error: " + uri + " does not exist on this device"; 168 | resultCallbacks.onDebugStatus(errorMsg); 169 | } 170 | else{ 171 | for (int i = 0; i < cursor.getColumnCount(); i++) { 172 | try { 173 | @SuppressLint("Range") String data = cursor.getString(cursor.getColumnIndex(cursor.getColumnName(i))); 174 | resultCallbacks.onSuccess(data); 175 | cursor.close(); 176 | return; 177 | } 178 | catch (Exception e) 179 | { 180 | resultCallbacks.onDebugStatus(e.getLocalizedMessage()); 181 | } 182 | } 183 | } 184 | } 185 | cursor.close(); 186 | resultCallbacks.onError("Data not found in Uri:" + uri); 187 | } 188 | } -------------------------------------------------------------------------------- /DeviceIdentifiersWrapper/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | DataWedgeProfileIntents 3 | 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Darryn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | *Please be aware that this library / application / sample is provided as a community project without any guarantee of support* 2 | ========================================================= 3 | 4 | [![](https://jitpack.io/v/ltrudu/DeviceIdentifiersWrapper.svg)](https://jitpack.io/#ltrudu/DeviceIdentifiersWrapper) 5 | [![](https://jitpack.io/v/ltrudu/DeviceIdentifiersWrapper/month.svg)](https://jitpack.io/#ltrudu/DeviceIdentifiersWrapper) 6 | 7 | 8 | # DeviceIdentifiersWrapper 9 | 10 | ## Easy access to Serial Number, IMEI and Bluetooth Mac Address and more !!! 11 | 12 | Forget about StageNow, EMDK, certificates, application signature... complexity.... 13 | 14 | Just get the Serial Number, the IMEI number, the Bluetooth Mac Address (and more, see below) of your Zebra device in one method call (see at the end of this document). 15 | 16 | Have fun with Zebra's devices :) 17 | 18 | 19 | 20 | 21 | 22 | ## Change Log !!! 23 | 24 | ## 0.12.0 : Added new methods: 25 | 26 | DIHelper.getProductModel to retrieve the product model. 27 | 28 | DIHelper.getIdentityDeviceID to retrieve the identity device ID. 29 | 30 | DIHelper.getWifiMacAddress to retrieve the Wifi Mac Address. 31 | 32 | DIHelper.getWifiAPMacAddress to retrieve the Wifi Access Point Mac Address. 33 | 34 | DIHelper.getWifiSSID to retrieve the Wifi SSID. 35 | 36 | DIHelper.getEthernetMacAddress to retrieve the Ethernet Mac Address if applicable. 37 | 38 | See sample App for more information. 39 | 40 | ## Added new method DIHelper.getBtMacAddress to retrieve Bluetooth Mac Address. 41 | 42 | ### 1. Change of REPOSITORY 43 | ### 2. UPDATED FOR A13... 44 | ### 3. Added a Sample repository running on <=A13 45 | 46 | ## Sample Repository 47 | https://github.com/ltrudu/DeviceIdentifiersWrapper-Sample 48 | 49 | Look for "TODO: MANDATORY FOR DeviceIdentifierWrapper" to find what you need to add to your AndroidManifest.xml and build files. 50 | 51 | ## V0.9 to V0.10 : Get Bluetooth Mac Address 52 | ```text 53 | Added method DIHelper.getBtMacAddress to retrieve device's Bluetooth Mac Address 54 | ``` 55 | 56 | ## V0.8 to V0.9 : Updated for A13 57 | ```text 58 | Added BIND_NOTIFICATION_LISTENER_SERVICE permission 59 | Added com.symbol.emdk.emdkservice to the queries element of the AndroidManifest.xml 60 | Added com.zebra.zebracontentprovider to the queries element of the AndroidManifest.xml 61 | API updated to 33 62 | ``` 63 | ## V0.4 to v0.8 : Basic cache mechanism & Wait for EMDK availability 64 | ```text 65 | Added basic cache mechanism. 66 | The IMei and the Serial number will be cached once they get retrieved. 67 | The cache can be reset with the method: 68 | DIHelper.resetCachedValues() 69 | Added a mechanism to wait for the EMDK if it is not available (when responding to the BOOT_COMPLETED event for ex.) 70 | To be tested... feel free to report any issue regarding this feature. 71 | Check the sample for a basic implementation. 72 | Added lots of logs that will be sent to logCat or to the onDebugStatus callback method. 73 | Updated gradle version to release 7.3.3 74 | ``` 75 | ## V0.3 : Update for A11 76 | ```text 77 | Update your graddle distribution to >= 7.3.3 78 | update your compileSdkVersion to 30 79 | Update your Manifest.xml file to add the Query element (as explained in this description) 80 | Add jitpack.io repository to the project build.graddle file : maven { url 'https://jitpack.io' } 81 | Update the dependency in the graddle application file: implementation 'com.github.ltrudu:DeviceIdentifiersWrapper:0.3' or replace 0.3 with + to get the latest version automatically 82 | Everything is explained in detail in this documentation. 83 | You can use the sample as a copy/paste source. 84 | ``` 85 | 86 | ## Important !! 87 | ```text 88 | Due to usage of the EMDK and the need to register the application, it is strongly advised to call the methods in your application class 89 | Check https://github.com/ltrudu/DeviceIdentifiersWrapper-Sample implementation. 90 | It's a basic implementation using static members. 91 | Feel free to remove statics and replace them with a better code in terms of architecture. 92 | The goal was to pass the idea that theses number should be retrieved only once, and the best place for it is the Application class. 93 | Note that a mechanism has been added in V0.4 to wait for the EMDK in case it would not be available (the classic use case is when your app respond to the BOOT_COMPLETED event that occurs way before the EMDK finishes its initialization) 94 | ``` 95 | 96 | ## Description 97 | A wrapper to easily retrieve the Serial Number and the IMEI number of an Android 10+ Zebra device. 98 | 99 | How to access device identifiers such as serial number and IMEI on Zebra devices running Android 10 100 | 101 | Android 10 limited access to device identifiers for all apps running on the platform regardless of their target API level. As explained in the docs for [Android 10 privacy changes](https://developer.android.com/about/versions/10/privacy/changes) this includes the serial number, IMEI and some other identifiable information. 102 | 103 | **Zebra mobile computers running Android 10 are able to access both the serial number and IMEI** however applications need to be **explicitly granted the ability** to do so and use a proprietary API. 104 | 105 | To access to this API, you must first register your application using the AccessMgr MX's CSP. 106 | 107 | You can do it using StageNow, more details here: https://github.com/darryncampbell/EMDK-DeviceIdentifiers-Sample 108 | 109 | Or you can use this wrapper that will automatically register your application if it is necessary. 110 | 111 | ## Implementation 112 | To use this helper on Zebra Android devices running Android 10 or higher, first declare a new permission in your AndroidManifest.xml 113 | 114 | ```xml 115 | 116 | 117 | 124 | 125 | 126 | 127 | 128 | ``` 129 | 130 | Then add the uses-library element to your application 131 | ```xml 132 | 133 | ``` 134 | 135 | Sample AdroidManifest.xml: 136 | ```xml 137 | 138 | 140 | TODO: Add these permissions to your manifest 141 | 142 | 143 | TODO: Add query element to your manifest 146 | 147 | 148 | 149 | 150 | 151 | 152 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | ``` 170 | Update your project build.graddle file to add jitpack repository 171 | ```text 172 | maven { url 'https://jitpack.io' } 173 | ``` 174 | Sample project build.gradle 175 | ```text 176 | buildscript { 177 | repositories { 178 | google() 179 | jcenter() 180 | maven { url 'https://jitpack.io' } 181 | } 182 | dependencies { 183 | classpath 'com.android.tools.build:gradle:7.2.1' 184 | 185 | // NOTE: Do not place your application dependencies here; they belong 186 | // in the individual module build.gradle files 187 | } 188 | } 189 | 190 | allprojects { 191 | repositories { 192 | jcenter() 193 | google() 194 | maven { url 'https://jitpack.io' } 195 | } 196 | } 197 | 198 | task clean(type: Delete) { 199 | delete rootProject.buildDir 200 | } 201 | ``` 202 | 203 | Finally, add DeviceIdentifierWrapper dependency to your application build.graddle file: 204 | ```text 205 | implementation 'com.github.ltrudu:DeviceIdentifiersWrapper:+' 206 | ``` 207 | 208 | Sample application build.graddle: 209 | ```text 210 | dependencies { 211 | implementation fileTree(dir: 'libs', include: ['*.jar']) 212 | implementation 'com.android.support:appcompat-v7:28.0.0' 213 | testImplementation 'junit:junit:4.13' 214 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 215 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 216 | implementation 'com.github.ltrudu:DeviceIdentifiersWrapper:+' 217 | } 218 | ``` 219 | 220 | Now you can use the following snippet codes to retrieve IMEI number and Serial Number information. 221 | 222 | 223 | Snippet code to use to retrieve the Serial Number of the device: 224 | ```java 225 | private void getSerialNumber(Context context) 226 | { 227 | DIHelper.getSerialNumber(context, new IDIResultCallbacks() { 228 | @Override 229 | public void onSuccess(String message) { 230 | // The message contains the serial number 231 | String mySerialNumber = message; 232 | } 233 | 234 | @Override 235 | public void onError(String message) { 236 | // An error occurred 237 | } 238 | 239 | @Override 240 | public void onDebugStatus(String message) { 241 | // You can use this method to get verbose information 242 | // about what's happening behind the curtain 243 | } 244 | }); 245 | } 246 | ``` 247 | 248 | 249 | Snippet code to use to retrieve the IMEI of the device: 250 | ```java 251 | private void getIMEINumber(Context context) 252 | { 253 | DIHelper.getIMEINumber(context, new IDIResultCallbacks() { 254 | @Override 255 | public void onSuccess(String message) { 256 | // We've got an EMEI number 257 | String myIMEI = message; 258 | } 259 | 260 | @Override 261 | public void onError(String message) { 262 | // An error occurred 263 | } 264 | 265 | @Override 266 | public void onDebugStatus(String message) { 267 | // You can use this method to get verbose information 268 | // about what's happening behind the curtain 269 | } 270 | }); 271 | } 272 | ``` 273 | 274 | Snippet code to use to retrieve the Bluetooth Mac Address of the device: 275 | ```java 276 | private void getBTMacAddress(Context context) 277 | { 278 | DIHelper.getBtMacAddress(context, new IDIResultCallbacks() { 279 | @Override 280 | public void onSuccess(String message) { 281 | // We've got the bt mac address 282 | String myBluetoothMacAddress = message; 283 | } 284 | 285 | @Override 286 | public void onError(String message) { 287 | // An error occurred 288 | } 289 | 290 | @Override 291 | public void onDebugStatus(String message) { 292 | // You can use this method to get verbose information 293 | // about what's happening behind the curtain 294 | } 295 | }); 296 | } 297 | ``` 298 | 299 | 300 | As the previous methods are asynchronous, if you need both information, it is strongly recommended to call the second request inside the onSuccess or onError of the first request. 301 | 302 | Sample code if you need to get both device identifiers: 303 | ```java 304 | private void getDevicesIdentifiers(Context context) 305 | { 306 | // We first ask for the SerialNumber 307 | DIHelper.getSerialNumber(context, new IDIResultCallbacks() { 308 | @Override 309 | public void onSuccess(String message) { 310 | // The message contains the serial number 311 | String mySerialNumber = message; 312 | // We've got the serial number, now we can ask for the IMEINumber 313 | DIHelper.getIMEINumber(context, new IDIResultCallbacks() { 314 | @Override 315 | public void onSuccess(String message) { 316 | // We've got an EMEI number 317 | String myIMEI = message; 318 | } 319 | 320 | @Override 321 | public void onError(String message) { 322 | // An error occurred 323 | } 324 | 325 | @Override 326 | public void onDebugStatus(String message) { 327 | // You can use this method to get verbose information 328 | // about what's happening behind the curtain 329 | } 330 | }); 331 | } 332 | 333 | @Override 334 | public void onError(String message) { 335 | // An error occured 336 | // Do something here with the error message 337 | // We had an error with the Serial Number, but it 338 | // doesn't prevent us from calling the getIMEINumber method 339 | DIHelper.getIMEINumber(context, new IDIResultCallbacks() { 340 | @Override 341 | public void onSuccess(String message) { 342 | // We've got an EMEI number 343 | String myIMEI = message; 344 | } 345 | 346 | @Override 347 | public void onError(String message) { 348 | // An error occurred 349 | } 350 | 351 | @Override 352 | public void onDebugStatus(String message) { 353 | // You can use this method to get verbose information 354 | // about what's happening behind the curtain } 355 | }); 356 | } 357 | 358 | @Override 359 | public void onDebugStatus(String message) { 360 | // You can use this method to get verbose information 361 | // about what's happening behind the curtain 362 | } 363 | }); 364 | } 365 | ``` 366 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | plugins { 3 | id 'com.android.application' version '7.2.2' apply false 4 | id 'com.android.library' version '7.2.2' apply false 5 | } 6 | 7 | task clean(type: Delete) { 8 | delete rootProject.buildDir 9 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZebraDevs/MX-EMDK-Android-Java-DeviceIdentifiersWrapper/5e8b69efefd98481a346e6c9372722d30d5b8c9c/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Jun 09 09:22:20 BST 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | google() 5 | mavenCentral() 6 | maven { url 'https://jitpack.io' } 7 | maven { 8 | url = "https://zebratech.jfrog.io/artifactory/EMDK-Android/" 9 | } 10 | } 11 | } 12 | dependencyResolutionManagement { 13 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 14 | repositories { 15 | google() 16 | mavenCentral() 17 | maven { url 'https://jitpack.io' } 18 | maven { 19 | url = "https://zebratech.jfrog.io/artifactory/EMDK-Android/" 20 | } 21 | } 22 | } 23 | 24 | 25 | include ':DeviceIdentifiersWrapper' 26 | rootProject.name = "DeviceIdentifiersWrapper" --------------------------------------------------------------------------------