<commit_before>// Copyright 2019 Google LLC
//
// 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.firebase.installations;

import android.text.TextUtils;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.google.android.gms.common.internal.Preconditions;
import com.google.android.gms.tasks.Task;
import com.google.android.gms.tasks.TaskCompletionSource;
import com.google.android.gms.tasks.Tasks;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseException;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.heartbeatinfo.HeartBeatInfo;
import com.google.firebase.installations.FirebaseInstallationsException.Status;
import com.google.firebase.installations.local.IidStore;
import com.google.firebase.installations.local.PersistedInstallation;
import com.google.firebase.installations.local.PersistedInstallationEntry;
import com.google.firebase.installations.remote.FirebaseInstallationServiceClient;
import com.google.firebase.installations.remote.InstallationResponse;
import com.google.firebase.installations.remote.TokenResult;
import com.google.firebase.platforminfo.UserAgentPublisher;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Entry point for Firebase installations.
 *
 * <p>The Firebase installations service:
 *
 * <ul>
 *   <li>provides a unique identifier for a Firebase installation
 *   <li>provides an auth token for a Firebase installation
 *   <li>provides a API to perform GDPR-compliant deletion of a Firebase installation.
 * </ul>
 */
public class FirebaseInstallations implements FirebaseInstallationsApi {
  private final FirebaseApp firebaseApp;
  private final FirebaseInstallationServiceClient serviceClient;
  private final PersistedInstallation persistedInstallation;
  private final Utils utils;
  private final IidStore iidStore;
  private final RandomFidGenerator fidGenerator;
  private final Object lock = new Object();
  private final ExecutorService backgroundExecutor;
  private final ExecutorService networkExecutor;

  @GuardedBy("lock")
  private final List<StateListener> listeners = new ArrayList<>();

  /* used for thread-level synchronization of generating and persisting fids */
  private static final Object lockGenerateFid = new Object();
  /* file used for process-level synchronization of generating and persisting fids */
  private static final String LOCKFILE_NAME_GENERATE_FID = "generatefid.lock";
  private static final String CHIME_FIREBASE_APP_NAME = "CHIME_ANDROID_SDK";
  private static final int CORE_POOL_SIZE = 0;
  private static final int MAXIMUM_POOL_SIZE = 1;
  private static final long KEEP_ALIVE_TIME_IN_SECONDS = 30L;
  private static final ThreadFactory THREAD_FACTORY =
      new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);

        @Override
        public Thread newThread(Runnable r) {
          return new Thread(
              r, String.format("firebase-installations-executor-%d", mCount.getAndIncrement()));
        }
      };

  /** package private constructor. */
  FirebaseInstallations(
      FirebaseApp firebaseApp,
      @Nullable UserAgentPublisher publisher,
      @Nullable HeartBeatInfo heartbeatInfo) {
    this(
        new ThreadPoolExecutor(
            CORE_POOL_SIZE,
            MAXIMUM_POOL_SIZE,
            KEEP_ALIVE_TIME_IN_SECONDS,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(),
            THREAD_FACTORY),
        firebaseApp,
        new FirebaseInstallationServiceClient(
            firebaseApp.getApplicationContext(), publisher, heartbeatInfo),
        new PersistedInstallation(firebaseApp),
        new Utils(),
        new IidStore(firebaseApp),
        new RandomFidGenerator());
  }

  FirebaseInstallations(
      ExecutorService backgroundExecutor,
      FirebaseApp firebaseApp,
      FirebaseInstallationServiceClient serviceClient,
      PersistedInstallation persistedInstallation,
      Utils utils,
      IidStore iidStore,
      RandomFidGenerator fidGenerator) {
    this.firebaseApp = firebaseApp;
    this.serviceClient = serviceClient;
    this.persistedInstallation = persistedInstallation;
    this.utils = utils;
    this.iidStore = iidStore;
    this.fidGenerator = fidGenerator;
    this.backgroundExecutor = backgroundExecutor;
    this.networkExecutor =
        new ThreadPoolExecutor(
            CORE_POOL_SIZE,
            MAXIMUM_POOL_SIZE,
            KEEP_ALIVE_TIME_IN_SECONDS,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(),
            THREAD_FACTORY);
  }

  /**
   * Perform pre-condition checks to make sure {@link FirebaseOptions#getApiKey()}, {@link
   * FirebaseOptions#getApplicationId()} , and ({@link FirebaseOptions#getProjectId()} are not null
   * or empty.
   */
  private static void preConditionChecks(@NonNull FirebaseApp app) {
    Preconditions.checkNotEmpty(app.getOptions().getApplicationId());
    Preconditions.checkNotEmpty(app.getOptions().getProjectId());
    Preconditions.checkNotEmpty(app.getOptions().getApiKey());
    Preconditions.checkArgument(
        Utils.isValidAppIdFormat(app.getOptions().getApplicationId()),
        "Please set your Application ID. A valid Firebase App ID is required to communicate "
            + "with Firebase server APIs: It identifies your application with Firebase."
            + "Please refer to https://firebase.google.com/support/privacy/init-options.");
    Preconditions.checkArgument(
        Utils.isValidApiKeyFormat(app.getOptions().getApiKey()),
        "Please set a valid API key. A Firebase API key is required to communicate with "
            + "Firebase server APIs: It authenticates your project with Google."
            + "Please refer to https://firebase.google.com/support/privacy/init-options.");
  }

  /** Returns the Project Id or Project Number for the Firebase Project. */
  @Nullable
  String getProjectIdentifier() {
    return firebaseApp.getOptions().getProjectId();
  }

  /**
   * Returns the {@link FirebaseInstallations} initialized with the default {@link FirebaseApp}.
   *
   * @return a {@link FirebaseInstallations} instance
   */
  @NonNull
  public static FirebaseInstallations getInstance() {
    FirebaseApp defaultFirebaseApp = FirebaseApp.getInstance();
    return getInstance(defaultFirebaseApp);
  }

  /**
   * Returns the {@link FirebaseInstallations} initialized with a custom {@link FirebaseApp}.
   *
   * @param app a custom {@link FirebaseApp}
   * @return a {@link FirebaseInstallations} instance
   */
  @NonNull
  public static FirebaseInstallations getInstance(@NonNull FirebaseApp app) {
    Preconditions.checkArgument(app != null, "Null is not a valid value of FirebaseApp.");
    preConditionChecks(app);
    return (FirebaseInstallations) app.get(FirebaseInstallationsApi.class);
  }

  /** Returns the application id of the {@link FirebaseApp} of this {@link FirebaseInstallations} */
  @VisibleForTesting
  String getApplicationId() {
    return firebaseApp.getOptions().getApplicationId();
  }

  /** API key used to identify your app to Google servers. */
  @Nullable
  String getApiKey() {
    return firebaseApp.getOptions().getApiKey();
  }

  /** Returns the nick name of the {@link FirebaseApp} of this {@link FirebaseInstallations} */
  @VisibleForTesting
  String getName() {
    return firebaseApp.getName();
  }

  /**
   * Returns a globally unique identifier of this Firebase app installation. This is a url-safe
   * base64 string of a 128-bit integer.
   */
  @NonNull
  @Override
  public Task<String> getId() {
    Task<String> task = addGetIdListener();
    backgroundExecutor.execute(this::doGetId);
    return task;
  }

  /**
   * Returns a valid authentication token for the Firebase installation. Generates a new token if
   * one doesn't exist, is expired, or is about to expire.
   *
   * <p>Should only be called if the Firebase installation is registered.
   *
   * @param forceRefresh Options to get an auth token either by force refreshing or not.
   */
  @NonNull
  @Override
  public Task<InstallationTokenResult> getToken(boolean forceRefresh) {
    Task<InstallationTokenResult> task = addGetAuthTokenListener();
    if (forceRefresh) {
      backgroundExecutor.execute(this::doGetAuthTokenForceRefresh);
    } else {
      backgroundExecutor.execute(this::doGetAuthTokenWithoutForceRefresh);
    }
    return task;
  }

  /**
   * Call to delete this Firebase app installation from the Firebase backend. This call may cause
   * Firebase Cloud Messaging, Firebase Remote Config, Firebase Predictions, or Firebase In-App
   * Messaging to not function properly.
   */
  @NonNull
  @Override
  public Task<Void> delete() {
    return Tasks.call(backgroundExecutor, this::deleteFirebaseInstallationId);
  }

  private Task<String> addGetIdListener() {<commit_msg>Revert "Move FIS precondition format check to installation constructor" (#1508)<commit_after>// Copyright 2019 Google LLC
//
// 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.firebase.installations;

import android.text.TextUtils;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.google.android.gms.common.internal.Preconditions;
import com.google.android.gms.tasks.Task;
import com.google.android.gms.tasks.TaskCompletionSource;
import com.google.android.gms.tasks.Tasks;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseException;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.heartbeatinfo.HeartBeatInfo;
import com.google.firebase.installations.FirebaseInstallationsException.Status;
import com.google.firebase.installations.local.IidStore;
import com.google.firebase.installations.local.PersistedInstallation;
import com.google.firebase.installations.local.PersistedInstallationEntry;
import com.google.firebase.installations.remote.FirebaseInstallationServiceClient;
import com.google.firebase.installations.remote.InstallationResponse;
import com.google.firebase.installations.remote.TokenResult;
import com.google.firebase.platforminfo.UserAgentPublisher;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Entry point for Firebase installations.
 *
 * <p>The Firebase installations service:
 *
 * <ul>
 *   <li>provides a unique identifier for a Firebase installation
 *   <li>provides an auth token for a Firebase installation
 *   <li>provides a API to perform GDPR-compliant deletion of a Firebase installation.
 * </ul>
 */
public class FirebaseInstallations implements FirebaseInstallationsApi {
  private final FirebaseApp firebaseApp;
  private final FirebaseInstallationServiceClient serviceClient;
  private final PersistedInstallation persistedInstallation;
  private final Utils utils;
  private final IidStore iidStore;
  private final RandomFidGenerator fidGenerator;
  private final Object lock = new Object();
  private final ExecutorService backgroundExecutor;
  private final ExecutorService networkExecutor;

  @GuardedBy("lock")
  private final List<StateListener> listeners = new ArrayList<>();

  /* used for thread-level synchronization of generating and persisting fids */
  private static final Object lockGenerateFid = new Object();
  /* file used for process-level synchronization of generating and persisting fids */
  private static final String LOCKFILE_NAME_GENERATE_FID = "generatefid.lock";
  private static final String CHIME_FIREBASE_APP_NAME = "CHIME_ANDROID_SDK";
  private static final int CORE_POOL_SIZE = 0;
  private static final int MAXIMUM_POOL_SIZE = 1;
  private static final long KEEP_ALIVE_TIME_IN_SECONDS = 30L;
  private static final ThreadFactory THREAD_FACTORY =
      new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);

        @Override
        public Thread newThread(Runnable r) {
          return new Thread(
              r, String.format("firebase-installations-executor-%d", mCount.getAndIncrement()));
        }
      };

  /** package private constructor. */
  FirebaseInstallations(
      FirebaseApp firebaseApp,
      @Nullable UserAgentPublisher publisher,
      @Nullable HeartBeatInfo heartbeatInfo) {
    this(
        new ThreadPoolExecutor(
            CORE_POOL_SIZE,
            MAXIMUM_POOL_SIZE,
            KEEP_ALIVE_TIME_IN_SECONDS,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(),
            THREAD_FACTORY),
        firebaseApp,
        new FirebaseInstallationServiceClient(
            firebaseApp.getApplicationContext(), publisher, heartbeatInfo),
        new PersistedInstallation(firebaseApp),
        new Utils(),
        new IidStore(firebaseApp),
        new RandomFidGenerator());
  }

  FirebaseInstallations(
      ExecutorService backgroundExecutor,
      FirebaseApp firebaseApp,
      FirebaseInstallationServiceClient serviceClient,
      PersistedInstallation persistedInstallation,
      Utils utils,
      IidStore iidStore,
      RandomFidGenerator fidGenerator) {
    this.firebaseApp = firebaseApp;
    this.serviceClient = serviceClient;
    this.persistedInstallation = persistedInstallation;
    this.utils = utils;
    this.iidStore = iidStore;
    this.fidGenerator = fidGenerator;
    this.backgroundExecutor = backgroundExecutor;
    this.networkExecutor =
        new ThreadPoolExecutor(
            CORE_POOL_SIZE,
            MAXIMUM_POOL_SIZE,
            KEEP_ALIVE_TIME_IN_SECONDS,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(),
            THREAD_FACTORY);
  }

  /**
   * Perform pre-condition checks to make sure {@link FirebaseOptions#getApiKey()}, {@link
   * FirebaseOptions#getApplicationId()} , and ({@link FirebaseOptions#getProjectId()} are not null
   * or empty.
   */
  private void preConditionChecks() {
    Preconditions.checkNotEmpty(getApplicationId());
    Preconditions.checkNotEmpty(getProjectIdentifier());
    Preconditions.checkNotEmpty(getApiKey());
    Preconditions.checkArgument(
        Utils.isValidAppIdFormat(getApplicationId()),
        "Please set your Application ID. A valid Firebase App ID is required to communicate "
            + "with Firebase server APIs: It identifies your application with Firebase."
            + "Please refer to https://firebase.google.com/support/privacy/init-options.");
    Preconditions.checkArgument(
        Utils.isValidApiKeyFormat(getApiKey()),
        "Please set a valid API key. A Firebase API key is required to communicate with "
            + "Firebase server APIs: It authenticates your project with Google."
            + "Please refer to https://firebase.google.com/support/privacy/init-options.");
  }

  /** Returns the Project Id or Project Number for the Firebase Project. */
  @Nullable
  String getProjectIdentifier() {
    return firebaseApp.getOptions().getProjectId();
  }

  /**
   * Returns the {@link FirebaseInstallations} initialized with the default {@link FirebaseApp}.
   *
   * @return a {@link FirebaseInstallations} instance
   */
  @NonNull
  public static FirebaseInstallations getInstance() {
    FirebaseApp defaultFirebaseApp = FirebaseApp.getInstance();
    return getInstance(defaultFirebaseApp);
  }

  /**
   * Returns the {@link FirebaseInstallations} initialized with a custom {@link FirebaseApp}.
   *
   * @param app a custom {@link FirebaseApp}
   * @return a {@link FirebaseInstallations} instance
   */
  @NonNull
  public static FirebaseInstallations getInstance(@NonNull FirebaseApp app) {
    Preconditions.checkArgument(app != null, "Null is not a valid value of FirebaseApp.");
    return (FirebaseInstallations) app.get(FirebaseInstallationsApi.class);
  }

  /** Returns the application id of the {@link FirebaseApp} of this {@link FirebaseInstallations} */
  @VisibleForTesting
  String getApplicationId() {
    return firebaseApp.getOptions().getApplicationId();
  }

  /** API key used to identify your app to Google servers. */
  @Nullable
  String getApiKey() {
    return firebaseApp.getOptions().getApiKey();
  }

  /** Returns the nick name of the {@link FirebaseApp} of this {@link FirebaseInstallations} */
  @VisibleForTesting
  String getName() {
    return firebaseApp.getName();
  }

  /**
   * Returns a globally unique identifier of this Firebase app installation. This is a url-safe
   * base64 string of a 128-bit integer.
   */
  @NonNull
  @Override
  public Task<String> getId() {
    preConditionChecks();
    Task<String> task = addGetIdListener();
    backgroundExecutor.execute(this::doGetId);
    return task;
  }

  /**
   * Returns a valid authentication token for the Firebase installation. Generates a new token if
   * one doesn't exist, is expired, or is about to expire.
   *
   * <p>Should only be called if the Firebase installation is registered.
   *
   * @param forceRefresh Options to get an auth token either by force refreshing or not.
   */
  @NonNull
  @Override
  public Task<InstallationTokenResult> getToken(boolean forceRefresh) {
    preConditionChecks();
    Task<InstallationTokenResult> task = addGetAuthTokenListener();
    if (forceRefresh) {
      backgroundExecutor.execute(this::doGetAuthTokenForceRefresh);
    } else {
      backgroundExecutor.execute(this::doGetAuthTokenWithoutForceRefresh);
    }
    return task;
  }

  /**
   * Call to delete this Firebase app installation from the Firebase backend. This call may cause
   * Firebase Cloud Messaging, Firebase Remote Config, Firebase Predictions, or Firebase In-App
   * Messaging to not function properly.
   */
  @NonNull
  @Override
  public Task<Void> delete() {
    return Tasks.call(backgroundExecutor, this::deleteFirebaseInstallationId);
  }

  private Task<String> addGetIdListener() {
{'fraction_non_alphanumeric': 0.08408730767264734, 'fraction_numerical': 0.0020446761744108777, 'mean_word_length': 3.942900454775139, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 37, 'https://': 4, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2282468', 'n_tokens_mistral': 5341, 'n_tokens_neox': 5237, 'n_words': 1499}