From 8ff5cbdbd42db614a0b801c8e4c23ea348f13e0c Mon Sep 17 00:00:00 2001 From: ArtursKadikis Date: Mon, 6 Mar 2023 21:58:07 +0200 Subject: [PATCH] Refactoring. Merging "CrashImpl" classes and "Device" classes. (#34) * Merging "CrashImpl" and "CrashImplCore". Solving some test issues due to updating JSON version. * Removing unused calls related to activities. * Merging "Device" with "DeviceCore" --- .../countly-sdk-java.app-java.main.iml | 2 +- .../countly-sdk-java.app-java.test.iml | 2 +- .../countly-sdk-java.sdk-java.main.iml | 2 +- .../countly-sdk-java.sdk-java.test.iml | 2 +- .../demo/BackendModePerformanceTests.java | 20 +- .../main/java/ly/count/sdk/java/Config.java | 28 +- .../src/main/java/ly/count/sdk/java/User.java | 4 +- .../ly/count/sdk/java/internal/CrashImpl.java | 275 ++++++++++++-- .../sdk/java/internal/CrashImplCore.java | 254 ------------- .../ly/count/sdk/java/internal/Device.java | 346 ++++++++++++++++-- .../count/sdk/java/internal/DeviceCore.java | 329 ----------------- .../ly/count/sdk/java/internal/EventImpl.java | 9 +- .../ly/count/sdk/java/internal/Module.java | 56 --- .../sdk/java/internal/ModuleBackendMode.java | 16 +- .../count/sdk/java/internal/ModuleBase.java | 32 -- .../count/sdk/java/internal/ModuleCrash.java | 18 +- .../sdk/java/internal/ModuleRequests.java | 10 +- .../sdk/java/internal/ModuleSessions.java | 47 --- .../count/sdk/java/internal/ModuleViews.java | 30 -- .../ly/count/sdk/java/internal/Request.java | 2 +- .../ly/count/sdk/java/internal/SDKCore.java | 2 +- .../count/sdk/java/internal/SessionImpl.java | 12 +- .../ly/count/sdk/java/internal/ViewImpl.java | 4 +- .../sdk/java/internal/BackendModeTests.java | 7 +- ...{DeviceCoreTests.java => DeviceTests.java} | 8 +- .../internal/ModuleRemoteConfigTests.java | 34 +- .../count/sdk/java/internal/TasksTests.java | 6 +- 27 files changed, 675 insertions(+), 882 deletions(-) delete mode 100644 sdk-java/src/main/java/ly/count/sdk/java/internal/CrashImplCore.java delete mode 100644 sdk-java/src/main/java/ly/count/sdk/java/internal/DeviceCore.java rename sdk-java/src/test/java/ly/count/sdk/java/internal/{DeviceCoreTests.java => DeviceTests.java} (85%) diff --git a/.idea/modules/app-java/countly-sdk-java.app-java.main.iml b/.idea/modules/app-java/countly-sdk-java.app-java.main.iml index ae3b8c943..95e7f103e 100644 --- a/.idea/modules/app-java/countly-sdk-java.app-java.main.iml +++ b/.idea/modules/app-java/countly-sdk-java.app-java.main.iml @@ -9,6 +9,6 @@ - + \ No newline at end of file diff --git a/.idea/modules/app-java/countly-sdk-java.app-java.test.iml b/.idea/modules/app-java/countly-sdk-java.app-java.test.iml index 8d09ad1b5..181646488 100644 --- a/.idea/modules/app-java/countly-sdk-java.app-java.test.iml +++ b/.idea/modules/app-java/countly-sdk-java.app-java.test.iml @@ -8,7 +8,7 @@ - + \ No newline at end of file diff --git a/.idea/modules/sdk-java/countly-sdk-java.sdk-java.main.iml b/.idea/modules/sdk-java/countly-sdk-java.sdk-java.main.iml index 4c3736e48..5f4a9a5bb 100644 --- a/.idea/modules/sdk-java/countly-sdk-java.sdk-java.main.iml +++ b/.idea/modules/sdk-java/countly-sdk-java.sdk-java.main.iml @@ -8,6 +8,6 @@ - + \ No newline at end of file diff --git a/.idea/modules/sdk-java/countly-sdk-java.sdk-java.test.iml b/.idea/modules/sdk-java/countly-sdk-java.sdk-java.test.iml index da24fc840..e6f04335d 100644 --- a/.idea/modules/sdk-java/countly-sdk-java.sdk-java.test.iml +++ b/.idea/modules/sdk-java/countly-sdk-java.sdk-java.test.iml @@ -9,7 +9,7 @@ - + diff --git a/app-java/src/main/java/ly/count/java/demo/BackendModePerformanceTests.java b/app-java/src/main/java/ly/count/java/demo/BackendModePerformanceTests.java index 56d34af57..db9e0173a 100644 --- a/app-java/src/main/java/ly/count/java/demo/BackendModePerformanceTests.java +++ b/app-java/src/main/java/ly/count/java/demo/BackendModePerformanceTests.java @@ -2,7 +2,7 @@ import ly.count.sdk.java.Config; import ly.count.sdk.java.Countly; -import ly.count.sdk.java.internal.DeviceCore; +import ly.count.sdk.java.internal.Device; import java.io.File; import java.util.HashMap; @@ -33,9 +33,9 @@ private static void initSDK(int eventQueueSize, int requestQueueSize) { static void performLargeRequestQueueSizeTest() { System.out.println("===== Test Started: 'Large request queue size' ====="); int requestQSize = 1000000; - System.out.printf("Before SDK Initialization: Total Memory = %dMb, Available RAM = %dMb %n", DeviceCore.dev.getRAMTotal(), DeviceCore.dev.getRAMAvailable()); + System.out.printf("Before SDK Initialization: Total Memory = %dMb, Available RAM = %dMb %n", Device.dev.getRAMTotal(), Device.dev.getRAMAvailable()); initSDK(1, requestQSize); - System.out.printf("After SDK Initialization: Total Memory = %d Mb, Available RAM= %d Mb %n", DeviceCore.dev.getRAMTotal(), DeviceCore.dev.getRAMAvailable()); + System.out.printf("After SDK Initialization: Total Memory = %d Mb, Available RAM= %d Mb %n", Device.dev.getRAMTotal(), Device.dev.getRAMAvailable()); int batchSize = requestQSize / 25; @@ -106,7 +106,7 @@ static void performLargeRequestQueueSizeTest() { Countly.backendMode().sessionBegin(DEVICE_ID, metrics, location, null); } - System.out.printf("After adding %d request: Total Memory = %d Mb, Available RAM= %d Mb %n", requestQSize, DeviceCore.dev.getRAMTotal(), DeviceCore.dev.getRAMAvailable()); + System.out.printf("After adding %d request: Total Memory = %d Mb, Available RAM= %d Mb %n", requestQSize, Device.dev.getRAMTotal(), Device.dev.getRAMAvailable()); Countly.stop(false); System.out.println("=====SDK Stop====="); @@ -115,9 +115,9 @@ static void performLargeRequestQueueSizeTest() { static void performLargeEventQueueTest() { int noOfEvents = 100000; System.out.println("===== Test Start: 'Large Event queues against multiple devices ids' ====="); - System.out.printf("Before SDK Initialization: Total Memory = %dMb, Available RAM = %dMb %n", DeviceCore.dev.getRAMTotal(), DeviceCore.dev.getRAMAvailable()); + System.out.printf("Before SDK Initialization: Total Memory = %dMb, Available RAM = %dMb %n", Device.dev.getRAMTotal(), Device.dev.getRAMAvailable()); initSDK(noOfEvents, 1000); - System.out.printf("After SDK Initialization: Total Memory = %d Mb, Available RAM= %d Mb %n", DeviceCore.dev.getRAMTotal(), DeviceCore.dev.getRAMAvailable()); + System.out.printf("After SDK Initialization: Total Memory = %d Mb, Available RAM= %d Mb %n", Device.dev.getRAMTotal(), Device.dev.getRAMAvailable()); int noOfDevices = 10; for (int d = 0; d <= noOfDevices; ++d) { @@ -132,7 +132,7 @@ static void performLargeEventQueueTest() { Countly.backendMode().recordEvent("device-id-" + d, "Event Key " + i, 1, 0.1, 5.0, segment, null); } } - System.out.printf("After adding %d events into event queue: Total Memory = %d Mb, Available RAM= %d Mb %n", noOfEvents * noOfDevices, DeviceCore.dev.getRAMTotal(), DeviceCore.dev.getRAMAvailable()); + System.out.printf("After adding %d events into event queue: Total Memory = %d Mb, Available RAM= %d Mb %n", noOfEvents * noOfDevices, Device.dev.getRAMTotal(), Device.dev.getRAMAvailable()); Countly.stop(false); System.out.println("=====SDK Stop====="); @@ -141,9 +141,9 @@ static void performLargeEventQueueTest() { static void recordBulkDataAndSendToServer() throws InterruptedException { System.out.println("===== Test Start: 'Record bulk data to server' ====="); - System.out.printf("Before SDK Initialization: Total Memory = %dMb, Available RAM = %dMb %n", DeviceCore.dev.getRAMTotal(), DeviceCore.dev.getRAMAvailable()); + System.out.printf("Before SDK Initialization: Total Memory = %dMb, Available RAM = %dMb %n", Device.dev.getRAMTotal(), Device.dev.getRAMAvailable()); initSDK(100, 1000); - System.out.printf("After SDK Initialization: Total Memory = %d Mb, Available RAM= %d Mb %n", DeviceCore.dev.getRAMTotal(), DeviceCore.dev.getRAMAvailable()); + System.out.printf("After SDK Initialization: Total Memory = %d Mb, Available RAM= %d Mb %n", Device.dev.getRAMTotal(), Device.dev.getRAMAvailable()); int countOfRequest = 10; int remaining = countOfRequest; int secondsToSleep = 5; @@ -166,7 +166,7 @@ static void recordBulkDataAndSendToServer() throws InterruptedException { } while (remaining != 0 || Countly.backendMode().getQueueSize() != 0); - System.out.printf("After successfully sending %d requests to server: Total Memory = %d Mb, Available RAM= %d Mb %n", countOfRequest, DeviceCore.dev.getRAMTotal(), DeviceCore.dev.getRAMAvailable()); + System.out.printf("After successfully sending %d requests to server: Total Memory = %d Mb, Available RAM= %d Mb %n", countOfRequest, Device.dev.getRAMTotal(), Device.dev.getRAMAvailable()); Countly.stop(false); System.out.println("=====SDK Stop====="); diff --git a/sdk-java/src/main/java/ly/count/sdk/java/Config.java b/sdk-java/src/main/java/ly/count/sdk/java/Config.java index 395050d6c..8337018be 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/Config.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/Config.java @@ -596,8 +596,8 @@ public Config(String serverURL, String serverAppKey) { * Whether to allow fallback from unavailable device id strategy to Countly OpenUDID derivative. * * @param deviceIdFallbackAllowed true if fallback is allowed - * @deprecated this will do nothing * @return {@code this} instance for method chaining + * @deprecated this will do nothing */ public Config setDeviceIdFallbackAllowed(boolean deviceIdFallbackAllowed) { return this; @@ -917,8 +917,8 @@ public Config setSessionCooldownPeriod(int sessionCooldownPeriod) { * Change name of SDK used in HTTP requests * * @param sdkName new name of SDK - * @deprecated Calling this function will do nothing * @return {@code this} instance for method chaining + * @deprecated Calling this function will do nothing */ public Config setSdkName(String sdkName) { return this; @@ -928,8 +928,8 @@ public Config setSdkName(String sdkName) { * Change version of SDK used in HTTP requests * * @param sdkVersion new version of SDK - * @deprecated Calling this function will do nothing * @return {@code this} instance for method chaining + * @deprecated Calling this function will do nothing */ public Config setSdkVersion(String sdkVersion) { return this; @@ -1102,8 +1102,8 @@ public Config addCertificatePin(String pemEncodedCertificate) { * To disable ANR reporting, use {@link #disableANRCrashReporting()}. * * @param periodInSeconds how much time the SDK waits between individual ANR checks - * @deprecated will do nothing * @return {@code this} instance for method chaining + * @deprecated will do nothing */ public Config setCrashReportingANRCheckingPeriod(int periodInSeconds) { return this; @@ -1111,8 +1111,9 @@ public Config setCrashReportingANRCheckingPeriod(int periodInSeconds) { /** * Disable ANR detection and thus reporting to Countly server. - * @deprecated will do nothing + * * @return {@code this} instance for method chaining + * @deprecated will do nothing */ public Config disableANRCrashReporting() { return this; @@ -1139,8 +1140,8 @@ public Config setCrashProcessorClass(Class crashProces * * @param feature feature index to override * @param cls {@link Class} to use instead of Countly SDK standard class - * @deprecated this will do nothing * @return {@code this} instance for method chaining + * @deprecated this will do nothing */ protected Config overrideModule(Integer feature, Class cls) { return this; @@ -1296,8 +1297,9 @@ public int getDeviceIdStrategy() { /** * Whether to allow fallback from unavailable device id strategy to any other available. - * @deprecated this will always return "true" + * * @return true if fallback is allowed + * @deprecated this will always return "true" */ public boolean isDeviceIdFallbackAllowed() { return deviceIdFallbackAllowed; @@ -1341,8 +1343,9 @@ public String getParameterTamperingProtectionSalt() { /** * Getter for {@link #sdkName} - * @deprecated this will be removed + * * @return {@link #sdkName} value + * @deprecated this will be removed */ public String getSdkName() { return sdkName; @@ -1350,8 +1353,9 @@ public String getSdkName() { /** * Getter for {@link #sdkVersion} - * @deprecated this will be removed + * * @return {@link #sdkVersion} value + * @deprecated this will be removed */ public String getSdkVersion() { return sdkVersion; @@ -1503,8 +1507,9 @@ public Set getCertificatePins() { /** * Getter for {@link #crashReportingANRCheckingPeriod} - * @Deprecated will always return "5" + * * @return {@link #crashReportingANRCheckingPeriod} value + * @Deprecated will always return "5" */ public int getCrashReportingANRCheckingPeriod() { return 5; @@ -1521,8 +1526,9 @@ public String getCrashProcessorClass() { /** * Getter for {@link #moduleOverrides} - * @deprecated this always return "null" + * * @return {@link #moduleOverrides} value for {@code Feature} specified + * @deprecated this always return "null" */ public Class getModuleOverride(int feature) { return null; diff --git a/sdk-java/src/main/java/ly/count/sdk/java/User.java b/sdk-java/src/main/java/ly/count/sdk/java/User.java index 9c28685ac..615d3fd1a 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/User.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/User.java @@ -1,5 +1,7 @@ package ly.count.sdk.java; +import ly.count.sdk.java.internal.Device; + import java.util.Map; import java.util.Set; @@ -107,7 +109,7 @@ public static Gender fromString(String v) { /** * Current user locale in "lang_COUNTRY", like en_US or de_DE if default device locale is overridden. - * If not set, Countly will use {@link ly.count.sdk.java.internal.DeviceCore#getLocale()}. + * If not set, Countly will use {@link Device#getLocale()}. * * @return current user locale */ diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/CrashImpl.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/CrashImpl.java index 331df8939..ea7677d7d 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/CrashImpl.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/CrashImpl.java @@ -1,42 +1,263 @@ package ly.count.sdk.java.internal; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + import ly.count.sdk.java.Crash; -import ly.count.sdk.java.internal.CrashImplCore; -import ly.count.sdk.java.internal.Storable; -public class CrashImpl extends CrashImplCore implements Crash, Storable { +/** + * Crash-encapsulating class + */ + +public class CrashImpl implements Crash, Storable { + private Log L = null; + + private final Long id; + private final JSONObject data; + private Throwable throwable; + private Map traces; + + protected CrashImpl(Log logger) { + this(Device.dev.uniformTimestamp(), logger); + } + + protected CrashImpl(Long id, Log logger) { + this.L = logger; + this.id = id; + this.data = new JSONObject(); + this.add("_nonfatal", true); + } + + @Override + public CrashImpl addThrowable(Throwable throwable) { + this.throwable = throwable; + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + this.throwable.printStackTrace(pw); + return add("_error", sw.toString()); + } + + @Override + public CrashImpl addException(Exception e) { + return addThrowable(e); + } + + @Override + public CrashImpl addTraces(Thread main, Map traces) { + if (traces == null) { + L.e("[CrashImpl traces cannot be null"); + return this; + } else { + this.traces = traces; + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + + if (main != null && traces.containsKey(main)) { + pw.println("Thread [main]:"); + printTraces(pw, null, traces.get(main)); + pw.append("\n\n"); + } + + for (Thread thread : traces.keySet()) { + if (thread != main) { + printTraces(pw, thread, traces.get(thread)); + pw.append("\n\n"); + } + } + return add("_type", "anr").add("_error", sw.toString()); + } + } + + private void printTraces(PrintWriter pw, Thread thread, StackTraceElement[] traces) { + if (thread != null) { + pw.append("Thread [").append(thread.getName()).append("]:\n"); + } + for (StackTraceElement el : traces) { + pw.append("\tat ").append(el == null ? "<>" : el.toString()).append("\n"); + } + } + + @Override + public CrashImpl setFatal(boolean fatal) { + return add("_nonfatal", !fatal); + } + + @Override + public CrashImpl setName(String name) { + return add("_name", name); + } + + @Override + public CrashImpl setSegments(Map segments) { + if (segments != null && segments.size() > 0) { + return add("_custom", new JSONObject(segments)); + } + return this; + } + + @Override + public CrashImpl setLogs(String[] logs) { + if (logs != null && logs.length > 0) { + return add("_logs", Utils.join(Arrays.asList(logs), "\n")); + } + return this; + } + + @Override + public Throwable getThrowable() { + return throwable; + } + + @Override + public Map getTraces() { + return traces; + } + + @Override + public boolean isFatal() { + try { + return !this.data.has("_nonfatal") || !this.data.getBoolean("_nonfatal"); + } catch (JSONException e) { + return true; + } + } - // protected CrashImpl() { - // super(); - // } + @Override + public String getName() { + try { + return this.data.has("_name") ? this.data.getString("_name") : null; + } catch (JSONException e) { + return null; + } + } - public CrashImpl(Long id, Log logger) { - super(id, logger); + @Override + public Map getSegments() { + try { + if (!this.data.has("_custom")) { + return null; + } + JSONObject object = this.data.getJSONObject("_custom"); + Map map = new HashMap<>(); + Iterator iterator = object.keys(); + while (iterator.hasNext()) { + String key = iterator.next(); + map.put(key, object.getString(key)); + } + return map; + } catch (JSONException e) { + return null; + } } @Override + public List getLogs() { + try { + String logs = this.data.getString("_logs"); + return Utils.isEmptyOrNull(logs) ? null : Arrays.asList(logs.split("\n")); + } catch (JSONException e) { + return null; + } + } + protected CrashImpl add(String key, Object value) { - return (CrashImpl) super.add(key, value); + if (Utils.isNotEmpty(key) && value != null) { + try { + this.data.put(key, value); + } catch (JSONException e) { + L.e("[CrashImpl Couldn't add " + key + " to a crash" + e); + } + } + return this; + } + + @Override + public byte[] store() { + try { + return data.toString().getBytes(Utils.UTF8); + } catch (UnsupportedEncodingException e) { + L.e("[CrashImpl UTF is not supported" + e); + return null; + } + } + + public boolean restore(byte[] data) { + try { + String json = new String(data, Utils.UTF8); + try { + JSONObject obj = new JSONObject(json); + Iterator iterator = obj.keys(); + while (iterator.hasNext()) { + String k = iterator.next(); + this.data.put(k, obj.get(k)); + } + } catch (JSONException e) { + L.e("[CrashImpl Couldn't decode crash data successfully" + e); + } + return true; + } catch (UnsupportedEncodingException e) { + L.e("[CrashImpl Cannot deserialize crash" + e); + } + + return false; + } + + @Override + public Long storageId() { + return id; + } + + @Override + public String storagePrefix() { + return getStoragePrefix(); + } + + public static String getStoragePrefix() { + return "crash"; } public CrashImpl putMetrics(CtxCore ctx, Long runningTime) { - super.putMetricsCore(ctx, runningTime); - return add("_device", Device.dev.getDevice()) - .add("_os", Device.dev.getOS()) - .add("_os_version", Device.dev.getOSVersion()) - .add("_resolution", Device.dev.getResolution()) - .add("_app_version", Device.dev.getAppVersion()) - .add("_manufacture", Device.dev.getManufacturer()) - .add("_cpu", Device.dev.getCpu()) - .add("_opengl", Device.dev.getOpenGL()) - .add("_ram_current", Device.dev.getRAMAvailable()) - .add("_ram_total", Device.dev.getRAMTotal()) - .add("_disk_current", Device.dev.getDiskAvailable()) - .add("_disk_total", Device.dev.getDiskTotal()) - .add("_bat", Device.dev.getBatteryLevel()) - .add("_run", runningTime) - .add("_orientation", Device.dev.getOrientation()) - .add("_online", Device.dev.isOnline()) - .add("_muted", Device.dev.isMuted()); + add("_device", Device.dev.getDevice()); + add("_os", Device.dev.getOS()); + add("_os_version", Device.dev.getOSVersion()); + add("_resolution", Device.dev.getResolution()); + add("_app_version", Device.dev.getAppVersion()); + add("_manufacture", Device.dev.getManufacturer()); + add("_cpu", Device.dev.getCpu()); + add("_opengl", Device.dev.getOpenGL()); + add("_ram_current", Device.dev.getRAMAvailable()); + add("_ram_total", Device.dev.getRAMTotal()); + add("_disk_current", Device.dev.getDiskAvailable()); + add("_disk_total", Device.dev.getDiskTotal()); + add("_bat", Device.dev.getBatteryLevel()); + add("_run", runningTime); + add("_orientation", Device.dev.getOrientation()); + add("_online", Device.dev.isOnline()); + add("_muted", Device.dev.isMuted()); + return this; + } + + public String getJSON() { + return data.toString(); + } + + public JSONObject getData() { + return data; + } + + @Override + public String toString() { + return data.toString(); } } diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/CrashImplCore.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/CrashImplCore.java deleted file mode 100644 index 645e9f626..000000000 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/CrashImplCore.java +++ /dev/null @@ -1,254 +0,0 @@ -package ly.count.sdk.java.internal; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.io.UnsupportedEncodingException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import ly.count.sdk.java.Crash; - -/** - * Crash-encapsulating class - */ - -public class CrashImplCore implements Crash, Storable { - private Log L = null; - - private final Long id; - private final JSONObject data; - private Throwable throwable; - private Map traces; - - protected CrashImplCore(Log logger) { - this(DeviceCore.dev.uniformTimestamp(), logger); - } - - protected CrashImplCore(Long id, Log logger) { - this.L = logger; - this.id = id; - this.data = new JSONObject(); - this.add("_nonfatal", true); - } - - @Override - public CrashImplCore addThrowable(Throwable throwable) { - this.throwable = throwable; - - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - this.throwable.printStackTrace(pw); - return add("_error", sw.toString()); - } - - @Override - public CrashImplCore addException(Exception e) { - return addThrowable(e); - } - - @Override - public CrashImplCore addTraces(Thread main, Map traces) { - if (traces == null) { - L.e("[CrashImpl traces cannot be null"); - return this; - } else { - this.traces = traces; - - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - - if (main != null && traces.containsKey(main)) { - pw.println("Thread [main]:"); - printTraces(pw, null, traces.get(main)); - pw.append("\n\n"); - } - - for (Thread thread : traces.keySet()) { - if (thread != main) { - printTraces(pw, thread, traces.get(thread)); - pw.append("\n\n"); - } - } - return add("_type", "anr").add("_error", sw.toString()); - } - } - - private void printTraces(PrintWriter pw, Thread thread, StackTraceElement[] traces) { - if (thread != null) { - pw.append("Thread [").append(thread.getName()).append("]:\n"); - } - for (StackTraceElement el : traces) { - pw.append("\tat ").append(el == null ? "<>" : el.toString()).append("\n"); - } - } - - @Override - public CrashImplCore setFatal(boolean fatal) { - return add("_nonfatal", !fatal); - } - - @Override - public CrashImplCore setName(String name) { - return add("_name", name); - } - - @Override - public CrashImplCore setSegments(Map segments) { - if (segments != null && segments.size() > 0) { - return add("_custom", new JSONObject(segments)); - } - return this; - } - - @Override - public CrashImplCore setLogs(String[] logs) { - if (logs != null && logs.length > 0) { - return add("_logs", Utils.join(Arrays.asList(logs), "\n")); - } - return this; - } - - @Override - public Throwable getThrowable() { - return throwable; - } - - @Override - public Map getTraces() { - return traces; - } - - @Override - public boolean isFatal() { - try { - return !this.data.has("_nonfatal") || !this.data.getBoolean("_nonfatal"); - } catch (JSONException e) { - return true; - } - } - - @Override - public String getName() { - try { - return this.data.has("_name") ? this.data.getString("_name") : null; - } catch (JSONException e) { - return null; - } - } - - @Override - public Map getSegments() { - try { - if (!this.data.has("_custom")) { - return null; - } - JSONObject object = this.data.getJSONObject("_custom"); - Map map = new HashMap<>(); - Iterator iterator = object.keys(); - while (iterator.hasNext()) { - String key = iterator.next(); - map.put(key, object.getString(key)); - } - return map; - } catch (JSONException e) { - return null; - } - } - - @Override - public List getLogs() { - try { - String logs = this.data.getString("_logs"); - return Utils.isEmptyOrNull(logs) ? null : Arrays.asList(logs.split("\n")); - } catch (JSONException e) { - return null; - } - } - - protected CrashImplCore add(String key, Object value) { - if (Utils.isNotEmpty(key) && value != null) { - try { - this.data.put(key, value); - } catch (JSONException e) { - L.e("[CrashImpl Couldn't add " + key + " to a crash" + e); - } - } - return this; - } - - @Override - public byte[] store() { - try { - return data.toString().getBytes(Utils.UTF8); - } catch (UnsupportedEncodingException e) { - L.e("[CrashImpl UTF is not supported" + e); - return null; - } - } - - public boolean restore(byte[] data) { - try { - String json = new String(data, Utils.UTF8); - try { - JSONObject obj = new JSONObject(json); - Iterator iterator = obj.keys(); - while (iterator.hasNext()) { - String k = iterator.next(); - this.data.put(k, obj.get(k)); - } - } catch (JSONException e) { - L.e("[CrashImpl Couldn't decode crash data successfully" + e); - } - return true; - } catch (UnsupportedEncodingException e) { - L.e("[CrashImpl Cannot deserialize crash" + e); - } - - return false; - } - - @Override - public Long storageId() { - return id; - } - - @Override - public String storagePrefix() { - return getStoragePrefix(); - } - - public static String getStoragePrefix() { - return "crash"; - } - - public CrashImplCore putMetricsCore(CtxCore ctx, Long runningTime) { - String version = ctx.getConfig().getApplicationVersion(); - return add("_os", DeviceCore.dev.getOS()) - .add("_app_version", Utils.isEmptyOrNull(version) ? "0.0" : version) - .add("_os_version", DeviceCore.dev.getOSVersion()) - .add("_ram_current", DeviceCore.dev.getRAMAvailable()) - .add("_ram_total", DeviceCore.dev.getRAMTotal()) - .add("_disk_current", DeviceCore.dev.getDiskAvailable()) - .add("_disk_total", DeviceCore.dev.getDiskTotal()) - .add("_run", runningTime); - } - - public String getJSON() { - return data.toString(); - } - - public JSONObject getData() { - return data; - } - - @Override - public String toString() { - return data.toString(); - } -} diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/Device.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/Device.java index 7765fa3aa..667c0d668 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/Device.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/Device.java @@ -1,14 +1,23 @@ package ly.count.sdk.java.internal; -import ly.count.sdk.java.internal.CtxCore; -import ly.count.sdk.java.internal.DeviceCore; -import ly.count.sdk.java.internal.Params; +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.TimeZone; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Class encapsulating most of device-specific logic: metrics, info, etc. - * In Java we don't have access to the metrics, so the SDK leaves this up to developer to set these if needed. */ -public class Device extends DeviceCore { + +public class Device { public static Device dev = new Device(); private String device; @@ -22,8 +31,311 @@ public class Device extends DeviceCore { private Boolean online; private Boolean muted; - private Device() { - DeviceCore.dev = dev = this; + protected Device() { + dev = this; + } + + /** + * One second in nanoseconds + */ + protected static final Double NS_IN_SECOND = 1000000000.0d; + protected static final Double NS_IN_MS = 1000000.0d; + protected static final Double MS_IN_SECOND = 1000d; + protected static final Long BYTES_IN_MB = 1024L * 1024; + + /** + * General interface for time generators. + */ + public interface TimeGenerator { + long timestamp(); + } + + /** + * Always increasing timer. + */ + static class UniformTimeGenerator implements TimeGenerator { + private Long last; + + @Override + public synchronized long timestamp() { + long ms = System.currentTimeMillis(); + if (last == null) { + last = ms; + } else if (last >= ms) { + last = last + 1; + return last; + } else { + last = ms; + } + return ms; + } + } + + /** + * Unique timer, keeps last 10 returned values in memory. + */ + static class UniqueTimeGenerator implements TimeGenerator { + List lastTsMs = new ArrayList<>(10); + long addition = 0; + + long currentTimeMillis() { + return System.currentTimeMillis() + addition; + } + + public synchronized long timestamp() { + long ms = currentTimeMillis(); + + // change time back case + if (lastTsMs.size() > 2) { + long min = Collections.min(lastTsMs); + if (ms < min) { + lastTsMs.clear(); + lastTsMs.add(ms); + return ms; + } + } + // usual case + while (lastTsMs.contains(ms)) { + ms += 1; + } + while (lastTsMs.size() >= 10) { + lastTsMs.remove(0); + } + lastTsMs.add(ms); + return ms; + } + } + + protected TimeGenerator uniqueTimer = new UniqueTimeGenerator(); + protected TimeGenerator uniformTimer = new UniformTimeGenerator(); + + /** + * Get operation system name + * + * @return the display name of the current operating system. + */ + public String getOS() { + return System.getProperty("os.name"); + } + + /** + * Get Android version + * + * @return current operating system version as a displayable string. + */ + public String getOSVersion() { + return System.getProperty("os.version", "n/a"); + // + " / " + System.getProperty("os.arch", "n/a"); + } + + /** + * Get device timezone offset in seconds + * + * @return timezone offset in seconds + */ + public int getTimezoneOffset() { + return TimeZone.getDefault().getOffset(new Date().getTime()) / 60000; + } + + /** + * Get current locale + * + * @return current locale (ex. "en_US"). + */ + public String getLocale() { + final Locale locale = Locale.getDefault(); + return locale.getLanguage() + "_" + locale.getCountry(); + } + + /** + * Build metrics {@link Params} object as required by Countly server + * + * @param ctx Ctx in which to request metrics + */ + public Params buildMetrics(final CtxCore ctx) { + Params params = new Params(); + params.obj("metrics") + .put("_device", getDevice()) + .put("_os", getOS()) + .put("_os_version", getOSVersion()) + .put("_resolution", getResolution()) + .put("_locale", getLocale()) + .put("_app_version", getAppVersion()) + .add(); + + return params; + } + + /** + * Wraps {@link System#currentTimeMillis()} to always return different value, even within + * same millisecond and even when time changes. Works in a limited window of 10 timestamps for now. + * + * @return unique time in ms + */ + public synchronized long uniqueTimestamp() { + return uniqueTimer.timestamp(); + } + + /** + * Wraps {@link System#currentTimeMillis()} to return always rising values. + * Resolves issue with device time updates via NTP or manually where time must go up. + * + * @return uniform time in ms + */ + public synchronized long uniformTimestamp() { + return uniformTimer.timestamp(); + } + + /** + * Get current day of week + * + * @return day of week value, Sunday = 0, Saturday = 6 + */ + public int currentDayOfWeek() { + int day = Calendar.getInstance().get(Calendar.DAY_OF_WEEK); + switch (day) { + case Calendar.SUNDAY: + return 0; + case Calendar.MONDAY: + return 1; + case Calendar.TUESDAY: + return 2; + case Calendar.WEDNESDAY: + return 3; + case Calendar.THURSDAY: + return 4; + case Calendar.FRIDAY: + return 5; + case Calendar.SATURDAY: + return 6; + } + return 0; + } + + /** + * Get current hour of day + * + * @return current hour of day + */ + public int currentHour() { + return Calendar.getInstance().get(Calendar.HOUR_OF_DAY); + } + + public int getHourFromCalendar(Calendar calendar) { + return calendar.get(Calendar.HOUR_OF_DAY); + } + + /** + * Convert time in nanoseconds to milliseconds + * + * @param ns time in nanoseconds + * @return ns in milliseconds + */ + public long nsToMs(long ns) { + return Math.round(ns / NS_IN_MS); + } + + /** + * Convert time in nanoseconds to seconds + * + * @param ns time in nanoseconds + * @return ns in seconds + */ + public long nsToSec(long ns) { + return Math.round(ns / NS_IN_SECOND); + } + + /** + * Convert time in seconds to nanoseconds + * + * @param sec time in seconds + * @return sec in nanoseconds + */ + public long secToNs(long sec) { + return Math.round(sec * NS_IN_SECOND); + } + + /** + * Convert time in seconds to milliseconds + * + * @param sec time in seconds + * @return sec in nanoseconds + */ + public long secToMs(long sec) { + return Math.round(sec * MS_IN_SECOND); + } + + /** + * Get total RAM in Mb + * + * @return total RAM in Mb or null if cannot determine + */ + public Long getRAMTotal() { + RandomAccessFile reader = null; + try { + reader = new RandomAccessFile("/proc/meminfo", "r"); + String load = reader.readLine(); + + // Get the Number value from the string + Pattern p = Pattern.compile("(\\d+)"); + Matcher m = p.matcher(load); + String value = ""; + while (m.find()) { + value = m.group(1); + } + return Long.parseLong(value) / 1024; + } catch (NumberFormatException e) { + System.out.print("[ERROR][DeviceCore] Cannot parse meminfo " + e.toString()); + return null; + } catch (IOException e) { + System.out.print("[ERROR] [DeviceCore] Cannot read meminfo " + e.toString()); + return null; + } finally { + try { + if (reader != null) { + reader.close(); + } + } catch (IOException ignored) { + } + } + } + + /** + * Get current device RAM amount. + * + * @return currently available RAM in Mb or {@code null} if couldn't determine + */ + public Long getRAMAvailable() { + Long total = Runtime.getRuntime().totalMemory(); + Long availMem = Runtime.getRuntime().freeMemory(); + return (total - availMem) / BYTES_IN_MB; + } + + /** + * Get current device disk space. + * + * @return currently available disk space in Mb + */ + public Long getDiskAvailable() { + long total = 0, free = 0; + for (File f : File.listRoots()) { + total += f.getTotalSpace(); + free += f.getUsableSpace(); + } + return (total - free) / BYTES_IN_MB; + } + + /** + * Get total device disk space. + * + * @return total device disk space in Mb + */ + public Long getDiskTotal() { + long total = 0; + for (File f : File.listRoots()) { + total += f.getTotalSpace(); + } + return total / BYTES_IN_MB; } /** @@ -215,24 +527,4 @@ public Device setMuted(Boolean muted) { this.muted = muted; return this; } - - /** - * Build metrics {@link Params} object as required by Countly server - * - * @param sdkctx Ctx in which to request metrics - */ - @Override - public Params buildMetrics(final CtxCore sdkctx) { - Params params = new Params(); - params.obj("metrics") - .put("_device", getDevice()) - .put("_os", getOS()) - .put("_os_version", getOSVersion()) - .put("_resolution", getResolution()) - .put("_locale", getLocale()) - .put("_app_version", getAppVersion()) - .add(); - - return params; - } } diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/DeviceCore.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/DeviceCore.java deleted file mode 100644 index ddf1ac9f0..000000000 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/DeviceCore.java +++ /dev/null @@ -1,329 +0,0 @@ -package ly.count.sdk.java.internal; - -import java.io.File; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.lang.management.ManagementFactory; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Locale; -import java.util.TimeZone; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Class encapsulating most of device-specific logic: metrics, info, etc. - */ - -public class DeviceCore { - public static DeviceCore dev = new DeviceCore(); - - protected DeviceCore() { - dev = this; - } - - /** - * One second in nanoseconds - */ - protected static final Double NS_IN_SECOND = 1000000000.0d; - protected static final Double NS_IN_MS = 1000000.0d; - protected static final Double MS_IN_SECOND = 1000d; - protected static final Long BYTES_IN_MB = 1024L * 1024; - - /** - * General interface for time generators. - */ - public interface TimeGenerator { - long timestamp(); - } - - /** - * Always increasing timer. - */ - static class UniformTimeGenerator implements TimeGenerator { - private Long last; - - @Override - public synchronized long timestamp() { - long ms = System.currentTimeMillis(); - if (last == null) { - last = ms; - } else if (last >= ms) { - last = last + 1; - return last; - } else { - last = ms; - } - return ms; - } - } - - /** - * Unique timer, keeps last 10 returned values in memory. - */ - static class UniqueTimeGenerator implements TimeGenerator { - List lastTsMs = new ArrayList<>(10); - long addition = 0; - - long currentTimeMillis() { - return System.currentTimeMillis() + addition; - } - - public synchronized long timestamp() { - long ms = currentTimeMillis(); - - // change time back case - if (lastTsMs.size() > 2) { - long min = Collections.min(lastTsMs); - if (ms < min) { - lastTsMs.clear(); - lastTsMs.add(ms); - return ms; - } - } - // usual case - while (lastTsMs.contains(ms)) { - ms += 1; - } - while (lastTsMs.size() >= 10) { - lastTsMs.remove(0); - } - lastTsMs.add(ms); - return ms; - } - } - - protected TimeGenerator uniqueTimer = new UniqueTimeGenerator(); - protected TimeGenerator uniformTimer = new UniformTimeGenerator(); - - /** - * Get operation system name - * - * @return the display name of the current operating system. - */ - public String getOS() { - return System.getProperty("os.name"); - } - - /** - * Get Android version - * - * @return current operating system version as a displayable string. - */ - public String getOSVersion() { - return System.getProperty("os.version", "n/a"); - // + " / " + System.getProperty("os.arch", "n/a"); - } - - /** - * Get device timezone offset in seconds - * - * @return timezone offset in seconds - */ - public int getTimezoneOffset() { - return TimeZone.getDefault().getOffset(new Date().getTime()) / 60000; - } - - /** - * Get current locale - * - * @return current locale (ex. "en_US"). - */ - public String getLocale() { - final Locale locale = Locale.getDefault(); - return locale.getLanguage() + "_" + locale.getCountry(); - } - - /** - * Build metrics {@link Params} object as required by Countly server - * - * @param ctx Ctx in which to request metrics - */ - public Params buildMetrics(final CtxCore ctx) { - Params params = new Params(); - params.obj("metrics") - .put("_os", getOS()) - .put("_os_version", getOSVersion()) - .put("_locale", getLocale()) - .put("_store", ctx.getConfig().getApplicationName()) - .put("_app_version", ctx.getConfig().getApplicationVersion()) - .add(); - - return params; - } - - /** - * Wraps {@link System#currentTimeMillis()} to always return different value, even within - * same millisecond and even when time changes. Works in a limited window of 10 timestamps for now. - * - * @return unique time in ms - */ - public synchronized long uniqueTimestamp() { - return uniqueTimer.timestamp(); - } - - /** - * Wraps {@link System#currentTimeMillis()} to return always rising values. - * Resolves issue with device time updates via NTP or manually where time must go up. - * - * @return uniform time in ms - */ - public synchronized long uniformTimestamp() { - return uniformTimer.timestamp(); - } - - /** - * Get current day of week - * - * @return day of week value, Sunday = 0, Saturday = 6 - */ - public int currentDayOfWeek() { - int day = Calendar.getInstance().get(Calendar.DAY_OF_WEEK); - switch (day) { - case Calendar.SUNDAY: - return 0; - case Calendar.MONDAY: - return 1; - case Calendar.TUESDAY: - return 2; - case Calendar.WEDNESDAY: - return 3; - case Calendar.THURSDAY: - return 4; - case Calendar.FRIDAY: - return 5; - case Calendar.SATURDAY: - return 6; - } - return 0; - } - - /** - * Get current hour of day - * - * @return current hour of day - */ - public int currentHour() { - return Calendar.getInstance().get(Calendar.HOUR_OF_DAY); - } - - public int getHourFromCalendar(Calendar calendar) { - return calendar.get(Calendar.HOUR_OF_DAY); - } - - /** - * Convert time in nanoseconds to milliseconds - * - * @param ns time in nanoseconds - * @return ns in milliseconds - */ - public long nsToMs(long ns) { - return Math.round(ns / NS_IN_MS); - } - - /** - * Convert time in nanoseconds to seconds - * - * @param ns time in nanoseconds - * @return ns in seconds - */ - public long nsToSec(long ns) { - return Math.round(ns / NS_IN_SECOND); - } - - /** - * Convert time in seconds to nanoseconds - * - * @param sec time in seconds - * @return sec in nanoseconds - */ - public long secToNs(long sec) { - return Math.round(sec * NS_IN_SECOND); - } - - /** - * Convert time in seconds to milliseconds - * - * @param sec time in seconds - * @return sec in nanoseconds - */ - public long secToMs(long sec) { - return Math.round(sec * MS_IN_SECOND); - } - - /** - * Get total RAM in Mb - * - * @return total RAM in Mb or null if cannot determine - */ - public Long getRAMTotal() { - RandomAccessFile reader = null; - try { - reader = new RandomAccessFile("/proc/meminfo", "r"); - String load = reader.readLine(); - - // Get the Number value from the string - Pattern p = Pattern.compile("(\\d+)"); - Matcher m = p.matcher(load); - String value = ""; - while (m.find()) { - value = m.group(1); - } - return Long.parseLong(value) / 1024; - } catch (NumberFormatException e) { - System.out.print("[ERROR][DeviceCore] Cannot parse meminfo " + e.toString()); - return null; - } catch (IOException e) { - System.out.print("[ERROR] [DeviceCore] Cannot read meminfo " + e.toString()); - return null; - } finally { - try { - if (reader != null) { - reader.close(); - } - } catch (IOException ignored) { - } - } - } - - /** - * Get current device RAM amount. - * - * @return currently available RAM in Mb or {@code null} if couldn't determine - */ - public Long getRAMAvailable() { - Long total = Runtime.getRuntime().totalMemory(); - Long availMem = Runtime.getRuntime().freeMemory(); - return (total - availMem) / BYTES_IN_MB; - } - - /** - * Get current device disk space. - * - * @return currently available disk space in Mb - */ - public Long getDiskAvailable() { - long total = 0, free = 0; - for (File f : File.listRoots()) { - total += f.getTotalSpace(); - free += f.getUsableSpace(); - } - return (total - free) / BYTES_IN_MB; - } - - /** - * Get total device disk space. - * - * @return total device disk space in Mb - */ - public Long getDiskTotal() { - long total = 0; - for (File f : File.listRoots()) { - total += f.getTotalSpace(); - } - return total / BYTES_IN_MB; - } -} diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java index 3de328a67..e8344422b 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java @@ -1,6 +1,5 @@ package ly.count.sdk.java.internal; -import ly.count.sdk.java.Config; import org.json.JSONException; import org.json.JSONObject; @@ -50,9 +49,9 @@ public interface EventRecorder { this.recorder = recorder; this.key = key; this.count = 1; - this.timestamp = DeviceCore.dev.uniqueTimestamp(); - this.hour = DeviceCore.dev.currentHour(); - this.dow = DeviceCore.dev.currentDayOfWeek(); + this.timestamp = Device.dev.uniqueTimestamp(); + this.hour = Device.dev.currentHour(); + this.dow = Device.dev.currentDayOfWeek(); } @Override @@ -77,7 +76,7 @@ public void endAndRecord() { return; } - setDuration((DeviceCore.dev.uniqueTimestamp() - timestamp) / 1000); + setDuration((Device.dev.uniqueTimestamp() - timestamp) / 1000); record(); } diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/Module.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/Module.java index bea8b12f1..24a99eeb9 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/Module.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/Module.java @@ -77,55 +77,6 @@ public interface Module { */ void onDeviceId(CtxCore ctx, Config.DID deviceId, Config.DID oldDeviceId); - /** - * Activity is being created. - * - * @param ctx {@link CtxCore} with activity set - */ - void onActivityCreated(CtxCore ctx); - - /** - * Activity is being launched. - * - * @param ctx {@link CtxCore} with activity set - */ - void onActivityStarted(CtxCore ctx); - - /** - * Activity is being resumed. - * - * @param ctx {@link CtxCore} with activity set - */ - void onActivityResumed(CtxCore ctx); - - /** - * Activity is being paused. - * - * @param ctx {@link CtxCore} with activity set - */ - void onActivityPaused(CtxCore ctx); - - /** - * Activity is being stopped. - * - * @param ctx {@link CtxCore} with activity set - */ - void onActivityStopped(CtxCore ctx); - - /** - * Activity is saving state. - * - * @param ctx {@link CtxCore} with activity set - */ - void onActivitySaveInstanceState(CtxCore ctx); - - /** - * Activity is being destroyed. - * - * @param ctx {@link CtxCore} with activity set - */ - void onActivityDestroyed(CtxCore ctx); - /** * Session is started. * @@ -167,13 +118,6 @@ public interface Module { */ void onRequestCompleted(Request request, String response, int responseCode); - /** - * Called when {@code android.content.res.Configuration} changes. - * - * @param ctx {@link CtxCore} with only context set - */ - void onConfigurationChanged(CtxCore ctx); - /** * @return Module feature index if any */ diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleBackendMode.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleBackendMode.java index f68022842..a2f063d35 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleBackendMode.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleBackendMode.java @@ -81,7 +81,7 @@ private void recordEventInternal(String deviceID, String key, int count, Double L.d(String.format("recordEventInternal: deviceID = %s, key = %s,, count = %d, sum = %f, dur = %f, segmentation = %s, timestamp = %d", deviceID, key, count, sum, dur, segmentation, timestamp)); if (timestamp == null || timestamp < 1) { - timestamp = DeviceCore.dev.uniqueTimestamp(); + timestamp = Device.dev.uniqueTimestamp(); } removeInvalidDataFromSegments(segmentation); @@ -103,7 +103,7 @@ private void sessionBeginInternal(String deviceID, Map metrics, L.d(String.format("sessionBeginInternal: deviceID = %s, timestamp = %d", deviceID, timestamp)); if (timestamp == null || timestamp < 1) { - timestamp = DeviceCore.dev.uniqueTimestamp(); + timestamp = Device.dev.uniqueTimestamp(); } Request request = new Request(); @@ -128,7 +128,7 @@ private void sessionUpdateInternal(String deviceID, Double duration, Long timest L.d(String.format("sessionUpdateInternal: deviceID = %s, duration = %f, timestamp = %d", deviceID, duration, timestamp)); if (timestamp == null || timestamp < 1) { - timestamp = DeviceCore.dev.uniqueTimestamp(); + timestamp = Device.dev.uniqueTimestamp(); } Request request = new Request(); @@ -143,7 +143,7 @@ private void sessionEndInternal(String deviceID, double duration, Long timestamp L.d(String.format("sessionEndInternal: deviceID = %s, duration = %f, timestamp = %d", deviceID, duration, timestamp)); if (timestamp == null || timestamp < 1) { - timestamp = DeviceCore.dev.uniqueTimestamp(); + timestamp = Device.dev.uniqueTimestamp(); } //Add events against device ID to request Q @@ -163,7 +163,7 @@ public void recordExceptionInternal(String deviceID, String message, String stac L.d(String.format("recordExceptionInternal: deviceID = %s, message = %s, stacktrace = %s, segmentation = %s, timestamp = %d", deviceID, message, stacktrace, segmentation, timestamp)); if (timestamp == null || timestamp < 1) { - timestamp = DeviceCore.dev.uniqueTimestamp(); + timestamp = Device.dev.uniqueTimestamp(); } removeInvalidDataFromSegments(segmentation); @@ -193,7 +193,7 @@ private void recordUserPropertiesInternal(String deviceID, Map u L.d(String.format("recordUserPropertiesInternal: deviceID = %s, userProperties = %s, timestamp = %d", deviceID, userProperties, timestamp)); if (timestamp == null || timestamp < 1) { - timestamp = DeviceCore.dev.uniqueTimestamp(); + timestamp = Device.dev.uniqueTimestamp(); } removeInvalidDataFromSegments(userProperties); @@ -234,7 +234,7 @@ void recordDirectRequestInternal(String deviceID, Map requestDat L.d(String.format("recordDirectRequestInternal: deviceID = %s, requestJson = %s, timestamp = %d", deviceID, requestData, timestamp)); if (timestamp == null || timestamp < 1) { - timestamp = DeviceCore.dev.uniqueTimestamp(); + timestamp = Device.dev.uniqueTimestamp(); } Request request = new Request(); @@ -292,7 +292,7 @@ private void addTimeInfoIntoRequest(Request request, Long timestamp) { request.params.add("dow", dow); request.params.add("hour", hour); request.params.add("timestamp", timestamp); - request.params.add("tz", DeviceCore.dev.getTimezoneOffset()); + request.params.add("tz", Device.dev.getTimezoneOffset()); } private synchronized void addEventsAgainstDeviceIdToRequestQ(String deviceID) { diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleBase.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleBase.java index aa65001ba..e5b77869b 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleBase.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleBase.java @@ -44,34 +44,6 @@ public void onContextAcquired(CtxCore ctx) { public void onLimitedContextAcquired(CtxCore ctx) { } - @Override - public void onActivityCreated(CtxCore ctx) { - } - - @Override - public void onActivityStarted(CtxCore ctx) { - } - - @Override - public void onActivityResumed(CtxCore ctx) { - } - - @Override - public void onActivityPaused(CtxCore ctx) { - } - - @Override - public void onActivityStopped(CtxCore ctx) { - } - - @Override - public void onActivitySaveInstanceState(CtxCore ctx) { - } - - @Override - public void onActivityDestroyed(CtxCore ctx) { - } - @Override public void onSessionBegan(Session session, CtxCore ctx) { } @@ -93,8 +65,4 @@ public Boolean onRequest(Request request) { public void onRequestCompleted(Request request, String response, int responseCode) { } - - @Override - public void onConfigurationChanged(CtxCore ctx) { - } } diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleCrash.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleCrash.java index b72176a5c..0ac989268 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleCrash.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleCrash.java @@ -41,7 +41,7 @@ public void stop(CtxCore ctx, boolean clear) { Thread.setDefaultUncaughtExceptionHandler(previousHandler); } if (clear) { - ctx.getSDK().sdkStorage.storablePurge(ctx, CrashImplCore.getStoragePrefix()); + ctx.getSDK().sdkStorage.storablePurge(ctx, CrashImpl.getStoragePrefix()); } } catch (Throwable t) { L.e("[ModuleCrash] Exception while stopping crash reporting" + t); @@ -77,7 +77,7 @@ public Integer getFeature() { return CoreFeature.CrashReporting.getIndex(); } - public CrashImplCore onCrash(CtxCore ctx, Throwable t, boolean fatal, String name, Map segments, String... logs) { + public CrashImpl onCrash(CtxCore ctx, Throwable t, boolean fatal, String name, Map segments, String... logs) { if (ctx.getConfig().isBackendModeEnabled()) { L.w("[ModuleCrash] onCrash: Skipping crash, backend mode is enabled!"); @@ -88,12 +88,12 @@ public CrashImplCore onCrash(CtxCore ctx, Throwable t, boolean fatal, String nam L.e("[ModuleCrash] Throwable cannot be null"); return null; } - return onCrash(ctx, new CrashImplCore(L).addThrowable(t).setFatal(fatal).setName(name).setSegments(segments).setLogs(logs)); + return onCrash(ctx, new CrashImpl(L).addThrowable(t).setFatal(fatal).setName(name).setSegments(segments).setLogs(logs)); } - public CrashImplCore onCrash(CtxCore ctx, CrashImplCore crash) { - long running = started == 0 ? 0 : DeviceCore.dev.nsToMs(System.nanoTime() - started); - crash.putMetricsCore(ctx, running); + public CrashImpl onCrash(CtxCore ctx, CrashImpl crash) { + long running = started == 0 ? 0 : Device.dev.nsToMs(System.nanoTime() - started); + crash.putMetrics(ctx, running); if (!crash.getData().has("_os")) { L.w("[ModuleCrash] onCrash, While recording an exception 'OS name' was either null or empty"); @@ -126,11 +126,7 @@ public CrashImplCore onCrash(CtxCore ctx, CrashImplCore crash) { return crash; } - public static void putCrashIntoParams(CrashImplCore crash, Params params) { + public static void putCrashIntoParams(CrashImpl crash, Params params) { params.add("crash", crash.getJSON()); } - - public enum CrashType { - STACK_OVERFLOW, DIVISION_BY_ZERO, OOM, RUNTIME_EXCEPTION, NULLPOINTER_EXCEPTION, ANR - } } diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleRequests.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleRequests.java index b8274eba4..cafaf9247 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleRequests.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleRequests.java @@ -24,7 +24,7 @@ public void init(InternalConfig config, Log logger) { @Override public void onContextAcquired(CtxCore ctx) { super.onContextAcquired(ctx); - ModuleRequests.metrics = DeviceCore.dev.buildMetrics(ctx); + ModuleRequests.metrics = Device.dev.buildMetrics(ctx); } @Override @@ -251,10 +251,10 @@ public static Future pushAsync(final CtxCore ctx, final Request request return null; } - request.params.add("timestamp", DeviceCore.dev.uniqueTimestamp()) - .add("tz", DeviceCore.dev.getTimezoneOffset()) - .add("hour", DeviceCore.dev.currentHour()) - .add("dow", DeviceCore.dev.currentDayOfWeek()); + request.params.add("timestamp", Device.dev.uniqueTimestamp()) + .add("tz", Device.dev.getTimezoneOffset()) + .add("hour", Device.dev.currentHour()) + .add("dow", Device.dev.currentDayOfWeek()); return Storage.pushAsync(ctx, request, new Tasks.Callback() { @Override diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleSessions.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleSessions.java index fb51e4001..fb0769e3b 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleSessions.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleSessions.java @@ -7,8 +7,6 @@ import ly.count.sdk.java.Config; public class ModuleSessions extends ModuleBase { - - private int activityCount = 0; private ScheduledExecutorService executor = null; /** @@ -114,51 +112,6 @@ public void onDeviceId(final CtxCore ctx, final Config.DID deviceId, final Confi } } - @Override - public synchronized void onActivityStarted(CtxCore ctx) { - if (ctx.getConfig().isAutoSessionsTrackingEnabled() && activityCount == 0) { - if (getSession() == null) { - L.i("[ModuleSessions] starting new session"); - session(ctx, null).begin(); - } - } - activityCount++; - } - - @Override - public synchronized void onActivityStopped(CtxCore ctx) { - activityCount--; - if (activityCount == 0) { - if (executor == null && ctx.getConfig().isAutoSessionsTrackingEnabled()) { - executor = Executors.newScheduledThreadPool(1); - } - if (executor != null) { - L.i("[ModuleSessions] stopping session"); - try { - executor.schedule(new Runnable() { - @Override - public void run() { - L.i("[ModuleSessions] ending session? activities " + activityCount + " session null? " + (getSession() == null) + " active? " + (getSession() != null && getSession().isActive())); - if (activityCount == 0 && getSession() != null && getSession().isActive()) { - getSession().end(); - } - } - }, ctx.getConfig().getSessionAutoCloseAfter(), TimeUnit.SECONDS); - - executor.shutdown(); - - if (!executor.awaitTermination(Math.min(31, ctx.getConfig().getSessionAutoCloseAfter() + 1), TimeUnit.SECONDS)) { - executor.shutdownNow(); - } - } catch (InterruptedException e) { - L.w("[ModuleSessions] Interrupted while waiting for session update executor to stop" + e); - executor.shutdownNow(); - } - executor = null; - } - } - } - public TimedEvents timedEvents() { return timedEvents; } diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleViews.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleViews.java index a2ee74504..2565cac34 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleViews.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleViews.java @@ -11,39 +11,9 @@ */ public class ModuleViews extends ModuleBase { - private Map views = null; - @Override public void init(InternalConfig config, Log logger) { super.init(config, logger); - views = new HashMap<>(); - } - - /** - * When new {@code Activity} started, starts new {@link View} with name - * set as {@code Activity} class name. - */ - @Override - public void onActivityStarted(CtxCore ctx) { - Session session = SDKCore.instance.getSession(); - if (session != null && SDKCore.enabled(CoreFeature.Views) && ctx.getConfig().isAutoViewsTrackingEnabled()) { - Class cls = ctx.getContext().getClass(); - views.put(ctx.getContext().hashCode(), session.view(cls.getSimpleName())); - } - } - - /** - * When {@code Activity} stopped, stops previously started {@link View}. - */ - @Override - public void onActivityStopped(CtxCore ctx) { - Session session = SDKCore.instance.getSession(); - if (session != null && SDKCore.enabled(CoreFeature.Views) && ctx.getConfig().isAutoViewsTrackingEnabled()) { - int cls = ctx.getContext().hashCode(); - if (views.containsKey(cls)) { - views.remove(cls).stop(false); - } - } } // TODO: questionable diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/Request.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/Request.java index cb72fbb87..2c5bce577 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/Request.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/Request.java @@ -28,7 +28,7 @@ public class Request implements Storable { * Create request from params with current time as id. */ public Request(Object... params) { - this.id = DeviceCore.dev.uniformTimestamp(); + this.id = Device.dev.uniformTimestamp(); this.params = new Params(params); } diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKCore.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKCore.java index b1b690a5d..de9114a4a 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKCore.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKCore.java @@ -675,7 +675,7 @@ public void onRequestCompleted(Request request, String response, int responseCod } protected void recover(CtxCore ctx) { - List crashes = Storage.list(ctx, CrashImplCore.getStoragePrefix()); + List crashes = Storage.list(ctx, CrashImpl.getStoragePrefix()); for (Long id : crashes) { L.i("[SDKCore] [SDKCore] Found unprocessed crash " + id); diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/SessionImpl.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/SessionImpl.java index c8f14f5a5..61dfeabc0 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/SessionImpl.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/SessionImpl.java @@ -77,7 +77,7 @@ public class SessionImpl implements Session, Storable, EventImpl.EventRecorder { */ protected SessionImpl(CtxCore ctx) { L = ctx.getLogger(); - this.id = DeviceCore.dev.uniformTimestamp(); + this.id = Device.dev.uniformTimestamp(); this.ctx = ctx; } @@ -87,7 +87,7 @@ protected SessionImpl(CtxCore ctx) { public SessionImpl(CtxCore ctx, Long id) { L = ctx.getLogger(); this.ctx = ctx; - this.id = id == null ? DeviceCore.dev.uniformTimestamp() : id; + this.id = id == null ? Device.dev.uniformTimestamp() : id; if (SDKCore.instance != null) { this.consents = SDKCore.instance.consents; } @@ -222,16 +222,16 @@ public void call(Boolean removed) throws Exception { } Boolean recover(Config config) { - if ((System.currentTimeMillis() - id) < DeviceCore.dev.secToMs(config.getSessionCooldownPeriod() * 2)) { + if ((System.currentTimeMillis() - id) < Device.dev.secToMs(config.getSessionCooldownPeriod() * 2)) { return null; } else { Future future = null; if (began == null) { return Storage.remove(ctx, this); } else if (ended == null && updated == null) { - future = end(began + DeviceCore.dev.secToNs(config.getSessionCooldownPeriod()), null, null); + future = end(began + Device.dev.secToNs(config.getSessionCooldownPeriod()), null, null); } else if (ended == null) { - future = end(updated + DeviceCore.dev.secToNs(config.getSessionCooldownPeriod()), null, null); + future = end(updated + Device.dev.secToNs(config.getSessionCooldownPeriod()), null, null); } else { // began != null && ended != null return Storage.remove(ctx, this); @@ -272,7 +272,7 @@ private Long updateDuration(Long now) { duration = now - updated; } updated = now; - return DeviceCore.dev.nsToSec(duration); + return Device.dev.nsToSec(duration); } public Event event(String key) { diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ViewImpl.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ViewImpl.java index e16f61052..73d45f4b1 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ViewImpl.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ViewImpl.java @@ -51,7 +51,7 @@ public void start(boolean firstView) { start = (EventImpl) session.event(EVENT).addSegments(NAME, this.name, VISIT, VISIT_VALUE, - SEGMENT, DeviceCore.dev.getOS()); + SEGMENT, Device.dev.getOS()); if (firstView) { start.addSegment(START, START_VALUE); @@ -82,7 +82,7 @@ public void stop(boolean lastView) { EventImpl event = (EventImpl) session.event(EVENT).addSegments(NAME, this.name, SEGMENT, SEGMENT_VALUE); - event.setDuration(DeviceCore.dev.uniqueTimestamp() - start.getTimestamp()); + event.setDuration(Device.dev.uniqueTimestamp() - start.getTimestamp()); if (lastView) { event.addSegment(EXIT, EXIT_VALUE); diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/BackendModeTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/BackendModeTests.java index 4ec91809c..f82f2ca3d 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/BackendModeTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/BackendModeTests.java @@ -11,6 +11,7 @@ import java.io.File; import java.io.PrintWriter; import java.io.StringWriter; +import java.math.BigDecimal; import java.util.*; @RunWith(JUnit4.class) @@ -794,7 +795,7 @@ public void testCrashSegmentDataType() { Assert.assertEquals(5, segments.length()); Assert.assertEquals("value", segments.get("key2")); Assert.assertEquals(1, segments.get("key3")); - Assert.assertEquals(20.5, segments.get("key4")); + Assert.assertEquals(BigDecimal.valueOf(20.5), segments.get("key4")); Assert.assertEquals(true, segments.get("key5")); Assert.assertEquals(10, segments.get("key7")); Assert.assertEquals(10, segments.get("key7")); @@ -891,7 +892,7 @@ private void validateRequestTimeFields(String deviceID, long timestamp, Request final int hour = calendar.get(Calendar.HOUR_OF_DAY); final int dow = calendar.get(Calendar.DAY_OF_WEEK) - 1; - Assert.assertEquals(DeviceCore.dev.getTimezoneOffset() + "", request.params.get("tz")); + Assert.assertEquals(Device.dev.getTimezoneOffset() + "", request.params.get("tz")); Assert.assertEquals(dow + "", request.params.get("dow")); Assert.assertEquals(hour + "", request.params.get("hour")); Assert.assertEquals(deviceID, request.params.get("device_id")); @@ -915,7 +916,7 @@ private void validateUserProperties(String userDetails, boolean validateUserDeta //Custom properties JSONObject customPropertiesJson = userDetailsJson.getJSONObject("custom"); Assert.assertEquals("black", customPropertiesJson.get("hair")); - Assert.assertEquals(5.9, customPropertiesJson.get("height")); + Assert.assertEquals(BigDecimal.valueOf(5.9), customPropertiesJson.get("height")); } if (validateOperation) { diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/DeviceCoreTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/DeviceTests.java similarity index 85% rename from sdk-java/src/test/java/ly/count/sdk/java/internal/DeviceCoreTests.java rename to sdk-java/src/test/java/ly/count/sdk/java/internal/DeviceTests.java index 8f1faa6e4..b1b76ba7f 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/DeviceCoreTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/DeviceTests.java @@ -3,11 +3,11 @@ import org.junit.Assert; import org.junit.Test; -public class DeviceCoreTests { +public class DeviceTests { @Test public void testAsIs() { - DeviceCore.UniqueTimeGenerator simulator = new DeviceCore.UniqueTimeGenerator(); + Device.UniqueTimeGenerator simulator = new Device.UniqueTimeGenerator(); long last = simulator.timestamp(); @@ -19,7 +19,7 @@ public void testAsIs() { @Test public void testMidTimeChange() { - DeviceCore.UniqueTimeGenerator simulator = new DeviceCore.UniqueTimeGenerator(); + Device.UniqueTimeGenerator simulator = new Device.UniqueTimeGenerator(); long last = simulator.timestamp(); @@ -52,7 +52,7 @@ public void testMidTimeChange() { @Test public void testMidTimeRandomChange() { - DeviceCore.UniqueTimeGenerator simulator = new DeviceCore.UniqueTimeGenerator(); + Device.UniqueTimeGenerator simulator = new Device.UniqueTimeGenerator(); long last = simulator.timestamp(); diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleRemoteConfigTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleRemoteConfigTests.java index ed4bb27b1..0da177cbc 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleRemoteConfigTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleRemoteConfigTests.java @@ -9,6 +9,8 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import java.math.BigDecimal; +import java.util.ArrayList; import java.util.Map; import ly.count.sdk.java.internal.ModuleRemoteConfig; @@ -109,9 +111,35 @@ boolean compareValueStores(ModuleRemoteConfig.RemoteConfigValueStore v1, ModuleR if (entry.getKey().equals(entry2.getKey())) { Object vv1 = entry.getValue(); Object vv2 = entry2.getValue(); - boolean res = vv1.equals(vv2); - if (!res) { - return false; + + if(vv2 instanceof BigDecimal) { + vv1 = BigDecimal.valueOf((double) vv1); + } + + //todo simplify this + if(vv2 instanceof ArrayList) { + ArrayList alVV1 = (ArrayList) vv1; + ArrayList alVV2 = (ArrayList) vv2; + Assert.assertEquals(alVV1.size(), alVV2.size()); + + for(int a = 0 ; a < alVV1.size() ; a++) { + Object vvv1 = alVV1.get(a); + Object vvv2 = alVV2.get(a); + + if(vvv2 instanceof BigDecimal) { + vvv1 = BigDecimal.valueOf((double) vvv1); + } + + boolean resInternal = vvv1.equals(vvv2); + if (!resInternal) { + return false; + } + } + } else { + boolean res = vv1.equals(vv2); + if (!res) { + return false; + } } } } diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/TasksTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/TasksTests.java index c70a67794..9d9e1ab78 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/TasksTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/TasksTests.java @@ -11,10 +11,6 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; -import ly.count.sdk.java.internal.DeviceCore; -import ly.count.sdk.java.internal.Log; -import ly.count.sdk.java.internal.Tasks; - @RunWith(JUnit4.class) public class TasksTests extends BaseTestsCore { private Tasks tasks; @@ -55,7 +51,7 @@ public Object call() throws Exception { }); long now = System.nanoTime(); other.shutdown(); - long timeToShutdown = DeviceCore.dev.nsToMs(System.nanoTime() - now); + long timeToShutdown = Device.dev.nsToMs(System.nanoTime() - now); Assert.assertTrue(Whitebox.getInternalState(other, "executor").isShutdown()); Assert.assertTrue(Whitebox.getInternalState(other, "executor").isTerminated()); //Assert.assertTrue(timeToShutdown > 100);//todo, this line fails when trying to publish (AK, 12.12.18)