Merge "Import updated Android SetupCompat Library 515497412" into udc-dev
diff --git a/bts/java/com/google/android/setupcompat/bts/AbstractSetupBtsReceiver.java b/bts/java/com/google/android/setupcompat/bts/AbstractSetupBtsReceiver.java
deleted file mode 100644
index 9e15e30..0000000
--- a/bts/java/com/google/android/setupcompat/bts/AbstractSetupBtsReceiver.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
- *
- * 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.google.android.setupcompat.bts;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Binder;
-import androidx.annotation.NonNull;
-import com.google.android.setupcompat.internal.Preconditions;
-import com.google.android.setupcompat.util.Logger;
-import java.util.concurrent.Executor;
-
-/** Class to receive broadcast intent from SUW, and execute the client's task in the executor. */
-public abstract class AbstractSetupBtsReceiver extends BroadcastReceiver {
-  private static final Logger LOG = new Logger(AbstractSetupBtsReceiver.class);
-
-  @Override
-  public void onReceive(Context context, Intent intent) {
-    if (intent != null && getIntentAction().equals(intent.getAction())) {
-      Executor executor = getExecutor();
-      String simpleClassName = this.getClass().getSimpleName();
-      if (executor != null) {
-        executor.execute(
-            () -> {
-              Preconditions.ensureNotOnMainThread(simpleClassName + "::onStartTask");
-              onStartTask();
-            });
-      }
-    } else {
-      LOG.w(
-          "["
-              + this.getClass().getSimpleName()
-              + "] Unauthorized binder uid="
-              + Binder.getCallingUid()
-              + ", intentAction="
-              + (intent == null ? "(null)" : intent.getAction()));
-    }
-  }
-
-  /**
-   * Gets the intent action that expected to execute the task. Use to avoid the receiver launch
-   * unexpectedly.
-   */
-  @NonNull
-  protected abstract String getIntentAction();
-
-  /** Returns the executor used to execute the task. */
-  @NonNull
-  protected abstract Executor getExecutor();
-
-  /** Tasks can be done before activity launched, in order to remove the loading before activity. */
-  protected abstract void onStartTask();
-}
diff --git a/bts/java/com/google/android/setupcompat/bts/AbstractSetupBtsService.java b/bts/java/com/google/android/setupcompat/bts/AbstractSetupBtsService.java
index eb10693..80066ab 100644
--- a/bts/java/com/google/android/setupcompat/bts/AbstractSetupBtsService.java
+++ b/bts/java/com/google/android/setupcompat/bts/AbstractSetupBtsService.java
@@ -256,7 +256,7 @@
 
         for (Signature signature : info.signingInfo.getApkContentsSigners()) {
           if (SETUP_WIZARD_RELEASE_CERTIFICATE_STRING.equals(signature.toCharsString())
-              || (allowDebugKeys
+              || (isAllowDebugKeysOrBuild()
                   && SETUP_WIZARD_DEBUG_CERTIFICATE_STRING.equals(signature.toCharsString()))) {
             return true;
           }
@@ -273,6 +273,10 @@
     return false;
   }
 
+  private boolean isAllowDebugKeysOrBuild() {
+    return Build.TYPE.equals("userdebug") || Build.TYPE.equals("eng") || allowDebugKeys;
+  }
+
   @VisibleForTesting
   boolean verifyCallingAppPermission() {
     int checkPermission =
diff --git a/main/java/com/google/android/setupcompat/logging/SetupMetricsLogger.java b/main/java/com/google/android/setupcompat/logging/SetupMetricsLogger.java
index 786494e..fab38a2 100644
--- a/main/java/com/google/android/setupcompat/logging/SetupMetricsLogger.java
+++ b/main/java/com/google/android/setupcompat/logging/SetupMetricsLogger.java
@@ -16,8 +16,10 @@
 
 package com.google.android.setupcompat.logging;
 
+import android.annotation.SuppressLint;
 import android.content.Context;
 import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
 import com.google.android.setupcompat.internal.Preconditions;
 import com.google.android.setupcompat.internal.SetupCompatServiceInvoker;
 import com.google.android.setupcompat.logging.internal.MetricBundleConverter;
@@ -25,7 +27,10 @@
 import com.google.android.setupcompat.util.Logger;
 import java.util.concurrent.TimeUnit;
 
-/** SetupMetricsLogger provides an easy way to log custom metrics to SetupWizard. */
+/**
+ * SetupMetricsLogger provides an easy way to log custom metrics to SetupWizard.
+ * (go/suw-metrics-collection-api)
+ */
 public class SetupMetricsLogger {
 
   private static final Logger LOG = new Logger("SetupMetricsLogger");
@@ -76,7 +81,7 @@
   }
 
   /**
-   * Logs setup collection metrics (go/suw-metrics-collection-api)
+   * Logs setup collection metrics
    */
   public static void logMetrics(
       @NonNull Context context, @NonNull ScreenKey screenKey, @NonNull SetupMetric... metrics) {
@@ -92,4 +97,46 @@
           MetricBundleConverter.createBundleForLoggingSetupMetric(screenKey, metric));
     }
   }
+
+  /**
+   * A non-static method to log setup collection metrics calling
+   * {@link #logMetrics(Context, ScreenKey, SetupMetric...)} as the actual implementation. This
+   * function is useful when performing unit tests in caller's implementation.
+   * <p>
+   * For unit testing, caller uses {@link #setInstanceForTesting(SetupMetricsLogger)} to inject the
+   * mocked SetupMetricsLogger instance and use {@link SetupMetricsLogger#get(Context)} to get the
+   * SetupMetricsLogger. And verify the this function is called with expected parameters.
+   *
+   * @see #logMetrics(Context, ScreenKey, SetupMetric...)
+   */
+  public void logMetrics(@NonNull ScreenKey screenKey, @NonNull SetupMetric... metrics) {
+    SetupMetricsLogger.logMetrics(context, screenKey, metrics);
+  }
+
+  private SetupMetricsLogger(Context context) {
+    this.context = context;
+  }
+
+  private final Context context;
+
+  /** Use this function to get a singleton of {@link SetupMetricsLogger} */
+  public static synchronized SetupMetricsLogger get(Context context) {
+    if (instance == null) {
+      instance = new SetupMetricsLogger(context.getApplicationContext());
+    }
+
+    return instance;
+  }
+
+  @VisibleForTesting
+  public static void setInstanceForTesting(SetupMetricsLogger testInstance) {
+    instance = testInstance;
+  }
+
+  // The instance is coming from Application context which alive during the application activate and
+  // it's not depend on the activities life cycle, so we can avoid memory leak. However linter
+  // cannot distinguish Application context or activity context, so we add @SuppressLint to avoid
+  // lint error.
+  @SuppressLint("StaticFieldLeak")
+  private static SetupMetricsLogger instance;
 }