From ee1525c5eae2ca88277a1f2ac6115b028b981a4f Mon Sep 17 00:00:00 2001 From: Mansi-mParticle <159845845+Mansi-mParticle@users.noreply.github.com> Date: Mon, 3 Jun 2024 09:58:58 -0400 Subject: [PATCH] fix: Refactor code for accessing Google Ad info (#491) --- .../com/mparticle/internal/MPUtility.java | 60 ++++++++++++----- .../com/mparticle/internal/MPUtilityTest.kt | 66 +++++++++++++++++++ 2 files changed, 109 insertions(+), 17 deletions(-) diff --git a/android-core/src/main/java/com/mparticle/internal/MPUtility.java b/android-core/src/main/java/com/mparticle/internal/MPUtility.java index 7293a7c77..b953d1003 100644 --- a/android-core/src/main/java/com/mparticle/internal/MPUtility.java +++ b/android-core/src/main/java/com/mparticle/internal/MPUtility.java @@ -21,8 +21,6 @@ import android.provider.Settings; import android.telephony.TelephonyManager; import android.util.DisplayMetrics; -import android.view.Display; -import android.view.WindowManager; import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; @@ -49,8 +47,6 @@ import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; -import java.text.NumberFormat; -import java.text.ParseException; import java.util.ArrayList; import java.util.Collection; import java.util.Date; @@ -59,6 +55,9 @@ import java.util.Map; import java.util.TimeZone; import java.util.UUID; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import javax.crypto.Mac; @@ -74,6 +73,7 @@ public class MPUtility { private static String sOpenUDID; private static final char[] HEX_CHARS = "0123456789abcdef".toCharArray(); private static final String TAG = MPUtility.class.toString(); + private static AdIdInfo googleAdIdInfo; public static long getAvailableMemory(Context context) { ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo(); @@ -146,23 +146,49 @@ public static AdIdInfo getAdIdInfo(Context context) { } private static AdIdInfo getGoogleAdIdInfo(Context context) { + if (googleAdIdInfo != null) { + fetchGoogleAdInfo(context, false); + return googleAdIdInfo; + } else { + fetchGoogleAdInfo(context, true); + } + return null; + } + + private static void fetchGoogleAdInfo(Context context, Boolean wait) { + ExecutorService executorService = Executors.newSingleThreadExecutor(); + Future future = executorService.submit(() -> { + try { + Class AdvertisingIdClient = Class + .forName("com.google.android.gms.ads.identifier.AdvertisingIdClient"); + Method getAdvertisingInfo = AdvertisingIdClient.getMethod("getAdvertisingIdInfo", + Context.class); + Object advertisingInfo = getAdvertisingInfo.invoke(null, context); + Method isLimitAdTrackingEnabled = advertisingInfo.getClass().getMethod( + "isLimitAdTrackingEnabled"); + Boolean limitAdTrackingEnabled = (Boolean) isLimitAdTrackingEnabled + .invoke(advertisingInfo); + + Method getId = advertisingInfo.getClass().getMethod("getId"); + String advertisingId = (String) getId.invoke(advertisingInfo); + googleAdIdInfo = new AdIdInfo(advertisingId, Boolean.TRUE.equals(limitAdTrackingEnabled), AdIdInfo.Advertiser.GOOGLE); + } catch (Exception e) { + Logger.info(TAG, "Could not locate Google Play Ads Identifier library"); + } + }); try { - Class AdvertisingIdClient = Class - .forName("com.google.android.gms.ads.identifier.AdvertisingIdClient"); - Method getAdvertisingInfo = AdvertisingIdClient.getMethod("getAdvertisingIdInfo", - Context.class); - Object advertisingInfo = getAdvertisingInfo.invoke(null, context); - Method isLimitAdTrackingEnabled = advertisingInfo.getClass().getMethod( - "isLimitAdTrackingEnabled"); - Boolean limitAdTrackingEnabled = (Boolean) isLimitAdTrackingEnabled - .invoke(advertisingInfo); - Method getId = advertisingInfo.getClass().getMethod("getId"); - String advertisingId = (String) getId.invoke(advertisingInfo); - return new AdIdInfo(advertisingId, limitAdTrackingEnabled, AdIdInfo.Advertiser.GOOGLE); + if (Boolean.TRUE.equals(wait)) { + future.get(); + } + } catch (InterruptedException ie) { + Logger.info(TAG, "Interrupted while waiting for Google Play Ads Identifier library" + ie); + Thread.currentThread().interrupt(); } catch (Exception e) { Logger.info(TAG, "Could not locate Google Play Ads Identifier library"); + } finally { + // Shutdown the executor service to release its resources + executorService.shutdown(); } - return null; } private static AdIdInfo getAmazonAdIdInfo(Context context) { diff --git a/android-core/src/test/kotlin/com/mparticle/internal/MPUtilityTest.kt b/android-core/src/test/kotlin/com/mparticle/internal/MPUtilityTest.kt index c5cf12a9e..8895ca0f3 100644 --- a/android-core/src/test/kotlin/com/mparticle/internal/MPUtilityTest.kt +++ b/android-core/src/test/kotlin/com/mparticle/internal/MPUtilityTest.kt @@ -6,19 +6,40 @@ import android.content.res.Configuration import android.content.res.Resources import android.telephony.TelephonyManager import android.util.DisplayMetrics +import com.mparticle.internal.MPUtility.AdIdInfo import com.mparticle.mock.MockContext import com.mparticle.mock.utils.RandomUtils +import junit.framework.TestCase import org.json.JSONArray import org.json.JSONException import org.json.JSONObject import org.junit.Assert +import org.junit.Before import org.junit.Test +import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.`when` +import org.mockito.MockitoAnnotations +import java.lang.reflect.Field +import java.lang.reflect.Method import java.util.Collections import java.util.UUID +import kotlin.test.assertNotNull +import kotlin.test.assertNull class MPUtilityTest { + + @Mock + private lateinit var mockContext: Context + + private lateinit var instance: MPUtility + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + instance = MPUtility() + } + @Test @Throws(Exception::class) fun testSetCheckedAttribute() { @@ -84,6 +105,12 @@ class MPUtilityTest { @Test @Throws(Exception::class) fun googleAdIdInfoWithoutPlayServicesAvailable() { + val adIDInfo: AdIdInfo? = null + val field: Field = MPUtility::class.java.getDeclaredField("googleAdIdInfo") + field.apply { + isAccessible = true + } + field.set(instance, adIDInfo) Assert.assertNull(MPUtility.getAdIdInfo(MockContext())) } @@ -258,6 +285,45 @@ class MPUtilityTest { Assert.assertNull(MPUtility.toNumberOrString(null)) } + @Test + fun testGetGoogleAdIdInfo() { + val adIDInfo = AdIdInfo("12345", true, AdIdInfo.Advertiser.GOOGLE) + val field: Field = MPUtility::class.java.getDeclaredField("googleAdIdInfo") + field.apply { + isAccessible = true + } + field.set(instance, adIDInfo) + + val method: Method = MPUtility::class.java.getDeclaredMethod( + "getGoogleAdIdInfo", + Context::class.java + ) + method.isAccessible = true + val result = method.invoke(instance, mockContext) + val mpUtilityResult: AdIdInfo = result as AdIdInfo + assertNotNull(result) + TestCase.assertEquals("12345", mpUtilityResult.id) + TestCase.assertEquals(true, mpUtilityResult.isLimitAdTrackingEnabled) + TestCase.assertEquals(AdIdInfo.Advertiser.GOOGLE, mpUtilityResult.advertiser) + } + + @Test + fun testGetGoogleAdIdInfo_WHEN_adIDInfo_IS_NULL() { + val adIDInfo: AdIdInfo? = null + val field: Field = MPUtility::class.java.getDeclaredField("googleAdIdInfo") + field.apply { + isAccessible = true + } + field.set(instance, adIDInfo) + val method: Method = MPUtility::class.java.getDeclaredMethod( + "getGoogleAdIdInfo", + Context::class.java + ) + method.isAccessible = true + val result = method.invoke(instance, mockContext) + assertNull(result) + } + @Test fun testGetOrientation() { val mockResources = Mockito.mock(