diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b76e20a..45672e1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +### Version 4.28.0 (2nd April 2021) +#### Changed +- Removed native iOS legacy code. + +#### Native SDKs +- [iOS@v4.28.0][ios_sdk_v4.28.0] +- [Android@v4.27.0][android_sdk_v4.27.0] + +--- + ### Version 4.22.1 (5th January 2021) #### Fixed - Fixed `AppTrackingTransparency` framework linking issue which caused crashes on pre iOS 14 devices. @@ -483,6 +493,7 @@ [ios_sdk_v4.21.1]: https://github.com/adjust/ios_sdk/tree/v4.21.1 [ios_sdk_v4.21.3]: https://github.com/adjust/ios_sdk/tree/v4.21.3 [ios_sdk_v4.24.0]: https://github.com/adjust/ios_sdk/tree/v4.24.0 +[ios_sdk_v4.28.0]: https://github.com/adjust/ios_sdk/tree/v4.28.0 [android_sdk_v2.1.3]: https://github.com/adjust/android_sdk/tree/v2.1.3 [android_sdk_v2.1.4]: https://github.com/adjust/android_sdk/tree/v2.1.4 @@ -505,3 +516,4 @@ [android_sdk_v4.18.4]: https://github.com/adjust/android_sdk/tree/v4.18.4 [android_sdk_v4.21.1]: https://github.com/adjust/android_sdk/tree/v4.21.1 [android_sdk_v4.25.0]: https://github.com/adjust/android_sdk/tree/v4.25.0 +[android_sdk_v4.27.0]: https://github.com/adjust/android_sdk/tree/v4.27.0 diff --git a/VERSION b/VERSION index 352b26ac..ad94be36 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.22.1 +4.28.0 diff --git a/doc/migrate.md b/doc/migrate.md index 0861d4bc..35c5cead 100644 --- a/doc/migrate.md +++ b/doc/migrate.md @@ -1,4 +1,4 @@ -## Migrate your Adjust SDK for Adobe AIR to 4.22.1 from 3.4.3 +## Migrate your Adjust SDK for Adobe AIR to 4.28.0 from 3.4.3 ### SDK initialization diff --git a/example/Main-app.xml b/example/Main-app.xml index 24985692..92122ee7 100644 --- a/example/Main-app.xml +++ b/example/Main-app.xml @@ -1,7 +1,7 @@ com.adjust.examples - 4.22.1 + 4.28.0 Adjust AIR SDK Demo diff --git a/example/lib/Adjust-4.22.1.ane b/example/lib/Adjust-4.22.1.ane deleted file mode 100644 index e0ebaef4..00000000 Binary files a/example/lib/Adjust-4.22.1.ane and /dev/null differ diff --git a/example/lib/Adjust-4.28.0.ane b/example/lib/Adjust-4.28.0.ane new file mode 100644 index 00000000..bb059777 Binary files /dev/null and b/example/lib/Adjust-4.28.0.ane differ diff --git a/ext/android/sdk b/ext/android/sdk index 0e7bc54c..4d69036b 160000 --- a/ext/android/sdk +++ b/ext/android/sdk @@ -1 +1 @@ -Subproject commit 0e7bc54c201d8a0608873331f1206968437f49d7 +Subproject commit 4d69036bf44285c03dff4d3ed94ef7839bc17a59 diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/ActivityHandler.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/ActivityHandler.java index ea36d99d..7ebdbe45 100644 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/ActivityHandler.java +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/ActivityHandler.java @@ -17,6 +17,9 @@ import android.net.Uri; import android.os.Handler; +import com.adjust.sdk.network.ActivityPackageSender; +import com.adjust.sdk.network.IActivityPackageSender; +import com.adjust.sdk.network.UtilNetworking; import com.adjust.sdk.scheduler.SingleThreadCachedScheduler; import com.adjust.sdk.scheduler.ThreadExecutor; import com.adjust.sdk.scheduler.TimerCycle; @@ -358,6 +361,7 @@ public void run() { public void finishedTrackingActivity(ResponseData responseData) { // redirect session responses to attribution handler to check for attribution information if (responseData instanceof SessionResponseData) { + logger.debug("Finished tracking session"); attributionHandler.checkSessionResponse((SessionResponseData)responseData); return; } @@ -466,6 +470,16 @@ public void run() { }); } + @Override + public void sendPreinstallReferrer() { + executor.submit(new Runnable() { + @Override + public void run() { + sendPreinstallReferrerI(); + } + }); + } + @Override public void sendInstallReferrer(final ReferrerDetails referrerDetails, final String referrerApi) { @@ -628,6 +642,26 @@ public void run() { }); } + @Override + public void trackThirdPartySharing(final AdjustThirdPartySharing adjustThirdPartySharing) { + executor.submit(new Runnable() { + @Override + public void run() { + trackThirdPartySharingI(adjustThirdPartySharing); + } + }); + } + + @Override + public void trackMeasurementConsent(final boolean consentMeasurement) { + executor.submit(new Runnable() { + @Override + public void run() { + trackMeasurementConsentI(consentMeasurement); + } + }); + } + @Override public void trackAdRevenue(final String source, final JSONObject adRevenueJson) { executor.submit(new Runnable() { @@ -694,21 +728,6 @@ public AdjustAttribution getAttribution() { return attribution; } - @Override - public String getBasePath() { - return this.basePath; - } - - @Override - public String getGdprPath() { - return this.gdprPath; - } - - @Override - public String getSubscriptionPath() { - return this.subscriptionPath; - } - public InternalState getInternalState() { return internalState; } @@ -730,10 +749,7 @@ private void initI() { readSessionPartnerParametersI(adjustConfig.context); if (adjustConfig.startEnabled != null) { - if (adjustConfig.preLaunchActionsArray == null) { - adjustConfig.preLaunchActionsArray = new ArrayList(); - } - adjustConfig.preLaunchActionsArray.add(new IRunActivityHandler() { + adjustConfig.preLaunchActions.preLaunchActionsArray.add(new IRunActivityHandler() { @Override public void run(ActivityHandler activityHandler) { activityHandler.setEnabledI(adjustConfig.startEnabled); @@ -799,8 +815,24 @@ public void run(ActivityHandler activityHandler) { SharedPreferencesManager sharedPreferencesManager = new SharedPreferencesManager(getContext()); if (sharedPreferencesManager.getGdprForgetMe()) { gdprForgetMe(); - } else if (sharedPreferencesManager.getDisableThirdPartySharing()) { - disableThirdPartySharing(); + } else { + if (sharedPreferencesManager.getDisableThirdPartySharing()) { + disableThirdPartySharing(); + } + for (AdjustThirdPartySharing adjustThirdPartySharing : + adjustConfig.preLaunchActions.preLaunchAdjustThirdPartySharingArray) + { + trackThirdPartySharing(adjustThirdPartySharing); + } + if (adjustConfig.preLaunchActions.lastMeasurementConsentTracked != null) { + trackMeasurementConsent( + adjustConfig.preLaunchActions. + lastMeasurementConsentTracked.booleanValue()); + } + + adjustConfig.preLaunchActions.preLaunchAdjustThirdPartySharingArray = + new ArrayList<>(); + adjustConfig.preLaunchActions.lastMeasurementConsentTracked = null; } } @@ -841,15 +873,44 @@ public void run() { UtilNetworking.setUserAgent(adjustConfig.userAgent); - this.basePath = adjustConfig.basePath; - this.gdprPath = adjustConfig.gdprPath; - this.subscriptionPath = adjustConfig.subscriptionPath; - - packageHandler = AdjustFactory.getPackageHandler(this, adjustConfig.context, toSendI(false)); - - attributionHandler = AdjustFactory.getAttributionHandler(this, toSendI(false)); - - sdkClickHandler = AdjustFactory.getSdkClickHandler(this, toSendI(true)); + IActivityPackageSender packageHandlerActivitySender = + new ActivityPackageSender( + adjustConfig.urlStrategy, + adjustConfig.basePath, + adjustConfig.gdprPath, + adjustConfig.subscriptionPath, + deviceInfo.clientSdk); + packageHandler = AdjustFactory.getPackageHandler( + this, + adjustConfig.context, + toSendI(false), + packageHandlerActivitySender); + + IActivityPackageSender attributionHandlerActivitySender = + new ActivityPackageSender( + adjustConfig.urlStrategy, + adjustConfig.basePath, + adjustConfig.gdprPath, + adjustConfig.subscriptionPath, + deviceInfo.clientSdk); + + attributionHandler = AdjustFactory.getAttributionHandler( + this, + toSendI(false), + attributionHandlerActivitySender); + + IActivityPackageSender sdkClickHandlerActivitySender = + new ActivityPackageSender( + adjustConfig.urlStrategy, + adjustConfig.basePath, + adjustConfig.gdprPath, + adjustConfig.subscriptionPath, + deviceInfo.clientSdk); + + sdkClickHandler = AdjustFactory.getSdkClickHandler( + this, + toSendI(true), + sdkClickHandlerActivitySender); if (isToUpdatePackagesI()) { updatePackagesI(); @@ -869,7 +930,7 @@ public void onInstallReferrerRead(ReferrerDetails referrerDetails) { } }); - preLaunchActionsI(adjustConfig.preLaunchActionsArray); + preLaunchActionsI(adjustConfig.preLaunchActions.preLaunchActionsArray); sendReftagReferrerI(); } @@ -877,6 +938,10 @@ private void checkForPreinstallI() { if (activityState == null) return; if (!activityState.enabled) return; if (activityState.isGdprForgotten) return; + + // sending preinstall referrer doesn't require preinstall tracking flag to be enabled + sendPreinstallReferrerI(); + if (!adjustConfig.preinstallTrackingEnabled) return; if (internalState.hasPreinstallBeenRead()) return; @@ -1082,6 +1147,21 @@ private void startFirstSessionI() { if (sharedPreferencesManager.getDisableThirdPartySharing()) { disableThirdPartySharingI(); } + for (AdjustThirdPartySharing adjustThirdPartySharing : + adjustConfig.preLaunchActions.preLaunchAdjustThirdPartySharingArray) + { + trackThirdPartySharingI(adjustThirdPartySharing); + } + if (adjustConfig.preLaunchActions.lastMeasurementConsentTracked != null) { + trackMeasurementConsentI( + adjustConfig.preLaunchActions. + lastMeasurementConsentTracked.booleanValue()); + } + + adjustConfig.preLaunchActions.preLaunchAdjustThirdPartySharingArray = + new ArrayList<>(); + adjustConfig.preLaunchActions.lastMeasurementConsentTracked = null; + activityState.sessionCount = 1; // this is the first session transferSessionPackageI(now); @@ -1306,6 +1386,8 @@ private void launchSdkClickResponseTasksI(SdkClickResponseData sdkClickResponseD } private void launchSessionResponseTasksI(SessionResponseData sessionResponseData) { + logger.debug("Launching SessionResponse tasks"); + // try to update adid from response updateAdidI(sessionResponseData.adid); @@ -1511,12 +1593,29 @@ private void setEnabledI(boolean enabled) { if (sharedPreferencesManager.getGdprForgetMe()) { gdprForgetMeI(); - } else if (sharedPreferencesManager.getDisableThirdPartySharing()) { - disableThirdPartySharingI(); + } else { + if (sharedPreferencesManager.getDisableThirdPartySharing()) { + disableThirdPartySharingI(); + } + for (AdjustThirdPartySharing adjustThirdPartySharing : + adjustConfig.preLaunchActions.preLaunchAdjustThirdPartySharingArray) + { + trackThirdPartySharingI(adjustThirdPartySharing); + } + if (adjustConfig.preLaunchActions.lastMeasurementConsentTracked != null) { + trackMeasurementConsentI( + adjustConfig.preLaunchActions. + lastMeasurementConsentTracked.booleanValue()); + } + + adjustConfig.preLaunchActions.preLaunchAdjustThirdPartySharingArray = + new ArrayList<>(); + adjustConfig.preLaunchActions.lastMeasurementConsentTracked = null; } // check if install was tracked if (!sharedPreferencesManager.getInstallTracked()) { + logger.debug("Detected that install was not tracked at enable time"); long now = System.currentTimeMillis(); trackNewSessionI(now); } @@ -1639,6 +1738,25 @@ private void sendReftagReferrerI() { sdkClickHandler.sendReftagReferrers(); } + private void sendPreinstallReferrerI() { + if (!isEnabledI()) { + return; + } + if (internalState.hasFirstSdkStartNotOcurred()) { + return; + } + + SharedPreferencesManager sharedPreferencesManager = + new SharedPreferencesManager(getContext()); + String referrerPayload = sharedPreferencesManager.getPreinstallReferrer(); + + if (referrerPayload == null || referrerPayload.isEmpty()) { + return; + } + + sdkClickHandler.sendPreinstallPayload(referrerPayload, Constants.SYSTEM_INSTALLER_REFERRER); + } + private void sendInstallReferrerI(ReferrerDetails referrerDetails, String referrerApi) { if (!isEnabledI()) { return; @@ -2112,6 +2230,53 @@ private void disableThirdPartySharingI() { } } + private void trackThirdPartySharingI(final AdjustThirdPartySharing adjustThirdPartySharing) { + if (!checkActivityStateI(activityState)) { + adjustConfig.preLaunchActions.preLaunchAdjustThirdPartySharingArray.add( + adjustThirdPartySharing); + return; + } + if (!isEnabledI()) { return; } + if (activityState.isGdprForgotten) { return; } + + long now = System.currentTimeMillis(); + PackageBuilder packageBuilder = new PackageBuilder( + adjustConfig, deviceInfo, activityState, sessionParameters, now); + + ActivityPackage activityPackage = + packageBuilder.buildThirdPartySharingPackage(adjustThirdPartySharing); + packageHandler.addPackage(activityPackage); + + if (adjustConfig.eventBufferingEnabled) { + logger.info("Buffered event %s", activityPackage.getSuffix()); + } else { + packageHandler.sendFirstPackage(); + } + } + + private void trackMeasurementConsentI(final boolean consentMeasurement) { + if (!checkActivityStateI(activityState)) { + adjustConfig.preLaunchActions.lastMeasurementConsentTracked = consentMeasurement; + return; + } + if (!isEnabledI()) { return; } + if (activityState.isGdprForgotten) { return; } + + long now = System.currentTimeMillis(); + PackageBuilder packageBuilder = new PackageBuilder( + adjustConfig, deviceInfo, activityState, sessionParameters, now); + + ActivityPackage activityPackage = + packageBuilder.buildMeasurementConsentPackage(consentMeasurement); + packageHandler.addPackage(activityPackage); + + if (adjustConfig.eventBufferingEnabled) { + logger.info("Buffered event %s", activityPackage.getSuffix()); + } else { + packageHandler.sendFirstPackage(); + } + } + private void trackAdRevenueI(String source, JSONObject adRevenueJson) { if (!checkActivityStateI(activityState)) { return; } if (!isEnabledI()) { return; } diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/ActivityKind.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/ActivityKind.java index 3d6961b3..125fdfa5 100644 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/ActivityKind.java +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/ActivityKind.java @@ -12,7 +12,9 @@ public enum ActivityKind { GDPR, AD_REVENUE, DISABLE_THIRD_PARTY_SHARING, - SUBSCRIPTION; + SUBSCRIPTION, + THIRD_PARTY_SHARING, + MEASUREMENT_CONSENT; public static ActivityKind fromString(String string) { if ("session".equals(string)) { @@ -33,6 +35,10 @@ public static ActivityKind fromString(String string) { return AD_REVENUE; } else if ("subscription".equals(string)) { return SUBSCRIPTION; + } else if ("third_party_sharing".equals(string)) { + return THIRD_PARTY_SHARING; + } else if ("measurement_consent".equals(string)) { + return MEASUREMENT_CONSENT; } else { return UNKNOWN; } @@ -59,6 +65,10 @@ public String toString() { return "ad_revenue"; case SUBSCRIPTION: return "subscription"; + case THIRD_PARTY_SHARING: + return "third_party_sharing"; + case MEASUREMENT_CONSENT: + return "measurement_consent"; default: return "unknown"; } diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/ActivityPackage.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/ActivityPackage.java index 3184d8a6..b2ae3767 100644 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/ActivityPackage.java +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/ActivityPackage.java @@ -16,7 +16,6 @@ import java.io.Serializable; import java.util.Arrays; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; @@ -202,7 +201,7 @@ public String getExtendedString() { return builder.toString(); } - protected String getFailureMessage() { + public String getFailureMessage() { return Util.formatString("Failed to track %s%s", activityKind.toString(), suffix); } diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/Adjust.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/Adjust.java index 418a1a67..36fb2b82 100644 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/Adjust.java +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/Adjust.java @@ -32,7 +32,7 @@ private Adjust() { */ public static synchronized AdjustInstance getDefaultInstance() { @SuppressWarnings("unused") - String VERSION = "!SDK-VERSION-STRING!:com.adjust.sdk:adjust-android:4.25.0"; + String VERSION = "!SDK-VERSION-STRING!:com.adjust.sdk:adjust-android:4.27.0"; if (defaultInstance == null) { defaultInstance = new AdjustInstance(); @@ -249,6 +249,18 @@ public static void disableThirdPartySharing(final Context context) { adjustInstance.disableThirdPartySharing(context); } + public static void trackThirdPartySharing( + final AdjustThirdPartySharing adjustThirdPartySharing) + { + AdjustInstance adjustInstance = Adjust.getDefaultInstance(); + adjustInstance.trackThirdPartySharing(adjustThirdPartySharing); + } + + public static void trackMeasurementConsent(final boolean consentMeasurement) { + AdjustInstance adjustInstance = Adjust.getDefaultInstance(); + adjustInstance.trackMeasurementConsent(consentMeasurement); + } + /** * Track ad revenue from a source provider * diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustAttribution.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustAttribution.java index 624d44cd..558f546c 100644 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustAttribution.java +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustAttribution.java @@ -7,7 +7,6 @@ import java.io.ObjectOutputStream; import java.io.ObjectStreamField; import java.io.Serializable; -import java.util.Locale; /** * Created by pfms on 07/11/14. diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustConfig.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustConfig.java index a2ad74f0..9b0c13be 100644 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustConfig.java +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustConfig.java @@ -2,22 +2,8 @@ import android.content.Context; -import java.util.Arrays; import java.util.List; -import static com.adjust.sdk.Constants.BASE_URL_CN; -import static com.adjust.sdk.Constants.BASE_URL_IN; -import static com.adjust.sdk.Constants.FALLBACK_BASE_URLS_CN; -import static com.adjust.sdk.Constants.FALLBACK_BASE_URLS_IN; -import static com.adjust.sdk.Constants.FALLBACK_GDPR_URLS_CN; -import static com.adjust.sdk.Constants.FALLBACK_GDPR_URLS_IN; -import static com.adjust.sdk.Constants.FALLBACK_SUBSCRIPTION_URLS_CN; -import static com.adjust.sdk.Constants.FALLBACK_SUBSCRIPTION_URLS_IN; -import static com.adjust.sdk.Constants.GDPR_URL_CN; -import static com.adjust.sdk.Constants.GDPR_URL_IN; -import static com.adjust.sdk.Constants.SUBSCRIPTION_URL_CN; -import static com.adjust.sdk.Constants.SUBSCRIPTION_URL_IN; - /** * Created by pfms on 06/11/14. */ @@ -42,7 +28,7 @@ public class AdjustConfig { OnDeeplinkResponseListener onDeeplinkResponseListener; boolean sendInBackground; Double delayStart; - List preLaunchActionsArray; + AdjustInstance.PreLaunchActions preLaunchActions; ILogger logger; String userAgent; String pushToken; @@ -53,16 +39,19 @@ public class AdjustConfig { String externalDeviceId; boolean preinstallTrackingEnabled; Boolean needsCost; + String urlStrategy; public static final String ENVIRONMENT_SANDBOX = "sandbox"; public static final String ENVIRONMENT_PRODUCTION = "production"; public static final String URL_STRATEGY_INDIA = "url_strategy_india"; public static final String URL_STRATEGY_CHINA = "url_strategy_china"; + public static final String DATA_RESIDENCY_EU = "data_residency_eu"; public static final String AD_REVENUE_MOPUB = "mopub"; public static final String AD_REVENUE_ADMOB = "admob"; public static final String AD_REVENUE_FB_NATIVE_AD = "facebook_native_ad"; + public static final String AD_REVENUE_FB_AUDIENCE_NETWORK = "facebook_audience_network"; public static final String AD_REVENUE_IRONSOURCE = "ironsource"; public static final String AD_REVENUE_FYBER = "fyber"; public static final String AD_REVENUE_AERSERV = "aerserv"; @@ -213,29 +202,13 @@ public void setUrlStrategy(String urlStrategy) { logger.error("Invalid url strategy"); return; } - - switch (urlStrategy) { - case URL_STRATEGY_INDIA: - AdjustFactory.setBaseUrl(BASE_URL_IN); - AdjustFactory.setGdprUrl(GDPR_URL_IN); - AdjustFactory.setSubscriptionUrl(SUBSCRIPTION_URL_IN); - AdjustFactory.setFallbackBaseUrls(Arrays.asList(FALLBACK_BASE_URLS_IN)); - AdjustFactory.setFallbackGdprUrls(Arrays.asList(FALLBACK_GDPR_URLS_IN)); - AdjustFactory.setFallbackSubscriptionUrls(Arrays.asList(FALLBACK_SUBSCRIPTION_URLS_IN)); - break; - - case URL_STRATEGY_CHINA: - AdjustFactory.setBaseUrl(BASE_URL_CN); - AdjustFactory.setGdprUrl(GDPR_URL_CN); - AdjustFactory.setSubscriptionUrl(SUBSCRIPTION_URL_CN); - AdjustFactory.setFallbackBaseUrls(Arrays.asList(FALLBACK_BASE_URLS_CN)); - AdjustFactory.setFallbackGdprUrls(Arrays.asList(FALLBACK_GDPR_URLS_CN)); - AdjustFactory.setFallbackSubscriptionUrls(Arrays.asList(FALLBACK_SUBSCRIPTION_URLS_CN)); - break; - - default: - logger.warn("Unrecognised url strategy %s", urlStrategy); + if (!urlStrategy.equals(URL_STRATEGY_INDIA) + && !urlStrategy.equals(URL_STRATEGY_CHINA) + && !urlStrategy.equals(DATA_RESIDENCY_EU)) + { + logger.warn("Unrecognised url strategy %s", urlStrategy); } + this.urlStrategy = urlStrategy; } private void setLogLevel(LogLevel logLevel, String environment) { diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustFactory.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustFactory.java index e8a8d0ee..0895ff8c 100644 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustFactory.java +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustFactory.java @@ -2,30 +2,18 @@ import android.content.Context; -import java.io.IOException; +import com.adjust.sdk.network.IActivityPackageSender; +import com.adjust.sdk.network.UtilNetworking; + import java.net.URL; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateEncodingException; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.util.Arrays; -import java.util.List; - -import javax.net.ssl.HostnameVerifier; + import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSession; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; public class AdjustFactory { private static IPackageHandler packageHandler = null; - private static IRequestHandler requestHandler = null; private static IAttributionHandler attributionHandler = null; private static IActivityHandler activityHandler = null; private static ILogger logger = null; - private static HttpsURLConnection httpsURLConnection = null; private static ISdkClickHandler sdkClickHandler = null; private static long timerInterval = -1; @@ -36,13 +24,11 @@ public class AdjustFactory { private static BackoffStrategy packageHandlerBackoffStrategy = null; private static BackoffStrategy installSessionBackoffStrategy = null; private static long maxDelayStart = -1; - private static String baseUrl = Constants.BASE_URL; - private static String gdprUrl = Constants.GDPR_URL; - private static String subscriptionUrl = Constants.SUBSCRIPTION_URL; - private static List fallbackBaseUrls = Arrays.asList(Constants.FALLBACK_BASE_URLS); - private static List fallbackGdprUrls = Arrays.asList(Constants.FALLBACK_GDPR_URLS); - private static List fallbackSubscriptionUrls = Arrays.asList(Constants.FALLBACK_SUBSCRIPTION_URLS); + private static String baseUrl = null; + private static String gdprUrl = null; + private static String subscriptionUrl = null; private static UtilNetworking.IConnectionOptions connectionOptions = null; + private static UtilNetworking.IHttpsURLConnectionProvider httpsURLConnectionProvider = null; private static boolean tryInstallReferrer = true; public static class URLGetConnection { @@ -54,25 +40,25 @@ public static class URLGetConnection { } } - public static IPackageHandler getPackageHandler(IActivityHandler activityHandler, - Context context, - boolean startsSending) { + public static IPackageHandler getPackageHandler( + IActivityHandler activityHandler, + Context context, + boolean startsSending, + IActivityPackageSender packageHandlerActivityPackageSender) + { if (packageHandler == null) { - return new PackageHandler(activityHandler, context, startsSending); + return new PackageHandler(activityHandler, + context, + startsSending, + packageHandlerActivityPackageSender); } - packageHandler.init(activityHandler, context, startsSending); + packageHandler.init(activityHandler, + context, + startsSending, + packageHandlerActivityPackageSender); return packageHandler; } - public static IRequestHandler getRequestHandler(IActivityHandler activityHandler, - IPackageHandler packageHandler) { - if (requestHandler == null) { - return new RequestHandler(activityHandler, packageHandler); - } - requestHandler.init(activityHandler, packageHandler); - return requestHandler; - } - public static ILogger getLogger() { if (logger == null) { // Logger needs to be "static" to retain the configuration throughout the app @@ -138,29 +124,34 @@ public static IActivityHandler getActivityHandler(AdjustConfig config) { return activityHandler; } - public static IAttributionHandler getAttributionHandler(IActivityHandler activityHandler, - boolean startsSending) { + public static IAttributionHandler getAttributionHandler( + IActivityHandler activityHandler, + boolean startsSending, + IActivityPackageSender packageHandlerActivityPackageSender) + { if (attributionHandler == null) { - return new AttributionHandler(activityHandler, startsSending); + return new AttributionHandler(activityHandler, + startsSending, + packageHandlerActivityPackageSender); } - attributionHandler.init(activityHandler, startsSending); + attributionHandler.init(activityHandler, + startsSending, + packageHandlerActivityPackageSender); return attributionHandler; } - public static HttpsURLConnection getHttpsURLConnection(URL url) throws IOException { - if (AdjustFactory.httpsURLConnection == null) { - return (HttpsURLConnection)url.openConnection(); - } - - return AdjustFactory.httpsURLConnection; - } - - public static ISdkClickHandler getSdkClickHandler(IActivityHandler activityHandler, boolean startsSending) { + public static ISdkClickHandler getSdkClickHandler( + IActivityHandler activityHandler, + boolean startsSending, + IActivityPackageSender packageHandlerActivityPackageSender) + { if (sdkClickHandler == null) { - return new SdkClickHandler(activityHandler, startsSending); + return new SdkClickHandler(activityHandler, + startsSending, + packageHandlerActivityPackageSender); } - sdkClickHandler.init(activityHandler, startsSending); + sdkClickHandler.init(activityHandler, startsSending, packageHandlerActivityPackageSender); return sdkClickHandler; } @@ -172,54 +163,31 @@ public static long getMaxDelayStart() { } public static String getBaseUrl() { - if (AdjustFactory.baseUrl == null) { - return Constants.BASE_URL; - } return AdjustFactory.baseUrl; } public static String getGdprUrl() { - if (AdjustFactory.gdprUrl == null) { - return Constants.GDPR_URL; - } return AdjustFactory.gdprUrl; } public static String getSubscriptionUrl() { - if (AdjustFactory.subscriptionUrl == null) { - return Constants.SUBSCRIPTION_URL; - } return AdjustFactory.subscriptionUrl; } - public static List getFallbackBaseUrls() { - if (AdjustFactory.fallbackBaseUrls == null) { - return Arrays.asList(Constants.FALLBACK_BASE_URLS); - } - return AdjustFactory.fallbackBaseUrls; - } - - public static List getFallbackGdprUrls() { - if (AdjustFactory.fallbackGdprUrls == null) { - return Arrays.asList(Constants.FALLBACK_GDPR_URLS); - } - return AdjustFactory.fallbackGdprUrls; - } - - public static List getFallbackSubscriptionUrls() { - if (AdjustFactory.fallbackSubscriptionUrls == null) { - return Arrays.asList(Constants.FALLBACK_GDPR_URLS); - } - return AdjustFactory.fallbackSubscriptionUrls; - } - public static UtilNetworking.IConnectionOptions getConnectionOptions() { if (connectionOptions == null) { - return new UtilNetworking.ConnectionOptions(); + return UtilNetworking.createDefaultConnectionOptions(); } return connectionOptions; } + public static UtilNetworking.IHttpsURLConnectionProvider getHttpsURLConnectionProvider() { + if (httpsURLConnectionProvider == null) { + return UtilNetworking.createDefaultHttpsURLConnectionProvider(); + } + return httpsURLConnectionProvider; + } + public static boolean getTryInstallReferrer() { return tryInstallReferrer; } @@ -228,10 +196,6 @@ public static void setPackageHandler(IPackageHandler packageHandler) { AdjustFactory.packageHandler = packageHandler; } - public static void setRequestHandler(IRequestHandler requestHandler) { - AdjustFactory.requestHandler = requestHandler; - } - public static void setLogger(ILogger logger) { AdjustFactory.logger = logger; } @@ -268,10 +232,6 @@ public static void setAttributionHandler(IAttributionHandler attributionHandler) AdjustFactory.attributionHandler = attributionHandler; } - public static void setHttpsURLConnection(HttpsURLConnection httpsURLConnection) { - AdjustFactory.httpsURLConnection = httpsURLConnection; - } - public static void setSdkClickHandler(ISdkClickHandler sdkClickHandler) { AdjustFactory.sdkClickHandler = sdkClickHandler; } @@ -288,82 +248,14 @@ public static void setSubscriptionUrl(String subscriptionUrl) { AdjustFactory.subscriptionUrl = subscriptionUrl; } - public static void setFallbackBaseUrls(List fallbackBaseUrls) { - AdjustFactory.fallbackBaseUrls = fallbackBaseUrls; - } - - public static void setFallbackGdprUrls(List fallbackGdprUrls) { - AdjustFactory.fallbackGdprUrls = fallbackGdprUrls; - } - - public static void setFallbackSubscriptionUrls(List fallbackSubscriptionUrls) { - AdjustFactory.fallbackSubscriptionUrls = fallbackSubscriptionUrls; - } - - public static void useTestConnectionOptions() { - AdjustFactory.connectionOptions = new UtilNetworking.IConnectionOptions() { - @Override - public void applyConnectionOptions(HttpsURLConnection connection, String clientSdk) { - UtilNetworking.ConnectionOptions defaultConnectionOption = new UtilNetworking.ConnectionOptions(); - defaultConnectionOption.applyConnectionOptions(connection, clientSdk); - try { - SSLContext sc = SSLContext.getInstance("TLS"); - sc.init(null, new TrustManager[]{ - new X509TrustManager() { - public java.security.cert.X509Certificate[] getAcceptedIssuers() { - getLogger().verbose("getAcceptedIssuers"); - return null; - } - public void checkClientTrusted( - X509Certificate[] certs, String authType) { - getLogger().verbose("checkClientTrusted "); - } - public void checkServerTrusted( - X509Certificate[] certs, String authType) throws CertificateException { - getLogger().verbose("checkServerTrusted "); - - String serverThumbprint = "7BCFF44099A35BC093BB48C5A6B9A516CDFDA0D1"; - X509Certificate certificate = certs[0]; - - MessageDigest md = null; - try { - md = MessageDigest.getInstance("SHA1"); - byte[] publicKey = md.digest(certificate.getEncoded()); - String hexString = byte2HexFormatted(publicKey); - - if (!hexString.equalsIgnoreCase(serverThumbprint)) { - throw new CertificateException(); - } - } catch (NoSuchAlgorithmException e) { - getLogger().error("testingMode error %s", e.getMessage()); - } catch (CertificateEncodingException e) { - getLogger().error("testingMode error %s", e.getMessage()); - } - } - } - }, new java.security.SecureRandom()); - connection.setSSLSocketFactory(sc.getSocketFactory()); - - connection.setHostnameVerifier(new HostnameVerifier() { - @Override - public boolean verify(String hostname, SSLSession session) { - getLogger().verbose("verify hostname "); - return isTestIp(hostname); - } - - private boolean isTestIp(String hostname) { - if (hostname.equals("10.0.2.2")) { - return true; - } - return hostname.startsWith("192.168."); - } - }); - } catch (Exception e) { - getLogger().error("testingMode error %s", e.getMessage()); - } - } - }; + public static void setConnectionOptions(UtilNetworking.IConnectionOptions connectionOptions) { + AdjustFactory.connectionOptions = connectionOptions; + } + public static void setHttpsURLConnectionProvider( + UtilNetworking.IHttpsURLConnectionProvider httpsURLConnectionProvider) + { + AdjustFactory.httpsURLConnectionProvider = httpsURLConnectionProvider; } public static void setTryInstallReferrer(boolean tryInstallReferrer) { @@ -406,11 +298,9 @@ public static void teardown(Context context) { PackageHandler.deleteState(context); } packageHandler = null; - requestHandler = null; attributionHandler = null; activityHandler = null; logger = null; - httpsURLConnection = null; sdkClickHandler = null; timerInterval = -1; @@ -424,6 +314,7 @@ public static void teardown(Context context) { gdprUrl = Constants.GDPR_URL; subscriptionUrl = Constants.SUBSCRIPTION_URL; connectionOptions = null; + httpsURLConnectionProvider = null; tryInstallReferrer = true; } } diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustFunction.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustFunction.java index e643c7ea..55203ac5 100644 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustFunction.java +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustFunction.java @@ -734,10 +734,10 @@ private FREObject SetTestOptions(FREContext freContext, FREObject[] freObjects) String value = freObjects[4].getAsString(); testOptions.gdprPath = value; } - if (freObjects[5] != null) { - boolean value = freObjects[5].getAsBool(); - testOptions.useTestConnectionOptions = value; - } + // if (freObjects[5] != null) { + // boolean value = freObjects[5].getAsBool(); + // testOptions.useTestConnectionOptions = value; + // } if (freObjects[6] != null) { String str = freObjects[6].getAsString(); long value = Long.parseLong(str); diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustInstance.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustInstance.java index 35738591..dd331c23 100644 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustInstance.java +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustInstance.java @@ -15,6 +15,18 @@ * @since 12th April 2014 */ public class AdjustInstance { + public static class PreLaunchActions { + public List preLaunchActionsArray; + public List preLaunchAdjustThirdPartySharingArray; + public Boolean lastMeasurementConsentTracked; + + public PreLaunchActions() { + preLaunchActionsArray = new ArrayList<>(); + preLaunchAdjustThirdPartySharingArray = new ArrayList<>(); + lastMeasurementConsentTracked = null; + } + } + /** * Push notifications token. */ @@ -35,10 +47,7 @@ public class AdjustInstance { */ private IActivityHandler activityHandler; - /** - * Array of actions that were requested before SDK initialisation. - */ - private List preLaunchActionsArray; + private PreLaunchActions preLaunchActions = new PreLaunchActions(); /** * Base path for Adjust packages. @@ -74,7 +83,7 @@ public void onCreate(final AdjustConfig adjustConfig) { return; } - adjustConfig.preLaunchActionsArray = preLaunchActionsArray; + adjustConfig.preLaunchActions = preLaunchActions; adjustConfig.pushToken = pushToken; adjustConfig.startEnabled = startEnabled; adjustConfig.startOffline = startOffline; @@ -194,6 +203,26 @@ public void sendReferrer(final String rawReferrer, final Context context) { } } + /** + * Called to process preinstall payload information sent with SYSTEM_INSTALLER_REFERRER intent. + * + * @param referrer Preinstall referrer content + * @param context Application context + */ + public void sendPreinstallReferrer(final String referrer, final Context context) { + // Check for referrer validity. If invalid, return. + if (referrer == null || referrer.length() == 0) { + return; + } + + savePreinstallReferrer(referrer, context); + if (checkActivityHandler("preinstall referrer")) { + if (activityHandler.isEnabled()) { + activityHandler.sendPreinstallReferrer(); + } + } + } + /** * Called to set SDK to offline or online mode. * @@ -228,10 +257,8 @@ public void addSessionCallbackParameter(final String key, final String value) { activityHandler.addSessionCallbackParameter(key, value); return; } - if (preLaunchActionsArray == null) { - preLaunchActionsArray = new ArrayList(); - } - preLaunchActionsArray.add(new IRunActivityHandler() { + + preLaunchActions.preLaunchActionsArray.add(new IRunActivityHandler() { @Override public void run(final ActivityHandler activityHandler) { activityHandler.addSessionCallbackParameterI(key, value); @@ -250,10 +277,7 @@ public void addSessionPartnerParameter(final String key, final String value) { activityHandler.addSessionPartnerParameter(key, value); return; } - if (preLaunchActionsArray == null) { - preLaunchActionsArray = new ArrayList(); - } - preLaunchActionsArray.add(new IRunActivityHandler() { + preLaunchActions.preLaunchActionsArray.add(new IRunActivityHandler() { @Override public void run(final ActivityHandler activityHandler) { activityHandler.addSessionPartnerParameterI(key, value); @@ -271,10 +295,7 @@ public void removeSessionCallbackParameter(final String key) { activityHandler.removeSessionCallbackParameter(key); return; } - if (preLaunchActionsArray == null) { - preLaunchActionsArray = new ArrayList(); - } - preLaunchActionsArray.add(new IRunActivityHandler() { + preLaunchActions.preLaunchActionsArray.add(new IRunActivityHandler() { @Override public void run(final ActivityHandler activityHandler) { activityHandler.removeSessionCallbackParameterI(key); @@ -292,10 +313,7 @@ public void removeSessionPartnerParameter(final String key) { activityHandler.removeSessionPartnerParameter(key); return; } - if (preLaunchActionsArray == null) { - preLaunchActionsArray = new ArrayList(); - } - preLaunchActionsArray.add(new IRunActivityHandler() { + preLaunchActions.preLaunchActionsArray.add(new IRunActivityHandler() { @Override public void run(final ActivityHandler activityHandler) { activityHandler.removeSessionPartnerParameterI(key); @@ -311,10 +329,7 @@ public void resetSessionCallbackParameters() { activityHandler.resetSessionCallbackParameters(); return; } - if (preLaunchActionsArray == null) { - preLaunchActionsArray = new ArrayList(); - } - preLaunchActionsArray.add(new IRunActivityHandler() { + preLaunchActions.preLaunchActionsArray.add(new IRunActivityHandler() { @Override public void run(final ActivityHandler activityHandler) { activityHandler.resetSessionCallbackParametersI(); @@ -330,10 +345,7 @@ public void resetSessionPartnerParameters() { activityHandler.resetSessionPartnerParameters(); return; } - if (preLaunchActionsArray == null) { - preLaunchActionsArray = new ArrayList(); - } - preLaunchActionsArray.add(new IRunActivityHandler() { + preLaunchActions.preLaunchActionsArray.add(new IRunActivityHandler() { @Override public void run(final ActivityHandler activityHandler) { activityHandler.resetSessionPartnerParametersI(); @@ -409,6 +421,24 @@ public void disableThirdPartySharing(final Context context) { activityHandler.disableThirdPartySharing(); } + public void trackThirdPartySharing(final AdjustThirdPartySharing adjustThirdPartySharing) { + if (!checkActivityHandler("third party sharing")) { + preLaunchActions.preLaunchAdjustThirdPartySharingArray.add(adjustThirdPartySharing); + return; + } + + activityHandler.trackThirdPartySharing(adjustThirdPartySharing); + } + + public void trackMeasurementConsent(final boolean consentMeasurement) { + if (!checkActivityHandler("measurement consent")) { + preLaunchActions.lastMeasurementConsentTracked = consentMeasurement; + return; + } + + activityHandler.trackMeasurementConsent(consentMeasurement); + } + /** * Track ad revenue from a source provider * @@ -531,6 +561,23 @@ public void run() { Util.runInBackground(command); } + /** + * Save preinstall referrer to shared preferences. + * + * @param referrer Preinstall referrer content + * @param context Application context + */ + private void savePreinstallReferrer(final String referrer, final Context context) { + Runnable command = new Runnable() { + @Override + public void run() { + SharedPreferencesManager sharedPreferencesManager = new SharedPreferencesManager(context); + sharedPreferencesManager.savePreinstallReferrer(referrer); + } + }; + Util.runInBackground(command); + } + /** * Save push token to shared preferences. * @@ -630,9 +677,6 @@ public void setTestOptions(AdjustTestOptions testOptions) { if (testOptions.subscriptionUrl != null) { AdjustFactory.setSubscriptionUrl(testOptions.subscriptionUrl); } - if (testOptions.useTestConnectionOptions != null && testOptions.useTestConnectionOptions.booleanValue()) { - AdjustFactory.useTestConnectionOptions(); - } if (testOptions.timerIntervalInMilliseconds != null) { AdjustFactory.setTimerInterval(testOptions.timerIntervalInMilliseconds); } diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustPlayStoreSubscription.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustPlayStoreSubscription.java index 60bde37d..7ee70db5 100644 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustPlayStoreSubscription.java +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustPlayStoreSubscription.java @@ -1,6 +1,5 @@ package com.adjust.sdk; -import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustPreinstallReferrerReceiver.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustPreinstallReferrerReceiver.java new file mode 100644 index 00000000..1af4a617 --- /dev/null +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustPreinstallReferrerReceiver.java @@ -0,0 +1,24 @@ +package com.adjust.sdk; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +import static com.adjust.sdk.Constants.EXTRA_SYSTEM_INSTALLER_REFERRER; + +public class AdjustPreinstallReferrerReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (intent == null) { + return; + } + + String referrer = intent.getStringExtra(EXTRA_SYSTEM_INSTALLER_REFERRER); + if (referrer == null) { + return; + } + + Adjust.getDefaultInstance().sendPreinstallReferrer(referrer, context); + } +} diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustTestOptions.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustTestOptions.java index 6ca6b418..fc4618a4 100644 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustTestOptions.java +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustTestOptions.java @@ -14,7 +14,6 @@ public class AdjustTestOptions { public String basePath; public String gdprPath; public String subscriptionPath; - public Boolean useTestConnectionOptions; public Long timerIntervalInMilliseconds; public Long timerStartInMilliseconds; public Long sessionIntervalInMilliseconds; diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustThirdPartySharing.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustThirdPartySharing.java new file mode 100644 index 00000000..5622415b --- /dev/null +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AdjustThirdPartySharing.java @@ -0,0 +1,33 @@ +package com.adjust.sdk; + +import java.util.HashMap; +import java.util.Map; + +public class AdjustThirdPartySharing { + Boolean isEnabled; + Map> granularOptions; + + public AdjustThirdPartySharing(final Boolean isEnabled) { + this.isEnabled = isEnabled; + granularOptions = new HashMap<>(); + } + + public void addGranularOption(final String partnerName, + final String key, + final String value) + { + if (partnerName == null || key == null || value == null) { + ILogger logger = AdjustFactory.getLogger(); + logger.error("Cannot add granular option with any null value"); + return; + } + + Map partnerOptions = granularOptions.get(partnerName); + if (partnerOptions == null) { + partnerOptions = new HashMap<>(); + granularOptions.put(partnerName, partnerOptions); + } + + partnerOptions.put(key, value); + } +} diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AttributionHandler.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AttributionHandler.java index 85bd6e09..aa1b984f 100644 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AttributionHandler.java +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/AttributionHandler.java @@ -10,6 +10,7 @@ import android.net.Uri; +import com.adjust.sdk.network.IActivityPackageSender; import com.adjust.sdk.scheduler.SingleThreadCachedScheduler; import com.adjust.sdk.scheduler.ThreadScheduler; import com.adjust.sdk.scheduler.TimerOnce; @@ -17,14 +18,17 @@ import org.json.JSONObject; import java.lang.ref.WeakReference; +import java.util.HashMap; import java.util.List; +import java.util.Map; -public class AttributionHandler implements IAttributionHandler { +public class AttributionHandler implements IAttributionHandler, + IActivityPackageSender.ResponseDataCallbackSubscriber +{ private static final String ATTRIBUTION_TIMER_NAME = "Attribution timer"; private boolean paused; - private String basePath; - private String clientSdk; private String lastInitiatedBy; + private IActivityPackageSender activityPackageSender; private ILogger logger; private TimerOnce timer; @@ -51,7 +55,10 @@ public void teardown() { activityHandlerWeakRef = null; } - public AttributionHandler(IActivityHandler activityHandler, boolean startsSending) { + public AttributionHandler(IActivityHandler activityHandler, + boolean startsSending, + IActivityPackageSender attributionHandlerActivityPackageSender) + { logger = AdjustFactory.getLogger(); scheduler = new SingleThreadCachedScheduler("AttributionHandler"); timer = new TimerOnce(new Runnable() { @@ -60,15 +67,18 @@ public void run() { sendAttributionRequest(); } }, ATTRIBUTION_TIMER_NAME); - basePath = activityHandler.getBasePath(); - clientSdk = activityHandler.getDeviceInfo().clientSdk; - init(activityHandler, startsSending); + + init(activityHandler, startsSending, attributionHandlerActivityPackageSender); } @Override - public void init(IActivityHandler activityHandler, boolean startsSending) { + public void init(IActivityHandler activityHandler, + boolean startsSending, + IActivityPackageSender attributionHandlerActivityPackageSender) + { this.activityHandlerWeakRef = new WeakReference(activityHandler); this.paused = !startsSending; + this.activityPackageSender = attributionHandlerActivityPackageSender; } @Override @@ -137,14 +147,7 @@ private void sendAttributionRequest() { scheduler.submit(new Runnable() { @Override public void run() { - List urls = UrlFactory.getBaseUrls(); - boolean requestProcessed = false; - for (int i=0; i 0) { - UrlFactory.prioritiseBaseUrl(urls.get(i)); - } - } + sendAttributionRequestI(); } }); } @@ -170,8 +173,8 @@ private void checkAttributionI(IActivityHandler activityHandler, ResponseData re return; } - long timerMilliseconds = responseData.jsonResponse.optLong("ask_in", -1); - if (timerMilliseconds >= 0) { + Long timerMilliseconds = responseData.askIn; // responseData.jsonResponse.optLong("ask_in", -1); + if (timerMilliseconds != null && timerMilliseconds >= 0) { activityHandler.setAskingAttribution(true); lastInitiatedBy = "backend"; getAttributionI(timerMilliseconds); @@ -179,24 +182,25 @@ private void checkAttributionI(IActivityHandler activityHandler, ResponseData re } activityHandler.setAskingAttribution(false); - JSONObject attributionJson = responseData.jsonResponse.optJSONObject("attribution"); - responseData.attribution = AdjustAttribution.fromJson( - attributionJson, - responseData.adid, - Util.getSdkPrefixPlatform(clientSdk)); } - private void checkSessionResponseI(IActivityHandler activityHandler, SessionResponseData sessionResponseData) { + private void checkSessionResponseI(IActivityHandler activityHandler, + SessionResponseData sessionResponseData) + { checkAttributionI(activityHandler, sessionResponseData); activityHandler.launchSessionResponseTasks(sessionResponseData); } - private void checkSdkClickResponseI(IActivityHandler activityHandler, SdkClickResponseData sdkClickResponseData) { + private void checkSdkClickResponseI(IActivityHandler activityHandler, + SdkClickResponseData sdkClickResponseData) + { checkAttributionI(activityHandler, sdkClickResponseData); activityHandler.launchSdkClickResponseTasks(sdkClickResponseData); } - private void checkAttributionResponseI(IActivityHandler activityHandler, AttributionResponseData attributionResponseData) { + private void checkAttributionResponseI(IActivityHandler activityHandler, + AttributionResponseData attributionResponseData) + { checkAttributionI(activityHandler, attributionResponseData); checkDeeplinkI(attributionResponseData); activityHandler.launchAttributionResponseTasks(attributionResponseData); @@ -206,6 +210,7 @@ private void checkDeeplinkI(AttributionResponseData attributionResponseData) { if (attributionResponseData.jsonResponse == null) { return; } + JSONObject attributionJson = attributionResponseData.jsonResponse.optJSONObject("attribution"); if (attributionJson == null) { return; @@ -217,34 +222,36 @@ private void checkDeeplinkI(AttributionResponseData attributionResponseData) { attributionResponseData.deeplink = Uri.parse(deeplinkString); } - private boolean sendAttributionRequestI(String baseUrl) { + private void sendAttributionRequestI() { if (activityHandlerWeakRef.get().getActivityState().isGdprForgotten) { - return true; + return; } if (paused) { logger.debug("Attribution handler is paused"); - return true; + return; } // Create attribution package before sending attribution request. ActivityPackage attributionPackage = buildAndGetAttributionPackage(); logger.verbose("%s", attributionPackage.getExtendedString()); - try { - ResponseData responseData = UtilNetworking.createGETHttpsURLConnection(attributionPackage, basePath, baseUrl); - if (!(responseData instanceof AttributionResponseData)) { - return true; - } - if (responseData.trackingState == TrackingState.OPTED_OUT) { - activityHandlerWeakRef.get().gotOptOutResponse(); - return true; - } - checkAttributionResponse((AttributionResponseData)responseData); - return true; - } catch (Exception e) { - logger.error("Failed to get attribution (%s)", e.getMessage()); - return false; - } + Map sendingParameters = generateSendingParametersI(); + + activityPackageSender.sendActivityPackage( + attributionPackage, + sendingParameters, + this); + } + + private Map generateSendingParametersI() { + HashMap sendingParameters = new HashMap<>(); + + long now = System.currentTimeMillis(); + String dateString = Util.dateFormatter.format(now); + + PackageBuilder.addString(sendingParameters, "sent_at", dateString); + + return sendingParameters; } private ActivityPackage buildAndGetAttributionPackage() { @@ -260,4 +267,28 @@ private ActivityPackage buildAndGetAttributionPackage() { lastInitiatedBy = null; return activityPackage; } + + @Override + public void onResponseDataCallback(final ResponseData responseData) { + scheduler.submit(new Runnable() { + @Override + public void run() { + IActivityHandler activityHandler = activityHandlerWeakRef.get(); + if (activityHandler == null) { + return; + } + + if (responseData.trackingState == TrackingState.OPTED_OUT) { + activityHandler.gotOptOutResponse(); + return; + } + + if (!(responseData instanceof AttributionResponseData)) { + return; + } + + checkAttributionResponseI(activityHandler, (AttributionResponseData)responseData); + } + }); + } } \ No newline at end of file diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/Constants.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/Constants.java index d46c43ec..21d30e4c 100644 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/Constants.java +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/Constants.java @@ -26,26 +26,10 @@ public interface Constants { String BASE_URL = "https://app.adjust.com"; String GDPR_URL = "https://gdpr.adjust.com"; String SUBSCRIPTION_URL = "https://subscription.adjust.com"; - String BASE_URL_IN = "https://app.adjust.net.in"; - String GDPR_URL_IN = "https://gdpr.adjust.net.in"; - String SUBSCRIPTION_URL_IN = "https://subscription.adjust.net.in"; - String BASE_URL_CN = "https://app.adjust.world"; - String GDPR_URL_CN = "https://gdpr.adjust.world"; - String SUBSCRIPTION_URL_CN = "https://subscription.adjust.world"; - - String[] FALLBACK_BASE_URLS = {BASE_URL_IN, BASE_URL_CN}; - String[] FALLBACK_GDPR_URLS = {GDPR_URL_IN, GDPR_URL_CN}; - String[] FALLBACK_SUBSCRIPTION_URLS = {SUBSCRIPTION_URL_IN, SUBSCRIPTION_URL_CN}; - String[] FALLBACK_BASE_URLS_IN = {BASE_URL}; - String[] FALLBACK_GDPR_URLS_IN = {GDPR_URL}; - String[] FALLBACK_SUBSCRIPTION_URLS_IN = {SUBSCRIPTION_URL}; - String[] FALLBACK_BASE_URLS_CN = {BASE_URL}; - String[] FALLBACK_GDPR_URLS_CN = {GDPR_URL}; - String[] FALLBACK_SUBSCRIPTION_URLS_CN = {SUBSCRIPTION_URL}; String SCHEME = "https"; String AUTHORITY = "app.adjust.com"; - String CLIENT_SDK = "android4.25.0"; + String CLIENT_SDK = "android4.27.0"; String LOGTAG = "Adjust"; String REFTAG = "reftag"; String INSTALL_REFERRER = "install_referrer"; @@ -75,6 +59,7 @@ public interface Constants { String MD5 = "MD5"; String SHA1 = "SHA-1"; String SHA256 = "SHA-256"; + int MINIMAL_ERROR_STATUS_CODE = 400; String CALLBACK_PARAMETERS = "callback_params"; String PARTNER_PARAMETERS = "partner_params"; @@ -92,6 +77,7 @@ public interface Constants { String CONTENT_PROVIDER_INTENT_ACTION = "content_provider_intent_action"; String CONTENT_PROVIDER_NO_PERMISSION = "content_provider_no_permission"; String FILE_SYSTEM = "file_system"; + String SYSTEM_INSTALLER_REFERRER = "system_installer_referrer"; String ADJUST_PREINSTALL_SYSTEM_PROPERTY_PREFIX = "adjust.preinstall."; String ADJUST_PREINSTALL_SYSTEM_PROPERTY_PATH = "adjust.preinstall.path"; @@ -99,4 +85,5 @@ public interface Constants { String ADJUST_PREINSTALL_CONTENT_URI_PATH = "trackers"; String ADJUST_PREINSTALL_CONTENT_PROVIDER_INTENT_ACTION = "com.attribution.REFERRAL_PROVIDER"; String ADJUST_PREINSTALL_FILE_SYSTEM_PATH = "/data/local/tmp/adjust.preinstall"; + String EXTRA_SYSTEM_INSTALLER_REFERRER = "com.attribution.EXTRA_SYSTEM_INSTALLER_REFERRER"; } diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/IActivityHandler.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/IActivityHandler.java index d33ed9bc..360c74a1 100644 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/IActivityHandler.java +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/IActivityHandler.java @@ -37,6 +37,8 @@ public interface IActivityHandler { void sendReftagReferrer(); + void sendPreinstallReferrer(); + void sendInstallReferrer(ReferrerDetails referrerDetails, String referrerApi); void setOfflineMode(boolean enabled); @@ -65,6 +67,10 @@ public interface IActivityHandler { void disableThirdPartySharing(); + void trackThirdPartySharing(AdjustThirdPartySharing adjustThirdPartySharing); + + void trackMeasurementConsent(boolean consentMeasurement); + void trackAdRevenue(String source, JSONObject adRevenueJson); void trackPlayStoreSubscription(AdjustPlayStoreSubscription subscription); @@ -84,10 +90,4 @@ public interface IActivityHandler { ActivityState getActivityState(); SessionParameters getSessionParameters(); - - String getBasePath(); - - String getGdprPath(); - - String getSubscriptionPath(); } diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/IAttributionHandler.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/IAttributionHandler.java index 8b52facb..9cb2291b 100644 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/IAttributionHandler.java +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/IAttributionHandler.java @@ -8,8 +8,12 @@ package com.adjust.sdk; +import com.adjust.sdk.network.IActivityPackageSender; + public interface IAttributionHandler { - void init(IActivityHandler activityHandler, boolean startsSending); + void init(IActivityHandler activityHandler, + boolean startsSending, + IActivityPackageSender attributionHandlerActivityPackageSender); void checkSessionResponse(SessionResponseData sessionResponseData); void checkSdkClickResponse(SdkClickResponseData sdkClickResponseData); void pauseSending(); diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/IPackageHandler.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/IPackageHandler.java index 89facefe..46ab4d53 100644 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/IPackageHandler.java +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/IPackageHandler.java @@ -2,17 +2,18 @@ import android.content.Context; +import com.adjust.sdk.network.IActivityPackageSender; + public interface IPackageHandler { - void init(IActivityHandler activityHandler, Context context, boolean startsSending); + void init(IActivityHandler activityHandler, + Context context, + boolean startsSending, + IActivityPackageSender packageHandlerActivityPackageSender); void addPackage(ActivityPackage activityPackage); void sendFirstPackage(); - void sendNextPackage(ResponseData responseData); - - void closeFirstPackage(ResponseData responseData, ActivityPackage activityPackage); - void pauseSending(); void resumeSending(); @@ -21,11 +22,5 @@ public interface IPackageHandler { void flush(); - String getBasePath(); - - String getGdprPath(); - - String getSubscriptionPath(); - void teardown(); } diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/IRequestHandler.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/IRequestHandler.java deleted file mode 100644 index f5932118..00000000 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/IRequestHandler.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.adjust.sdk; - -public interface IRequestHandler { - void init(IActivityHandler activityHandler, IPackageHandler packageHandler); - - void sendPackage(ActivityPackage activityPackage, int queueSize); - - void teardown(); -} diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/ISdkClickHandler.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/ISdkClickHandler.java index 25fa584e..22e64c2a 100644 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/ISdkClickHandler.java +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/ISdkClickHandler.java @@ -1,5 +1,7 @@ package com.adjust.sdk; +import com.adjust.sdk.network.IActivityPackageSender; + /** * SdkClickHandler interface. * @@ -14,7 +16,9 @@ public interface ISdkClickHandler { * @param activityHandler Activity handler instance. * @param startsSending Is sending paused? */ - void init(IActivityHandler activityHandler, boolean startsSending); + void init(IActivityHandler activityHandler, + boolean startsSending, + IActivityPackageSender sdkClickHandlerActivityPackageSender); /** * Pause sending from SdkClickHandler. diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/InstallReferrer.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/InstallReferrer.java index fcd3a6ec..31863360 100644 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/InstallReferrer.java +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/InstallReferrer.java @@ -2,6 +2,8 @@ import android.content.Context; +import com.adjust.sdk.scheduler.SingleThreadCachedScheduler; +import com.adjust.sdk.scheduler.ThreadExecutor; import com.adjust.sdk.scheduler.TimerOnce; import java.lang.reflect.InvocationHandler; @@ -94,6 +96,8 @@ public class InstallReferrer implements InvocationHandler { private Object playInstallReferrer; + private ThreadExecutor executor; + /** * Default constructor. * @@ -113,6 +117,7 @@ public void run() { } }, "InstallReferrer"); this.referrerCallback = referrerCallback; + this.executor = new SingleThreadCachedScheduler("InstallReferrer"); } private Object createInstallReferrer(Context context, InstallReferrerReadListener referrerCallback, ILogger logger) { @@ -258,8 +263,29 @@ private void startConnection(final Class listenerClass, final Object listenerPro * {@inheritDoc} */ @Override - public Object invoke(final Object proxy, final Method method, Object[] args) - throws Throwable { + public Object invoke(final Object proxy, final Method method, final Object[] args) + throws Throwable + { + executor.submit(new Runnable() { + @Override + public + void run() { + try { + invokeI(proxy, method, args); + } catch (Throwable throwable) { + logger.error("invoke error (%s) thrown by (%s)", + throwable.getMessage(), + throwable.getClass().getCanonicalName()); + } + } + }); + + return null; + } + + private Object invokeI(final Object proxy, final Method method, Object[] args) + throws Throwable + { if (method == null) { logger.error("InstallReferrer invoke method null"); return null; @@ -298,10 +324,10 @@ public Object invoke(final Object proxy, final Method method, Object[] args) return null; } - onInstallReferrerSetupFinishedInt(responseCode); + onInstallReferrerSetupFinishedIntI(responseCode); } else if (methodName.equals("onInstallReferrerServiceDisconnected")) { logger.debug("Connection to install referrer service was lost. Retrying ..."); - retry(); + retryI(); } return null; } @@ -311,7 +337,7 @@ public Object invoke(final Object proxy, final Method method, Object[] args) * * @param responseCode Response code from install referrer service */ - private void onInstallReferrerSetupFinishedInt(final int responseCode) { + private void onInstallReferrerSetupFinishedIntI(final int responseCode) { boolean retryAtEnd = false; switch (responseCode) { /** Success. */ @@ -381,7 +407,7 @@ private void onInstallReferrerSetupFinishedInt(final int responseCode) { break; } if (retryAtEnd) { - retry(); + retryI(); } else { shouldTryToRead.set(false); closeReferrerClient(); @@ -557,7 +583,7 @@ private Boolean getBooleanGooglePlayInstantParam(final Object referrerDetails) { /** * Retry connection to install referrer service. */ - private void retry() { + private void retryI() { if (!shouldTryToRead.get()) { logger.debug("Should not try to read Install referrer"); closeReferrerClient(); diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/InstallReferrerHuawei.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/InstallReferrerHuawei.java index 8727096f..4cbf6466 100644 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/InstallReferrerHuawei.java +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/InstallReferrerHuawei.java @@ -5,7 +5,6 @@ import android.database.Cursor; import android.net.Uri; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; public class InstallReferrerHuawei { diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/PackageBuilder.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/PackageBuilder.java index cc67837f..d2828b46 100644 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/PackageBuilder.java +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/PackageBuilder.java @@ -183,6 +183,35 @@ ActivityPackage buildDisableThirdPartySharingPackage() { return activityPackage; } + ActivityPackage buildThirdPartySharingPackage( + final AdjustThirdPartySharing adjustThirdPartySharing) + { + Map parameters = getThirdPartySharingParameters(adjustThirdPartySharing); + ActivityPackage activityPackage = getDefaultActivityPackage(ActivityKind.THIRD_PARTY_SHARING); + activityPackage.setPath("/third_party_sharing"); + activityPackage.setSuffix(""); + + AdjustSigner.sign(parameters, ActivityKind.THIRD_PARTY_SHARING.toString(), + activityPackage.getClientSdk(), adjustConfig.context, adjustConfig.logger); + + activityPackage.setParameters(parameters); + return activityPackage; + } + + ActivityPackage buildMeasurementConsentPackage(final boolean consentMeasurement) { + Map parameters = getMeasurementConsentParameters(consentMeasurement); + ActivityPackage activityPackage = + getDefaultActivityPackage(ActivityKind.MEASUREMENT_CONSENT); + activityPackage.setPath("/measurement_consent"); + activityPackage.setSuffix(""); + + AdjustSigner.sign(parameters, ActivityKind.MEASUREMENT_CONSENT.toString(), + activityPackage.getClientSdk(), adjustConfig.context, adjustConfig.logger); + + activityPackage.setParameters(parameters); + return activityPackage; + } + ActivityPackage buildAdRevenuePackage(String source, JSONObject adRevenueJson) { Map parameters = getAdRevenueParameters(source, adRevenueJson); ActivityPackage adRevenuePackage = getDefaultActivityPackage(ActivityKind.AD_REVENUE); @@ -234,17 +263,20 @@ private Map getSessionParameters(boolean isInDelay) { // Device identifiers. deviceInfo.reloadPlayIds(adjustConfig.context); PackageBuilder.addString(parameters, "android_uuid", activityStateCopy.uuid); - PackageBuilder.addBoolean(parameters, "tracking_enabled", deviceInfo.isTrackingEnabled); PackageBuilder.addString(parameters, "gps_adid", deviceInfo.playAdId); - PackageBuilder.addString(parameters, "gps_adid_src", deviceInfo.playAdIdSource); PackageBuilder.addLong(parameters, "gps_adid_attempt", deviceInfo.playAdIdAttempt); + PackageBuilder.addString(parameters, "gps_adid_src", deviceInfo.playAdIdSource); + PackageBuilder.addBoolean(parameters, "tracking_enabled", deviceInfo.isTrackingEnabled); + PackageBuilder.addString(parameters, "fire_adid", Util.getFireAdvertisingId(contentResolver)); + PackageBuilder.addBoolean(parameters, "fire_tracking_enabled", Util.getFireTrackingEnabled(contentResolver)); - if (!containsPlayIds(parameters)) { - logger.warn("Google Advertising ID not detected, fallback to non Google Play identifiers will take place"); + if (!containsPlayIds(parameters) && !containsFireIds(parameters)) { + logger.warn("Google Advertising ID or Fire Advertising ID not detected, " + + "fallback to non Google Play and Fire identifiers will take place"); deviceInfo.reloadNonPlayIds(adjustConfig.context); - PackageBuilder.addString(parameters, "mac_sha1", deviceInfo.macSha1); - PackageBuilder.addString(parameters, "mac_md5", deviceInfo.macShortMd5); PackageBuilder.addString(parameters, "android_id", deviceInfo.androidId); + PackageBuilder.addString(parameters, "mac_md5", deviceInfo.macShortMd5); + PackageBuilder.addString(parameters, "mac_sha1", deviceInfo.macSha1); } // Rest of the parameters. @@ -269,8 +301,6 @@ private Map getSessionParameters(boolean isInDelay) { PackageBuilder.addBoolean(parameters, "event_buffering_enabled", adjustConfig.eventBufferingEnabled); PackageBuilder.addString(parameters, "external_device_id", adjustConfig.externalDeviceId); PackageBuilder.addString(parameters, "fb_id", deviceInfo.fbAttributionId); - PackageBuilder.addString(parameters, "fire_adid", Util.getFireAdvertisingId(contentResolver)); - PackageBuilder.addBoolean(parameters, "fire_tracking_enabled", Util.getFireTrackingEnabled(contentResolver)); PackageBuilder.addString(parameters, "hardware_name", deviceInfo.hardwareName); PackageBuilder.addString(parameters, "installed_at", deviceInfo.appInstallTime); PackageBuilder.addString(parameters, "language", deviceInfo.language); @@ -323,17 +353,20 @@ public Map getEventParameters(AdjustEvent event, boolean isInDel // Device identifiers. deviceInfo.reloadPlayIds(adjustConfig.context); PackageBuilder.addString(parameters, "android_uuid", activityStateCopy.uuid); - PackageBuilder.addBoolean(parameters, "tracking_enabled", deviceInfo.isTrackingEnabled); PackageBuilder.addString(parameters, "gps_adid", deviceInfo.playAdId); - PackageBuilder.addString(parameters, "gps_adid_src", deviceInfo.playAdIdSource); PackageBuilder.addLong(parameters, "gps_adid_attempt", deviceInfo.playAdIdAttempt); + PackageBuilder.addString(parameters, "gps_adid_src", deviceInfo.playAdIdSource); + PackageBuilder.addBoolean(parameters, "tracking_enabled", deviceInfo.isTrackingEnabled); + PackageBuilder.addString(parameters, "fire_adid", Util.getFireAdvertisingId(contentResolver)); + PackageBuilder.addBoolean(parameters, "fire_tracking_enabled", Util.getFireTrackingEnabled(contentResolver)); - if (!containsPlayIds(parameters)) { - logger.warn("Google Advertising ID not detected, fallback to non Google Play identifiers will take place"); + if (!containsPlayIds(parameters) && !containsFireIds(parameters)) { + logger.warn("Google Advertising ID or Fire Advertising ID not detected, " + + "fallback to non Google Play and Fire identifiers will take place"); deviceInfo.reloadNonPlayIds(adjustConfig.context); - PackageBuilder.addString(parameters, "mac_sha1", deviceInfo.macSha1); - PackageBuilder.addString(parameters, "mac_md5", deviceInfo.macShortMd5); PackageBuilder.addString(parameters, "android_id", deviceInfo.androidId); + PackageBuilder.addString(parameters, "mac_md5", deviceInfo.macShortMd5); + PackageBuilder.addString(parameters, "mac_sha1", deviceInfo.macSha1); } // Rest of the parameters. @@ -361,8 +394,6 @@ public Map getEventParameters(AdjustEvent event, boolean isInDel PackageBuilder.addString(parameters, "event_token", event.eventToken); PackageBuilder.addString(parameters, "external_device_id", adjustConfig.externalDeviceId); PackageBuilder.addString(parameters, "fb_id", deviceInfo.fbAttributionId); - PackageBuilder.addString(parameters, "fire_adid", Util.getFireAdvertisingId(contentResolver)); - PackageBuilder.addBoolean(parameters, "fire_tracking_enabled", Util.getFireTrackingEnabled(contentResolver)); PackageBuilder.addString(parameters, "hardware_name", deviceInfo.hardwareName); PackageBuilder.addString(parameters, "language", deviceInfo.language); PackageBuilder.addString(parameters, "mcc", Util.getMcc(adjustConfig.context)); @@ -407,17 +438,20 @@ private Map getInfoParameters(String source) { // Device identifiers. deviceInfo.reloadPlayIds(adjustConfig.context); PackageBuilder.addString(parameters, "android_uuid", activityStateCopy.uuid); - PackageBuilder.addBoolean(parameters, "tracking_enabled", deviceInfo.isTrackingEnabled); PackageBuilder.addString(parameters, "gps_adid", deviceInfo.playAdId); - PackageBuilder.addString(parameters, "gps_adid_src", deviceInfo.playAdIdSource); PackageBuilder.addLong(parameters, "gps_adid_attempt", deviceInfo.playAdIdAttempt); + PackageBuilder.addString(parameters, "gps_adid_src", deviceInfo.playAdIdSource); + PackageBuilder.addBoolean(parameters, "tracking_enabled", deviceInfo.isTrackingEnabled); + PackageBuilder.addString(parameters, "fire_adid", Util.getFireAdvertisingId(contentResolver)); + PackageBuilder.addBoolean(parameters, "fire_tracking_enabled", Util.getFireTrackingEnabled(contentResolver)); - if (!containsPlayIds(parameters)) { - logger.warn("Google Advertising ID not detected, fallback to non Google Play identifiers will take place"); + if (!containsPlayIds(parameters) && !containsFireIds(parameters)) { + logger.warn("Google Advertising ID or Fire Advertising ID not detected, " + + "fallback to non Google Play and Fire identifiers will take place"); deviceInfo.reloadNonPlayIds(adjustConfig.context); - PackageBuilder.addString(parameters, "mac_sha1", deviceInfo.macSha1); - PackageBuilder.addString(parameters, "mac_md5", deviceInfo.macShortMd5); PackageBuilder.addString(parameters, "android_id", deviceInfo.androidId); + PackageBuilder.addString(parameters, "mac_md5", deviceInfo.macShortMd5); + PackageBuilder.addString(parameters, "mac_sha1", deviceInfo.macSha1); } // Rest of the parameters. @@ -430,8 +464,6 @@ private Map getInfoParameters(String source) { PackageBuilder.addString(parameters, "environment", adjustConfig.environment); PackageBuilder.addBoolean(parameters, "event_buffering_enabled", adjustConfig.eventBufferingEnabled); PackageBuilder.addString(parameters, "external_device_id", adjustConfig.externalDeviceId); - PackageBuilder.addString(parameters, "fire_adid", Util.getFireAdvertisingId(contentResolver)); - PackageBuilder.addBoolean(parameters, "fire_tracking_enabled", Util.getFireTrackingEnabled(contentResolver)); PackageBuilder.addBoolean(parameters, "needs_response_details", true); PackageBuilder.addString(parameters, "push_token", activityStateCopy.pushToken); PackageBuilder.addString(parameters, "secret_id", adjustConfig.secretId); @@ -460,17 +492,20 @@ private Map getClickParameters(String source) { // Device identifiers. deviceInfo.reloadPlayIds(adjustConfig.context); PackageBuilder.addString(parameters, "android_uuid", activityStateCopy.uuid); - PackageBuilder.addBoolean(parameters, "tracking_enabled", deviceInfo.isTrackingEnabled); PackageBuilder.addString(parameters, "gps_adid", deviceInfo.playAdId); - PackageBuilder.addString(parameters, "gps_adid_src", deviceInfo.playAdIdSource); PackageBuilder.addLong(parameters, "gps_adid_attempt", deviceInfo.playAdIdAttempt); + PackageBuilder.addString(parameters, "gps_adid_src", deviceInfo.playAdIdSource); + PackageBuilder.addBoolean(parameters, "tracking_enabled", deviceInfo.isTrackingEnabled); + PackageBuilder.addString(parameters, "fire_adid", Util.getFireAdvertisingId(contentResolver)); + PackageBuilder.addBoolean(parameters, "fire_tracking_enabled", Util.getFireTrackingEnabled(contentResolver)); - if (!containsPlayIds(parameters)) { - logger.warn("Google Advertising ID not detected, fallback to non Google Play identifiers will take place"); + if (!containsPlayIds(parameters) && !containsFireIds(parameters)) { + logger.warn("Google Advertising ID or Fire Advertising ID not detected, " + + "fallback to non Google Play and Fire identifiers will take place"); deviceInfo.reloadNonPlayIds(adjustConfig.context); - PackageBuilder.addString(parameters, "mac_sha1", deviceInfo.macSha1); - PackageBuilder.addString(parameters, "mac_md5", deviceInfo.macShortMd5); PackageBuilder.addString(parameters, "android_id", deviceInfo.androidId); + PackageBuilder.addString(parameters, "mac_md5", deviceInfo.macShortMd5); + PackageBuilder.addString(parameters, "mac_sha1", deviceInfo.macSha1); } // Attribution parameters. @@ -507,8 +542,6 @@ private Map getClickParameters(String source) { PackageBuilder.addBoolean(parameters, "event_buffering_enabled", adjustConfig.eventBufferingEnabled); PackageBuilder.addString(parameters, "external_device_id", adjustConfig.externalDeviceId); PackageBuilder.addString(parameters, "fb_id", deviceInfo.fbAttributionId); - PackageBuilder.addString(parameters, "fire_adid", Util.getFireAdvertisingId(contentResolver)); - PackageBuilder.addBoolean(parameters, "fire_tracking_enabled", Util.getFireTrackingEnabled(contentResolver)); PackageBuilder.addBoolean(parameters, "google_play_instant", googlePlayInstant); PackageBuilder.addString(parameters, "hardware_name", deviceInfo.hardwareName); PackageBuilder.addDateInSeconds(parameters, "install_begin_time", installBeginTimeInSeconds); @@ -568,17 +601,20 @@ private Map getAttributionParameters(String initiatedBy) { // Device identifiers. deviceInfo.reloadPlayIds(adjustConfig.context); PackageBuilder.addString(parameters, "android_uuid", activityStateCopy.uuid); - PackageBuilder.addBoolean(parameters, "tracking_enabled", deviceInfo.isTrackingEnabled); PackageBuilder.addString(parameters, "gps_adid", deviceInfo.playAdId); - PackageBuilder.addString(parameters, "gps_adid_src", deviceInfo.playAdIdSource); PackageBuilder.addLong(parameters, "gps_adid_attempt", deviceInfo.playAdIdAttempt); + PackageBuilder.addString(parameters, "gps_adid_src", deviceInfo.playAdIdSource); + PackageBuilder.addBoolean(parameters, "tracking_enabled", deviceInfo.isTrackingEnabled); + PackageBuilder.addString(parameters, "fire_adid", Util.getFireAdvertisingId(contentResolver)); + PackageBuilder.addBoolean(parameters, "fire_tracking_enabled", Util.getFireTrackingEnabled(contentResolver)); - if (!containsPlayIds(parameters)) { - logger.warn("Google Advertising ID not detected, fallback to non Google Play identifiers will take place"); + if (!containsPlayIds(parameters) && !containsFireIds(parameters)) { + logger.warn("Google Advertising ID or Fire Advertising ID not detected, " + + "fallback to non Google Play and Fire identifiers will take place"); deviceInfo.reloadNonPlayIds(adjustConfig.context); - PackageBuilder.addString(parameters, "mac_sha1", deviceInfo.macSha1); - PackageBuilder.addString(parameters, "mac_md5", deviceInfo.macShortMd5); PackageBuilder.addString(parameters, "android_id", deviceInfo.androidId); + PackageBuilder.addString(parameters, "mac_md5", deviceInfo.macShortMd5); + PackageBuilder.addString(parameters, "mac_sha1", deviceInfo.macSha1); } // Rest of the parameters. @@ -595,8 +631,6 @@ private Map getAttributionParameters(String initiatedBy) { PackageBuilder.addString(parameters, "environment", adjustConfig.environment); PackageBuilder.addBoolean(parameters, "event_buffering_enabled", adjustConfig.eventBufferingEnabled); PackageBuilder.addString(parameters, "external_device_id", adjustConfig.externalDeviceId); - PackageBuilder.addString(parameters, "fire_adid", Util.getFireAdvertisingId(contentResolver)); - PackageBuilder.addBoolean(parameters, "fire_tracking_enabled", Util.getFireTrackingEnabled(contentResolver)); PackageBuilder.addString(parameters, "initiated_by", initiatedBy); PackageBuilder.addBoolean(parameters, "needs_response_details", true); PackageBuilder.addString(parameters, "os_name", deviceInfo.osName); @@ -628,17 +662,20 @@ private Map getGdprParameters() { // Device identifiers. deviceInfo.reloadPlayIds(adjustConfig.context); PackageBuilder.addString(parameters, "android_uuid", activityStateCopy.uuid); - PackageBuilder.addBoolean(parameters, "tracking_enabled", deviceInfo.isTrackingEnabled); PackageBuilder.addString(parameters, "gps_adid", deviceInfo.playAdId); - PackageBuilder.addString(parameters, "gps_adid_src", deviceInfo.playAdIdSource); PackageBuilder.addLong(parameters, "gps_adid_attempt", deviceInfo.playAdIdAttempt); + PackageBuilder.addString(parameters, "gps_adid_src", deviceInfo.playAdIdSource); + PackageBuilder.addBoolean(parameters, "tracking_enabled", deviceInfo.isTrackingEnabled); + PackageBuilder.addString(parameters, "fire_adid", Util.getFireAdvertisingId(contentResolver)); + PackageBuilder.addBoolean(parameters, "fire_tracking_enabled", Util.getFireTrackingEnabled(contentResolver)); - if (!containsPlayIds(parameters)) { - logger.warn("Google Advertising ID not detected, fallback to non Google Play identifiers will take place"); + if (!containsPlayIds(parameters) && !containsFireIds(parameters)) { + logger.warn("Google Advertising ID or Fire Advertising ID not detected, " + + "fallback to non Google Play and Fire identifiers will take place"); deviceInfo.reloadNonPlayIds(adjustConfig.context); - PackageBuilder.addString(parameters, "mac_sha1", deviceInfo.macSha1); - PackageBuilder.addString(parameters, "mac_md5", deviceInfo.macShortMd5); PackageBuilder.addString(parameters, "android_id", deviceInfo.androidId); + PackageBuilder.addString(parameters, "mac_md5", deviceInfo.macShortMd5); + PackageBuilder.addString(parameters, "mac_sha1", deviceInfo.macSha1); } // Rest of the parameters. @@ -655,8 +692,6 @@ private Map getGdprParameters() { PackageBuilder.addString(parameters, "environment", adjustConfig.environment); PackageBuilder.addBoolean(parameters, "event_buffering_enabled", adjustConfig.eventBufferingEnabled); PackageBuilder.addString(parameters, "external_device_id", adjustConfig.externalDeviceId); - PackageBuilder.addString(parameters, "fire_adid", Util.getFireAdvertisingId(contentResolver)); - PackageBuilder.addBoolean(parameters, "fire_tracking_enabled", Util.getFireTrackingEnabled(contentResolver)); PackageBuilder.addBoolean(parameters, "needs_response_details", true); PackageBuilder.addString(parameters, "os_name", deviceInfo.osName); PackageBuilder.addString(parameters, "os_version", deviceInfo.osVersion); @@ -687,17 +722,20 @@ private Map getDisableThirdPartySharingParameters() { // Device identifiers. deviceInfo.reloadPlayIds(adjustConfig.context); PackageBuilder.addString(parameters, "android_uuid", activityStateCopy.uuid); - PackageBuilder.addBoolean(parameters, "tracking_enabled", deviceInfo.isTrackingEnabled); PackageBuilder.addString(parameters, "gps_adid", deviceInfo.playAdId); - PackageBuilder.addString(parameters, "gps_adid_src", deviceInfo.playAdIdSource); PackageBuilder.addLong(parameters, "gps_adid_attempt", deviceInfo.playAdIdAttempt); + PackageBuilder.addString(parameters, "gps_adid_src", deviceInfo.playAdIdSource); + PackageBuilder.addBoolean(parameters, "tracking_enabled", deviceInfo.isTrackingEnabled); + PackageBuilder.addString(parameters, "fire_adid", Util.getFireAdvertisingId(contentResolver)); + PackageBuilder.addBoolean(parameters, "fire_tracking_enabled", Util.getFireTrackingEnabled(contentResolver)); - if (!containsPlayIds(parameters)) { - logger.warn("Google Advertising ID not detected, fallback to non Google Play identifiers will take place"); + if (!containsPlayIds(parameters) && !containsFireIds(parameters)) { + logger.warn("Google Advertising ID or Fire Advertising ID not detected, " + + "fallback to non Google Play and Fire identifiers will take place"); deviceInfo.reloadNonPlayIds(adjustConfig.context); - PackageBuilder.addString(parameters, "mac_sha1", deviceInfo.macSha1); - PackageBuilder.addString(parameters, "mac_md5", deviceInfo.macShortMd5); PackageBuilder.addString(parameters, "android_id", deviceInfo.androidId); + PackageBuilder.addString(parameters, "mac_md5", deviceInfo.macShortMd5); + PackageBuilder.addString(parameters, "mac_sha1", deviceInfo.macSha1); } // Rest of the parameters. @@ -714,8 +752,141 @@ private Map getDisableThirdPartySharingParameters() { PackageBuilder.addString(parameters, "environment", adjustConfig.environment); PackageBuilder.addBoolean(parameters, "event_buffering_enabled", adjustConfig.eventBufferingEnabled); PackageBuilder.addString(parameters, "external_device_id", adjustConfig.externalDeviceId); + PackageBuilder.addBoolean(parameters, "needs_response_details", true); + PackageBuilder.addString(parameters, "os_name", deviceInfo.osName); + PackageBuilder.addString(parameters, "os_version", deviceInfo.osVersion); + PackageBuilder.addString(parameters, "package_name", deviceInfo.packageName); + PackageBuilder.addString(parameters, "push_token", activityStateCopy.pushToken); + PackageBuilder.addString(parameters, "secret_id", adjustConfig.secretId); + + checkDeviceIds(parameters); + return parameters; + } + + private Map getThirdPartySharingParameters + (final AdjustThirdPartySharing adjustThirdPartySharing) + { + ContentResolver contentResolver = adjustConfig.context.getContentResolver(); + Map parameters = new HashMap(); + Map imeiParameters = Reflection.getImeiParameters(adjustConfig.context, logger); + + // Check if plugin is used and if yes, add read parameters. + if (imeiParameters != null) { + parameters.putAll(imeiParameters); + } + + // Check if oaid plugin is used and if yes, add the parameter + Map oaidParameters = Reflection.getOaidParameters(adjustConfig.context, logger); + if (oaidParameters != null) { + parameters.putAll(oaidParameters); + } + + // Third Party Sharing + if (adjustThirdPartySharing.isEnabled != null) { + PackageBuilder.addString(parameters, "sharing", + adjustThirdPartySharing.isEnabled.booleanValue() ? + "enable" : "disable"); + } + PackageBuilder.addMapJson(parameters, "granular_third_party_sharing_options", + adjustThirdPartySharing.granularOptions); + + // Device identifiers. + deviceInfo.reloadPlayIds(adjustConfig.context); + PackageBuilder.addString(parameters, "android_uuid", activityStateCopy.uuid); + PackageBuilder.addString(parameters, "gps_adid", deviceInfo.playAdId); + PackageBuilder.addLong(parameters, "gps_adid_attempt", deviceInfo.playAdIdAttempt); + PackageBuilder.addString(parameters, "gps_adid_src", deviceInfo.playAdIdSource); + PackageBuilder.addBoolean(parameters, "tracking_enabled", deviceInfo.isTrackingEnabled); + PackageBuilder.addString(parameters, "fire_adid", Util.getFireAdvertisingId(contentResolver)); + PackageBuilder.addBoolean(parameters, "fire_tracking_enabled", Util.getFireTrackingEnabled(contentResolver)); + + if (!containsPlayIds(parameters) && !containsFireIds(parameters)) { + logger.warn("Google Advertising ID or Fire Advertising ID not detected, " + + "fallback to non Google Play and Fire identifiers will take place"); + deviceInfo.reloadNonPlayIds(adjustConfig.context); + PackageBuilder.addString(parameters, "android_id", deviceInfo.androidId); + PackageBuilder.addString(parameters, "mac_md5", deviceInfo.macShortMd5); + PackageBuilder.addString(parameters, "mac_sha1", deviceInfo.macSha1); + } + + // Rest of the parameters. + PackageBuilder.addString(parameters, "api_level", deviceInfo.apiLevel); + PackageBuilder.addString(parameters, "app_secret", adjustConfig.appSecret); + PackageBuilder.addString(parameters, "app_token", adjustConfig.appToken); + PackageBuilder.addString(parameters, "app_version", deviceInfo.appVersion); + PackageBuilder.addBoolean(parameters, "attribution_deeplink", true); + PackageBuilder.addDateInMilliseconds(parameters, "created_at", createdAt); + PackageBuilder.addBoolean(parameters, "device_known", adjustConfig.deviceKnown); + PackageBuilder.addString(parameters, "device_name", deviceInfo.deviceName); + PackageBuilder.addString(parameters, "device_type", deviceInfo.deviceType); + PackageBuilder.addString(parameters, "environment", adjustConfig.environment); + PackageBuilder.addBoolean(parameters, "event_buffering_enabled", adjustConfig.eventBufferingEnabled); + PackageBuilder.addString(parameters, "external_device_id", adjustConfig.externalDeviceId); + PackageBuilder.addBoolean(parameters, "needs_response_details", true); + PackageBuilder.addString(parameters, "os_name", deviceInfo.osName); + PackageBuilder.addString(parameters, "os_version", deviceInfo.osVersion); + PackageBuilder.addString(parameters, "package_name", deviceInfo.packageName); + PackageBuilder.addString(parameters, "push_token", activityStateCopy.pushToken); + PackageBuilder.addString(parameters, "secret_id", adjustConfig.secretId); + + checkDeviceIds(parameters); + return parameters; + } + + private Map getMeasurementConsentParameters( + final boolean consentMeasurement) + { + ContentResolver contentResolver = adjustConfig.context.getContentResolver(); + Map parameters = new HashMap(); + Map imeiParameters = Reflection.getImeiParameters(adjustConfig.context, logger); + + // Check if plugin is used and if yes, add read parameters. + if (imeiParameters != null) { + parameters.putAll(imeiParameters); + } + + // Check if oaid plugin is used and if yes, add the parameter + Map oaidParameters = Reflection.getOaidParameters(adjustConfig.context, logger); + if (oaidParameters != null) { + parameters.putAll(oaidParameters); + } + + // Measurement Consent + PackageBuilder.addString(parameters, "measurement", + consentMeasurement ? "enable" : "disable"); + + // Device identifiers. + deviceInfo.reloadPlayIds(adjustConfig.context); + PackageBuilder.addString(parameters, "android_uuid", activityStateCopy.uuid); + PackageBuilder.addString(parameters, "gps_adid", deviceInfo.playAdId); + PackageBuilder.addLong(parameters, "gps_adid_attempt", deviceInfo.playAdIdAttempt); + PackageBuilder.addString(parameters, "gps_adid_src", deviceInfo.playAdIdSource); + PackageBuilder.addBoolean(parameters, "tracking_enabled", deviceInfo.isTrackingEnabled); PackageBuilder.addString(parameters, "fire_adid", Util.getFireAdvertisingId(contentResolver)); PackageBuilder.addBoolean(parameters, "fire_tracking_enabled", Util.getFireTrackingEnabled(contentResolver)); + + if (!containsPlayIds(parameters) && !containsFireIds(parameters)) { + logger.warn("Google Advertising ID or Fire Advertising ID not detected, " + + "fallback to non Google Play and Fire identifiers will take place"); + deviceInfo.reloadNonPlayIds(adjustConfig.context); + PackageBuilder.addString(parameters, "android_id", deviceInfo.androidId); + PackageBuilder.addString(parameters, "mac_md5", deviceInfo.macShortMd5); + PackageBuilder.addString(parameters, "mac_sha1", deviceInfo.macSha1); + } + + // Rest of the parameters. + PackageBuilder.addString(parameters, "api_level", deviceInfo.apiLevel); + PackageBuilder.addString(parameters, "app_secret", adjustConfig.appSecret); + PackageBuilder.addString(parameters, "app_token", adjustConfig.appToken); + PackageBuilder.addString(parameters, "app_version", deviceInfo.appVersion); + PackageBuilder.addBoolean(parameters, "attribution_deeplink", true); + PackageBuilder.addDateInMilliseconds(parameters, "created_at", createdAt); + PackageBuilder.addBoolean(parameters, "device_known", adjustConfig.deviceKnown); + PackageBuilder.addString(parameters, "device_name", deviceInfo.deviceName); + PackageBuilder.addString(parameters, "device_type", deviceInfo.deviceType); + PackageBuilder.addString(parameters, "environment", adjustConfig.environment); + PackageBuilder.addBoolean(parameters, "event_buffering_enabled", adjustConfig.eventBufferingEnabled); + PackageBuilder.addString(parameters, "external_device_id", adjustConfig.externalDeviceId); PackageBuilder.addBoolean(parameters, "needs_response_details", true); PackageBuilder.addString(parameters, "os_name", deviceInfo.osName); PackageBuilder.addString(parameters, "os_version", deviceInfo.osVersion); @@ -746,17 +917,20 @@ private Map getAdRevenueParameters(String source, JSONObject adR // Device identifiers. deviceInfo.reloadPlayIds(adjustConfig.context); PackageBuilder.addString(parameters, "android_uuid", activityStateCopy.uuid); - PackageBuilder.addBoolean(parameters, "tracking_enabled", deviceInfo.isTrackingEnabled); PackageBuilder.addString(parameters, "gps_adid", deviceInfo.playAdId); - PackageBuilder.addString(parameters, "gps_adid_src", deviceInfo.playAdIdSource); PackageBuilder.addLong(parameters, "gps_adid_attempt", deviceInfo.playAdIdAttempt); + PackageBuilder.addString(parameters, "gps_adid_src", deviceInfo.playAdIdSource); + PackageBuilder.addBoolean(parameters, "tracking_enabled", deviceInfo.isTrackingEnabled); + PackageBuilder.addString(parameters, "fire_adid", Util.getFireAdvertisingId(contentResolver)); + PackageBuilder.addBoolean(parameters, "fire_tracking_enabled", Util.getFireTrackingEnabled(contentResolver)); - if (!containsPlayIds(parameters)) { - logger.warn("Google Advertising ID not detected, fallback to non Google Play identifiers will take place"); + if (!containsPlayIds(parameters) && !containsFireIds(parameters)) { + logger.warn("Google Advertising ID or Fire Advertising ID not detected, " + + "fallback to non Google Play and Fire identifiers will take place"); deviceInfo.reloadNonPlayIds(adjustConfig.context); - PackageBuilder.addString(parameters, "mac_sha1", deviceInfo.macSha1); - PackageBuilder.addString(parameters, "mac_md5", deviceInfo.macShortMd5); PackageBuilder.addString(parameters, "android_id", deviceInfo.androidId); + PackageBuilder.addString(parameters, "mac_md5", deviceInfo.macShortMd5); + PackageBuilder.addString(parameters, "mac_sha1", deviceInfo.macSha1); } // Rest of the parameters. @@ -781,8 +955,6 @@ private Map getAdRevenueParameters(String source, JSONObject adR PackageBuilder.addBoolean(parameters, "event_buffering_enabled", adjustConfig.eventBufferingEnabled); PackageBuilder.addString(parameters, "external_device_id", adjustConfig.externalDeviceId); PackageBuilder.addString(parameters, "fb_id", deviceInfo.fbAttributionId); - PackageBuilder.addString(parameters, "fire_adid", Util.getFireAdvertisingId(contentResolver)); - PackageBuilder.addBoolean(parameters, "fire_tracking_enabled", Util.getFireTrackingEnabled(contentResolver)); PackageBuilder.addString(parameters, "hardware_name", deviceInfo.hardwareName); PackageBuilder.addString(parameters, "installed_at", deviceInfo.appInstallTime); PackageBuilder.addString(parameters, "language", deviceInfo.language); @@ -831,17 +1003,20 @@ private Map getSubscriptionParameters(AdjustPlayStoreSubscriptio // Device identifiers. deviceInfo.reloadPlayIds(adjustConfig.context); PackageBuilder.addString(parameters, "android_uuid", activityStateCopy.uuid); - PackageBuilder.addBoolean(parameters, "tracking_enabled", deviceInfo.isTrackingEnabled); PackageBuilder.addString(parameters, "gps_adid", deviceInfo.playAdId); - PackageBuilder.addString(parameters, "gps_adid_src", deviceInfo.playAdIdSource); PackageBuilder.addLong(parameters, "gps_adid_attempt", deviceInfo.playAdIdAttempt); + PackageBuilder.addString(parameters, "gps_adid_src", deviceInfo.playAdIdSource); + PackageBuilder.addBoolean(parameters, "tracking_enabled", deviceInfo.isTrackingEnabled); + PackageBuilder.addString(parameters, "fire_adid", Util.getFireAdvertisingId(contentResolver)); + PackageBuilder.addBoolean(parameters, "fire_tracking_enabled", Util.getFireTrackingEnabled(contentResolver)); - if (!containsPlayIds(parameters)) { - logger.warn("Google Advertising ID not detected, fallback to non Google Play identifiers will take place"); + if (!containsPlayIds(parameters) && !containsFireIds(parameters)) { + logger.warn("Google Advertising ID or Fire Advertising ID not detected, " + + "fallback to non Google Play and Fire identifiers will take place"); deviceInfo.reloadNonPlayIds(adjustConfig.context); - PackageBuilder.addString(parameters, "mac_sha1", deviceInfo.macSha1); - PackageBuilder.addString(parameters, "mac_md5", deviceInfo.macShortMd5); PackageBuilder.addString(parameters, "android_id", deviceInfo.androidId); + PackageBuilder.addString(parameters, "mac_md5", deviceInfo.macShortMd5); + PackageBuilder.addString(parameters, "mac_sha1", deviceInfo.macSha1); } // Callback and partner parameters. @@ -872,8 +1047,6 @@ private Map getSubscriptionParameters(AdjustPlayStoreSubscriptio PackageBuilder.addBoolean(parameters, "event_buffering_enabled", adjustConfig.eventBufferingEnabled); PackageBuilder.addString(parameters, "external_device_id", adjustConfig.externalDeviceId); PackageBuilder.addString(parameters, "fb_id", deviceInfo.fbAttributionId); - PackageBuilder.addString(parameters, "fire_adid", Util.getFireAdvertisingId(contentResolver)); - PackageBuilder.addBoolean(parameters, "fire_tracking_enabled", Util.getFireTrackingEnabled(contentResolver)); PackageBuilder.addString(parameters, "hardware_name", deviceInfo.hardwareName); PackageBuilder.addString(parameters, "installed_at", deviceInfo.appInstallTime); PackageBuilder.addString(parameters, "language", deviceInfo.language); @@ -897,13 +1070,14 @@ private Map getSubscriptionParameters(AdjustPlayStoreSubscriptio PackageBuilder.addDuration(parameters, "time_spent", activityStateCopy.timeSpent); PackageBuilder.addString(parameters, "updated_at", deviceInfo.appUpdateTime); - PackageBuilder.addLong(parameters, "revenue", subscription.getPrice()); - PackageBuilder.addDateInMilliseconds(parameters, "transaction_date", subscription.getPurchaseTime()); + // subscription specific parameters + PackageBuilder.addString(parameters, "billing_store", subscription.getBillingStore()); PackageBuilder.addString(parameters, "currency", subscription.getCurrency()); PackageBuilder.addString(parameters, "product_id", subscription.getSku()); - PackageBuilder.addString(parameters, "receipt", subscription.getSignature()); PackageBuilder.addString(parameters, "purchase_token", subscription.getPurchaseToken()); - PackageBuilder.addString(parameters, "billing_store", subscription.getBillingStore()); + PackageBuilder.addString(parameters, "receipt", subscription.getSignature()); + PackageBuilder.addLong(parameters, "revenue", subscription.getPrice()); + PackageBuilder.addDateInMilliseconds(parameters, "transaction_date", subscription.getPurchaseTime()); PackageBuilder.addString(parameters, "transaction_id", subscription.getOrderId()); checkDeviceIds(parameters); @@ -939,7 +1113,7 @@ static void addJsonObject(Map parameters, String key, JSONObject PackageBuilder.addString(parameters, key, jsonObject.toString()); } - static void addMapJson(Map parameters, String key, Map map) { + static void addMapJson(Map parameters, String key, Map map) { if (map == null) { return; } @@ -1007,6 +1181,13 @@ private boolean containsPlayIds(Map parameters) { return parameters.containsKey("gps_adid"); } + private boolean containsFireIds(Map parameters) { + if (parameters == null) { + return false; + } + return parameters.containsKey("fire_adid"); + } + private void checkDeviceIds(Map parameters) { if (parameters != null && !parameters.containsKey("mac_sha1") && !parameters.containsKey("mac_md5") diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/PackageHandler.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/PackageHandler.java index 3e22fb64..9a4672a7 100644 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/PackageHandler.java +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/PackageHandler.java @@ -11,11 +11,13 @@ import android.content.Context; +import com.adjust.sdk.network.IActivityPackageSender; import com.adjust.sdk.scheduler.SingleThreadCachedScheduler; import com.adjust.sdk.scheduler.ThreadScheduler; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; @@ -24,12 +26,14 @@ import static com.adjust.sdk.Constants.PARTNER_PARAMETERS; // persistent -public class PackageHandler implements IPackageHandler { +public class PackageHandler implements IPackageHandler, + IActivityPackageSender.ResponseDataCallbackSubscriber +{ private static final String PACKAGE_QUEUE_FILENAME = "AdjustIoPackageQueue"; private static final String PACKAGE_QUEUE_NAME = "Package queue"; private ThreadScheduler scheduler; - private IRequestHandler requestHandler; + private IActivityPackageSender activityPackageSender; private WeakReference activityHandlerWeakRef; private List packageQueue; private AtomicBoolean isSending; @@ -38,9 +42,6 @@ public class PackageHandler implements IPackageHandler { private ILogger logger; private BackoffStrategy backoffStrategy; private BackoffStrategy backoffStrategyForInstallSession; - private String basePath; - private String gdprPath; - private String subscriptionPath; @Override public void teardown() { @@ -51,14 +52,10 @@ public void teardown() { if (activityHandlerWeakRef != null) { activityHandlerWeakRef.clear(); } - if (requestHandler != null) { - requestHandler.teardown(); - } if (packageQueue != null) { packageQueue.clear(); } scheduler = null; - requestHandler = null; activityHandlerWeakRef = null; packageQueue = null; isSending = null; @@ -73,13 +70,16 @@ static void deleteState(Context context) { public PackageHandler(IActivityHandler activityHandler, Context context, - boolean startsSending) { + boolean startsSending, + IActivityPackageSender packageHandlerActivityPackageSender) + { this.scheduler = new SingleThreadCachedScheduler("PackageHandler"); this.logger = AdjustFactory.getLogger(); this.backoffStrategy = AdjustFactory.getPackageHandlerBackoffStrategy(); this.backoffStrategyForInstallSession = AdjustFactory.getInstallSessionBackoffStrategy(); - init(activityHandler, context, startsSending); + + init(activityHandler, context, startsSending, packageHandlerActivityPackageSender); scheduler.submit(new Runnable() { @Override @@ -90,13 +90,15 @@ public void run() { } @Override - public void init(IActivityHandler activityHandler, Context context, boolean startsSending) { + public void init(IActivityHandler activityHandler, + Context context, + boolean startsSending, + IActivityPackageSender packageHandlerActivityPackageSender) + { this.activityHandlerWeakRef = new WeakReference(activityHandler); this.context = context; this.paused = !startsSending; - this.basePath = activityHandler.getBasePath(); - this.gdprPath = activityHandler.getGdprPath(); - this.subscriptionPath = activityHandler.getSubscriptionPath(); + this.activityPackageSender = packageHandlerActivityPackageSender; } // add a package to the queue @@ -121,29 +123,29 @@ public void run() { }); } - // remove oldest package and try to send the next one - // (after success or possibly permanent failure) @Override - public void sendNextPackage(ResponseData responseData) { - scheduler.submit(new Runnable() { - @Override - public void run() { - sendNextI(); - } - }); - + public void onResponseDataCallback(final ResponseData responseData) { + logger.debug("Got response in PackageHandler"); IActivityHandler activityHandler = activityHandlerWeakRef.get(); - if (activityHandler != null) { - activityHandler.finishedTrackingActivity(responseData); + if (activityHandler != null && + responseData.trackingState == TrackingState.OPTED_OUT) { + activityHandler.gotOptOutResponse(); } - } - // close the package to retry in the future (after temporary failure) - @Override - public void closeFirstPackage(ResponseData responseData, ActivityPackage activityPackage) { - responseData.willRetry = true; + if (!responseData.willRetry) { + scheduler.submit(new Runnable() { + @Override + public void run() { + sendNextI(); + } + }); + + if (activityHandler != null) { + activityHandler.finishedTrackingActivity(responseData); + } + return; + } - IActivityHandler activityHandler = activityHandlerWeakRef.get(); if (activityHandler != null) { activityHandler.finishedTrackingActivity(responseData); } @@ -159,17 +161,19 @@ public void run() { } }; - if (activityPackage == null) { + if (responseData.activityPackage == null) { runnable.run(); return; } - int retries = activityPackage.increaseRetries(); + int retries = responseData.activityPackage.increaseRetries(); long waitTimeMilliSeconds; SharedPreferencesManager sharedPreferencesManager = new SharedPreferencesManager(context); - if (activityPackage.getActivityKind() == ActivityKind.SESSION && !sharedPreferencesManager.getInstallTracked()) { + if (responseData.activityPackage.getActivityKind() == + ActivityKind.SESSION && !sharedPreferencesManager.getInstallTracked()) + { waitTimeMilliSeconds = Util.getWaitingTime(retries, backoffStrategyForInstallSession); } else { waitTimeMilliSeconds = Util.getWaitingTime(retries, backoffStrategy); @@ -220,26 +224,8 @@ public void run() { }); } - @Override - public String getBasePath() { - return this.basePath; - } - - @Override - public String getGdprPath() { - return this.gdprPath; - } - - @Override - public String getSubscriptionPath() { - return this.subscriptionPath; - } - // internal methods run in dedicated queue thread - private void initI() { - requestHandler = AdjustFactory.getRequestHandler(activityHandlerWeakRef.get(), this); - isSending = new AtomicBoolean(); readPackageQueueI(); @@ -267,8 +253,27 @@ private void sendFirstI() { return; } + Map sendingParameters = generateSendingParametersI(); + ActivityPackage firstPackage = packageQueue.get(0); - requestHandler.sendPackage(firstPackage, packageQueue.size() - 1); + activityPackageSender.sendActivityPackage(firstPackage, + sendingParameters, + this); + } + + private Map generateSendingParametersI() { + HashMap sendingParameters = new HashMap<>(); + + long now = System.currentTimeMillis(); + String dateString = Util.dateFormatter.format(now); + + PackageBuilder.addString(sendingParameters, "sent_at", dateString); + + int queueSize = packageQueue.size() - 1; + if (queueSize > 0) { + PackageBuilder.addLong(sendingParameters, "queue_size", queueSize); + } + return sendingParameters; } private void sendNextI() { diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/Reflection.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/Reflection.java index bf0dcd95..812798d7 100644 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/Reflection.java +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/Reflection.java @@ -1,15 +1,10 @@ package com.adjust.sdk; import android.content.Context; -import android.content.res.Configuration; -import android.os.Build; -import android.os.LocaleList; -import android.telephony.TelephonyManager; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.util.Locale; import java.util.Map; public class Reflection { diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/RequestHandler.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/RequestHandler.java deleted file mode 100644 index 46ad4a22..00000000 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/RequestHandler.java +++ /dev/null @@ -1,198 +0,0 @@ -// -// RequestHandler.java -// Adjust -// -// Created by Christian Wellenbrock on 2013-06-25. -// Copyright (c) 2013 adjust GmbH. All rights reserved. -// See the file MIT-LICENSE for copying permission. -// - -package com.adjust.sdk; - -import com.adjust.sdk.scheduler.SingleThreadCachedScheduler; -import com.adjust.sdk.scheduler.ThreadExecutor; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.lang.ref.WeakReference; -import java.net.SocketTimeoutException; -import java.util.List; - -public class RequestHandler implements IRequestHandler { - private ThreadExecutor executor; - private WeakReference packageHandlerWeakRef; - private WeakReference activityHandlerWeakRef; - private ILogger logger; - private String basePath; - private String gdprPath; - private String subscriptionPath; - - public RequestHandler(IActivityHandler activityHandler, IPackageHandler packageHandler) { - this.logger = AdjustFactory.getLogger(); - this.executor = new SingleThreadCachedScheduler("RequestHandler"); - init(activityHandler, packageHandler); - this.basePath = packageHandler.getBasePath(); - this.gdprPath = packageHandler.getGdprPath(); - this.subscriptionPath = packageHandler.getSubscriptionPath(); - } - - @Override - public void init(IActivityHandler activityHandler, IPackageHandler packageHandler) { - this.packageHandlerWeakRef = new WeakReference(packageHandler); - this.activityHandlerWeakRef = new WeakReference(activityHandler); - } - - @Override - public void sendPackage(final ActivityPackage activityPackage, final int queueSize) { - executor.submit(new Runnable() { - @Override - public void run() { - - List urls; - boolean requestProcessed = false; - if (activityPackage.getActivityKind() == ActivityKind.GDPR) { - urls = UrlFactory.getGdprUrls(); - for (int i=0; i 0) { - UrlFactory.prioritiseGdprUrl(urls.get(i)); - } - } - } else if (activityPackage.getActivityKind() == ActivityKind.SUBSCRIPTION) { - urls = UrlFactory.getSubscriptionUrls(); - for (int i=0; i 0) { - UrlFactory.prioritiseSubscriptionUrl(urls.get(i)); - } - } - } else { - urls = UrlFactory.getBaseUrls(); - for (int i=0; i 0) { - UrlFactory.prioritiseBaseUrl(urls.get(i)); - } - } - } - } - }); - } - - @Override - public void teardown() { - logger.verbose("RequestHandler teardown"); - if (executor != null) { - executor.teardown(); - } - if (packageHandlerWeakRef != null) { - packageHandlerWeakRef.clear(); - } - if (activityHandlerWeakRef != null) { - activityHandlerWeakRef.clear(); - } - executor = null; - packageHandlerWeakRef = null; - activityHandlerWeakRef = null; - logger = null; - } - - private boolean sendI(ActivityPackage activityPackage, int queueSize, String targetURL, boolean isLastUrl) { - - try { - ResponseData responseData = UtilNetworking.createPOSTHttpsURLConnection(targetURL, activityPackage, queueSize); - - IPackageHandler packageHandler = packageHandlerWeakRef.get(); - if (packageHandler == null) { - return true; - } - IActivityHandler activityHandler = activityHandlerWeakRef.get(); - if (activityHandler == null) { - return true; - } - - if (responseData.trackingState == TrackingState.OPTED_OUT) { - activityHandler.gotOptOutResponse(); - return true; - } - - if (responseData.jsonResponse == null) { - packageHandler.closeFirstPackage(responseData, activityPackage); - return true; - } - - packageHandler.sendNextPackage(responseData); - return true; - } catch (UnsupportedEncodingException e) { - sendNextPackageI(activityPackage, "Failed to encode parameters", e); - return true; - } catch (SocketTimeoutException e) { - if (isLastUrl) { - closePackageI(activityPackage, "Request timed out", e); - } - return false; - } catch (IOException e) { - if (isLastUrl) { - closePackageI(activityPackage, "Request failed", e); - } - return false; - } catch (Throwable e) { - sendNextPackageI(activityPackage, "Runtime exception", e); - return true; - } - } - - // close current package because it failed - private void closePackageI(ActivityPackage activityPackage, String message, Throwable throwable) { - final String packageMessage = activityPackage.getFailureMessage(); - final String reasonString = Util.getReasonString(message, throwable); - String finalMessage = Util.formatString("%s. (%s) Will retry later", packageMessage, reasonString); - logger.error(finalMessage); - - ResponseData responseData = ResponseData.buildResponseData(activityPackage); - responseData.message = finalMessage; - - IPackageHandler packageHandler = packageHandlerWeakRef.get(); - if (packageHandler == null) { - return; - } - - packageHandler.closeFirstPackage(responseData, activityPackage); - } - - // send next package because the current package failed - private void sendNextPackageI(ActivityPackage activityPackage, String message, Throwable throwable) { - final String failureMessage = activityPackage.getFailureMessage(); - final String reasonString = Util.getReasonString(message, throwable); - String finalMessage = Util.formatString("%s. (%s)", failureMessage, reasonString); - logger.error(finalMessage); - - ResponseData responseData = ResponseData.buildResponseData(activityPackage); - responseData.message = finalMessage; - - IPackageHandler packageHandler = packageHandlerWeakRef.get(); - if (packageHandler == null) { - return; - } - - packageHandler.sendNextPackage(responseData); - } -} diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/ResponseData.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/ResponseData.java index 2eed109b..63dc6521 100644 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/ResponseData.java +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/ResponseData.java @@ -2,6 +2,8 @@ import org.json.JSONObject; +import java.util.Map; + /** * Adjust SDK * Created by Pedro Silva (@nonelse) on 3rd December 2015. @@ -17,10 +19,22 @@ public class ResponseData { public ActivityKind activityKind; public TrackingState trackingState; public AdjustAttribution attribution; + public Long askIn; + public Long retryIn; + public Long continueIn; + + public ActivityPackage activityPackage; + public Map sendingParameters; - protected ResponseData() {} + protected ResponseData() { + success = false; + willRetry = false; + } - public static ResponseData buildResponseData(ActivityPackage activityPackage) { + public static ResponseData buildResponseData( + ActivityPackage activityPackage, + Map sendingParameters) + { ResponseData responseData; ActivityKind activityKind = activityPackage.getActivityKind(); switch (activityKind) { @@ -41,6 +55,8 @@ public static ResponseData buildResponseData(ActivityPackage activityPackage) { break; } responseData.activityKind = activityKind; + responseData.activityPackage = activityPackage; + responseData.sendingParameters = sendingParameters; return responseData; } diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/SdkClickHandler.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/SdkClickHandler.java index 4e58d283..19ec264c 100644 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/SdkClickHandler.java +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/SdkClickHandler.java @@ -1,17 +1,17 @@ package com.adjust.sdk; +import com.adjust.sdk.network.IActivityPackageSender; import com.adjust.sdk.scheduler.SingleThreadCachedScheduler; import com.adjust.sdk.scheduler.ThreadScheduler; import org.json.JSONArray; import org.json.JSONException; -import java.io.IOException; -import java.io.UnsupportedEncodingException; import java.lang.ref.WeakReference; -import java.net.SocketTimeoutException; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * SdkClickHandler class. @@ -56,11 +56,6 @@ public class SdkClickHandler implements ISdkClickHandler { */ private BackoffStrategy backoffStrategy; - /** - * Base path. - */ - private String basePath; - /** * Sending queue. */ @@ -76,15 +71,20 @@ public class SdkClickHandler implements ISdkClickHandler { */ private WeakReference activityHandlerWeakRef; + private IActivityPackageSender activityPackageSender; + /** * SdkClickHandler constructor. * * @param activityHandler ActivityHandler reference * @param startsSending Is sending paused? */ - public SdkClickHandler(final IActivityHandler activityHandler, final boolean startsSending) { - init(activityHandler, startsSending); - + public SdkClickHandler(final IActivityHandler activityHandler, + final boolean startsSending, + final IActivityPackageSender sdkClickHandlerActivityPackageSender) + { + init(activityHandler, startsSending, sdkClickHandlerActivityPackageSender); + this. logger = AdjustFactory.getLogger(); backoffStrategy = AdjustFactory.getSdkClickBackoffStrategy(); scheduler = new SingleThreadCachedScheduler("SdkClickHandler"); @@ -94,11 +94,13 @@ public SdkClickHandler(final IActivityHandler activityHandler, final boolean sta * {@inheritDoc} */ @Override - public void init(final IActivityHandler activityHandler, final boolean startsSending) { + public void init(final IActivityHandler activityHandler, + final boolean startsSending, + final IActivityPackageSender sdkClickHandlerActivityPackageSender) { paused = !startsSending; packageQueue = new ArrayList(); activityHandlerWeakRef = new WeakReference(activityHandler); - basePath = activityHandler.getBasePath(); + activityPackageSender = sdkClickHandlerActivityPackageSender; } /** @@ -281,20 +283,7 @@ private void sendNextSdkClickI() { Runnable runnable = new Runnable() { @Override public void run() { - List urls = UrlFactory.getBaseUrls(); - boolean requestProcessed = false; - for (int i=0; i 0) { - UrlFactory.prioritiseBaseUrl(urls.get(i)); - } - } + sendSdkClickI(sdkClickPackage); sendNextSdkClick(); } }; @@ -317,10 +306,8 @@ public void run() { * Send sdk_click package passed as the parameter (runs within scheduled executor). * * @param sdkClickPackage sdk_click package to be sent. - * @param targetURL end point where sdk_click package to be sent. - * @param isLastUrl indicates if it is last in the fallback. */ - private boolean sendSdkClickI(final ActivityPackage sdkClickPackage, final String targetURL, final boolean isLastUrl) { + private void sendSdkClickI(final ActivityPackage sdkClickPackage) { IActivityHandler activityHandler = activityHandlerWeakRef.get(); String source = sdkClickPackage.getParameters().get("source"); boolean isReftag = source != null && source.equals(SOURCE_REFTAG); @@ -336,7 +323,7 @@ private boolean sendSdkClickI(final ActivityPackage sdkClickPackage, final Strin sdkClickPackage.getClickTimeInMilliseconds()); if (rawReferrer == null) { - return true; + return; } } @@ -366,83 +353,87 @@ private boolean sendSdkClickI(final ActivityPackage sdkClickPackage, final Strin boolean isPreinstall = source != null && source.equals(Constants.PREINSTALL); - try { - SdkClickResponseData responseData = (SdkClickResponseData) UtilNetworking.createPOSTHttpsURLConnection( - targetURL, - sdkClickPackage, - packageQueue.size() - 1); + Map sendingParameters = generateSendingParametersI(); - if (responseData.jsonResponse == null) { - retrySendingI(sdkClickPackage); - return true; - } + ResponseData responseData = activityPackageSender.sendActivityPackageSync( + sdkClickPackage, + sendingParameters); - if (activityHandler == null) { - return true; - } + if (!(responseData instanceof SdkClickResponseData)) { + return; + } - if (responseData.trackingState == TrackingState.OPTED_OUT) { - activityHandler.gotOptOutResponse(); - return true; - } + SdkClickResponseData sdkClickResponseData = (SdkClickResponseData)responseData; - if (isReftag) { - // Remove referrer from shared preferences after sdk_click is sent. - SharedPreferencesManager sharedPreferencesManager - = new SharedPreferencesManager(activityHandler.getContext()); + if (sdkClickResponseData.willRetry) { + retrySendingI(sdkClickPackage); + return; + } - sharedPreferencesManager.removeRawReferrer( - rawReferrerString, - sdkClickPackage.getClickTimeInMilliseconds()); - } + if (activityHandler == null) { + return; + } - if (isInstallReferrer) { - // After successfully sending install referrer, store sent values in activity state. - responseData.clickTime = clickTime; - responseData.installBegin = installBegin; - responseData.installReferrer = installReferrer; - responseData.clickTimeServer = clickTimeServer; - responseData.installBeginServer = installBeginServer; - responseData.installVersion = installVersion; - responseData.googlePlayInstant = googlePlayInstant; - responseData.referrerApi = referrerApi; - responseData.isInstallReferrer = true; - } + if (sdkClickResponseData.trackingState == TrackingState.OPTED_OUT) { + activityHandler.gotOptOutResponse(); + return; + } + + if (isReftag) { + // Remove referrer from shared preferences after sdk_click is sent. + SharedPreferencesManager sharedPreferencesManager + = new SharedPreferencesManager(activityHandler.getContext()); - if (isPreinstall) { - String payloadLocation = sdkClickPackage.getParameters().get("found_location"); - if (payloadLocation != null && !payloadLocation.isEmpty()) { - // update preinstall flag in shared preferences after sdk_click is sent. - SharedPreferencesManager sharedPreferencesManager - = new SharedPreferencesManager(activityHandler.getContext()); + sharedPreferencesManager.removeRawReferrer( + rawReferrerString, + sdkClickPackage.getClickTimeInMilliseconds()); + } + + if (isInstallReferrer) { + // After successfully sending install referrer, store sent values in activity state. + sdkClickResponseData.clickTime = clickTime; + sdkClickResponseData.installBegin = installBegin; + sdkClickResponseData.installReferrer = installReferrer; + sdkClickResponseData.clickTimeServer = clickTimeServer; + sdkClickResponseData.installBeginServer = installBeginServer; + sdkClickResponseData.installVersion = installVersion; + sdkClickResponseData.googlePlayInstant = googlePlayInstant; + sdkClickResponseData.referrerApi = referrerApi; + sdkClickResponseData.isInstallReferrer = true; + } + if (isPreinstall) { + String payloadLocation = sdkClickPackage.getParameters().get("found_location"); + if (payloadLocation != null && !payloadLocation.isEmpty()) { + // update preinstall flag in shared preferences after sdk_click is sent. + SharedPreferencesManager sharedPreferencesManager + = new SharedPreferencesManager(activityHandler.getContext()); + + if (Constants.SYSTEM_INSTALLER_REFERRER.equalsIgnoreCase(payloadLocation)) { + sharedPreferencesManager.removePreinstallReferrer(); + } else { long currentStatus = sharedPreferencesManager.getPreinstallPayloadReadStatus(); long updatedStatus = PreinstallUtil.markAsRead(payloadLocation, currentStatus); sharedPreferencesManager.setPreinstallPayloadReadStatus(updatedStatus); } } + } - activityHandler.finishedTrackingActivity(responseData); - return true; - } catch (UnsupportedEncodingException e) { - logErrorMessageI(sdkClickPackage, "Sdk_click failed to encode parameters", e); - return true; - } catch (SocketTimeoutException e) { - logErrorMessageI(sdkClickPackage, "Sdk_click request timed out. Will retry later", e); - if (isLastUrl) { - retrySendingI(sdkClickPackage); - } - return false; - } catch (IOException e) { - logErrorMessageI(sdkClickPackage, "Sdk_click request failed. Will retry later", e); - if (isLastUrl) { - retrySendingI(sdkClickPackage); - } - return false; - } catch (Throwable e) { - logErrorMessageI(sdkClickPackage, "Sdk_click runtime exception", e); - return true; + activityHandler.finishedTrackingActivity(sdkClickResponseData); + } + private Map generateSendingParametersI() { + HashMap sendingParameters = new HashMap<>(); + + long now = System.currentTimeMillis(); + String dateString = Util.dateFormatter.format(now); + + PackageBuilder.addString(sendingParameters, "sent_at", dateString); + + int queueSize = packageQueue.size() - 1; + if (queueSize > 0) { + PackageBuilder.addLong(sendingParameters, "queue_size", queueSize); } + return sendingParameters; } /** diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/SessionParameters.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/SessionParameters.java index 52f9f7be..aa5abc89 100644 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/SessionParameters.java +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/SessionParameters.java @@ -1,10 +1,5 @@ package com.adjust.sdk; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.ObjectStreamField; -import java.io.Serializable; import java.util.HashMap; import java.util.Map; diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/SharedPreferencesManager.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/SharedPreferencesManager.java index aa800e6e..f4c64cce 100644 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/SharedPreferencesManager.java +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/SharedPreferencesManager.java @@ -37,13 +37,18 @@ public class SharedPreferencesManager { private static final String PREFS_KEY_GDPR_FORGET_ME = "gdpr_forget_me"; - private static final String PREFS_KEY_DISABLE_THIRD_PARTY_SHARING = "disable_third_party_sharing"; + private static final String PREFS_KEY_DISABLE_THIRD_PARTY_SHARING + = "disable_third_party_sharing"; private static final String PREFS_KEY_DEEPLINK_URL = "deeplink_url"; private static final String PREFS_KEY_DEEPLINK_CLICK_TIME = "deeplink_click_time"; - private static final String PREFS_KEY_PREINSTALL_PAYLOAD_READ_STATUS = "preinstall_payload_read_status"; + private static final String PREFS_KEY_PREINSTALL_PAYLOAD_READ_STATUS + = "preinstall_payload_read_status"; + + private static final String PREFS_KEY_PREINSTALL_SYSTEM_INSTALLER_REFERRER + = "preinstall_system_installer_referrer"; /** * Index for raw referrer string content in saved JSONArray object. @@ -210,6 +215,33 @@ public synchronized JSONArray getRawReferrerArray() { return new JSONArray(); } + /** + * Save preinstall referrer string into shared preferences. + * + * @param referrer Preinstall referrer string + */ + public synchronized void savePreinstallReferrer(final String referrer) { + saveString(PREFS_KEY_PREINSTALL_SYSTEM_INSTALLER_REFERRER, referrer); + } + + /** + * Get saved preinstall referrer string from shared preferences. + * + * @return referrer Preinstall referrer string + */ + public synchronized String getPreinstallReferrer() { + return getString(PREFS_KEY_PREINSTALL_SYSTEM_INSTALLER_REFERRER); + } + + /** + * Remove saved preinstall referrer string from shared preferences. + * + * @return referrer Preinstall referrer string + */ + public synchronized void removePreinstallReferrer() { + remove(PREFS_KEY_PREINSTALL_SYSTEM_INSTALLER_REFERRER); + } + /** * Initially called upon ActivityHandler initialisation. * Used to check if any of the still existing referrers was unsuccessfully being sent before app got killed. diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/UrlFactory.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/UrlFactory.java deleted file mode 100644 index 5c47f22a..00000000 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/UrlFactory.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.adjust.sdk; - -import java.util.ArrayList; -import java.util.List; - -public class UrlFactory { - - private static List baseUrls = null; - private static List gdprUrls = null; - private static List subscriptionUrls = null; - - public static List getBaseUrls() { - if (baseUrls == null) { - baseUrls = new ArrayList(); - baseUrls.add(AdjustFactory.getBaseUrl()); - baseUrls.addAll(AdjustFactory.getFallbackBaseUrls()); - } - - return baseUrls; - } - - public static List getGdprUrls() { - if (gdprUrls == null) { - gdprUrls = new ArrayList(); - gdprUrls.add(AdjustFactory.getGdprUrl()); - gdprUrls.addAll(AdjustFactory.getFallbackGdprUrls()); - } - - return gdprUrls; - } - - public static List getSubscriptionUrls() { - if (subscriptionUrls == null) { - subscriptionUrls = new ArrayList(); - subscriptionUrls.add(AdjustFactory.getSubscriptionUrl()); - subscriptionUrls.addAll(AdjustFactory.getFallbackSubscriptionUrls()); - } - - return subscriptionUrls; - } - - public synchronized static void prioritiseBaseUrl(String baseUrl) { - if (baseUrls.indexOf(baseUrl) != 0) { - baseUrls.remove(baseUrl); - baseUrls.add(0, baseUrl); - } - } - - public synchronized static void prioritiseGdprUrl(String gdprUrl) { - if (gdprUrls.indexOf(gdprUrl) != 0) { - gdprUrls.remove(gdprUrl); - gdprUrls.add(0, gdprUrl); - } - } - - public synchronized static void prioritiseSubscriptionUrl(String subscriptionUrl) { - if (subscriptionUrls.indexOf(subscriptionUrl) != 0) { - subscriptionUrls.remove(subscriptionUrl); - subscriptionUrls.add(0, subscriptionUrl); - } - } -} diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/UtilNetworking.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/UtilNetworking.java deleted file mode 100644 index 54dd3ead..00000000 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/UtilNetworking.java +++ /dev/null @@ -1,465 +0,0 @@ -package com.adjust.sdk; - -import android.net.Uri; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.BufferedReader; -import java.io.DataOutputStream; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLEncoder; -import java.util.HashMap; -import java.util.Map; - -import javax.net.ssl.HttpsURLConnection; - -/** - * Created by uerceg on 03/04/2017. - */ - -public class UtilNetworking { - private static String userAgent; - - private static ILogger getLogger() { - return AdjustFactory.getLogger(); - } - - public static void setUserAgent(String userAgent) { - UtilNetworking.userAgent = userAgent; - } - - public static ResponseData createPOSTHttpsURLConnection(String urlString, ActivityPackage activityPackage, int queueSize) throws Exception { - DataOutputStream wr = null; - - try { - URL url = new URL(urlString); - HttpsURLConnection connection = AdjustFactory.getHttpsURLConnection(url); - Map parameters = new HashMap(activityPackage.getParameters()); - - IConnectionOptions connectionOptions = AdjustFactory.getConnectionOptions(); - connectionOptions.applyConnectionOptions(connection, activityPackage.getClientSdk()); - - extractEventCallbackId(parameters); - - String authorizationHeader = buildAuthorizationHeader(parameters, - activityPackage.getActivityKind().toString()); - if (authorizationHeader != null) { - connection.setRequestProperty("Authorization", authorizationHeader); - } - - connection.setRequestMethod("POST"); - connection.setUseCaches(false); - connection.setDoInput(true); - connection.setDoOutput(true); - - wr = new DataOutputStream(connection.getOutputStream()); - wr.writeBytes(getPostDataString(parameters, queueSize)); - - ResponseData responseData = readHttpResponse(connection, activityPackage); - - return responseData; - } catch (Exception e) { - throw e; - } finally { - try { - if (wr != null) { - wr.flush(); - wr.close(); - } - } catch (Exception e) {} - } - } - - public static ResponseData createGETHttpsURLConnection(ActivityPackage activityPackage, String basePath, String baseUrl) throws Exception { - try { - Map parameters = new HashMap(activityPackage.getParameters()); - - extractEventCallbackId(parameters); - - Uri uri = buildUri(activityPackage.getPath(), parameters, basePath, baseUrl); - - URL url = new URL(uri.toString()); - HttpsURLConnection connection = AdjustFactory.getHttpsURLConnection(url); - - IConnectionOptions connectionOptions = AdjustFactory.getConnectionOptions(); - connectionOptions.applyConnectionOptions(connection, activityPackage.getClientSdk()); - - String authorizationHeader = buildAuthorizationHeader(parameters, activityPackage.getActivityKind().toString()); - if (authorizationHeader != null) { - connection.setRequestProperty("Authorization", authorizationHeader); - } - - connection.setRequestMethod("GET"); - - ResponseData responseData = readHttpResponse(connection, activityPackage); - - return responseData; - } catch (Exception e) { - throw e; - } - } - - private static ResponseData readHttpResponse(HttpsURLConnection connection, ActivityPackage activityPackage) throws Exception { - StringBuffer sb = new StringBuffer(); - ILogger logger = getLogger(); - Integer responseCode = null; - - ResponseData responseData = ResponseData.buildResponseData(activityPackage); - - try { - connection.connect(); - - responseCode = connection.getResponseCode(); - InputStream inputStream; - - if (responseCode >= 400) { - inputStream = connection.getErrorStream(); - } else { - inputStream = connection.getInputStream(); - } - - InputStreamReader inputStreamReader = new InputStreamReader(inputStream); - BufferedReader bufferedReader = new BufferedReader(inputStreamReader); - - String line; - - while ((line = bufferedReader.readLine()) != null) { - sb.append(line); - } - } - catch (Exception e) { - logger.error("Failed to read response. (%s)", e.getMessage()); - throw e; - } finally { - if (connection != null) { - connection.disconnect(); - } - } - - String stringResponse = sb.toString(); - logger.verbose("Response: %s", stringResponse); - - if (responseCode == 429) { - logger.error("Too frequent requests to the endpoint (429)"); - return responseData; - } - if (stringResponse == null || stringResponse.length() == 0) { - return responseData; - } - - JSONObject jsonResponse = null; - - try { - jsonResponse = new JSONObject(stringResponse); - } catch (JSONException e) { - String message = Util.formatString("Failed to parse json response. (%s)", e.getMessage()); - logger.error(message); - responseData.message = message; - } - - if (jsonResponse == null) { - return responseData; - } - - responseData.jsonResponse = jsonResponse; - - String message = jsonResponse.optString("message", null); - responseData.message = message; - responseData.timestamp = jsonResponse.optString("timestamp", null); - responseData.adid = jsonResponse.optString("adid", null); - String trackingState = jsonResponse.optString("tracking_state", null); - if (trackingState != null) { - if (trackingState.equals("opted_out")) { - responseData.trackingState = TrackingState.OPTED_OUT; - } - } - - if (message == null) { - message = "No message found"; - } - - if (responseCode != null && responseCode == HttpsURLConnection.HTTP_OK) { - logger.info("%s", message); - responseData.success = true; - } else { - logger.error("%s", message); - } - - return responseData; - } - - private static String getPostDataString(Map body, int queueSize) throws UnsupportedEncodingException { - StringBuilder result = new StringBuilder(); - - for (Map.Entry entry : body.entrySet()) { - String encodedName = URLEncoder.encode(entry.getKey(), Constants.ENCODING); - String value = entry.getValue(); - String encodedValue = value != null ? URLEncoder.encode(value, Constants.ENCODING) : ""; - - if (result.length() > 0) { - result.append("&"); - } - - result.append(encodedName); - result.append("="); - result.append(encodedValue); - } - - long now = System.currentTimeMillis(); - String dateString = Util.dateFormatter.format(now); - - result.append("&"); - result.append(URLEncoder.encode("sent_at", Constants.ENCODING)); - result.append("="); - result.append(URLEncoder.encode(dateString, Constants.ENCODING)); - - if (queueSize > 0) { - result.append("&"); - result.append(URLEncoder.encode("queue_size", Constants.ENCODING)); - result.append("="); - result.append(URLEncoder.encode("" + queueSize, Constants.ENCODING)); - } - - return result.toString(); - } - - private static Uri buildUri(String path, Map parameters, String basePath, String url) { - Uri.Builder uriBuilder = new Uri.Builder(); - - String scheme = Constants.SCHEME; - String authority = Constants.AUTHORITY; - String initialPath = ""; - - try { - if (basePath != null) { - url += basePath; - } - URL baseUrl = new URL(url); - scheme = baseUrl.getProtocol(); - authority = baseUrl.getAuthority(); - initialPath = baseUrl.getPath(); - } catch (MalformedURLException e) { - getLogger().error("Unable to parse endpoint (%s)", e.getMessage()); - } - - uriBuilder.scheme(scheme); - uriBuilder.encodedAuthority(authority); - uriBuilder.path(initialPath); - uriBuilder.appendPath(path); - - for (Map.Entry entry : parameters.entrySet()) { - uriBuilder.appendQueryParameter(entry.getKey(), entry.getValue()); - } - - long now = System.currentTimeMillis(); - String dateString = Util.dateFormatter.format(now); - - uriBuilder.appendQueryParameter("sent_at", dateString); - - return uriBuilder.build(); - } - - private static String extractAppSecret(final Map parameters) { - return parameters.remove("app_secret"); - } - - private static String extractSecretId(final Map parameters) { - return parameters.remove("secret_id"); - } - - private static String extractSignature(final Map parameters) { - return parameters.remove("signature"); - } - - private static String extractAlgorithm(final Map parameters) { - return parameters.remove("algorithm"); - } - - private static String extractNativeVersion(final Map parameters) { - return parameters.remove("native_version"); - } - - private static String extractHeadersId(final Map parameters) { - return parameters.remove("headers_id"); - } - - private static void extractEventCallbackId(final Map parameters) { - parameters.remove("event_callback_id"); - } - - private static String buildAuthorizationHeader(final Map parameters, - final String activityKind) { - String secretId = extractSecretId(parameters); - String headersId = extractHeadersId(parameters); - String signature = extractSignature(parameters); - String algorithm = extractAlgorithm(parameters); - String nativeVersion = extractNativeVersion(parameters); - - String authorizationHeader = buildAuthorizationHeaderV2(signature, secretId, - headersId, algorithm, nativeVersion); - if (authorizationHeader != null) { - return authorizationHeader; - } - - String appSecret = extractAppSecret(parameters); - return buildAuthorizationHeaderV1(parameters, appSecret, secretId, activityKind); - } - - private static String buildAuthorizationHeaderV1(final Map parameters, - final String appSecret, - final String secretId, - final String activityKind) { - // check if the secret exists and it's not empty - if (appSecret == null || appSecret.length() == 0) { - return null; - } - String appSecretName = "app_secret"; - - Map signatureDetails = getSignature(parameters, activityKind, appSecret); - - String algorithm = "sha256"; - String signature = Util.sha256(signatureDetails.get("clear_signature")); - String fields = signatureDetails.get("fields"); - - String secretIdHeader = Util.formatString("secret_id=\"%s\"", secretId); - String signatureHeader = Util.formatString("signature=\"%s\"", signature); - String algorithmHeader = Util.formatString("algorithm=\"%s\"", algorithm); - String fieldsHeader = Util.formatString("headers=\"%s\"", fields); - - String authorizationHeader = Util.formatString("Signature %s,%s,%s,%s", - secretIdHeader, signatureHeader, algorithmHeader, fieldsHeader); - getLogger().verbose("authorizationHeader: %s", authorizationHeader); - - return authorizationHeader; - } - - private static String buildAuthorizationHeaderV2(final String signature, - final String secretId, - final String headersId, - final String algorithm, - final String nativeVersion) { - if (secretId == null || signature == null || headersId == null) { - return null; - } - - String signatureHeader = Util.formatString("signature=\"%s\"", signature); - String secretIdHeader = Util.formatString("secret_id=\"%s\"", secretId); - String idHeader = Util.formatString("headers_id=\"%s\"", headersId); - String algorithmHeader = Util.formatString("algorithm=\"%s\"", algorithm != null ? algorithm : "adj1"); - String nativeVersionHeader = Util.formatString("native_version=\"%s\"", nativeVersion != null ? nativeVersion : ""); - - String authorizationHeader = Util.formatString("Signature %s,%s,%s,%s,%s", - signatureHeader, secretIdHeader, algorithmHeader, idHeader, nativeVersionHeader); - - getLogger().verbose("authorizationHeader: %s", authorizationHeader); - - return authorizationHeader; - } - - private static Map getSignature(final Map parameters, - final String activityKind, - final String appSecret) - { - String activityKindName = "activity_kind"; - String activityKindValue = activityKind; - - String createdAtName = "created_at"; - String createdAt = parameters.get(createdAtName); - - String deviceIdentifierName = getValidIdentifier(parameters); - String deviceIdentifier = parameters.get(deviceIdentifierName); - - String sourceName = "source"; - String sourceValue = parameters.get(sourceName); - - String payloadName = "payload"; - String payloadValue = parameters.get(payloadName); - - Map signatureParams = new HashMap(); - - signatureParams.put("app_secret", appSecret); - signatureParams.put(createdAtName, createdAt); - signatureParams.put(activityKindName, activityKindValue); - signatureParams.put(deviceIdentifierName, deviceIdentifier); - - if (sourceValue != null) { - signatureParams.put(sourceName, sourceValue); - } - - if (payloadValue != null) { - signatureParams.put(payloadName, payloadValue); - } - - String fields = ""; - String clearSignature = ""; - - for (Map.Entry entry : signatureParams.entrySet()) { - if (entry.getValue() != null) { - fields += entry.getKey() + " "; - clearSignature += entry.getValue(); - } - } - - // Remove last empty space. - fields = fields.substring(0, fields.length() - 1); - - HashMap signature = new HashMap(); - - signature.put("clear_signature", clearSignature); - signature.put("fields", fields); - - return signature; - } - - private static String getValidIdentifier(final Map parameters) { - String googleAdIdName = "gps_adid"; - String fireAdIdName = "fire_adid"; - String androidIdName = "android_id"; - String macSha1Name = "mac_sha1"; - String macMd5Name = "mac_md5"; - String androidUUIDName= "android_uuid"; - - if (parameters.get(googleAdIdName) != null) { - return googleAdIdName; - } - if (parameters.get(fireAdIdName) != null) { - return fireAdIdName; - } - if (parameters.get(androidIdName) != null) { - return androidIdName; - } - if (parameters.get(macSha1Name) != null) { - return macSha1Name; - } - if (parameters.get(macMd5Name) != null) { - return macMd5Name; - } - if (parameters.get(androidUUIDName) != null) { - return androidUUIDName; - } - - return null; - } - - public interface IConnectionOptions { - void applyConnectionOptions(HttpsURLConnection connection, String clientSdk); - } - - static class ConnectionOptions implements IConnectionOptions { - @Override - public void applyConnectionOptions(HttpsURLConnection connection, String clientSdk) { - connection.setRequestProperty("Client-SDK", clientSdk); - connection.setConnectTimeout(Constants.ONE_MINUTE); - connection.setReadTimeout(Constants.ONE_MINUTE); - - if (userAgent != null) { - connection.setRequestProperty("User-Agent", userAgent); - } - } - } -} diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/network/ActivityPackageSender.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/network/ActivityPackageSender.java new file mode 100644 index 00000000..240ac935 --- /dev/null +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/network/ActivityPackageSender.java @@ -0,0 +1,698 @@ +package com.adjust.sdk.network; + +import android.net.Uri; + +import com.adjust.sdk.ActivityKind; +import com.adjust.sdk.ActivityPackage; +import com.adjust.sdk.AdjustAttribution; +import com.adjust.sdk.AdjustFactory; +import com.adjust.sdk.Constants; +import com.adjust.sdk.ILogger; +import com.adjust.sdk.ResponseData; +import com.adjust.sdk.TrackingState; +import com.adjust.sdk.Util; +import com.adjust.sdk.scheduler.SingleThreadCachedScheduler; +import com.adjust.sdk.scheduler.ThreadExecutor; +import com.adjust.sdk.network.UtilNetworking.IHttpsURLConnectionProvider; +import com.adjust.sdk.network.UtilNetworking.IConnectionOptions; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.net.ProtocolException; +import java.net.SocketTimeoutException; +import java.net.URL; +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.Map; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLHandshakeException; + +public class ActivityPackageSender implements IActivityPackageSender { + private String basePath; + private String gdprPath; + private String subscriptionPath; + private String clientSdk; + + private ILogger logger; + private ThreadExecutor executor; + private UrlStrategy urlStrategy; + private IHttpsURLConnectionProvider httpsURLConnectionProvider; + private IConnectionOptions connectionOptions; + + public ActivityPackageSender(final String adjustUrlStrategy, + final String basePath, + final String gdprPath, + final String subscriptionPath, + final String clientSdk) + { + this.basePath = basePath; + this.gdprPath = gdprPath; + this.subscriptionPath = subscriptionPath; + this.clientSdk = clientSdk; + + logger = AdjustFactory.getLogger(); + + executor = new SingleThreadCachedScheduler("ActivityPackageSender"); + + urlStrategy = new UrlStrategy( + AdjustFactory.getBaseUrl(), + AdjustFactory.getGdprUrl(), + AdjustFactory.getSubscriptionUrl(), + adjustUrlStrategy); + + httpsURLConnectionProvider = AdjustFactory.getHttpsURLConnectionProvider(); + + connectionOptions = AdjustFactory.getConnectionOptions(); + } + + @Override + public void sendActivityPackage(final ActivityPackage activityPackage, + final Map sendingParameters, + final ResponseDataCallbackSubscriber responseCallback) + { + executor.submit(new Runnable() { + @Override + public void run() { + responseCallback.onResponseDataCallback( + sendActivityPackageSync(activityPackage, sendingParameters)); + } + }); + } + + @Override + public ResponseData sendActivityPackageSync(final ActivityPackage activityPackage, + final Map sendingParameters) + { + boolean retryToSend; + ResponseData responseData; + do { + responseData = + ResponseData.buildResponseData(activityPackage, sendingParameters); + + tryToGetResponse(responseData); + + retryToSend = shouldRetryToSend(responseData); + } while (retryToSend); + + return responseData; + } + + private boolean shouldRetryToSend(final ResponseData responseData) { + if (!responseData.willRetry) { + logger.debug("Will not retry with current url strategy"); + urlStrategy.resetAfterSuccess(); + return false; + } + + if (urlStrategy.shouldRetryAfterFailure(responseData.activityKind)) { + logger.error("Failed with current url strategy, but it will retry with new"); + return true; + } else { + logger.error("Failed with current url strategy and it will not retry"); + // Stop retrying with different type and return to caller + return false; + } + } + + private void tryToGetResponse(final ResponseData responseData) { + DataOutputStream dataOutputStream = null; + + try { + ActivityPackage activityPackage = responseData.activityPackage; + Map sendingParameters = responseData.sendingParameters; + + boolean shouldUseGET = + responseData.activityPackage.getActivityKind() == ActivityKind.ATTRIBUTION; + final String urlString; + if (shouldUseGET) { + extractEventCallbackId(activityPackage.getParameters()); + + urlString = generateUrlStringForGET(activityPackage, sendingParameters); + } else { + urlString = generateUrlStringForPOST(activityPackage); + } + + final URL url = new URL(urlString); + final HttpsURLConnection connection = + httpsURLConnectionProvider.generateHttpsURLConnection(url); + + // get and apply connection options (default or for tests) + connectionOptions.applyConnectionOptions(connection, activityPackage.getClientSdk()); + + String authorizationHeader = buildAuthorizationHeader(activityPackage); + if (authorizationHeader != null) { + connection.setRequestProperty("Authorization", authorizationHeader); + } + + if (shouldUseGET) { + dataOutputStream = configConnectionForGET(connection); + } else { + extractEventCallbackId(activityPackage.getParameters()); + + dataOutputStream = + configConnectionForPOST(connection, activityPackage, sendingParameters); + } + + // read connection response + Integer responseCode = readConnectionResponse(connection, responseData); + + responseData.success = + responseData.jsonResponse != null + && responseData.retryIn == null + && responseCode != null + && responseCode.intValue() == HttpsURLConnection.HTTP_OK; + // it is only processed by the server if it contains + // a JSON response *AND* does not contain a retry_in + responseData.willRetry = + responseData.jsonResponse == null || responseData.retryIn != null; + } catch (final UnsupportedEncodingException exception) { + + localError(exception, "Failed to encode parameters", responseData); + + } catch (final MalformedURLException exception) { + + localError(exception, "Malformed URL", responseData); + + } catch (final ProtocolException exception) { + + localError(exception, "Protocol Error", responseData); + + } catch (final SocketTimeoutException exception) { + + // timeout is remote/network related -> did not fail locally + remoteError(exception, "Request timed out", responseData); + + } catch (final SSLHandshakeException exception) { + + // failed due certificate from the server -> did not fail locally + remoteError(exception, "Certificate failed", responseData); + + } catch (final IOException exception) { + + // IO is the network -> did not fail locally + remoteError(exception, "Request failed", responseData); + + } catch (final Throwable t) { + + // not sure if error is local or not -> assume it is local + localError(t, "Sending SDK package", responseData); + + } finally { + try { + if (dataOutputStream != null) { + dataOutputStream.flush(); + dataOutputStream.close(); + } + } catch (final IOException ioException) { + String errorMessage = errorMessage(ioException, + "Flushing and closing connection output stream", + responseData.activityPackage); + logger.error(errorMessage); + } + } + } + + private void localError(Throwable throwable, String description, ResponseData responseData) { + String finalMessage = errorMessage(throwable, description, responseData.activityPackage); + + logger.error(finalMessage); + responseData.message = finalMessage; + + responseData.willRetry = false; + } + + private void remoteError(Throwable throwable, String description, ResponseData responseData) { + String finalMessage = errorMessage(throwable, description, responseData.activityPackage) + + " Will retry later"; + + logger.error(finalMessage); + responseData.message = finalMessage; + + responseData.willRetry = true; + } + + private String errorMessage(final Throwable throwable, + final String description, + final ActivityPackage activityPackage) + { + final String failureMessage = activityPackage.getFailureMessage(); + final String reasonString = Util.getReasonString(description, throwable); + return Util.formatString("%s. (%s)", failureMessage, reasonString); + } + + private String generateUrlStringForGET(final ActivityPackage activityPackage, + final Map sendingParameters) + throws MalformedURLException + { + String targetUrl = urlStrategy.targetUrlByActivityKind(activityPackage.getActivityKind()); + + // extra path, if present, has the format '/X/Y' + String urlWithPath = + urlWithExtraPathByActivityKind(activityPackage.getActivityKind(), targetUrl); + + final URL urlObject = new URL(urlWithPath); + final Uri.Builder uriBuilder = new Uri.Builder(); + uriBuilder.scheme(urlObject.getProtocol()); + uriBuilder.encodedAuthority(urlObject.getAuthority()); + uriBuilder.path(urlObject.getPath()); + uriBuilder.appendPath(activityPackage.getPath()); + + logger.debug("Making request to url: %s", uriBuilder.toString()); + + for (final Map.Entry entry : activityPackage.getParameters().entrySet()) { + uriBuilder.appendQueryParameter(entry.getKey(), entry.getValue()); + } + + if (sendingParameters != null) { + for (final Map.Entry entry: sendingParameters.entrySet()) { + uriBuilder.appendQueryParameter(entry.getKey(), entry.getValue()); + } + } + + return uriBuilder.build().toString(); + } + + private String generateUrlStringForPOST(final ActivityPackage activityPackage) + { + String targetUrl = + urlStrategy.targetUrlByActivityKind(activityPackage.getActivityKind()); + + // extra path, if present, has the format '/X/Y' + String urlWithPath = + urlWithExtraPathByActivityKind(activityPackage.getActivityKind(), targetUrl); + + + // 'targetUrl' does not end with '/', but activity package paths that are sent by POST + // do start with '/', so it's not added om between + String urlString = Util.formatString("%s%s", urlWithPath, activityPackage.getPath()); + + logger.debug("Making request to url : %s", urlString); + + return urlString; + } + + private String urlWithExtraPathByActivityKind(final ActivityKind activityKind, + final String targetUrl) + { + if (activityKind == ActivityKind.GDPR) { + return gdprPath != null ? targetUrl + gdprPath : targetUrl; + } else if (activityKind == ActivityKind.SUBSCRIPTION) { + return subscriptionPath != null ? targetUrl + subscriptionPath : targetUrl; + } else { + return basePath != null ? targetUrl + basePath : targetUrl; + } + } + + private DataOutputStream configConnectionForGET(final HttpsURLConnection connection) + throws ProtocolException + { + // set default GET configuration options + connection.setRequestMethod("GET"); + + return null; + } + + private DataOutputStream configConnectionForPOST(final HttpsURLConnection connection, + final ActivityPackage activityPackage, + final Map sendingParameters) + throws ProtocolException, + UnsupportedEncodingException, + IOException + { + // set default POST configuration options + connection.setRequestMethod("POST"); + // don't allow caching, that is controlled by retrying mechanisms + connection.setUseCaches(false); + // necessary to read the response + connection.setDoInput(true); + // necessary to pass the body to the connection + connection.setDoOutput(true); + + // build POST body + final String postBodyString = generatePOSTBodyString( + activityPackage.getParameters(), + sendingParameters); + + if (postBodyString == null) { + return null; + } + + // write POST body to connection + final DataOutputStream dataOutputStream = + new DataOutputStream(connection.getOutputStream()); + dataOutputStream.writeBytes(postBodyString); + + return dataOutputStream; + } + + private String generatePOSTBodyString(final Map parameters, + final Map sendingParameters) + throws UnsupportedEncodingException + { + if (parameters.isEmpty()) { + return null; + } + + final StringBuilder postStringBuilder = new StringBuilder(); + + injectParametersToPOSTStringBuilder(parameters, postStringBuilder); + injectParametersToPOSTStringBuilder(sendingParameters, postStringBuilder); + + // delete last added & + if (postStringBuilder.length() > 0 + && postStringBuilder.charAt(postStringBuilder.length() - 1) == '&') + { + postStringBuilder.deleteCharAt(postStringBuilder.length() - 1); + } + return postStringBuilder.toString(); + } + + private void injectParametersToPOSTStringBuilder( + final Map parametersToInject, + final StringBuilder postStringBuilder) + throws UnsupportedEncodingException + { + if (parametersToInject == null || parametersToInject.isEmpty()) { + return; + } + + for (final Map.Entry entry : parametersToInject.entrySet()) { + final String encodedName = URLEncoder.encode(entry.getKey(), Constants.ENCODING); + final String value = entry.getValue(); + final String encodedValue = value != null + ? URLEncoder.encode(value, Constants.ENCODING) : ""; + postStringBuilder.append(encodedName); + postStringBuilder.append("="); + postStringBuilder.append(encodedValue); + postStringBuilder.append("&"); + } + } + + Integer readConnectionResponse(final HttpsURLConnection connection, + final ResponseData responseData) + { + final StringBuilder responseStringBuilder = new StringBuilder(); + Integer responseCode = null; + + // connect and read response to string builder + try { + connection.connect(); + + responseCode = connection.getResponseCode(); + final InputStream inputStream; + + if (responseCode.intValue() >= Constants.MINIMAL_ERROR_STATUS_CODE) { + inputStream = connection.getErrorStream(); + } else { + inputStream = connection.getInputStream(); + } + + final InputStreamReader inputStreamReader = new InputStreamReader(inputStream); + final BufferedReader bufferedReader = new BufferedReader(inputStreamReader); + + String line; + while ((line = bufferedReader.readLine()) != null) { + responseStringBuilder.append(line); + } + } catch (final IOException ioException) { + String errorMessage = errorMessage(ioException, + "Connecting and reading response", + responseData.activityPackage); + logger.error(errorMessage); + } finally { + if (connection != null) { + connection.disconnect(); + } + } + + if (responseStringBuilder.length() == 0) { + logger.error( "Empty response string buffer"); + return responseCode; + } + + if (responseCode == 429) { + logger.error("Too frequent requests to the endpoint (429)"); + return responseCode; + } + + // extract response string from string builder + final String responseString = responseStringBuilder.toString(); + logger.debug("Response string: %s", responseString); + + parseResponse(responseData, responseString); + + final String responseMessage = responseData.message; + if (responseMessage == null) { + return responseCode; + } + + // log response message + if (responseCode != null && responseCode.intValue() == HttpsURLConnection.HTTP_OK) { + logger.info("Response message: %s", responseMessage); + } else { + logger.error("Response message: %s", responseMessage); + } + + return responseCode; + } + + private void parseResponse(final ResponseData responseData, final String responseString) { + if (responseString.length() == 0) { + logger.error("Empty response string"); + return; + } + + JSONObject jsonResponse = null; + // convert string response to JSON object + try { + jsonResponse = new JSONObject(responseString); + } catch (final JSONException jsonException) { + String errorMessage = errorMessage(jsonException, + "Failed to parse JSON response", + responseData.activityPackage); + logger.error(errorMessage); + } + + if (jsonResponse == null) { + return; + } + + responseData.jsonResponse = jsonResponse; + + responseData.message = UtilNetworking.extractJsonString(jsonResponse,"message"); + responseData.adid = UtilNetworking.extractJsonString(jsonResponse, "adid"); + responseData.timestamp = UtilNetworking.extractJsonString(jsonResponse, "timestamp"); + String trackingState = + UtilNetworking.extractJsonString(jsonResponse, "tracking_state"); + if (trackingState != null) { + if (trackingState.equals("opted_out")) { + responseData.trackingState = TrackingState.OPTED_OUT; + } + } + + responseData.askIn = UtilNetworking.extractJsonLong(jsonResponse, "ask_in"); + responseData.retryIn = UtilNetworking.extractJsonLong(jsonResponse, "retry_in"); + responseData.continueIn = UtilNetworking.extractJsonLong(jsonResponse, "continue_in"); + + JSONObject attributionJson = jsonResponse.optJSONObject("attribution"); + responseData.attribution = AdjustAttribution.fromJson( + attributionJson, + responseData.adid, + Util.getSdkPrefixPlatform(clientSdk)); + } + + private String buildAuthorizationHeader(final ActivityPackage activityPackage) { + Map parameters = activityPackage.getParameters(); + String activityKindString = activityPackage.getActivityKind().toString(); + + String secretId = extractSecretId(parameters); + String headersId = extractHeadersId(parameters); + String signature = extractSignature(parameters); + String algorithm = extractAlgorithm(parameters); + String nativeVersion = extractNativeVersion(parameters); + + String authorizationHeader = buildAuthorizationHeaderV2(signature, secretId, + headersId, algorithm, nativeVersion); + if (authorizationHeader != null) { + return authorizationHeader; + } + + String appSecret = extractAppSecret(parameters); + return buildAuthorizationHeaderV1(parameters, appSecret, secretId, activityKindString); + } + + private String buildAuthorizationHeaderV1(final Map parameters, + final String appSecret, + final String secretId, + final String activityKindString) + { + // check if the secret exists and it's not empty + if (appSecret == null || appSecret.length() == 0) { + return null; + } + String appSecretName = "app_secret"; + + Map signatureDetails = getSignature(parameters, activityKindString, appSecret); + + String algorithm = "sha256"; + String signature = Util.sha256(signatureDetails.get("clear_signature")); + String fields = signatureDetails.get("fields"); + + String secretIdHeader = Util.formatString("secret_id=\"%s\"", secretId); + String signatureHeader = Util.formatString("signature=\"%s\"", signature); + String algorithmHeader = Util.formatString("algorithm=\"%s\"", algorithm); + String fieldsHeader = Util.formatString("headers=\"%s\"", fields); + + String authorizationHeader = Util.formatString("Signature %s,%s,%s,%s", + secretIdHeader, signatureHeader, algorithmHeader, fieldsHeader); + logger.verbose("authorizationHeader: %s", authorizationHeader); + + return authorizationHeader; + } + + private String buildAuthorizationHeaderV2(final String signature, + final String secretId, + final String headersId, + final String algorithm, + final String nativeVersion) + { + if (secretId == null || signature == null || headersId == null) { + return null; + } + + String signatureHeader = Util.formatString("signature=\"%s\"", signature); + String secretIdHeader = Util.formatString("secret_id=\"%s\"", secretId); + String idHeader = Util.formatString("headers_id=\"%s\"", headersId); + String algorithmHeader = Util.formatString("algorithm=\"%s\"", algorithm != null ? algorithm : "adj1"); + String nativeVersionHeader = Util.formatString("native_version=\"%s\"", nativeVersion != null ? nativeVersion : ""); + + String authorizationHeader = Util.formatString("Signature %s,%s,%s,%s,%s", + signatureHeader, secretIdHeader, algorithmHeader, idHeader, nativeVersionHeader); + + logger.verbose("authorizationHeader: %s", authorizationHeader); + + return authorizationHeader; + } + + private Map getSignature(final Map parameters, + final String activityKindString, + final String appSecret) + { + String activityKindName = "activity_kind"; + String activityKindValue = activityKindString; + + String createdAtName = "created_at"; + String createdAt = parameters.get(createdAtName); + + String deviceIdentifierName = getValidIdentifier(parameters); + String deviceIdentifier = parameters.get(deviceIdentifierName); + + String sourceName = "source"; + String sourceValue = parameters.get(sourceName); + + String payloadName = "payload"; + String payloadValue = parameters.get(payloadName); + + Map signatureParams = new HashMap(); + + signatureParams.put("app_secret", appSecret); + signatureParams.put(createdAtName, createdAt); + signatureParams.put(activityKindName, activityKindValue); + signatureParams.put(deviceIdentifierName, deviceIdentifier); + + if (sourceValue != null) { + signatureParams.put(sourceName, sourceValue); + } + + if (payloadValue != null) { + signatureParams.put(payloadName, payloadValue); + } + + String fields = ""; + String clearSignature = ""; + + for (Map.Entry entry : signatureParams.entrySet()) { + if (entry.getValue() != null) { + fields += entry.getKey() + " "; + clearSignature += entry.getValue(); + } + } + + // Remove last empty space. + fields = fields.substring(0, fields.length() - 1); + + HashMap signature = new HashMap(); + + signature.put("clear_signature", clearSignature); + signature.put("fields", fields); + + return signature; + } + + private String getValidIdentifier(final Map parameters) { + String googleAdIdName = "gps_adid"; + String fireAdIdName = "fire_adid"; + String androidIdName = "android_id"; + String macSha1Name = "mac_sha1"; + String macMd5Name = "mac_md5"; + String androidUUIDName= "android_uuid"; + + if (parameters.get(googleAdIdName) != null) { + return googleAdIdName; + } + if (parameters.get(fireAdIdName) != null) { + return fireAdIdName; + } + if (parameters.get(androidIdName) != null) { + return androidIdName; + } + if (parameters.get(macSha1Name) != null) { + return macSha1Name; + } + if (parameters.get(macMd5Name) != null) { + return macMd5Name; + } + if (parameters.get(androidUUIDName) != null) { + return androidUUIDName; + } + + return null; + } + + private static String extractAppSecret(final Map parameters) { + return parameters.remove("app_secret"); + } + + private static String extractSecretId(final Map parameters) { + return parameters.remove("secret_id"); + } + + private static String extractSignature(final Map parameters) { + return parameters.remove("signature"); + } + + private static String extractAlgorithm(final Map parameters) { + return parameters.remove("algorithm"); + } + + private static String extractNativeVersion(final Map parameters) { + return parameters.remove("native_version"); + } + + private static String extractHeadersId(final Map parameters) { + return parameters.remove("headers_id"); + } + + private static void extractEventCallbackId(final Map parameters) { + parameters.remove("event_callback_id"); + } +} diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/network/IActivityPackageSender.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/network/IActivityPackageSender.java new file mode 100644 index 00000000..88c5b737 --- /dev/null +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/network/IActivityPackageSender.java @@ -0,0 +1,19 @@ +package com.adjust.sdk.network; + +import com.adjust.sdk.ActivityPackage; +import com.adjust.sdk.ResponseData; + +import java.util.Map; + +public interface IActivityPackageSender { + interface ResponseDataCallbackSubscriber { + void onResponseDataCallback(ResponseData responseData); + } + + void sendActivityPackage(ActivityPackage activityPackage, + Map sendingParameters, + ResponseDataCallbackSubscriber responseCallback); + + ResponseData sendActivityPackageSync(ActivityPackage activityPackage, + Map sendingParameters); +} diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/network/UrlStrategy.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/network/UrlStrategy.java new file mode 100644 index 00000000..49261a24 --- /dev/null +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/network/UrlStrategy.java @@ -0,0 +1,160 @@ +package com.adjust.sdk.network; + +import com.adjust.sdk.ActivityKind; +import com.adjust.sdk.Constants; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static com.adjust.sdk.AdjustConfig.URL_STRATEGY_CHINA; +import static com.adjust.sdk.AdjustConfig.URL_STRATEGY_INDIA; +import static com.adjust.sdk.AdjustConfig.DATA_RESIDENCY_EU; + +public class UrlStrategy { + private static final String BASE_URL_INDIA = "https://app.adjust.net.in"; + private static final String GDPR_URL_INDIA = "https://gdpr.adjust.net.in"; + private static final String SUBSCRIPTION_URL_INDIA = "https://subscription.adjust.net.in"; + + private static final String BASE_URL_CHINA = "https://app.adjust.world"; + private static final String GDPR_URL_CHINA = "https://gdpr.adjust.world"; + private static final String SUBSCRIPTION_URL_CHINA = "https://subscription.adjust.world"; + + private static final String BASE_URL_EU = "https://app.eu.adjust.com"; + private static final String GDPR_URL_EU = "https://gdpr.eu.adjust.com"; + private static final String SUBSCRIPTION_URL_EU = "https://subscription.eu.adjust.com"; + + private final String baseUrlOverwrite; + private final String gdprUrlOverwrite; + private final String subscriptionUrlOverwrite; + + final List baseUrlChoicesList; + final List gdprUrlChoicesList; + final List subscriptionUrlChoicesList; + boolean wasLastAttemptSuccess; + int choiceIndex; + int startingChoiceIndex; + boolean wasLastAttemptWithOverwrittenUrl; + + public UrlStrategy(final String baseUrlOverwrite, + final String gdprUrlOverwrite, + final String subscriptionUrlOverwrite, + final String adjustUrlStrategy) + { + this.baseUrlOverwrite = baseUrlOverwrite; + this.gdprUrlOverwrite = gdprUrlOverwrite; + this.subscriptionUrlOverwrite = subscriptionUrlOverwrite; + + baseUrlChoicesList = baseUrlChoices(adjustUrlStrategy); + gdprUrlChoicesList = gdprUrlChoices(adjustUrlStrategy); + subscriptionUrlChoicesList = subscriptionUrlChoices(adjustUrlStrategy); + + wasLastAttemptSuccess = false; + choiceIndex = 0; + startingChoiceIndex = 0; + wasLastAttemptWithOverwrittenUrl = false; + } + + public void resetAfterSuccess() { + startingChoiceIndex = choiceIndex; + wasLastAttemptSuccess = true; + } + + @SuppressWarnings("UnnecessaryLocalVariable") + public boolean shouldRetryAfterFailure(final ActivityKind activityKind) { + wasLastAttemptSuccess = false; + + // does not need to "rotate" choice index + // since it will use the same overwritten url + // might as well stop retrying in the same sending "session" + // and let the backoff strategy pick it up + if (wasLastAttemptWithOverwrittenUrl) { + return false; + } + + int choiceListSize; + + if (activityKind == ActivityKind.GDPR) { + choiceListSize = gdprUrlChoicesList.size(); + } else if (activityKind == ActivityKind.SUBSCRIPTION) { + choiceListSize = subscriptionUrlChoicesList.size(); + } else { + choiceListSize = baseUrlChoicesList.size(); + } + + final int nextChoiceIndex = (choiceIndex + 1) % choiceListSize; + choiceIndex = nextChoiceIndex; + + final boolean nextChoiceHasNotReturnedToStartingChoice = + choiceIndex != startingChoiceIndex; + + return nextChoiceHasNotReturnedToStartingChoice; + } + + public String targetUrlByActivityKind(final ActivityKind activityKind) { + if (activityKind == ActivityKind.GDPR) { + if (gdprUrlOverwrite != null) { + wasLastAttemptWithOverwrittenUrl = true; + return gdprUrlOverwrite; + } else { + wasLastAttemptWithOverwrittenUrl = false; + return gdprUrlChoicesList.get(choiceIndex); + } + } else if (activityKind == ActivityKind.SUBSCRIPTION) { + if (subscriptionUrlOverwrite != null) { + wasLastAttemptWithOverwrittenUrl = true; + return subscriptionUrlOverwrite; + } else { + wasLastAttemptWithOverwrittenUrl = false; + return subscriptionUrlChoicesList.get(choiceIndex); + } + } else { + if (baseUrlOverwrite != null) { + wasLastAttemptWithOverwrittenUrl = true; + return baseUrlOverwrite; + } else { + wasLastAttemptWithOverwrittenUrl = false; + return baseUrlChoicesList.get(choiceIndex); + } + } + } + + private static List baseUrlChoices(final String urlStrategy) + { + if (URL_STRATEGY_INDIA.equals(urlStrategy)) { + return Arrays.asList(BASE_URL_INDIA, Constants.BASE_URL); + } else if (URL_STRATEGY_CHINA.equals(urlStrategy)) { + return Arrays.asList(BASE_URL_CHINA, Constants.BASE_URL); + } else if (DATA_RESIDENCY_EU.equals(urlStrategy)) { + return Collections.singletonList(BASE_URL_EU); + } else { + return Arrays.asList(Constants.BASE_URL, BASE_URL_INDIA, BASE_URL_CHINA); + } + } + private static List gdprUrlChoices(final String urlStrategy) + { + if (URL_STRATEGY_INDIA.equals(urlStrategy)) { + return Arrays.asList(GDPR_URL_INDIA, Constants.GDPR_URL); + } else if (URL_STRATEGY_CHINA.equals(urlStrategy)) { + return Arrays.asList(GDPR_URL_CHINA, Constants.GDPR_URL); + } else if (DATA_RESIDENCY_EU.equals(urlStrategy)) { + return Collections.singletonList(GDPR_URL_EU); + } else { + return Arrays.asList(Constants.GDPR_URL, GDPR_URL_INDIA, GDPR_URL_CHINA); + } + } + private static List subscriptionUrlChoices(final String urlStrategy) + { + if (URL_STRATEGY_INDIA.equals(urlStrategy)) { + return Arrays.asList(SUBSCRIPTION_URL_INDIA, Constants.SUBSCRIPTION_URL); + } else if (URL_STRATEGY_CHINA.equals(urlStrategy)) { + return Arrays.asList(SUBSCRIPTION_URL_CHINA, Constants.SUBSCRIPTION_URL); + } else if (DATA_RESIDENCY_EU.equals(urlStrategy)) { + return Collections.singletonList(SUBSCRIPTION_URL_EU); + } else { + return Arrays.asList(Constants.SUBSCRIPTION_URL, + SUBSCRIPTION_URL_INDIA, + SUBSCRIPTION_URL_CHINA); + } + } +} diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/network/UtilNetworking.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/network/UtilNetworking.java new file mode 100644 index 00000000..3f297792 --- /dev/null +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/network/UtilNetworking.java @@ -0,0 +1,97 @@ +package com.adjust.sdk.network; + +import com.adjust.sdk.AdjustFactory; +import com.adjust.sdk.Constants; +import com.adjust.sdk.ILogger; + +import org.json.JSONObject; + +import java.io.IOException; +import java.net.URL; + +import javax.net.ssl.HttpsURLConnection; + +/** + * Created by uerceg on 03/04/2017. + */ + +public class UtilNetworking { + private static String userAgent; + + private static ILogger getLogger() { + return AdjustFactory.getLogger(); + } + + public static void setUserAgent(String userAgent) { + UtilNetworking.userAgent = userAgent; + } + + public interface IConnectionOptions { + void applyConnectionOptions(HttpsURLConnection connection, String clientSdk); + } + + public static IConnectionOptions createDefaultConnectionOptions() { + return new IConnectionOptions() { + @Override + public void applyConnectionOptions(final HttpsURLConnection connection, + final String clientSdk) + { + connection.setRequestProperty("Client-SDK", clientSdk); + connection.setConnectTimeout(Constants.ONE_MINUTE); + connection.setReadTimeout(Constants.ONE_MINUTE); + + if (userAgent != null) { + connection.setRequestProperty("User-Agent", userAgent); + } + + } + }; + } + + public interface IHttpsURLConnectionProvider { + HttpsURLConnection generateHttpsURLConnection(URL url) throws IOException; + } + + public static IHttpsURLConnectionProvider createDefaultHttpsURLConnectionProvider() { + return new IHttpsURLConnectionProvider() { + @Override + public HttpsURLConnection generateHttpsURLConnection(final URL url) + throws IOException + { + return (HttpsURLConnection) url.openConnection(); + } + }; + } + + public static String extractJsonString(final JSONObject jsonObject, final String name) { + // taken from JSONObject.optString(...) to add null fallback + final Object object = jsonObject.opt(name); + if (object instanceof String) { + return (String) object; + } + if (object != null) { + return object.toString(); + } + return null; + } + + public static Long extractJsonLong(final JSONObject jsonObject, + final String name) + { + // taken from JSONObject.optLong(...) to add null fallback + final Object object = jsonObject.opt(name); + if (object instanceof Long) { + return (Long) object; + } + if (object instanceof Number) { + return ((Number) object).longValue(); + } + if (object instanceof String) { + try { + return (long) Double.parseDouble((String) object); + } catch (final NumberFormatException ignored) { + } + } + return null; + } +} diff --git a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/scheduler/TimerOnce.java b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/scheduler/TimerOnce.java index cec60fca..959cad75 100644 --- a/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/scheduler/TimerOnce.java +++ b/ext/android/src/AdjustExtension/extension/src/main/java/com/adjust/sdk/scheduler/TimerOnce.java @@ -4,10 +4,8 @@ import com.adjust.sdk.ILogger; import com.adjust.sdk.Util; -import java.util.concurrent.ExecutionException; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; /** * Created by pfms on 08/05/15. diff --git a/ext/ios/sdk b/ext/ios/sdk index 773cc771..8790a530 160000 --- a/ext/ios/sdk +++ b/ext/ios/sdk @@ -1 +1 @@ -Subproject commit 773cc771193e8f3e5cf7d3b865b6b042da8854a0 +Subproject commit 8790a5300d00a11b8f51c7cc66c94165ac656f8c diff --git a/src/com/adjust/sdk/Adjust.as b/src/com/adjust/sdk/Adjust.as index 791e63bf..e1760d75 100644 --- a/src/com/adjust/sdk/Adjust.as +++ b/src/com/adjust/sdk/Adjust.as @@ -4,7 +4,7 @@ package com.adjust.sdk { import flash.external.ExtensionContext; public class Adjust extends EventDispatcher { - private static var sdkPrefix:String = "adobe_air4.22.1"; + private static var sdkPrefix:String = "adobe_air4.28.0"; private static var errorMessage:String = "Adjust: SDK not started. Start it manually using the 'start' method"; private static var hasSdkStarted:Boolean = false; diff --git a/src/extension.xml b/src/extension.xml index 64d1377f..cff6df6d 100644 --- a/src/extension.xml +++ b/src/extension.xml @@ -1,6 +1,6 @@ com.adjust.sdk - 4.22.1 + 4.28.0 diff --git a/test/app/Main-app.xml b/test/app/Main-app.xml index 5f7649ed..2e94bc81 100644 --- a/test/app/Main-app.xml +++ b/test/app/Main-app.xml @@ -1,7 +1,7 @@ com.adjust.examples - 4.22.1 + 4.28.1 Adjust AIR SDK Test diff --git a/test/app/Main.as b/test/app/Main.as index 37b6b488..5fa2d9b9 100644 --- a/test/app/Main.as +++ b/test/app/Main.as @@ -8,9 +8,9 @@ package { public class Main extends Sprite { // Android: Make sure to use HTTPS with port 8443 with a physical device. // iOS: Make sure to use HTTP with port 8080 with a physical device. - public static var ipAddress:String = '192.168.86.33'; - public static var baseUrl:String = 'https://' + ipAddress + ':8443'; - public static var gdprUrl:String = 'https://' + ipAddress + ':8443'; + public static var ipAddress:String = '192.168.86.75'; + public static var baseUrl:String = 'http://' + ipAddress + ':8080'; + public static var gdprUrl:String = 'http://' + ipAddress + ':8080'; public static var controlUrl:String = 'ws://' + ipAddress + ':1987'; private static var commandExecutor:CommandExecutor; diff --git a/test/app/lib/AdjustTest-4.22.0.ane b/test/app/lib/AdjustTest-4.22.0.ane deleted file mode 100644 index 7284a8f7..00000000 Binary files a/test/app/lib/AdjustTest-4.22.0.ane and /dev/null differ diff --git a/test/app/lib/AdjustTest-4.28.0.ane b/test/app/lib/AdjustTest-4.28.0.ane new file mode 100644 index 00000000..e2d75599 Binary files /dev/null and b/test/app/lib/AdjustTest-4.28.0.ane differ diff --git a/test/plugin/ios/AdjustTestLibrary.framework/AdjustTestLibrary b/test/plugin/ios/AdjustTestLibrary.framework/AdjustTestLibrary index 58d52edb..576ad9fb 100644 Binary files a/test/plugin/ios/AdjustTestLibrary.framework/AdjustTestLibrary and b/test/plugin/ios/AdjustTestLibrary.framework/AdjustTestLibrary differ diff --git a/test/plugin/ios/AdjustTestLibrary.framework/Versions/A/AdjustTestLibrary b/test/plugin/ios/AdjustTestLibrary.framework/Versions/A/AdjustTestLibrary index 58d52edb..576ad9fb 100644 Binary files a/test/plugin/ios/AdjustTestLibrary.framework/Versions/A/AdjustTestLibrary and b/test/plugin/ios/AdjustTestLibrary.framework/Versions/A/AdjustTestLibrary differ diff --git a/test/plugin/ios/AdjustTestLibrary.framework/Versions/Current/AdjustTestLibrary b/test/plugin/ios/AdjustTestLibrary.framework/Versions/Current/AdjustTestLibrary index 58d52edb..576ad9fb 100644 Binary files a/test/plugin/ios/AdjustTestLibrary.framework/Versions/Current/AdjustTestLibrary and b/test/plugin/ios/AdjustTestLibrary.framework/Versions/Current/AdjustTestLibrary differ diff --git a/test/plugin/ios/src/AdjustTestExtension/AdjustTestLibrary.framework/AdjustTestLibrary b/test/plugin/ios/src/AdjustTestExtension/AdjustTestLibrary.framework/AdjustTestLibrary index 58d52edb..576ad9fb 100644 Binary files a/test/plugin/ios/src/AdjustTestExtension/AdjustTestLibrary.framework/AdjustTestLibrary and b/test/plugin/ios/src/AdjustTestExtension/AdjustTestLibrary.framework/AdjustTestLibrary differ diff --git a/test/plugin/ios/src/AdjustTestExtension/AdjustTestLibrary.framework/Versions/A/AdjustTestLibrary b/test/plugin/ios/src/AdjustTestExtension/AdjustTestLibrary.framework/Versions/A/AdjustTestLibrary index 58d52edb..576ad9fb 100644 Binary files a/test/plugin/ios/src/AdjustTestExtension/AdjustTestLibrary.framework/Versions/A/AdjustTestLibrary and b/test/plugin/ios/src/AdjustTestExtension/AdjustTestLibrary.framework/Versions/A/AdjustTestLibrary differ diff --git a/test/plugin/ios/src/AdjustTestExtension/AdjustTestLibrary.framework/Versions/Current/AdjustTestLibrary b/test/plugin/ios/src/AdjustTestExtension/AdjustTestLibrary.framework/Versions/Current/AdjustTestLibrary index 58d52edb..576ad9fb 100644 Binary files a/test/plugin/ios/src/AdjustTestExtension/AdjustTestLibrary.framework/Versions/Current/AdjustTestLibrary and b/test/plugin/ios/src/AdjustTestExtension/AdjustTestLibrary.framework/Versions/Current/AdjustTestLibrary differ diff --git a/test/plugin/src/extension.xml b/test/plugin/src/extension.xml index 5bc5ac26..dfd6c279 100644 --- a/test/plugin/src/extension.xml +++ b/test/plugin/src/extension.xml @@ -1,6 +1,6 @@ com.adjust.test - 4.22.1 + 4.28.0