• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.android.setupcompat.logging;
18 
19 import android.annotation.SuppressLint;
20 import android.content.Context;
21 import androidx.annotation.NonNull;
22 import androidx.annotation.VisibleForTesting;
23 import com.google.android.setupcompat.internal.Preconditions;
24 import com.google.android.setupcompat.internal.SetupCompatServiceInvoker;
25 import com.google.android.setupcompat.logging.internal.MetricBundleConverter;
26 import com.google.android.setupcompat.logging.internal.SetupMetricsLoggingConstants.MetricType;
27 import com.google.android.setupcompat.util.Logger;
28 import java.util.concurrent.TimeUnit;
29 
30 /**
31  * SetupMetricsLogger provides an easy way to log custom metrics to SetupWizard.
32  * (go/suw-metrics-collection-api)
33  */
34 public class SetupMetricsLogger {
35 
36   private static final Logger LOG = new Logger("SetupMetricsLogger");
37 
38   /** Logs an instance of {@link CustomEvent} to SetupWizard. */
logCustomEvent(@onNull Context context, @NonNull CustomEvent customEvent)39   public static void logCustomEvent(@NonNull Context context, @NonNull CustomEvent customEvent) {
40     Preconditions.checkNotNull(context, "Context cannot be null.");
41     Preconditions.checkNotNull(customEvent, "CustomEvent cannot be null.");
42     SetupCompatServiceInvoker.get(context)
43         .logMetricEvent(
44             MetricType.CUSTOM_EVENT, MetricBundleConverter.createBundleForLogging(customEvent));
45   }
46 
47   /** Increments the counter value with the name {@code counterName} by {@code times}. */
logCounter( @onNull Context context, @NonNull MetricKey counterName, int times)48   public static void logCounter(
49       @NonNull Context context, @NonNull MetricKey counterName, int times) {
50     Preconditions.checkNotNull(context, "Context cannot be null.");
51     Preconditions.checkNotNull(counterName, "CounterName cannot be null.");
52     Preconditions.checkArgument(times > 0, "Counter cannot be negative.");
53     SetupCompatServiceInvoker.get(context)
54         .logMetricEvent(
55             MetricType.COUNTER_EVENT,
56             MetricBundleConverter.createBundleForLoggingCounter(counterName, times));
57   }
58 
59   /**
60    * Logs the {@link Timer}'s duration by calling {@link #logDuration(Context, MetricKey, long)}.
61    */
logDuration(@onNull Context context, @NonNull Timer timer)62   public static void logDuration(@NonNull Context context, @NonNull Timer timer) {
63     Preconditions.checkNotNull(context, "Context cannot be null.");
64     Preconditions.checkNotNull(timer, "Timer cannot be null.");
65     Preconditions.checkArgument(
66         timer.isStopped(), "Timer should be stopped before calling logDuration.");
67     logDuration(
68         context, timer.getMetricKey(), TimeUnit.NANOSECONDS.toMillis(timer.getDurationInNanos()));
69   }
70 
71   /** Logs a duration event to SetupWizard. */
logDuration( @onNull Context context, @NonNull MetricKey timerName, long timeInMillis)72   public static void logDuration(
73       @NonNull Context context, @NonNull MetricKey timerName, long timeInMillis) {
74     Preconditions.checkNotNull(context, "Context cannot be null.");
75     Preconditions.checkNotNull(timerName, "Timer name cannot be null.");
76     Preconditions.checkArgument(timeInMillis >= 0, "Duration cannot be negative.");
77     SetupCompatServiceInvoker.get(context)
78         .logMetricEvent(
79             MetricType.DURATION_EVENT,
80             MetricBundleConverter.createBundleForLoggingTimer(timerName, timeInMillis));
81   }
82 
83   /**
84    * Logs setup collection metrics
85    */
logMetrics( @onNull Context context, @NonNull ScreenKey screenKey, @NonNull SetupMetric... metrics)86   public static void logMetrics(
87       @NonNull Context context, @NonNull ScreenKey screenKey, @NonNull SetupMetric... metrics) {
88     Preconditions.checkNotNull(context, "Context cannot be null.");
89     Preconditions.checkNotNull(screenKey, "ScreenKey cannot be null.");
90     Preconditions.checkNotNull(metrics, "SetupMetric cannot be null.");
91 
92     for (SetupMetric metric : metrics) {
93       LOG.atDebug("Log metric: " + screenKey + ", " + metric);
94 
95       SetupCompatServiceInvoker.get(context).logMetricEvent(
96           MetricType.SETUP_COLLECTION_EVENT,
97           MetricBundleConverter.createBundleForLoggingSetupMetric(screenKey, metric));
98     }
99   }
100 
101   /**
102    * A non-static method to log setup collection metrics calling
103    * {@link #logMetrics(Context, ScreenKey, SetupMetric...)} as the actual implementation. This
104    * function is useful when performing unit tests in caller's implementation.
105    * <p>
106    * For unit testing, caller uses {@link #setInstanceForTesting(SetupMetricsLogger)} to inject the
107    * mocked SetupMetricsLogger instance and use {@link SetupMetricsLogger#get(Context)} to get the
108    * SetupMetricsLogger. And verify the this function is called with expected parameters.
109    *
110    * @see #logMetrics(Context, ScreenKey, SetupMetric...)
111    */
logMetrics(@onNull ScreenKey screenKey, @NonNull SetupMetric... metrics)112   public void logMetrics(@NonNull ScreenKey screenKey, @NonNull SetupMetric... metrics) {
113     SetupMetricsLogger.logMetrics(context, screenKey, metrics);
114   }
115 
SetupMetricsLogger(Context context)116   private SetupMetricsLogger(Context context) {
117     this.context = context;
118   }
119 
120   private final Context context;
121 
122   /** Use this function to get a singleton of {@link SetupMetricsLogger} */
get(Context context)123   public static synchronized SetupMetricsLogger get(Context context) {
124     if (instance == null) {
125       instance = new SetupMetricsLogger(context.getApplicationContext());
126     }
127 
128     return instance;
129   }
130 
131   @VisibleForTesting
setInstanceForTesting(SetupMetricsLogger testInstance)132   public static void setInstanceForTesting(SetupMetricsLogger testInstance) {
133     instance = testInstance;
134   }
135 
136   // The instance is coming from Application context which alive during the application activate and
137   // it's not depend on the activities life cycle, so we can avoid memory leak. However linter
138   // cannot distinguish Application context or activity context, so we add @SuppressLint to avoid
139   // lint error.
140   @SuppressLint("StaticFieldLeak")
141   private static SetupMetricsLogger instance;
142 }
143