From f82ce4a2d1ebf151f6fcacf72164f129708d1464 Mon Sep 17 00:00:00 2001 From: hannah-smartbear Date: Tue, 5 Mar 2024 13:47:43 +0000 Subject: [PATCH 1/2] Adding generateAnonymousId config option --- .../api/bugsnag-android-core.api | 16 ++++-- .../com/bugsnag/android/DeviceIdStoreTest.kt | 55 ++++++++++++++++--- .../com/bugsnag/android/ConfigInternal.kt | 1 + .../com/bugsnag/android/Configuration.java | 20 +++++++ .../java/com/bugsnag/android/DeviceIdStore.kt | 17 +++++- .../bugsnag/android/ManifestConfigLoader.kt | 2 + .../java/com/bugsnag/android/StorageModule.kt | 3 +- .../java/com/bugsnag/android/UserStore.kt | 6 +- .../android/internal/ImmutableConfig.kt | 2 + .../com/bugsnag/android/ConfigSerializer.java | 1 + .../java/com/bugsnag/android/TestData.java | 1 + 11 files changed, 106 insertions(+), 18 deletions(-) diff --git a/bugsnag-android-core/api/bugsnag-android-core.api b/bugsnag-android-core/api/bugsnag-android-core.api index 3509b235bd..10b89831f3 100644 --- a/bugsnag-android-core/api/bugsnag-android-core.api +++ b/bugsnag-android-core/api/bugsnag-android-core.api @@ -175,6 +175,7 @@ public class com/bugsnag/android/Configuration : com/bugsnag/android/CallbackAwa public fun getEnabledErrorTypes ()Lcom/bugsnag/android/ErrorTypes; public fun getEnabledReleaseStages ()Ljava/util/Set; public fun getEndpoints ()Lcom/bugsnag/android/EndpointConfiguration; + public fun getGenerateAnonymousId ()Z public fun getLaunchDurationMillis ()J public fun getLogger ()Lcom/bugsnag/android/Logger; public fun getMaxBreadcrumbs ()I @@ -214,6 +215,7 @@ public class com/bugsnag/android/Configuration : com/bugsnag/android/CallbackAwa public fun setEnabledErrorTypes (Lcom/bugsnag/android/ErrorTypes;)V public fun setEnabledReleaseStages (Ljava/util/Set;)V public fun setEndpoints (Lcom/bugsnag/android/EndpointConfiguration;)V + public fun setGenerateAnonymousId (Z)V public fun setLaunchDurationMillis (J)V public fun setLogger (Lcom/bugsnag/android/Logger;)V public fun setMaxBreadcrumbs (I)V @@ -861,7 +863,7 @@ public final class com/bugsnag/android/internal/DateUtils { } public final class com/bugsnag/android/internal/ImmutableConfig { - public fun (Ljava/lang/String;ZLcom/bugsnag/android/ErrorTypes;ZLcom/bugsnag/android/ThreadSendPolicy;Ljava/util/Collection;Ljava/util/Collection;Ljava/util/Collection;Ljava/util/Set;Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lcom/bugsnag/android/Delivery;Lcom/bugsnag/android/EndpointConfiguration;ZJLcom/bugsnag/android/Logger;IIIIJLkotlin/Lazy;ZZLandroid/content/pm/PackageInfo;Landroid/content/pm/ApplicationInfo;Ljava/util/Collection;)V + public fun (Ljava/lang/String;ZLcom/bugsnag/android/ErrorTypes;ZLcom/bugsnag/android/ThreadSendPolicy;Ljava/util/Collection;Ljava/util/Collection;Ljava/util/Collection;Ljava/util/Set;Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lcom/bugsnag/android/Delivery;Lcom/bugsnag/android/EndpointConfiguration;ZJLcom/bugsnag/android/Logger;IIIIJLkotlin/Lazy;ZZZLandroid/content/pm/PackageInfo;Landroid/content/pm/ApplicationInfo;Ljava/util/Collection;)V public final fun component1 ()Ljava/lang/String; public final fun component10 ()Ljava/util/Set; public final fun component11 ()Ljava/lang/String; @@ -883,18 +885,19 @@ public final class com/bugsnag/android/internal/ImmutableConfig { public final fun component26 ()Lkotlin/Lazy; public final fun component27 ()Z public final fun component28 ()Z - public final fun component29 ()Landroid/content/pm/PackageInfo; + public final fun component29 ()Z public final fun component3 ()Lcom/bugsnag/android/ErrorTypes; - public final fun component30 ()Landroid/content/pm/ApplicationInfo; - public final fun component31 ()Ljava/util/Collection; + public final fun component30 ()Landroid/content/pm/PackageInfo; + public final fun component31 ()Landroid/content/pm/ApplicationInfo; + public final fun component32 ()Ljava/util/Collection; public final fun component4 ()Z public final fun component5 ()Lcom/bugsnag/android/ThreadSendPolicy; public final fun component6 ()Ljava/util/Collection; public final fun component7 ()Ljava/util/Collection; public final fun component8 ()Ljava/util/Collection; public final fun component9 ()Ljava/util/Set; - public final fun copy (Ljava/lang/String;ZLcom/bugsnag/android/ErrorTypes;ZLcom/bugsnag/android/ThreadSendPolicy;Ljava/util/Collection;Ljava/util/Collection;Ljava/util/Collection;Ljava/util/Set;Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lcom/bugsnag/android/Delivery;Lcom/bugsnag/android/EndpointConfiguration;ZJLcom/bugsnag/android/Logger;IIIIJLkotlin/Lazy;ZZLandroid/content/pm/PackageInfo;Landroid/content/pm/ApplicationInfo;Ljava/util/Collection;)Lcom/bugsnag/android/internal/ImmutableConfig; - public static synthetic fun copy$default (Lcom/bugsnag/android/internal/ImmutableConfig;Ljava/lang/String;ZLcom/bugsnag/android/ErrorTypes;ZLcom/bugsnag/android/ThreadSendPolicy;Ljava/util/Collection;Ljava/util/Collection;Ljava/util/Collection;Ljava/util/Set;Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lcom/bugsnag/android/Delivery;Lcom/bugsnag/android/EndpointConfiguration;ZJLcom/bugsnag/android/Logger;IIIIJLkotlin/Lazy;ZZLandroid/content/pm/PackageInfo;Landroid/content/pm/ApplicationInfo;Ljava/util/Collection;ILjava/lang/Object;)Lcom/bugsnag/android/internal/ImmutableConfig; + public final fun copy (Ljava/lang/String;ZLcom/bugsnag/android/ErrorTypes;ZLcom/bugsnag/android/ThreadSendPolicy;Ljava/util/Collection;Ljava/util/Collection;Ljava/util/Collection;Ljava/util/Set;Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lcom/bugsnag/android/Delivery;Lcom/bugsnag/android/EndpointConfiguration;ZJLcom/bugsnag/android/Logger;IIIIJLkotlin/Lazy;ZZZLandroid/content/pm/PackageInfo;Landroid/content/pm/ApplicationInfo;Ljava/util/Collection;)Lcom/bugsnag/android/internal/ImmutableConfig; + public static synthetic fun copy$default (Lcom/bugsnag/android/internal/ImmutableConfig;Ljava/lang/String;ZLcom/bugsnag/android/ErrorTypes;ZLcom/bugsnag/android/ThreadSendPolicy;Ljava/util/Collection;Ljava/util/Collection;Ljava/util/Collection;Ljava/util/Set;Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lcom/bugsnag/android/Delivery;Lcom/bugsnag/android/EndpointConfiguration;ZJLcom/bugsnag/android/Logger;IIIIJLkotlin/Lazy;ZZZLandroid/content/pm/PackageInfo;Landroid/content/pm/ApplicationInfo;Ljava/util/Collection;ILjava/lang/Object;)Lcom/bugsnag/android/internal/ImmutableConfig; public fun equals (Ljava/lang/Object;)Z public final fun getApiKey ()Ljava/lang/String; public final fun getAppInfo ()Landroid/content/pm/ApplicationInfo; @@ -910,6 +913,7 @@ public final class com/bugsnag/android/internal/ImmutableConfig { public final fun getEnabledErrorTypes ()Lcom/bugsnag/android/ErrorTypes; public final fun getEnabledReleaseStages ()Ljava/util/Collection; public final fun getEndpoints ()Lcom/bugsnag/android/EndpointConfiguration; + public final fun getGenerateAnonymousId ()Z public final fun getLaunchDurationMillis ()J public final fun getLogger ()Lcom/bugsnag/android/Logger; public final fun getMaxBreadcrumbs ()I diff --git a/bugsnag-android-core/src/androidTest/java/com/bugsnag/android/DeviceIdStoreTest.kt b/bugsnag-android-core/src/androidTest/java/com/bugsnag/android/DeviceIdStoreTest.kt index a2089ccc80..89da8224fa 100644 --- a/bugsnag-android-core/src/androidTest/java/com/bugsnag/android/DeviceIdStoreTest.kt +++ b/bugsnag-android-core/src/androidTest/java/com/bugsnag/android/DeviceIdStoreTest.kt @@ -2,6 +2,7 @@ package com.bugsnag.android import android.content.Context import androidx.test.core.app.ApplicationProvider +import com.bugsnag.android.internal.ImmutableConfig import junit.framework.TestCase.assertNull import org.junit.Assert.assertEquals import org.junit.Before @@ -46,6 +47,12 @@ internal class DeviceIdStoreTest { prefs.edit().remove("install.iud").commit() } + private fun generateConfig(generateAnonymousId: Boolean): ImmutableConfig { + val config = BugsnagTestUtils.generateConfiguration() + config.generateAnonymousId = generateAnonymousId + return BugsnagTestUtils.convert(config) + } + /** * A file should be created if it does not already exist */ @@ -61,8 +68,9 @@ internal class DeviceIdStoreTest { uuidProvider(0), nonExistentInternalFile, uuidProvider(1), - prefMigrator, - NoopLogger + sharedPrefMigrator = prefMigrator, + logger = NoopLogger, + config = generateConfig(true) ) assertEquals(ids[0], store.loadDeviceId()!!) @@ -85,7 +93,8 @@ internal class DeviceIdStoreTest { fileInternal, uuidProvider(1), prefMigrator, - NoopLogger + logger = NoopLogger, + config = generateConfig(true) ) assertEquals(ids[0], store.loadDeviceId()!!) @@ -106,7 +115,8 @@ internal class DeviceIdStoreTest { fileInternal, uuidProvider(1), prefMigrator, - NoopLogger + logger = NoopLogger, + config = generateConfig(true) ) assertEquals(ids[0], store.loadDeviceId()!!) @@ -127,7 +137,8 @@ internal class DeviceIdStoreTest { fileInternal, uuidProvider(3), prefMigrator, - NoopLogger + logger = NoopLogger, + config = generateConfig(true) ) val storeA = DeviceIdStore( ctx, @@ -136,7 +147,8 @@ internal class DeviceIdStoreTest { fileInternal, uuidProvider(1), prefMigrator, - NoopLogger + logger = NoopLogger, + config = generateConfig(true) ) // device ID is loaded without writing file @@ -170,7 +182,8 @@ internal class DeviceIdStoreTest { nonReadableInternalFile, uuidProvider(1), prefMigrator, - NoopLogger + logger = NoopLogger, + config = generateConfig(true) ) assertNull(store.loadDeviceId()) assertNull(store.loadInternalDeviceId()) @@ -188,7 +201,8 @@ internal class DeviceIdStoreTest { fileInternal, uuidProvider(1), prefMigrator, - NoopLogger + logger = NoopLogger, + config = generateConfig(true) ) // load the device ID on many different background threads. @@ -224,7 +238,8 @@ internal class DeviceIdStoreTest { fileInternal, uuidProvider(1), prefMigrator, - NoopLogger + logger = NoopLogger, + config = generateConfig(true) ) val context = ApplicationProvider.getApplicationContext() @@ -237,4 +252,26 @@ internal class DeviceIdStoreTest { assertEquals(prefsId, storeDeviceId) assertEquals(prefDeviceId, storeDeviceId) } + + /** + * If generateAnonymousId is false, no device ID is returned (even if one is saved) + */ + @Test + fun loadWithoutGenerateAnonymousId() { + file.writeText("{\"id\": \"${ids[0]}\"}") + fileInternal.writeText("{\"id\": \"${ids[1]}\"}") + val store = DeviceIdStore( + ctx, + file, + uuidProvider(0), + fileInternal, + uuidProvider(1), + sharedPrefMigrator = prefMigrator, + logger = NoopLogger, + config = generateConfig(false) + ) + + assertNull(store.loadDeviceId()) + assertNull(store.loadInternalDeviceId()) + } } diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/ConfigInternal.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/ConfigInternal.kt index 5f83006426..f6565b7b3f 100644 --- a/bugsnag-android-core/src/main/java/com/bugsnag/android/ConfigInternal.kt +++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/ConfigInternal.kt @@ -25,6 +25,7 @@ internal class ConfigInternal( var releaseStage: String? = null var sendThreads: ThreadSendPolicy = ThreadSendPolicy.ALWAYS var persistUser: Boolean = true + var generateAnonymousId: Boolean = true var launchDurationMillis: Long = DEFAULT_LAUNCH_CRASH_THRESHOLD_MS diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/Configuration.java b/bugsnag-android-core/src/main/java/com/bugsnag/android/Configuration.java index ebdc8cdc7b..c0b4e68f4c 100644 --- a/bugsnag-android-core/src/main/java/com/bugsnag/android/Configuration.java +++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/Configuration.java @@ -178,6 +178,26 @@ public void setPersistUser(boolean persistUser) { impl.setPersistUser(persistUser); } + /** + * Set whether or not Bugsnag should generate an anonymous ID and persist it in local storage + * + * If disabled, any device ID that has been persisted will not be retrieved, and no new + * device ID will be generated or stored + */ + public boolean getGenerateAnonymousId() { + return impl.getGenerateAnonymousId(); + } + + /** + * Set whether or not Bugsnag should generate an anonymous ID and persist it in local storage + * + * If disabled, any device ID that has been persisted will not be retrieved, and no new + * device ID will be generated or stored + */ + public void setGenerateAnonymousId(boolean generateAnonymousId) { + impl.setGenerateAnonymousId(generateAnonymousId); + } + /** * Sets the directory where event and session JSON payloads should be persisted if a network * request is not successful. If you use Bugsnag in multiple processes, then a unique diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/DeviceIdStore.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/DeviceIdStore.kt index 31b7c7dc28..74dc3e87e9 100644 --- a/bugsnag-android-core/src/main/java/com/bugsnag/android/DeviceIdStore.kt +++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/DeviceIdStore.kt @@ -1,6 +1,7 @@ package com.bugsnag.android import android.content.Context +import com.bugsnag.android.internal.ImmutableConfig import java.io.File import java.util.UUID @@ -8,18 +9,20 @@ import java.util.UUID * This class is responsible for persisting and retrieving the device ID and internal device ID, * which uniquely identify this device in various contexts. */ -internal class DeviceIdStore @JvmOverloads constructor( +internal class DeviceIdStore @JvmOverloads @Suppress("LongParameterList") constructor( context: Context, deviceIdfile: File = File(context.filesDir, "device-id"), deviceIdGenerator: () -> UUID = { UUID.randomUUID() }, internalDeviceIdfile: File = File(context.filesDir, "internal-device-id"), internalDeviceIdGenerator: () -> UUID = { UUID.randomUUID() }, private val sharedPrefMigrator: SharedPrefMigrator, + config: ImmutableConfig, logger: Logger ) { private val persistence: DeviceIdPersistence private val internalPersistence: DeviceIdPersistence + private val generateId = config.generateAnonymousId init { persistence = DeviceIdFilePersistence(deviceIdfile, deviceIdGenerator, logger) @@ -35,6 +38,12 @@ internal class DeviceIdStore @JvmOverloads constructor( * be used. If no value is present then a random UUID will be generated and persisted. */ fun loadDeviceId(): String? { + // If generateAnonymousId = false, return null + // so that a previously persisted device ID is not returned, + // or a new one is not generated and persisted + if (!generateId) { + return null + } var result = persistence.loadDeviceId(false) if (result != null) { return result @@ -47,6 +56,12 @@ internal class DeviceIdStore @JvmOverloads constructor( } fun loadInternalDeviceId(): String? { + // If generateAnonymousId = false, return null + // so that a previously persisted device ID is not returned, + // or a new one is not generated and persisted + if (!generateId) { + return null + } return internalPersistence.loadDeviceId(true) } } diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/ManifestConfigLoader.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/ManifestConfigLoader.kt index 8214f192df..b357cd9a14 100644 --- a/bugsnag-android-core/src/main/java/com/bugsnag/android/ManifestConfigLoader.kt +++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/ManifestConfigLoader.kt @@ -20,6 +20,7 @@ internal class ManifestConfigLoader { private const val AUTO_DETECT_ERRORS = "$BUGSNAG_NS.AUTO_DETECT_ERRORS" private const val PERSIST_USER = "$BUGSNAG_NS.PERSIST_USER" private const val SEND_THREADS = "$BUGSNAG_NS.SEND_THREADS" + private const val GENERATE_ANONYMOUS_ID = "$BUGSNAG_NS.GENERATE_ANONYMOUS_ID" // endpoints private const val ENDPOINT_NOTIFY = "$BUGSNAG_NS.ENDPOINT_NOTIFY" @@ -108,6 +109,7 @@ internal class ManifestConfigLoader { autoTrackSessions = data.getBoolean(AUTO_TRACK_SESSIONS, autoTrackSessions) autoDetectErrors = data.getBoolean(AUTO_DETECT_ERRORS, autoDetectErrors) persistUser = data.getBoolean(PERSIST_USER, persistUser) + generateAnonymousId = data.getBoolean(GENERATE_ANONYMOUS_ID, generateAnonymousId) val str = data.getString(SEND_THREADS) diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/StorageModule.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/StorageModule.kt index 5bf1d288eb..8defc58ac4 100644 --- a/bugsnag-android-core/src/main/java/com/bugsnag/android/StorageModule.kt +++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/StorageModule.kt @@ -19,7 +19,8 @@ internal class StorageModule( DeviceIdStore( appContext, sharedPrefMigrator = sharedPrefMigrator, - logger = logger + logger = logger, + config = immutableConfig ) } diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/UserStore.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/UserStore.kt index 87adf40e16..548bcdbc35 100644 --- a/bugsnag-android-core/src/main/java/com/bugsnag/android/UserStore.kt +++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/UserStore.kt @@ -30,7 +30,9 @@ internal class UserStore @JvmOverloads constructor( * [Configuration.getPersistUser] is true. * * If no user is stored on disk, then a default [User] is used which uses the device ID - * as its ID. + * as its ID (unless the generateAnonymousId config option is set to false, in which case the + * device ID and therefore the user ID is set to + * null). * * The [UserState] provides a mechanism for observing value changes to its user property, * so to avoid interfering with this the method should only be called once for each [Client]. @@ -46,6 +48,8 @@ internal class UserStore @JvmOverloads constructor( val userState = when { loadedUser != null && validUser(loadedUser) -> UserState(loadedUser) + // if generateAnonymousId config option is false, the deviceId should already be null + // here else -> UserState(User(deviceId, null, null)) } diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/internal/ImmutableConfig.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/internal/ImmutableConfig.kt index eed21dbb50..17e195727b 100644 --- a/bugsnag-android-core/src/main/java/com/bugsnag/android/internal/ImmutableConfig.kt +++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/internal/ImmutableConfig.kt @@ -57,6 +57,7 @@ data class ImmutableConfig( val persistenceDirectory: Lazy, val sendLaunchCrashesSynchronously: Boolean, val attemptDeliveryOnCrash: Boolean, + val generateAnonymousId: Boolean, // results cached here to avoid unnecessary lookups in Client. val packageInfo: PackageInfo?, @@ -166,6 +167,7 @@ internal fun convertToImmutableConfig( delivery = config.delivery, endpoints = config.endpoints, persistUser = config.persistUser, + generateAnonymousId = config.generateAnonymousId, launchDurationMillis = config.launchDurationMillis, logger = config.logger!!, maxBreadcrumbs = config.maxBreadcrumbs, diff --git a/bugsnag-plugin-react-native/src/main/java/com/bugsnag/android/ConfigSerializer.java b/bugsnag-plugin-react-native/src/main/java/com/bugsnag/android/ConfigSerializer.java index 0d6369490a..d064c1a987 100644 --- a/bugsnag-plugin-react-native/src/main/java/com/bugsnag/android/ConfigSerializer.java +++ b/bugsnag-plugin-react-native/src/main/java/com/bugsnag/android/ConfigSerializer.java @@ -26,6 +26,7 @@ public void serialize(Map map, ImmutableConfig config) { map.put("versionCode", config.getVersionCode()); map.put("type", config.getAppType()); map.put("persistUser", config.getPersistUser()); + map.put("generateAnonymousId", config.getGenerateAnonymousId()); map.put("launchCrashThresholdMs", (int) config.getLaunchDurationMillis()); map.put("maxBreadcrumbs", config.getMaxBreadcrumbs()); map.put("enabledBreadcrumbTypes", serializeBreadrumbTypes(config)); diff --git a/bugsnag-plugin-react-native/src/test/java/com/bugsnag/android/TestData.java b/bugsnag-plugin-react-native/src/test/java/com/bugsnag/android/TestData.java index 467b751ba0..b08eae88d2 100644 --- a/bugsnag-plugin-react-native/src/test/java/com/bugsnag/android/TestData.java +++ b/bugsnag-plugin-react-native/src/test/java/com/bugsnag/android/TestData.java @@ -54,6 +54,7 @@ public File invoke() { }), true, true, + true, null, null, Collections.singleton(Pattern.compile(".*password.*")) From 4bf6c5ff12b02d76165989eeb5fc7a3c24f773a4 Mon Sep 17 00:00:00 2001 From: jason Date: Wed, 6 Mar 2024 09:34:42 +0000 Subject: [PATCH 2/2] test(DeviceSerializationTest): replaced mktime with timegm to avoid timezone settings --- bugsnag-plugin-android-ndk/src/test/cpp/test_serializer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bugsnag-plugin-android-ndk/src/test/cpp/test_serializer.c b/bugsnag-plugin-android-ndk/src/test/cpp/test_serializer.c index e67a8b3fd4..69da848637 100644 --- a/bugsnag-plugin-android-ndk/src/test/cpp/test_serializer.c +++ b/bugsnag-plugin-android-ndk/src/test/cpp/test_serializer.c @@ -50,7 +50,7 @@ void loadDeviceTestCase(bugsnag_event *event) { strcpy(device->orientation, "portrait"); struct tm time = { 0, 0, 0, 1, 12, 128 }; - device->time = mktime(&time); + device->time = timegm(&time); strcpy(device->id, "f5gh7"); device->jailbroken = true; strcpy(device->locale, "En");