Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Installation referrer tracking #8299

Merged
merged 17 commits into from
Sep 12, 2018

Conversation

mzorz
Copy link
Contributor

@mzorz mzorz commented Sep 11, 2018

Closes #8298

This PR captures installation referrer information and sends it to Track.

What is being captured?

Here's a sample of the information as returned by Google's Install Referrer Library:

"install_referrer_timestamp_begin" -> "1532955852"
"install_referrer_timestamp_click" -> "0"
"install_referrer" -> "utm_source=google-play&utm_medium=organic"

as gotten from this code:

ReferrerDetails response = mReferrerClient.getInstallReferrer();
response.getInstallReferrer();
response.getReferrerClickTimestampSeconds();
response.getInstallBeginTimestampSeconds();

In case we don't have access to it, we try to obtain it from the com.android.vending.INSTALL_REFERRER broadcast, and this is an example of it:

"install_referrer" -> "utm_source=test_source\&utm_medium=test_medium\&utm_term=test_term\&utm_content=test_content\&utm_campaign=test_name"

(only the install_referrer string is obtained in this case).

How?

This is done by means of 3 different entry points:

  • by implementing the Google Play Referrer API library, which works on devices that have the Play Store app version 8.3.73 and on.
  • by programmatically registering a BroadcastReceiver on the implicit Intent.ACTION_PACKAGE_FIRST_LAUNCH (can't be registered on the Android Manifest as per since API 26). Such an intent should be broadcasted when the application is first launched, and is the recommended way as explained here.
  • by listening to the Play Store app explicit com.android.vending.INSTALL_REFERRER intent, which is broadcasted each time a new application is installed from Google Play using the Play Store app.

A note on capture / processing separation

As per my tests the intent Intent.ACTION_PACKAGE_FIRST_LAUNCH was not being called. I believe it is probably due to the need for the app to come from Google Play and be installed once, which of course we could only test by preparing a new alpha build and uploading to the store. This intent cannot be simulated from adb because it can only be issued by the System (otherwise a SecurityException is thrown). For this, I decided to implement a mechanism that doesn't rely on this intent being listened to alone, and went on and implemented both a Service (< API 26) and a JobService (API >= 26) that encapsulate the needed work to interact with the new Install Referrer library and send the info to Tracks. This has the advantage that detection (the entry points) and processing are well separated, and we can rely on the execution of this (or for the case, any other needed task) will be performed without affecting the normal execution of the app. With this then, the service is also triggered in WPMainActivity with a AppPrefs flag check to see if the referrer has already been obtained or not.

That said, we could also remove the Intent.ACTION_PACKAGE_FIRST_LAUNCH BroadcastReceiver altogether if we feel it's not necessary (putting this option under review consideration as well), given the Service will run anyway upon launching (and first launch is a particular case of launch, which we'll be tracking with Prefs anyway).

Moreover, note that we try to get the referrer information as many times as needed until we get it.
Once we've gotten the information, it will not be queried anymore. On the contrary, relying on Intent.ACTION_PACKAGE_FIRST_LAUNCH alone and failing to obtain the needed referrer string would then be not ideal.

Regarding Services

The Service / JobService implementation is based on the previous experience - for reference check the related PRs in #7604

To test:

Install Referrer API Library.

  1. start the app
  2. once the login screen is shown, something like this should be seen in the logs:
09-11 15:49:25.094 30084-30084/org.wordpress.android I/WordPress-STATS: 🔵 Tracked: installation_referrer_obtained, Properties: {"install_referrer_timestamp_click":0,"install_referrer":"utm_source=google-play&utm_medium=organic","install_referrer_timestamp_begin":1532955852}

This information is obtained from the Installation Referrer API Library.

Install broadcast receiver

  1. connect your phone to your computer via USB
  2. start the app
  3. open a Terminal window
  4. issue the following command:
adb shell

am broadcast -a com.android.vending.INSTALL_REFERRER -n org.wordpress.android/.util.analytics.receiver.InstallationReferrerReceiver --es "referrer" "utm_source=test_source\&utm_medium=test_medium\&utm_term=test_term\&utm_content=test_content\&utm_campaign=test_name"
  1. the logs should show something like this:
09-11 16:59:06.849 31897-31897/org.wordpress.android I/WordPress-UTILS: installation referrer RECEIVER: received
09-11 16:59:09.355 31897-31897/org.wordpress.android I/WordPress-UTILS: installation referrer job service > job scheduled
09-11 16:59:10.628 31897-31897/org.wordpress.android I/WordPress-UTILS: installation referrer job service > started
09-11 16:59:10.664 31897-31897/org.wordpress.android I/WordPress-STATS: 🔵 Tracked: installation_referrer_obtained, Properties: {"install_referrer":"utm_source=test_source\\&utm_medium=test_medium\\&utm_term=test_term\\&utm_content=test_content\\&utm_campaign=test_name"}
09-11 16:59:12.584 31897-31897/org.wordpress.android I/WordPress-UTILS: installation referrer job service > completed

This should be tested both on API 26 >= and API < 26 devices. I tested on a Google Pixel with Android 8.1 (both tests worked) and a Samsung Galaxy S3 with Android 4.2.2 (only the second test worked as it has an older version of Google Play - it fails gracefully through the case InstallReferrerResponse.FEATURE_NOT_SUPPORTED switch case).

@mzorz mzorz added this to the 11.0 milestone Sep 11, 2018
@mzorz mzorz requested a review from theck13 September 11, 2018 20:21
@mzorz
Copy link
Contributor Author

mzorz commented Sep 11, 2018

Marked [Not Ready for Review] as we need to target 10.9 by request of @elibud - working on it 👍

@mzorz mzorz modified the milestones: 11.0, 10.9 Sep 11, 2018
@mzorz mzorz changed the base branch from develop to release/10.9 September 11, 2018 21:12
@mzorz
Copy link
Contributor Author

mzorz commented Sep 11, 2018

Ok! [Status Not Ready for Review] lifted :D was an easy thing as there were no differences between my version of develop and origin/release/10.9 😅 . All good to review now @theck13 - thanks in advance 🙇

@theck13
Copy link
Contributor

theck13 commented Sep 12, 2018

we could also remove the Intent.ACTION_PACKAGE_FIRST_LAUNCH BroadcastReceiver altogether if we feel it's not necessary

relying on Intent.ACTION_PACKAGE_FIRST_LAUNCH alone and failing to obtain the needed referrer string would then be not ideal

These two comments make me think we should remove the BroadcastReceiver for Intent.ACTION_PACKAGE_FIRST_LAUNCH. It doesn't sound like a reliable entry point and redundant since we'll get the same information from the Service/JobService running at every launch. Is that correct?

@mzorz
Copy link
Contributor Author

mzorz commented Sep 12, 2018

These two comments make me think we should remove the BroadcastReceiver for Intent.ACTION_PACKAGE_FIRST_LAUNCH. It doesn't sound like a reliable entry point and redundant since we'll get the same information from the Service/JobService running at every launch. Is that correct?

That is correct, yes, and I was thinking the same. Will remove in an upcoming commit then! 👍

@mzorz
Copy link
Contributor Author

mzorz commented Sep 12, 2018

These two comments make me think we should remove the BroadcastReceiver for Intent.ACTION_PACKAGE_FIRST_LAUNCH

Removed the PackageFirstLaunchReceiver in 4bfb225.

Copy link
Contributor

@theck13 theck13 left a comment

Choose a reason for hiding this comment

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

I left a few comments and questions about the code. It works as described and I was able to see the proper log entries while testing!

}
}


Copy link
Contributor

Choose a reason for hiding this comment

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

Can we leave only one newline at the end of the file for code consistency?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

addressed in 7d4e91a

import org.wordpress.android.util.AppLog;
import org.wordpress.android.util.analytics.service.InstallationReferrerServiceStarter;


Copy link
Contributor

Choose a reason for hiding this comment

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

There are two newlines between the import statements and class. Can we have only one for code consistency?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

addressed in 7d4e91a

import org.wordpress.android.util.AppLog;
import org.wordpress.android.util.AppLog.T;


Copy link
Contributor

Choose a reason for hiding this comment

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

There are two newlines between the import statements and class. Can we have only one for code consistency?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

addressed in 93b4956

new InstallationReferrerServiceLogic(this, this);
logic.performTask(
new Bundle(params.getExtras()),
params);
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we move this new ... and params); code to the previous line for readability?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

addressed in 93b4956

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
AppLog.i(T.UTILS, "installation referrer service > task: " + startId + " started");
mInstallationReferrerServiceLogic.performTask(intent.getExtras(), Integer.valueOf(startId));
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we need the Integer.valueOf() boxing since startId is an int.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good call! addressed in 695974b

// mark referrer as obtained so we don't try fetching it again
// Citing here:
// Caution: The install referrer information will be available for 90 days and won't
// change unless the application is reinstalled. To avoid unecessary API calls in your
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we change unecessary to unnecessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Wow! Nice catch @theck13 :D it's copied from Google's documentation 😅
https://developer.android.com/google/play/installreferrer/library
screen shot 2018-09-12 at 17 35 49

fixed in 91c4c58

break;
case InstallReferrerResponse.SERVICE_UNAVAILABLE:
// Connection could not be established
// same as above, this is a retriable error
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we change this is a retriable error to this error can be retried?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

addressed in 0cd3efc

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants