
com.android.tools.analytics.UsageTracker Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tracker Show documentation
Show all versions of tracker Show documentation
Library for tracking usage analytics.
The newest version!
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Eclipse Public License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.eclipse.org/org/documents/epl-v10.php
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.analytics;
import com.android.annotations.NonNull;
import com.android.annotations.VisibleForTesting;
import com.android.utils.DateProvider;
import com.android.utils.ILogger;
import com.google.wireless.android.play.playlog.proto.ClientAnalytics;
import com.google.wireless.android.sdk.stats.AndroidStudioEvent;
import com.google.wireless.android.sdk.stats.ProductDetails;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.UUID;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* UsageTracker is an api to report usage of features. This data is used to improve
* future versions of Android Studio and related tools.
*
* The tracker has an API to logDetails usage (in the form of protobuf messages).
* A separate system called the Analytics Publisher takes the logs and sends them
* to Google's servers for analysis.
*/
public abstract class UsageTracker implements AutoCloseable {
private static final Object sGate = new Object();
@VisibleForTesting static String sSessionId = UUID.randomUUID().toString();
@VisibleForTesting public static DateProvider sDateProvider = DateProvider.SYSTEM;
private static UsageTracker sInstance = new NullUsageTracker(new AnalyticsSettings(), null);
private final AnalyticsSettings mAnalyticsSettings;
private final ScheduledExecutorService mScheduler;
private int mMaxJournalSize;
private long mMaxJournalTime;
private String mVersion;
@VisibleForTesting protected long mStartTimeMs = sDateProvider.now().getTime();
protected UsageTracker(
AnalyticsSettings analyticsSettings, ScheduledExecutorService scheduler) {
this.mAnalyticsSettings = analyticsSettings;
this.mScheduler = scheduler;
}
/**
* Indicates whether this UsageTracker has a maximum size at which point logs need to be flushed.
* Zero or less indicates no maximum size at which to flush.
*/
public int getMaxJournalSize() {
return mMaxJournalSize;
}
/*
* Sets a maximum size at which point logs need to be flushed. Zero or less indicates no
* flushing until @{link #close()} is called.
*/
public void setMaxJournalSize(int maxJournalSize) {
this.mMaxJournalSize = maxJournalSize;
}
/**
* Indicates whether this UsageTracker has a timeout at which point logs need to be flushed.
* Zero or less indicates no timeout is set.
*
* @return timeout in nano-seconds.
*/
public long getMaxJournalTime() {
return mMaxJournalTime;
}
/**
* Sets a timeout at which point logs need to be flushed. Zero or less indicates no timeout
* should be used.
*/
public void setMaxJournalTime(long duration, TimeUnit unit) {
this.mMaxJournalTime = unit.toNanos(duration);
}
/**
* Gets the version specified for this UsageTracker. This version when specified is used
* to populate the product_details.version field of AndroidStudioEvent at time of logging
* As the version of the product generating the event can be different of the version uploading
* the event.
*/
@NonNull public String getVersion() {
return mVersion;
}
/**
* Set the version specified for this UsageTracker. This version when specified is used
* to populate the product_details.version field of AndroidStudioEvent at time of logging
* As the version of the product generating the event can be different of the version uploading
* the event.
*/
public void setVersion(@NonNull String version) {
mVersion = version;
}
/** Gets the analytics settings used by this tracker. */
public AnalyticsSettings getAnalyticsSettings() {
return mAnalyticsSettings;
}
/** Gets the scheduler used by this tracker. */
public ScheduledExecutorService getScheduler() {
return mScheduler;
}
/** Logs usage data provided in the @{link AndroidStudioEvent}. */
public void log(@NonNull AndroidStudioEvent.Builder studioEvent) {
studioEvent.setStudioSessionId(sSessionId);
if (mVersion != null && !studioEvent.hasProductDetails()) {
studioEvent.setProductDetails(ProductDetails.newBuilder().setVersion(mVersion));
}
long now = sDateProvider.now().getTime();
try {
logDetails(
ClientAnalytics.LogEvent.newBuilder()
.setEventTimeMs(now)
.setEventUptimeMs(now - mStartTimeMs)
.setSourceExtension(studioEvent.build().toByteString()));
} catch (NullPointerException exception) {
// TODO: Temporary fix for http://b.android.com/224994. We should remove this try-catch
// block once there is a permanent fix.
logDetails(
ClientAnalytics.LogEvent.newBuilder()
.setEventTimeMs(now)
.setEventUptimeMs(now - mStartTimeMs));
}
}
/**
* Logs usage data provided in the @{link ClientAnalytics.LogEvent}. Normally using {#log} is
* preferred please talk to this code's author if you need {@link #logDetails} instead.
*/
public abstract void logDetails(@NonNull ClientAnalytics.LogEvent.Builder logEvent);
/**
* Gets an instance of the {@link UsageTracker} that has been initialized correctly for this process.
*/
@NonNull
public static UsageTracker getInstance() {
synchronized (sGate) {
return sInstance;
}
}
/**
* Initializes a {@link UsageTracker} for use throughout this process based on user opt-in and
* other settings.
*/
public static UsageTracker initialize(
@NonNull AnalyticsSettings analyticsSettings,
@NonNull ScheduledExecutorService scheduler) {
synchronized (sGate) {
if (analyticsSettings.hasOptedIn()) {
sInstance =
new JournalingUsageTracker(
analyticsSettings,
scheduler,
Paths.get(AnalyticsPaths.getSpoolDirectory()));
} else {
sInstance = new NullUsageTracker(analyticsSettings, scheduler);
}
return sInstance;
}
}
/**
* Sets the global instance to the provided tracker so tests can provide their own UsageTracker
* implementation. NOTE: Should only be used from tests.
*/
@VisibleForTesting
public static UsageTracker setInstanceForTest(UsageTracker tracker) {
return sInstance = tracker;
}
/**
* resets the global instance to the null usage tracker, to clean state in tests. NOTE: Should
* only be used from tests.
*/
@VisibleForTesting
public static void cleanAfterTesting() {
sInstance = new NullUsageTracker(new AnalyticsSettings(), null);
}
public static AnalyticsSettings updateSettingsAndTracker(
boolean optIn, @NonNull ILogger logger, @NonNull ScheduledExecutorService scheduler) {
UsageTracker current = getInstance();
AnalyticsSettings settings = AnalyticsSettings.getInstance(logger);
if (optIn != settings.hasOptedIn()) {
settings.setHasOptedIn(optIn);
try {
settings.saveSettings();
} catch (IOException e) {
logger.error(e, "Unable to save analytics settings");
}
}
try {
current.close();
} catch (Exception e) {
logger.error(e, "Unable to close existing analytics tracker");
}
initialize(settings, scheduler);
return settings;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy