Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set ID #305

Merged
merged 3 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
* ! Minor breaking change ! The "maxSegmentationValues" limit will now be applied by default. This limit would limit the maximum number of developer-provided segmentation values when using features. The current limit is 100 entries.
* ! Minor breaking change ! "maxStackTraceLinePerThread" and "maxStackTraceLineLength" limits will be applied by default now. This would limit the maximum lines of stack traces per thread and the maximum stack trace length, respectively. Default values are in order of 30 and 200.

* Added a new function "setID(newDeviceId)" for managing device id changes according to the device ID Type.

## 24.4.0
* ! Minor breaking change ! If a manual session is already started, it will not be possible to call "BeginSession" without stopping the previous one
* ! Minor breaking change ! If a manual session has not been started, it will not be possible to call "UpdateSession"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package ly.count.android.sdk;

import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(AndroidJUnit4.class)
public class ModuleDeviceIdTests {

@Before
public void setUp() {
Countly.sharedInstance().halt();
TestUtils.getCountyStore().clear();
}

@After
public void tearDown() {
TestUtils.getCountyStore().clear();
}

/**
* "setID" with custom device id
* - validate that device id is developer supplied
* - set same id and validate that it is not set
*/
@Test
public void setID_sameCustom() {
Countly countly = new Countly().init(TestUtils.createBaseConfig(TestUtils.commonDeviceId)); // custom id provided
validateDeviceIdDeveloperSupplied(TestUtils.commonDeviceId, countly);

countly.deviceId().setID(TestUtils.commonDeviceId);
validateDeviceIdDeveloperSupplied(TestUtils.commonDeviceId, countly);
}

/**
* "setID" with custom device id
* - validate that device id is developer supplied
* - set new id and validate that it is set
*/
@Test
public void setID_custom() {
Countly countly = new Countly().init(TestUtils.createBaseConfig(TestUtils.commonDeviceId)); // custom id provided
validateDeviceIdDeveloperSupplied(TestUtils.commonDeviceId, countly); // validate id exist and developer supplied

String newId = "NEW_ID";
countly.deviceId().setID(newId);
validateDeviceIdDeveloperSupplied(newId, countly);
}

/**
* "setID"
* - Validate that device id is generated by the sdk
* - Set a new id and validate that it is set
* - Set null and validate that it is not changed
* - Set empty and validate that it is not changed
* - Set the same id and validate that it is not changed
*/
//@Test TODO enable this test after RQ migration is merged
public void setID() {
Countly countly = new Countly().init(TestUtils.createBaseConfig(null)); // no custom id provided
validateDeviceIdIsSdkGenerated(countly); // validate id exist and sdk generated

String newId = "NEW_ID";
countly.deviceId().setID(newId);
validateDeviceIdDeveloperSupplied(newId, countly);

countly.deviceId().setID(null);
validateDeviceIdDeveloperSupplied(newId, countly);

countly.deviceId().setID("");
validateDeviceIdDeveloperSupplied(newId, countly);

countly.deviceId().setID(countly.deviceId().getID());
validateDeviceIdDeveloperSupplied(newId, countly);
}

private void validateDeviceIdDeveloperSupplied(String expectedDeviceId, Countly countly) {
Assert.assertEquals(expectedDeviceId, countly.deviceId().getID());
Assert.assertEquals(DeviceIdType.DEVELOPER_SUPPLIED, countly.deviceId().getType());
}

private void validateDeviceIdIsSdkGenerated(Countly countly) {
String deviceId = countly.deviceId().getID();
try {
Assert.assertTrue(validateDeviceIDIsAndroidID(deviceId));
Assert.assertEquals(DeviceIdType.OPEN_UDID, countly.deviceId().getType());
} catch (IllegalArgumentException e) {
Assert.fail("Device id is not a valid OPEN_UDID");
}
}

/**
* Validates that the device id is a valid UUID and starts with "CLY_"
*/
private boolean validateDeviceIDIsAndroidID(String androidId) {
// Check if androidId is null or empty
if (androidId == null || androidId.isEmpty()) {
return false;
}

// Check if androidId is a known invalid value
if (androidId.equals("9774d56d682e549c") || androidId.equalsIgnoreCase("unknown")) {
return false;
}

// Check if androidId is a valid hexadecimal value
try {
Long.parseLong(androidId, 16);
} catch (NumberFormatException e) {
return false;
}

return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,12 @@ public static CountlyConfig createScenarioEventIDConfig(SafeIDGenerator safeView
}

public static CountlyConfig createBaseConfig() {
return createBaseConfig(commonDeviceId);
}

public static CountlyConfig createBaseConfig(String deviceId) {
CountlyConfig cc = new CountlyConfig(getApplication(), commonAppKey, commonURL)
.setDeviceId(commonDeviceId)
.setDeviceId(deviceId)
.setLoggingEnabled(true)
.enableCrashReporting();

Expand Down
39 changes: 39 additions & 0 deletions sdk/src/main/java/ly/count/android/sdk/ModuleDeviceId.java
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,31 @@ void changeDeviceIdWithMergeInternal(@NonNull String deviceId) {
}
}

private void setIDInternal(String newDeviceID) {
if (Utils.isNullOrEmpty(newDeviceID)) {
L.w("[ModuleDeviceId] setID, Empty id passed to setID method");
return;
}

if (deviceIdInstance.getCurrentId() != null && deviceIdInstance.getCurrentId().equals(newDeviceID)) {
arifBurakDemiray marked this conversation as resolved.
Show resolved Hide resolved
L.w("[ModuleDeviceId] setID, Same id passed to setID method, ignoring");
return;
}

DeviceIdType currentType = deviceIdInstance.getType();

if (currentType.equals(DeviceIdType.DEVELOPER_SUPPLIED)) {
// an ID was provided by the host app previously
// we can assume that a device ID change with merge was executed previously
// now we change it without merging
changeDeviceIdWithoutMergeInternal(newDeviceID);
} else {
// SDK generated ID
// we change device ID with merge so that data is combined
changeDeviceIdWithMergeInternal(newDeviceID);
}
}

@Override
public void initFinished(@NonNull CountlyConfig config) {
if (exitTempIdAfterInit) {
Expand Down Expand Up @@ -309,6 +334,20 @@ public String getID() {
}
}

/**
* Sets device ID according to the device ID Type.
* If previous ID was Developer Supplied sets it without merge, otherwise with merge.
*
* @param newDeviceID device id to set
*/
public void setID(String newDeviceID) {
synchronized (_cly) {
L.d("[DeviceId] Calling 'setID'");

setIDInternal(newDeviceID);
}
}

/**
* Returns the type of the device ID used by countly for this device.
*
Expand Down