Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 73 additions & 5 deletions android-core/src/main/java/com/mparticle/MParticle.java
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ private MParticle(MParticleOptions options) {
* @param interval in seconds
*/
public void setUpdateInterval(int interval) {
MParticle.logRoktApiUsage("SET_UPLOAD_INTERVAL");
long intervalMillis = interval * 1000L;
if ((intervalMillis >= 1 && mConfigManager.getUploadInterval() != intervalMillis)) {
upload();
Expand Down Expand Up @@ -281,6 +282,45 @@ public static void setInstance(@Nullable MParticle instance) {
MParticle.instance = instance;
}

// Rokt public-API-usage diagnostics are suppressed while SDK/kit internals invoke a public API
// (auto-upload, the Rokt kit's attribute enrichment, deferred push-token modify, etc.) so only
// genuine partner calls are reported. Synchronous by design: logRoktApiUsage runs at each
// instrumented method's first line, on the caller's thread, before any dispatch — so this flag
// is active for that call. ponytail: covers synchronous re-entry only, which is exactly how the
// instrumented methods emit; async internal paths that need it call withoutRoktApiUsage directly.
private static final ThreadLocal<Boolean> sSuppressRoktApiUsage = new ThreadLocal<>();

/**
* @hide Internal: forwards a bounded, non-PII public-API-usage diagnostic code to the Rokt kit
* (only when the kit is active). No-op when mParticle isn't started, Rokt isn't integrated, or
* the call originates from SDK/kit internals (see {@link #withoutRoktApiUsage(Runnable)}).
* Reads the static instance directly so it stays quiet (no getInstance() warning) on the hot path.
* Not intended for partner use.
*/
public static void logRoktApiUsage(@Nullable String code) {
if (Boolean.TRUE.equals(sSuppressRoktApiUsage.get())) {
return;
}
MParticle mp = instance;
if (mp != null && mp.mKitManager != null) {
mp.mKitManager.logRoktApiDiagnostic(code);
}
}

/**
* @hide Internal: run SDK/kit work that invokes public APIs without emitting Rokt usage
* diagnostics, so internal re-entry isn't misreported as a partner call. Not for partner use.
*/
public static void withoutRoktApiUsage(@NonNull Runnable action) {
boolean previous = Boolean.TRUE.equals(sSuppressRoktApiUsage.get());
sSuppressRoktApiUsage.set(true);
try {
action.run();
} finally {
sSuppressRoktApiUsage.set(previous);
}
}

/**
* Switch the SDK to a new API key and secret.
* Will first batch all events that have not been sent to mParticle into upload records,
Expand All @@ -291,6 +331,7 @@ public static void setInstance(@Nullable MParticle instance) {
@param options Required to initialize the SDK properly
*/
public static void switchWorkspace(@NonNull MParticleOptions options) {
MParticle.logRoktApiUsage("SWITCH_WORKSPACE");
synchronized (MParticle.class) {
MParticle localInstance = instance;
if (localInstance == null) {
Expand Down Expand Up @@ -446,6 +487,7 @@ boolean isSessionActive() {
* Force upload all queued messages to the mParticle server.
*/
public void upload() {
MParticle.logRoktApiUsage("UPLOAD");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Internal uploads misreported as partner

Medium Severity

upload now always emits UPLOAD, but SDK-internal callers still invoke it directly without withoutRoktApiUsage. Session-end endUploadLoop, alias-driven uploads, and the nested upload inside setUpdateInterval are therefore reported as partner API usage whenever the Rokt kit is active, contradicting the intended never-over-log behavior.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 8a4d883. Configure here.

mMessageManager.doUpload();
}

Expand All @@ -454,6 +496,7 @@ public void upload() {
* automatically retrieved upon installation from Google Play.
*/
public void setInstallReferrer(@Nullable String referrer) {
MParticle.logRoktApiUsage("SET_INSTALL_REFERRER");
InstallReferrerHelper.setInstallReferrer(mAppContext, referrer);
}

Expand All @@ -468,6 +511,7 @@ public String getInstallReferrer() {
}

public void logEvent(@NonNull BaseEvent event) {
MParticle.logRoktApiUsage("LOG_EVENT");
if (event instanceof MPEvent && event.isShouldUploadEvent()) {
logMPEvent((MPEvent) event);
} else if (event instanceof CommerceEvent && event.isShouldUploadEvent()) {
Expand Down Expand Up @@ -519,6 +563,7 @@ private void logCommerceEvent(@NonNull CommerceEvent event) {
* @param contextInfo An MPProduct or any set of data to associate with this increase in LTV (optional)
*/
public void logLtvIncrease(@NonNull BigDecimal valueIncreased, @Nullable String eventName, @Nullable Map<String, String> contextInfo) {
MParticle.logRoktApiUsage("LOG_LTV_INCREASE");
if (valueIncreased == null) {
Logger.error("ValueIncreased must not be null.");
return;
Expand All @@ -528,11 +573,13 @@ public void logLtvIncrease(@NonNull BigDecimal valueIncreased, @Nullable String
}
contextInfo.put(MessageKey.RESERVED_KEY_LTV, valueIncreased.toPlainString());
contextInfo.put(Constants.MethodName.METHOD_NAME, Constants.MethodName.LOG_LTV);
logEvent(
final Map<String, String> ltvContextInfo = contextInfo;
// Internal re-entry: this is LOG_LTV_INCREASE, not a partner LOG_EVENT call.
withoutRoktApiUsage(() -> logEvent(
new MPEvent.Builder(eventName == null ? "Increase LTV" : eventName, EventType.Transaction)
.customAttributes(contextInfo)
.customAttributes(ltvContextInfo)
.build()
);
));
}

/**
Expand Down Expand Up @@ -572,6 +619,7 @@ public void logScreen(@NonNull String screenName, @Nullable Map<String, String>
* @param screenEvent an event object, the name of the event will be used as the screen name
*/
public void logScreen(@NonNull MPEvent screenEvent) {
MParticle.logRoktApiUsage("LOG_SCREEN");
screenEvent.setScreenEvent(true);
if (MPUtility.isEmpty(screenEvent.getEventName())) {
Logger.error("screenName is required for logScreen.");
Expand All @@ -598,6 +646,7 @@ public void logScreen(@NonNull MPEvent screenEvent) {
* @param breadcrumb
*/
public void leaveBreadcrumb(@NonNull String breadcrumb) {
MParticle.logRoktApiUsage("LEAVE_BREADCRUMB");
if (mConfigManager.isEnabled()) {
if (MPUtility.isEmpty(breadcrumb)) {
Logger.error("breadcrumb is required for leaveBreadcrumb.");
Expand Down Expand Up @@ -630,6 +679,7 @@ public void logError(@NonNull String message) {
* @param errorAttributes a Map of data attributes to associate with this error
*/
public void logError(@NonNull String message, @Nullable Map<String, String> errorAttributes) {
MParticle.logRoktApiUsage("LOG_ERROR");
if (mConfigManager.isEnabled()) {
if (MPUtility.isEmpty(message)) {
Logger.error("message is required for logErrorEvent.");
Expand All @@ -646,6 +696,7 @@ public void logError(@NonNull String message, @Nullable Map<String, String> erro
}

public void logNetworkPerformance(@NonNull String url, long startTime, @NonNull String method, long length, long bytesSent, long bytesReceived, @Nullable String requestString, int responseCode) {
MParticle.logRoktApiUsage("LOG_NETWORK_PERFORMANCE");
if (mConfigManager.isEnabled()) {
mAppStateManager.ensureActiveSession();
mMessageManager.logNetworkPerformanceEvent(startTime, method, url, length, bytesSent, bytesReceived, requestString);
Expand Down Expand Up @@ -694,6 +745,7 @@ public AttributionListener getAttributionListener() {
*/
@NonNull
public Map<Integer, AttributionResult> getAttributionResults() {
MParticle.logRoktApiUsage("GET_ATTRIBUTION_INFO");
return mKitManager.getAttributionResults();
}

Expand All @@ -706,6 +758,7 @@ public Map<Integer, AttributionResult> getAttributionResults() {
* @param message the name of the error event to be tracked
*/
public void logException(@NonNull Exception exception, @Nullable Map<String, String> eventData, @Nullable String message) {
MParticle.logRoktApiUsage("LOG_EXCEPTION");
if (mConfigManager.isEnabled()) {
mAppStateManager.ensureActiveSession();
JSONObject eventDataJSON = MPUtility.enforceAttributeConstraints(eventData);
Expand Down Expand Up @@ -821,6 +874,7 @@ public void setLocation(@Nullable Location location) {
* @param value the attribute value. This value will be converted to its String representation as dictated by its <code>toString()</code> method.
*/
public void setSessionAttribute(@NonNull String key, @Nullable Object value) {
MParticle.logRoktApiUsage("SET_SESSION_ATTRIBUTE");
if (key == null) {
Logger.warning("setSessionAttribute called with null key. Ignoring...");
return;
Expand All @@ -845,6 +899,7 @@ public void setSessionAttribute(@NonNull String key, @Nullable Object value) {
* @param value the attribute value
*/
public void incrementSessionAttribute(@NonNull String key, int value) {
MParticle.logRoktApiUsage("INCREMENT_SESSION_ATTRIBUTE");
if (key == null) {
Logger.warning("incrementSessionAttribute called with null key. Ignoring...");
return;
Expand Down Expand Up @@ -875,6 +930,7 @@ public Boolean getOptOut() {
* @param optOutStatus set to <code>true</code> to opt out of event tracking
*/
public void setOptOut(@NonNull Boolean optOutStatus) {
MParticle.logRoktApiUsage("SET_OPT_OUT");
if (optOutStatus != null) {
if (optOutStatus != mConfigManager.getOptedOut()) {
if (!optOutStatus) {
Expand Down Expand Up @@ -916,6 +972,7 @@ public ConsentState getDeviceConsentState() {
* @param state the device-level consent state, or {@code null} to clear the override
*/
public void setDeviceConsentState(@Nullable ConsentState state) {
MParticle.logRoktApiUsage("SET_DEVICE_CONSENT_STATE");
ConsentState oldState = mConfigManager.getEffectiveConsentState(mConfigManager.getMpid());
mConfigManager.setDeviceConsentState(state);
ConsentState newState = mConfigManager.getEffectiveConsentState(mConfigManager.getMpid());
Expand Down Expand Up @@ -945,6 +1002,7 @@ public Boolean isDeviceBasedConsentEnabled() {
*/
@Nullable
public Uri getSurveyUrl(final int kitId) {
MParticle.logRoktApiUsage("GET_SURVEY_URL");
return mKitManager.getSurveyUrl(kitId, null, null);
}

Expand Down Expand Up @@ -1009,6 +1067,7 @@ public void registerWebView(@NonNull WebView webView) {
@SuppressLint("AddJavascriptInterface")
@RequiresApi(17)
public void registerWebView(@NonNull WebView webView, String requiredBridgeName) {
MParticle.logRoktApiUsage("REGISTER_WEBVIEW");
MParticleJSInterface.registerWebView(webView, requiredBridgeName);
}

Expand All @@ -1022,6 +1081,7 @@ public void registerWebView(@NonNull WebView webView, String requiredBridgeName)
* @see MParticle.LogLevel
*/
public static void setLogLevel(@NonNull LogLevel level) {
MParticle.logRoktApiUsage("SET_LOG_LEVEL");
if (level != null) {
Logger.setMinLogLevel(level, true);
}
Expand Down Expand Up @@ -1100,6 +1160,7 @@ public void onAudioStopped() {
* @see MParticle.ServiceProviders
*/
public boolean isKitActive(int serviceProviderId) {
MParticle.logRoktApiUsage("IS_KIT_ACTIVE");
return mKitManager.isKitActive(serviceProviderId);
}

Expand All @@ -1112,6 +1173,7 @@ public boolean isKitActive(int serviceProviderId) {
*/
@Nullable
public Object getKitInstance(int kitId) {
MParticle.logRoktApiUsage("GET_KIT_INSTANCE");
return mKitManager.getKitInstance(kitId);
}

Expand All @@ -1131,6 +1193,7 @@ public void logPushRegistration(@Nullable String instanceId, @Nullable String se
* @param intent
*/
public void logNotification(@NonNull Intent intent) {
MParticle.logRoktApiUsage("LOG_NOTIFICATION");
if (mConfigManager.isEnabled()) {
ProviderCloudMessage message = ProviderCloudMessage.createMessage(intent, ConfigManager.getPushKeys(mAppContext));
mMessageManager.logNotification(message, getAppState());
Expand Down Expand Up @@ -1161,6 +1224,7 @@ void logNotification(@NonNull ProviderCloudMessage cloudMessage, boolean startSe
* @param intent
*/
public void logNotificationOpened(@NonNull Intent intent) {
MParticle.logRoktApiUsage("LOG_NOTIFICATION_OPENED");
logNotification(ProviderCloudMessage.createMessage(intent, ConfigManager.getPushKeys(mAppContext)),
true, MParticle.getAppState(), ProviderCloudMessage.FLAG_READ | ProviderCloudMessage.FLAG_DIRECT_OPEN);
}
Expand Down Expand Up @@ -1219,6 +1283,7 @@ public IdentityApi Identity() {
* @param context
*/
public static void reset(@NonNull Context context) {
MParticle.logRoktApiUsage("RESET");
reset(context, true, false);
}

Expand Down Expand Up @@ -1604,9 +1669,10 @@ public void onUserIdentified(MParticleUser user, MParticleUser previousUser) {
}

private void sendPushTokenModifyRequest(MParticleUser user, @Nullable String instanceId, @Nullable String oldInstanceId) {
Identity().modify(new Builder(user)
// Internal SDK bookkeeping (push-token refresh) — must not be reported as a partner MODIFY call.
withoutRoktApiUsage(() -> Identity().modify(new Builder(user)
.pushToken(instanceId, oldInstanceId)
.build());
.build()));
}

class Builder extends IdentityApiRequest.Builder {
Expand Down Expand Up @@ -1641,6 +1707,7 @@ protected IdentityApiRequest.Builder pushToken(@Nullable String newPushToken, @N
* also pass a null or empty map here to remove all of the attributes.
*/
public void setIntegrationAttributes(int integrationId, @Nullable Map<String, String> attributes) {
MParticle.logRoktApiUsage("SET_INTEGRATION_ATTRIBUTES");
this.Internal().getConfigManager().setIntegrationAttributes(integrationId, attributes);
}

Expand All @@ -1658,6 +1725,7 @@ public void setIntegrationAttributes(int integrationId, @Nullable Map<String, St
*/
@NonNull
public Map<String, String> getIntegrationAttributes(int integrationId) {
MParticle.logRoktApiUsage("GET_INTEGRATION_ATTRIBUTES");
return this.Internal().getConfigManager().getIntegrationAttributes(integrationId);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ public MParticleUser getUser(@NonNull Long mpid) {
*/
@NonNull
public List<MParticleUser> getUsers() {
MParticle.logRoktApiUsage("GET_USERS");
List<MParticleUser> users = new ArrayList<MParticleUser>();
Set<Long> mpids = mConfigManager.getMpids();
mpids.remove(Constants.TEMPORARY_MPID);
Expand Down Expand Up @@ -165,6 +166,7 @@ public MParticleTask<IdentityApiResult> logout() {
*/
@NonNull
public MParticleTask<IdentityApiResult> logout(@Nullable final IdentityApiRequest logoutRequest) {
MParticle.logRoktApiUsage("LOGOUT");
return makeIdentityRequest(logoutRequest, new IdentityNetworkRequestRunnable() {
@Override
public IdentityHttpResponse request(IdentityApiRequest request) throws Exception {
Expand Down Expand Up @@ -200,6 +202,7 @@ public MParticleTask<IdentityApiResult> login() {
*/
@NonNull
public MParticleTask<IdentityApiResult> login(@Nullable final IdentityApiRequest loginRequest) {
MParticle.logRoktApiUsage("LOGIN");
return makeIdentityRequest(loginRequest, new IdentityNetworkRequestRunnable() {
@Override
public IdentityHttpResponse request(IdentityApiRequest request) throws Exception {
Expand All @@ -223,6 +226,7 @@ public void onPostExecute(IdentityApiResult result) {
*/
@NonNull
public MParticleTask<IdentityApiResult> identify(@Nullable final IdentityApiRequest identifyRequest) {
MParticle.logRoktApiUsage("IDENTIFY");
return makeIdentityRequest(identifyRequest, new IdentityNetworkRequestRunnable() {
@Override
public IdentityHttpResponse request(IdentityApiRequest request) throws Exception {
Expand All @@ -246,6 +250,7 @@ public void onPostExecute(IdentityApiResult result) {
*/
@NonNull
public BaseIdentityTask modify(@NonNull final IdentityApiRequest updateRequest) {
MParticle.logRoktApiUsage("MODIFY");
boolean devMode = MPUtility.isDevEnv() || MPUtility.isAppDebuggable(mContext);
final BaseIdentityTask task = new BaseIdentityTask();

Expand Down Expand Up @@ -296,6 +301,7 @@ public void run() {
* @return
*/
public boolean aliasUsers(@NonNull AliasRequest aliasRequest) {
MParticle.logRoktApiUsage("ALIAS_USERS");
if (aliasRequest.getDestinationMpid() == 0 || aliasRequest.getSourceMpid() == 0) {
Logger.error("AliasRequest does not have a valid destinationMpid and a valid sourceMpid");
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,21 +94,25 @@ public boolean setUserAttribute(String key, Object value) {

@Override
public boolean setUserAttributeList(String key, Object value) {
MParticle.logRoktApiUsage("SET_USER_ATTRIBUTE_LIST");
return mUserDelegate.setUserAttributeList(key, value, getId());
}

@Override
public boolean incrementUserAttribute(String key, Number value) {
MParticle.logRoktApiUsage("INCREMENT_USER_ATTRIBUTE");
return mUserDelegate.incrementUserAttribute(key, value, getId());
}

@Override
public boolean removeUserAttribute(String key) {
MParticle.logRoktApiUsage("REMOVE_USER_ATTRIBUTE");
return mUserDelegate.removeUserAttribute(key, getId());
}

@Override
public boolean setUserTag(@NonNull String tag) {
MParticle.logRoktApiUsage("SET_USER_TAG");
return setUserAttribute(tag, null);
}

Expand All @@ -120,11 +124,13 @@ MParticleUser setUserDelegate(MParticleUserDelegate mParticleUserDelegate) {

@Override
public ConsentState getConsentState() {
MParticle.logRoktApiUsage("GET_CONSENT_STATE");
return mUserDelegate.getConsentState(getId());
}

@Override
public void setConsentState(ConsentState state) {
MParticle.logRoktApiUsage("SET_CONSENT_STATE");
mUserDelegate.setConsentState(state, getId());
}

Expand All @@ -145,6 +151,7 @@ public long getLastSeenTime() {

@Override
public AudienceTask<AudienceResponse> getUserAudiences() {
MParticle.logRoktApiUsage("GET_USER_AUDIENCES");
return mUserDelegate.getUserAudiences(getId());
}

Expand Down
Loading
Loading