package org.mozilla.vrbrowser.telemetry; import android.content.Context; import android.content.res.Resources; import android.os.StrictMode; import android.os.SystemClock; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.UiThread; import org.mozilla.telemetry.Telemetry; import org.mozilla.telemetry.TelemetryHolder; import org.mozilla.telemetry.config.TelemetryConfiguration; import org.mozilla.telemetry.event.TelemetryEvent; import org.mozilla.telemetry.measurement.SearchesMeasurement; import org.mozilla.telemetry.net.TelemetryClient; import org.mozilla.telemetry.ping.TelemetryCorePingBuilder; import org.mozilla.telemetry.ping.TelemetryMobileEventPingBuilder; import org.mozilla.telemetry.schedule.TelemetryScheduler; import org.mozilla.telemetry.schedule.jobscheduler.JobSchedulerTelemetryScheduler; import org.mozilla.telemetry.serialize.JSONPingSerializer; import org.mozilla.telemetry.storage.FileTelemetryStorage; import org.mozilla.vrbrowser.BuildConfig; import org.mozilla.vrbrowser.R; import org.mozilla.vrbrowser.browser.SettingsStore; import org.mozilla.vrbrowser.browser.engine.EngineProvider; import org.mozilla.vrbrowser.search.SearchEngineWrapper; import org.mozilla.vrbrowser.utils.DeviceType; import org.mozilla.vrbrowser.utils.SystemUtils; import org.mozilla.vrbrowser.utils.UrlUtils; import java.net.URI; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import mozilla.components.concept.fetch.Client; import mozilla.components.lib.fetch.httpurlconnection.HttpURLConnectionClient; import static java.lang.Math.toIntExact; import static org.mozilla.vrbrowser.ui.widgets.Windows.MAX_WINDOWS; import static org.mozilla.vrbrowser.ui.widgets.Windows.WindowPlacement; public class TelemetryWrapper { private final static String APP_NAME = "FirefoxReality"; private final static String LOGTAG = SystemUtils.createLogtag(TelemetryWrapper.class); private final static int MIN_LOAD_TIME = 40; private final static int LOADING_BUCKET_SIZE_MS = 100; private final static int MIN_IMMERSIVE_TIME = 1000; private final static int IMMERSIVE_BUCKET_SIZE_MS = 10000; private final static int HISTOGRAM_MIN_INDEX = 0; private final static int HISTOGRAM_SIZE = 200; private static HashSet<String> domainMap = new HashSet<String>(); private static int[] loadingTimeHistogram = new int[HISTOGRAM_SIZE]; private static int[] immersiveHistogram = new int[HISTOGRAM_SIZE]; private static int numUri = 0; private static long startLoadPageTime = 0; private static long startImmersiveTime = 0; private static long sessionStartTime = 0; // Multi-window events private final static int MULTI_WINDOW_BIN_SIZE_MS = 10000; private static HashMap<Integer, Long> windowLifetime = new HashMap<>(); private static int windowsMovesCount = 0; private static int windowsResizesCount = 0; private static long[] activePlacementStartTime = new long[MAX_WINDOWS]; private static long[] activePlacementTime = new long[MAX_WINDOWS]; private static long[] openWindowsStartTime = new long[MAX_WINDOWS]; private static long[] openPrivateWindowsStartTime = new long[MAX_WINDOWS]; private static long[] openWindowsTime = new long[MAX_WINDOWS]; private static long[] openPrivateWindowsTime = new long[MAX_WINDOWS]; private static int[] openWindows = new int[MAX_WINDOWS]; private static int[] openPrivateWindows = new int[MAX_WINDOWS]; private static TelemetryHistogram windowsLifetimeHistogram = new TelemetryHistogram(HISTOGRAM_SIZE, MULTI_WINDOW_BIN_SIZE_MS, 0); private class Category { private static final String ACTION = "action"; private static final String HISTOGRAM = "histogram"; } private class Method { private static final String FOREGROUND = "foreground"; private static final String BACKGROUND = "background"; private static final String OPEN = "open"; private static final String TYPE_URL = "type_url"; private static final String TYPE_QUERY = "type_query"; // TODO: Support "select_query" after providing search suggestion. private static final String VOICE_QUERY = "voice_query"; private static final String IMMERSIVE_MODE = "immersive_mode"; private static final String TELEMETRY_STATUS = "status"; // How long is a window open for / window life private static final String WINDOW_LIFETIME = "window_lifetime"; // Frequency of window moves private static final String WINDOWS_MOVES_FREQ = "windows_move_freq"; // Frequency of window resizes private static final String WINDOWS_RESIZE_FREQ = "windows_resize_freq"; // When a session is multi-window, what time is each position the active window private static final String PLACEMENTS_ACTIVE_TIME = "place_active_time"; // When a session is multi-window, what time are one, two or three windows open private static final String OPEN_WINDOWS_TIME = "open_windows_time"; // The weight of windows are opened at a time, per session private static final String WINDOWS_OPEN_W = "windows_open_w"; } private class Object { private static final String APP = "app"; private static final String BROWSER = "browser"; private static final String SEARCH_BAR = "search_bar"; private static final String VOICE_INPUT = "voice_input"; private static final String WINDOW = "window"; } private class Extra { private static final String TOTAL_URI_COUNT = "total_uri_count"; private static final String UNIQUE_DOMAINS_COUNT = "unique_domains_count"; private static final String WINDOW_MOVES_COUNT = "windows_moves_count"; private static final String WINDOW_RESIZE_COUNT = "windows_resize_count"; private static final String LEFT_WINDOW_ACTIVE_TIME = "left_window_active_time"; private static final String FRONT_WINDOW_ACTIVE_TIME = "front_window_active_time"; private static final String RIGHT_WINDOW_ACTIVE_TIME = "right_window_active_time"; private static final String ONE_OPEN_WINDOWS_TIME = "one_w_open_time"; private static final String TWO_OPEN_WINDOWS_TIME = "two_w_open_time"; private static final String THREE_OPEN_WINDOWS_TIME = "thr_w_open_time"; private static final String ONE_WINDOWS_OPENED = "one_w_open"; private static final String TWO_WINDOWS_OPENED = "two_w_open"; private static final String THREE_WINDOWS_OPENED = "thr_w_open"; private static final String ONE_PRIVATE_WINDOWS_OPENED = "one_pri_w_open"; private static final String TWO_PRIVATE_WINDOWS_OPENED = "two_pri_w_open"; private static final String THREE_PRIVATE_WINDOWS_OPENED = "thr_pri_w_open"; private static final String TELEMETRY_STATUS = "telemetry_status"; } // We should call this at the application initial stage. Instead, // it would be called when users turn on/off the setting of telemetry. // e.g., SettingsStore.getInstance(context).setTelemetryEnabled(); public static void init(@NonNull Context aContext, @NonNull Client client) { // When initializing the telemetry library it will make sure that all directories exist and // are readable/writable. final StrictMode.ThreadPolicy threadPolicy = StrictMode.allowThreadDiskWrites(); try { final Resources resources = aContext.getResources(); final boolean telemetryEnabled = SettingsStore.getInstance(aContext).isTelemetryEnabled(); final TelemetryConfiguration configuration = new TelemetryConfiguration(aContext) .setServerEndpoint("https://incoming.telemetry.mozilla.org") .setAppName(APP_NAME + "_" + DeviceType.getDeviceTypeId()) .setUpdateChannel(BuildConfig.BUILD_TYPE) .setPreferencesImportantForTelemetry(resources.getString(R.string.settings_key_locale)) .setCollectionEnabled(telemetryEnabled) .setUploadEnabled(telemetryEnabled) // We need to set this to 1 as we want the telemetry opt-in/out ping always to be sent and the minimum is 3 by default. .setMinimumEventsForUpload(1) .setBuildId(String.valueOf(BuildConfig.VERSION_CODE)); final JSONPingSerializer serializer = new JSONPingSerializer(); final FileTelemetryStorage storage = new FileTelemetryStorage(configuration, serializer); TelemetryScheduler scheduler; if (DeviceType.isOculus6DOFBuild()) { scheduler = new FxRTelemetryScheduler(); } else { scheduler = new JobSchedulerTelemetryScheduler(); } TelemetryHolder.set(new Telemetry(configuration, storage, new TelemetryClient(client), scheduler) .addPingBuilder(new TelemetryCorePingBuilder(configuration)) .addPingBuilder(new TelemetryMobileEventPingBuilder(configuration))); // Check if the Telemetry status has ever been saved (enabled/disabled) boolean saved = SettingsStore.getInstance(aContext).telemetryStatusSaved(); // Check if we have already sent the previous status event boolean sent = SettingsStore.getInstance(aContext).isTelemetryPingUpdateSent(); // If the Telemetry status has been changed but that ping has not been sent, we send it now // This should only been true for versions of the app prior to implementing the Telemetry status ping // We only send the status ping if it was disabled if (saved && !sent && !telemetryEnabled) { telemetryStatus(false); SettingsStore.getInstance(aContext).setTelemetryPingUpdateSent(true); } } finally { StrictMode.setThreadPolicy(threadPolicy); } sessionStartTime = SystemClock.elapsedRealtime(); } @UiThread public static void start() { // Call Telemetry.scheduleUpload() early. // See https://github.com/MozillaReality/FirefoxReality/issues/1353 TelemetryHolder.get() .queuePing(TelemetryCorePingBuilder.TYPE) .queuePing(TelemetryMobileEventPingBuilder.TYPE) .scheduleUpload(); TelemetryHolder.get().recordSessionStart(); TelemetryEvent.create(Category.ACTION, Method.FOREGROUND, Object.APP).queue(); } @UiThread public static void stop() { queueHistogram(); queueMultiWindowEvents(); TelemetryEvent.create(Category.ACTION, Method.BACKGROUND, Object.APP).queue(); TelemetryHolder.get().recordSessionEnd(); TelemetryHolder.get() .queuePing(TelemetryCorePingBuilder.TYPE) .queuePing(TelemetryMobileEventPingBuilder.TYPE) .scheduleUpload(); } @UiThread public static void urlBarEvent(boolean aIsUrl) { if (aIsUrl) { TelemetryWrapper.browseEvent(); } else { TelemetryWrapper.searchEnterEvent(); } } @UiThread public static void voiceInputEvent() { Telemetry telemetry = TelemetryHolder.get(); TelemetryEvent.create(Category.ACTION, Method.VOICE_QUERY, Object.VOICE_INPUT).queue(); String searchEngine = getDefaultSearchEngineIdentifierForTelemetry(telemetry.getConfiguration().getContext()); telemetry.recordSearch(SearchesMeasurement.LOCATION_ACTIONBAR, searchEngine); } private static String getDefaultSearchEngineIdentifierForTelemetry(Context aContext) { return SearchEngineWrapper.get(aContext).getResourceURL(); } private static void queueHistogram() { // Upload loading time histogram TelemetryEvent loadingHistogramEvent = TelemetryEvent.create(Category.HISTOGRAM, Method.FOREGROUND, Object.BROWSER); for (int bucketIndex = 0; bucketIndex < loadingTimeHistogram.length; ++bucketIndex) { loadingHistogramEvent.extra(Integer.toString(bucketIndex * LOADING_BUCKET_SIZE_MS), Integer.toString(loadingTimeHistogram[bucketIndex])); } loadingHistogramEvent.queue(); // Clear loading histogram array after queueing it loadingTimeHistogram = new int[HISTOGRAM_SIZE]; // Upload immersive time histogram TelemetryEvent immersiveHistogramEvent = TelemetryEvent.create(Category.HISTOGRAM, Method.IMMERSIVE_MODE, Object.BROWSER); for (int bucketIndex = 0; bucketIndex < immersiveHistogram.length; ++bucketIndex) { immersiveHistogramEvent.extra(Integer.toString(bucketIndex * IMMERSIVE_BUCKET_SIZE_MS), Integer.toString(immersiveHistogram[bucketIndex])); } immersiveHistogramEvent.queue(); // Clear loading histogram array after queueing it immersiveHistogram = new int[HISTOGRAM_SIZE]; // We only upload the domain and URI counts to the probes without including // users' URI info. TelemetryEvent.create(Category.ACTION, Method.OPEN, Object.BROWSER).extra( Extra.UNIQUE_DOMAINS_COUNT, Integer.toString(domainMap.size()) ).queue(); domainMap.clear(); TelemetryEvent.create(Category.ACTION, Method.OPEN, Object.BROWSER).extra( Extra.TOTAL_URI_COUNT, Integer.toString(numUri) ).queue(); numUri = 0; } private static void searchEnterEvent() { Telemetry telemetry = TelemetryHolder.get(); TelemetryEvent.create(Category.ACTION, Method.TYPE_QUERY, Object.SEARCH_BAR).queue(); String searchEngine = getDefaultSearchEngineIdentifierForTelemetry(telemetry.getConfiguration().getContext()); telemetry.recordSearch(SearchesMeasurement.LOCATION_ACTIONBAR, searchEngine); } private static void browseEvent() { TelemetryEvent event = TelemetryEvent.create(Category.ACTION, Method.TYPE_URL, Object.SEARCH_BAR); // TODO: Working on autocomplete result. event.queue(); } @UiThread public static void startPageLoadTime() { startLoadPageTime = SystemClock.elapsedRealtime(); } @UiThread public static void uploadPageLoadToHistogram(String uri) { if (startLoadPageTime == 0) { return; } if (uri == null) { return; } try { URI uriLink = URI.create(uri); if (uriLink.getHost() == null) { return; } domainMap.add(UrlUtils.stripCommonSubdomains(uriLink.getHost())); numUri++; long elapsedLoad = SystemClock.elapsedRealtime() - startLoadPageTime; if (elapsedLoad < MIN_LOAD_TIME) { return; } int histogramLoadIndex = toIntExact(elapsedLoad / LOADING_BUCKET_SIZE_MS); Log.d(LOGTAG, "Sent load to histogram"); if (histogramLoadIndex > (HISTOGRAM_SIZE - 2)) { histogramLoadIndex = HISTOGRAM_SIZE - 1; Log.e(LOGTAG, "the loading histogram size is overflow."); } else if (histogramLoadIndex < HISTOGRAM_MIN_INDEX) { histogramLoadIndex = HISTOGRAM_MIN_INDEX; } loadingTimeHistogram[histogramLoadIndex]++; } catch (IllegalArgumentException e) { Log.e(LOGTAG, "Invalid URL", e); } } @UiThread public static void startImmersive() { startImmersiveTime = SystemClock.elapsedRealtime(); } @UiThread public static void uploadImmersiveToHistogram() { if (startImmersiveTime == 0) { return; } long elapsedImmersive = SystemClock.elapsedRealtime() - startImmersiveTime; if (elapsedImmersive < MIN_IMMERSIVE_TIME) { return; } int histogramImmersiveIndex = toIntExact(elapsedImmersive / IMMERSIVE_BUCKET_SIZE_MS); Log.i(LOGTAG, "Send immersive time spent to histogram."); if (histogramImmersiveIndex > (HISTOGRAM_SIZE - 2)) { histogramImmersiveIndex = HISTOGRAM_SIZE - 1; Log.e(LOGTAG, "the immersive histogram size is overflow."); } else if (histogramImmersiveIndex < HISTOGRAM_MIN_INDEX) { histogramImmersiveIndex = HISTOGRAM_MIN_INDEX; } immersiveHistogram[histogramImmersiveIndex]++; } /** * Helper method for queuing histograms. This will transform the raw histogram into * a Telemetry historam event and queue it for future delivery. * @param histogram The histogram to be queued * @param method The TelemetryEvent method String * @param object The TelemetryEvent object String */ private static void queueHistogram(@NonNull TelemetryHistogram histogram, @NonNull String method, @NonNull String object) { TelemetryEvent event = TelemetryEvent.create(Category.HISTOGRAM, method, object); int[] hist = histogram.getHistogram(); for (int bucketIndex = 0; bucketIndex < hist.length; ++bucketIndex) { event.extra( Integer.toString(bucketIndex * histogram.getBinSize()), Integer.toString(hist[bucketIndex])); Log.d(LOGTAG, "\tHistogram bucket: [" + "" + bucketIndex * histogram.getBinSize() + ", " + hist[bucketIndex] + "]"); } event.queue(); } // Multi-window related events public static void queueMultiWindowEvents() { // Queue windows lifetime histogram queueWindowsLifetimeHistogram(); // Queue Windows moves freq during the session queueWindowsMovesCountEvent(); // Queue Windows resizes freq during the session queueWindowsResizesCountEvent(); // Queue Windows active time. queueActiveWindowTimeEvent(); // Queue Windows active weight. queueOpenWindowsWeightEvent(); // Queue open Windows time pct queueOpenWindowsTimeEvent(); } public static void openWindowEvent(int windowId) { windowLifetime.put(windowId, SystemClock.elapsedRealtime()); } public static void closeWindowEvent(int windowId) { windowsLifetimeHistogram.addData(SystemClock.elapsedRealtime() - windowLifetime.get(windowId)); windowLifetime.remove(windowId); } public static void windowsMoveEvent() { windowsMovesCount++; Log.d(LOGTAG, "Windows moves: " + windowsMovesCount); } public static void windowsResizeEvent() { windowsResizesCount++; Log.d(LOGTAG, "Windows resizes: " + windowsResizesCount); } public static void activePlacementEvent(int from, boolean active) { if (active) { activePlacementStartTime[from] = SystemClock.elapsedRealtime(); } else { if (activePlacementStartTime[from] != 0) { activePlacementTime[from] += SystemClock.elapsedRealtime() - activePlacementStartTime[from]; activePlacementStartTime[from] = 0; } } Log.d(LOGTAG, "Placements times:"); Log.d(LOGTAG, "\tFRONT: " + activePlacementTime[WindowPlacement.FRONT.getValue()]); Log.d(LOGTAG, "\tLEFT: " + activePlacementTime[WindowPlacement.LEFT.getValue()]); Log.d(LOGTAG, "\tRIGHT: " + activePlacementTime[WindowPlacement.RIGHT.getValue()]); } public static void openWindowsEvent(int from, int to, boolean isPrivate) { if (isPrivate) { if (from > 0) { openPrivateWindowsTime[from-1] += SystemClock.elapsedRealtime() - openPrivateWindowsStartTime[from-1]; openPrivateWindowsStartTime[from-1] = 0; } if (to > 0) { openPrivateWindows[to-1]++; openPrivateWindowsStartTime[to-1] = SystemClock.elapsedRealtime(); } Log.d(LOGTAG, "Placements times (private):"); Log.d(LOGTAG, "\tONE: " + openPrivateWindowsTime[WindowPlacement.FRONT.getValue()]); Log.d(LOGTAG, "\tTWO: " + openPrivateWindowsTime[WindowPlacement.LEFT.getValue()]); Log.d(LOGTAG, "\tTHREE: " + openPrivateWindowsTime[WindowPlacement.RIGHT.getValue()]); Log.d(LOGTAG, "Open Windows Count (private):"); Log.d(LOGTAG, "\tFRONT: " + openPrivateWindows[WindowPlacement.FRONT.getValue()]); Log.d(LOGTAG, "\tLEFT: " + openPrivateWindows[WindowPlacement.LEFT.getValue()]); Log.d(LOGTAG, "\tRIGHT: " + openPrivateWindows[WindowPlacement.RIGHT.getValue()]); } else { if (from > 0) { openWindowsTime[from-1] += SystemClock.elapsedRealtime() - openWindowsStartTime[from-1]; openWindowsStartTime[from-1] = 0; } if (to > 0) { openWindows[to-1]++; openWindowsStartTime[to-1] = SystemClock.elapsedRealtime(); } Log.d(LOGTAG, "Placements times:"); Log.d(LOGTAG, "\tONE: " + openWindowsTime[WindowPlacement.FRONT.getValue()]); Log.d(LOGTAG, "\tTWO: " + openWindowsTime[WindowPlacement.LEFT.getValue()]); Log.d(LOGTAG, "\tTHREE: " + openWindowsTime[WindowPlacement.RIGHT.getValue()]); Log.d(LOGTAG, "Open Windows Count:"); Log.d(LOGTAG, "\tFRONT: " + openWindows[WindowPlacement.FRONT.getValue()]); Log.d(LOGTAG, "\tLEFT: " + openWindows[WindowPlacement.LEFT.getValue()]); Log.d(LOGTAG, "\tRIGHT: " + openWindows[WindowPlacement.RIGHT.getValue()]); } } private static void queueWindowsLifetimeHistogram() { for (Map.Entry<Integer, Long> entry : windowLifetime.entrySet()) { windowsLifetimeHistogram.addData(SystemClock.elapsedRealtime() - entry.getValue()); } Log.d(LOGTAG, "[Queue] Windows Lifetime Histogram:"); queueHistogram(windowsLifetimeHistogram, Method.WINDOW_LIFETIME, Object.WINDOW); windowsLifetimeHistogram = new TelemetryHistogram(HISTOGRAM_SIZE, MULTI_WINDOW_BIN_SIZE_MS, 0); for(Map.Entry<Integer, Long> entry : windowLifetime.entrySet()) { windowLifetime.put(entry.getKey(), SystemClock.elapsedRealtime()); } } private static void queueWindowsMovesCountEvent() { TelemetryEvent event = TelemetryEvent.create(Category.ACTION, Method.WINDOWS_MOVES_FREQ, Object.WINDOW); event.extra(Extra.WINDOW_MOVES_COUNT, Integer.toString(windowsMovesCount)); event.queue(); Log.d(LOGTAG, "[Queue] Windows Moves per session: " + windowsMovesCount); windowsMovesCount = 0; } private static void queueWindowsResizesCountEvent() { TelemetryEvent event = TelemetryEvent.create(Category.ACTION, Method.WINDOWS_RESIZE_FREQ, Object.WINDOW); event.extra(Extra.WINDOW_RESIZE_COUNT, Integer.toString(windowsResizesCount)); event.queue(); Log.d(LOGTAG, "[Queue] Windows Resizes per session: " + windowsResizesCount); windowsResizesCount = 0; } private static void queueActiveWindowTimeEvent() { for (int index = 0; index< MAX_WINDOWS; index++) { if (activePlacementStartTime[index] != 0) { activePlacementTime[index] += SystemClock.elapsedRealtime() - activePlacementStartTime[index]; activePlacementStartTime[index] = SystemClock.elapsedRealtime(); } } TelemetryEvent event = TelemetryEvent.create(Category.ACTION, Method.PLACEMENTS_ACTIVE_TIME, Object.WINDOW); event.extra(Extra.LEFT_WINDOW_ACTIVE_TIME, String.valueOf(activePlacementTime[WindowPlacement.LEFT.getValue()])); event.extra(Extra.FRONT_WINDOW_ACTIVE_TIME, String.valueOf(activePlacementTime[WindowPlacement.FRONT.getValue()])); event.extra(Extra.RIGHT_WINDOW_ACTIVE_TIME, String.valueOf(activePlacementTime[WindowPlacement.RIGHT.getValue()])); event.queue(); Log.d(LOGTAG, "[Queue] Placements Active time total:"); Log.d(LOGTAG, "\tFRONT: " + activePlacementTime[WindowPlacement.FRONT.getValue()]); Log.d(LOGTAG, "\tLEFT: " + activePlacementTime[WindowPlacement.LEFT.getValue()]); Log.d(LOGTAG, "\tRIGHT: " + activePlacementTime[WindowPlacement.RIGHT.getValue()]); for (int index = 0; index< MAX_WINDOWS; index++) { activePlacementTime[index] = 0; } } public static void resetOpenedWindowsCount(int number, boolean isPrivate) { if (isPrivate) { for (int i=0; i<openPrivateWindows.length; i++) { openPrivateWindows[i] = 0; } if (number > 0) { openPrivateWindows[number-1] = 1; } } else { for (int i=0; i<openWindows.length; i++) { openWindows[i] = 0; } if (number > 0) { openWindows[number-1] = 1; } } } private static void queueOpenWindowsWeightEvent() { TelemetryEvent event = TelemetryEvent.create(Category.ACTION, Method.WINDOWS_OPEN_W, Object.WINDOW); event.extra(Extra.ONE_WINDOWS_OPENED, String.valueOf(openWindows[0])); event.extra(Extra.TWO_WINDOWS_OPENED, String.valueOf(openWindows[1])); event.extra(Extra.THREE_WINDOWS_OPENED, String.valueOf(openWindows[2])); event.extra(Extra.ONE_PRIVATE_WINDOWS_OPENED, String.valueOf(openPrivateWindows[0])); event.extra(Extra.TWO_PRIVATE_WINDOWS_OPENED, String.valueOf(openPrivateWindows[1])); event.extra(Extra.THREE_PRIVATE_WINDOWS_OPENED, String.valueOf(openPrivateWindows[2])); event.queue(); Log.d(LOGTAG, "[Queue] Open Windows Number:"); Log.d(LOGTAG, "\tRegular 1: " + openWindows[0]); Log.d(LOGTAG, "\tRegular 2: " + openWindows[1]); Log.d(LOGTAG, "\tRegular 3: " + openWindows[2]); Log.d(LOGTAG, "\tPrivate 1: " + openPrivateWindows[0]); Log.d(LOGTAG, "\tPrivate 2: " + openPrivateWindows[1]); Log.d(LOGTAG, "\tPrivate 3: " + openPrivateWindows[2]); for (int index = 0; index< MAX_WINDOWS; index++) { if (openWindows[index] != 0) { openWindows[index] = 1; } if (openPrivateWindows[index] != 0) { openPrivateWindows[index] = 1; } } } private static void queueOpenWindowsTimeEvent() { for (int index = 0; index< MAX_WINDOWS; index++) { if (openWindowsStartTime[index] != 0) { openWindowsTime[index] += SystemClock.elapsedRealtime() - openWindowsStartTime[index]; openWindowsStartTime[index] = SystemClock.elapsedRealtime(); } if (openPrivateWindowsStartTime[index] != 0) { openPrivateWindowsTime[index] += SystemClock.elapsedRealtime() - openPrivateWindowsStartTime[index]; openPrivateWindowsStartTime[index] = SystemClock.elapsedRealtime(); } } TelemetryEvent event = TelemetryEvent.create(Category.ACTION, Method.OPEN_WINDOWS_TIME, Object.WINDOW); event.extra(Extra.ONE_OPEN_WINDOWS_TIME, String.valueOf(openWindowsTime[0]+openPrivateWindowsTime[0])); event.extra(Extra.TWO_OPEN_WINDOWS_TIME, String.valueOf(openWindowsTime[1]+openPrivateWindowsTime[1])); event.extra(Extra.THREE_OPEN_WINDOWS_TIME, String.valueOf(openWindowsTime[2]+openPrivateWindowsTime[2])); event.queue(); Log.d(LOGTAG, "[Queue] Open Windows time:"); Log.d(LOGTAG, "\tONE: " + String.valueOf(openWindowsTime[0] + openPrivateWindowsTime[0])); Log.d(LOGTAG, "\tTWO: " + String.valueOf(openWindowsTime[1] + openPrivateWindowsTime[1])); Log.d(LOGTAG, "\tTHREE: " + String.valueOf(openWindowsTime[2] + openPrivateWindowsTime[2])); for (int index = 0; index< MAX_WINDOWS; index++) { openWindowsTime[index] = 0; openPrivateWindowsTime[index] = 0; } } public static void telemetryStatus(boolean status) { TelemetryEvent event = TelemetryEvent.create(Category.ACTION, Method.TELEMETRY_STATUS, Object.APP); event.extra(Extra.TELEMETRY_STATUS, String.valueOf(status)); event.queue(); // We flush immediately as the Telemetry is going to be turned off in case of opting-out // and we want to make sure that this ping is delivered. TelemetryHolder.get() .queuePing(TelemetryCorePingBuilder.TYPE) .queuePing(TelemetryMobileEventPingBuilder.TYPE) .scheduleUpload(); } }