/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.appevents.suggestedevents

import android.content.Context
import android.content.SharedPreferences
import android.view.View
import com.facebook.FacebookSdk
import com.facebook.appevents.codeless.internal.ViewHierarchy
import com.facebook.appevents.internal.ViewHierarchyConstants.CLASS_NAME_KEY
import com.facebook.appevents.internal.ViewHierarchyConstants.TEXT_KEY
import com.facebook.internal.Utility.jsonStrToMap
import com.facebook.internal.Utility.mapToJsonStr
import com.facebook.internal.Utility.sha256hash
import com.facebook.internal.instrument.crashshield.AutoHandleExceptions
import java.util.concurrent.atomic.AtomicBoolean
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject

@AutoHandleExceptions
internal object PredictionHistoryManager {
  private val clickedViewPaths: MutableMap<String, String> = mutableMapOf()
  private const val SUGGESTED_EVENTS_HISTORY = "SUGGESTED_EVENTS_HISTORY"
  private const val CLICKED_PATH_STORE = "com.facebook.internal.SUGGESTED_EVENTS_HISTORY"
  private lateinit var shardPreferences: SharedPreferences
  private val initialized = AtomicBoolean(false)
  private fun initAndWait() {
    if (initialized.get()) {
      return
    }
    shardPreferences =
        FacebookSdk.getApplicationContext()
            .getSharedPreferences(CLICKED_PATH_STORE, Context.MODE_PRIVATE)
    clickedViewPaths.putAll(
        jsonStrToMap(shardPreferences.getString(SUGGESTED_EVENTS_HISTORY, "") ?: ""))
    initialized.set(true)
  }

  @JvmStatic
  fun addPrediction(pathID: String, predictedEvent: String) {
    if (!initialized.get()) {
      initAndWait()
    }
    clickedViewPaths[pathID] = predictedEvent
    shardPreferences
        .edit()
        .putString(SUGGESTED_EVENTS_HISTORY, mapToJsonStr(clickedViewPaths.toMap()))
        .apply()
  }

  @JvmStatic
  fun getPathID(view: View, text: String): String? {
    var view: View? = view
    val pathRoute = JSONObject()
    try {
      pathRoute.put(TEXT_KEY, text)
      val currentPath = JSONArray()
      while (view != null) {
        currentPath.put(view.javaClass.simpleName)
        view = ViewHierarchy.getParentOfView(view) as View?
      }
      pathRoute.put(CLASS_NAME_KEY, currentPath)
    } catch (je: JSONException) {
      /*no op*/
    }
    return sha256hash(pathRoute.toString())
  }

  @JvmStatic
  fun queryEvent(pathID: String): String? {
    return if (clickedViewPaths.containsKey(pathID)) clickedViewPaths[pathID] else null
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.appevents.suggestedevents

import android.app.Activity
import androidx.annotation.RestrictTo
import androidx.annotation.VisibleForTesting
import com.facebook.FacebookSdk
import com.facebook.appevents.internal.ActivityLifecycleTracker.getCurrentActivity
import com.facebook.appevents.ml.ModelManager
import com.facebook.appevents.ml.ModelManager.getRuleFile
import com.facebook.appevents.suggestedevents.FeatureExtractor.initialize
import com.facebook.appevents.suggestedevents.FeatureExtractor.isInitialized
import com.facebook.internal.FetchedAppSettingsManager.queryAppSettings
import com.facebook.internal.instrument.crashshield.AutoHandleExceptions
import java.util.concurrent.atomic.AtomicBoolean
import org.json.JSONObject

@AutoHandleExceptions
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
object SuggestedEventsManager {
  private val enabled = AtomicBoolean(false)
  private val productionEvents: MutableSet<String> = mutableSetOf()
  private val eligibleEvents: MutableSet<String> = mutableSetOf()
  private const val PRODUCTION_EVENTS_KEY = "production_events"
  private const val ELIGIBLE_EVENTS_KEY = "eligible_for_prediction_events"

  @Synchronized
  @JvmStatic
  fun enable() {
    FacebookSdk.getExecutor()
        .execute(
            Runnable {
              if (enabled.get()) {
                return@Runnable
              }
              enabled.set(true)
              initialize()
            })
  }

  private fun initialize() {
    try {
      val settings = queryAppSettings(FacebookSdk.getApplicationId(), false) ?: return
      val rawSuggestedEventSetting = settings.suggestedEventsSetting ?: return
      populateEventsFromRawJsonString(rawSuggestedEventSetting)
      if (productionEvents.isNotEmpty() || eligibleEvents.isNotEmpty()) {
        val ruleFile = getRuleFile(ModelManager.Task.MTML_APP_EVENT_PREDICTION) ?: return
        initialize(ruleFile)
        val currActivity = getCurrentActivity()
        if (currActivity != null) {
          trackActivity(currActivity)
        }
      }
    } catch (e: Exception) {
      /*no op*/
    }
  }

  @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
  internal fun populateEventsFromRawJsonString(rawSuggestedEventSetting: String?) {
    try {
      val jsonObject = JSONObject(rawSuggestedEventSetting)
      if (jsonObject.has(PRODUCTION_EVENTS_KEY)) {
        val jsonArray = jsonObject.getJSONArray(PRODUCTION_EVENTS_KEY)
        for (i in 0 until jsonArray.length()) {
          productionEvents.add(jsonArray.getString(i))
        }
      }
      if (jsonObject.has(ELIGIBLE_EVENTS_KEY)) {
        val jsonArray = jsonObject.getJSONArray(ELIGIBLE_EVENTS_KEY)
        for (i in 0 until jsonArray.length()) {
          eligibleEvents.add(jsonArray.getString(i))
        }
      }
    } catch (e: Exception) {
      /*noop*/
    }
  }

  @JvmStatic
  fun trackActivity(activity: Activity) {
    try {
      if (enabled.get() &&
          isInitialized() &&
          (!productionEvents.isEmpty() || !eligibleEvents.isEmpty())) {
        ViewObserver.startTrackingActivity(activity)
      } else {
        ViewObserver.stopTrackingActivity(activity)
      }
    } catch (e: Exception) {
      /*no op*/
    }
  }

  @JvmStatic
  fun isEnabled(): Boolean {
    return enabled.get()
  }

  @JvmStatic
  internal fun isProductionEvents(event: String): Boolean {
    return productionEvents.contains(event)
  }

  @JvmStatic
  internal fun isEligibleEvents(event: String): Boolean {
    return eligibleEvents.contains(event)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.appevents.suggestedevents

import android.text.TextUtils
import android.view.View
import android.widget.AdapterView
import android.widget.DatePicker
import android.widget.EditText
import android.widget.RadioGroup
import android.widget.RatingBar
import android.widget.Spinner
import android.widget.Switch
import android.widget.TimePicker
import com.facebook.appevents.codeless.internal.ViewHierarchy
import com.facebook.appevents.internal.ViewHierarchyConstants.CHILDREN_VIEW_KEY
import com.facebook.appevents.internal.ViewHierarchyConstants.CLASS_NAME_KEY
import com.facebook.appevents.internal.ViewHierarchyConstants.CLASS_TYPE_BITMASK_KEY
import com.facebook.appevents.internal.ViewHierarchyConstants.HINT_KEY
import com.facebook.appevents.internal.ViewHierarchyConstants.INPUT_TYPE_KEY
import com.facebook.appevents.internal.ViewHierarchyConstants.IS_INTERACTED_KEY
import com.facebook.appevents.internal.ViewHierarchyConstants.TEXT_KEY
import com.facebook.internal.instrument.crashshield.AutoHandleExceptions
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject

@AutoHandleExceptions
internal object SuggestedEventViewHierarchy {
  private val blacklistedViews: List<Class<out View>> =
      listOf(
          Switch::class.java,
          Spinner::class.java,
          DatePicker::class.java,
          TimePicker::class.java,
          RadioGroup::class.java,
          RatingBar::class.java,
          EditText::class.java,
          AdapterView::class.java)

  @JvmStatic
  fun getDictionaryOfView(view: View, clickedView: View): JSONObject {
    val json = JSONObject()
    try {
      if (view === clickedView) {
        json.put(IS_INTERACTED_KEY, true)
      }
      updateBasicInfo(view, json)
      val childViews = JSONArray()
      val children = ViewHierarchy.getChildrenOfView(view)
      for (child in children) {
        val childInfo = getDictionaryOfView(child, clickedView)
        childViews.put(childInfo)
      }
      json.put(CHILDREN_VIEW_KEY, childViews)
    } catch (e: JSONException) {
      /*no op*/
    }
    return json
  }

  @JvmStatic
  fun updateBasicInfo(view: View, json: JSONObject) {
    try {
      val text = ViewHierarchy.getTextOfView(view)
      val hint = ViewHierarchy.getHintOfView(view)
      json.put(CLASS_NAME_KEY, view.javaClass.simpleName)
      json.put(CLASS_TYPE_BITMASK_KEY, ViewHierarchy.getClassTypeBitmask(view))
      if (text.isNotEmpty()) {
        json.put(TEXT_KEY, text)
      }
      if (hint.isNotEmpty()) {
        json.put(HINT_KEY, hint)
      }
      if (view is EditText) {
        json.put(INPUT_TYPE_KEY, view.inputType)
      }
    } catch (e: JSONException) {
      /*no op*/
    }
  }

  @JvmStatic
  fun getAllClickableViews(view: View): List<View> {
    val clickableViews: MutableList<View> = ArrayList()
    for (viewClass in blacklistedViews) {
      if (viewClass.isInstance(view)) {
        return clickableViews
      }
    }
    if (view.isClickable) {
      clickableViews.add(view)
    }
    val children = ViewHierarchy.getChildrenOfView(view)
    for (child in children) {
      clickableViews.addAll(getAllClickableViews(child))
    }
    return clickableViews
  }

  @JvmStatic
  fun getTextOfViewRecursively(hostView: View): String {
    val text = ViewHierarchy.getTextOfView(hostView)
    if (text.isNotEmpty()) {
      return text
    }
    val childrenText = getTextOfChildren(hostView)
    return TextUtils.join(" ", childrenText)
  }

  private fun getTextOfChildren(view: View): List<String?> {
    val childrenText: MutableList<String?> = ArrayList()
    val childrenView = ViewHierarchy.getChildrenOfView(view)
    for (childView in childrenView) {
      val childText = ViewHierarchy.getTextOfView(childView)
      if (childText.isNotEmpty()) {
        childrenText.add(childText)
      }
      childrenText.addAll(getTextOfChildren(childView))
    }
    return childrenText
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.appevents.suggestedevents

import android.app.Activity
import android.os.Handler
import android.os.Looper
import android.view.ViewTreeObserver
import com.facebook.appevents.codeless.internal.SensitiveUserDataUtils
import com.facebook.appevents.internal.AppEventUtility.getRootView
import com.facebook.appevents.suggestedevents.ViewOnClickListener.Companion.attachListener
import com.facebook.internal.instrument.crashshield.AutoHandleExceptions
import java.lang.ref.WeakReference
import java.util.concurrent.atomic.AtomicBoolean

@AutoHandleExceptions
internal class ViewObserver private constructor(activity: Activity) :
    ViewTreeObserver.OnGlobalLayoutListener {
  private val activityWeakReference: WeakReference<Activity> = WeakReference(activity)
  private val uiThreadHandler: Handler = Handler(Looper.getMainLooper())
  private val isTracking: AtomicBoolean = AtomicBoolean(false)
  private fun startTracking() {
    if (isTracking.getAndSet(true)) {
      return
    }
    val rootView = getRootView(activityWeakReference.get()) ?: return
    val observer = rootView.viewTreeObserver
    if (observer.isAlive) {
      observer.addOnGlobalLayoutListener(this)
      process()
    }
  }

  private fun stopTracking() {
    if (!isTracking.getAndSet(false)) {
      return
    }
    val rootView = getRootView(activityWeakReference.get()) ?: return
    val observer = rootView.viewTreeObserver
    if (!observer.isAlive) {
      return
    }
    observer.removeOnGlobalLayoutListener(this)
  }

  override fun onGlobalLayout() {
    process()
  }

  private fun process() {
    val runnable = Runnable {
      try {
        val rootView = getRootView(activityWeakReference.get())
        val activity = activityWeakReference.get()
        if (rootView == null || activity == null) {
          return@Runnable
        }
        val clickableViews = SuggestedEventViewHierarchy.getAllClickableViews(rootView)
        for (view in clickableViews) {
          if (SensitiveUserDataUtils.isSensitiveUserData(view)) {
            continue
          }
          val text = SuggestedEventViewHierarchy.getTextOfViewRecursively(view)
          if (text.isNotEmpty() && text.length <= MAX_TEXT_LENGTH) {
            attachListener(view, rootView, activity.localClassName)
          }
        }
      } catch (e: Exception) {
        /*no op*/
      }
    }
    if (Thread.currentThread() === Looper.getMainLooper().thread) {
      runnable.run()
    } else {
      uiThreadHandler.post(runnable)
    }
  }

  companion object {
    private val observers: MutableMap<Int, ViewObserver> = HashMap()
    private const val MAX_TEXT_LENGTH = 300

    @JvmStatic
    fun startTrackingActivity(activity: Activity) {
      val key = activity.hashCode()
      val observer = observers.getOrPut(key) { ViewObserver(activity) }
      observer.startTracking()
    }

    @JvmStatic
    fun stopTrackingActivity(activity: Activity) {
      val key = activity.hashCode()
      observers.remove(key)?.stopTracking()
    }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.appevents.suggestedevents

import android.os.Bundle
import android.view.View
import androidx.annotation.RestrictTo
import com.facebook.FacebookSdk
import com.facebook.GraphRequest.Companion.newPostRequest
import com.facebook.appevents.InternalAppEventsLogger
import com.facebook.appevents.codeless.internal.ViewHierarchy
import com.facebook.appevents.internal.ViewHierarchyConstants.SCREEN_NAME_KEY
import com.facebook.appevents.internal.ViewHierarchyConstants.VIEW_KEY
import com.facebook.appevents.ml.ModelManager
import com.facebook.appevents.ml.ModelManager.predict
import com.facebook.appevents.suggestedevents.FeatureExtractor.getDenseFeatures
import com.facebook.appevents.suggestedevents.FeatureExtractor.getTextFeature
import com.facebook.appevents.suggestedevents.SuggestedEventsManager.isEligibleEvents
import com.facebook.appevents.suggestedevents.SuggestedEventsManager.isProductionEvents
import com.facebook.internal.Utility.getAppName
import com.facebook.internal.Utility.runOnNonUiThread
import com.facebook.internal.instrument.crashshield.AutoHandleExceptions
import java.lang.ref.WeakReference
import java.util.Locale
import org.json.JSONException
import org.json.JSONObject

@AutoHandleExceptions
@RestrictTo(RestrictTo.Scope.LIBRARY)
class ViewOnClickListener
private constructor(hostView: View, rootView: View, activityName: String) : View.OnClickListener {
  private val baseListener: View.OnClickListener? =
      ViewHierarchy.getExistingOnClickListener(hostView)
  private val rootViewWeakReference: WeakReference<View> = WeakReference(rootView)
  private val hostViewWeakReference: WeakReference<View> = WeakReference(hostView)
  private val activityName: String = activityName.toLowerCase().replace("activity", "")
  override fun onClick(view: View) {
    baseListener?.onClick(view)
    process()
  }

  private fun process() {
    val rootView = rootViewWeakReference.get()
    val hostView = hostViewWeakReference.get()
    if (rootView == null || hostView == null) {
      return
    }
    try {
      val buttonText = SuggestedEventViewHierarchy.getTextOfViewRecursively(hostView)
      // query history
      val pathID = PredictionHistoryManager.getPathID(hostView, buttonText) ?: return
      if (queryHistoryAndProcess(pathID, buttonText)) {
        return
      }

      // run prediction
      val data = JSONObject()
      data.put(VIEW_KEY, SuggestedEventViewHierarchy.getDictionaryOfView(rootView, hostView))
      data.put(SCREEN_NAME_KEY, activityName)
      predictAndProcess(pathID, buttonText, data)
    } catch (e: Exception) {
      /*no op*/
    }
  }

  private fun predictAndProcess(pathID: String, buttonText: String, viewData: JSONObject) {
    runOnNonUiThread(
        Runnable {
          try {
            val appName = getAppName(FacebookSdk.getApplicationContext()).toLowerCase()
            val dense = getDenseFeatures(viewData, appName)
            val textFeature = getTextFeature(buttonText, activityName, appName)
            if (dense == null) {
              return@Runnable
            }
            val predictedEvents =
                predict(
                    ModelManager.Task.MTML_APP_EVENT_PREDICTION,
                    arrayOf(dense),
                    arrayOf(textFeature))
                    ?: return@Runnable
            val predictedEvent = predictedEvents[0]
            PredictionHistoryManager.addPrediction(pathID, predictedEvent)
            if (predictedEvent != OTHER_EVENT) {
              processPredictedResult(predictedEvent, buttonText, dense)
            }
          } catch (e: Exception) {
            /*no op*/
          }
        })
  }

  companion object {
    private const val API_ENDPOINT = "%s/suggested_events"
    const val OTHER_EVENT = "other"
    private val viewsAttachedListener: MutableSet<Int> = HashSet()

    @JvmStatic
    internal fun attachListener(hostView: View, rootView: View, activityName: String) {
      val key = hostView.hashCode()
      if (!viewsAttachedListener.contains(key)) {
        ViewHierarchy.setOnClickListener(
            hostView, ViewOnClickListener(hostView, rootView, activityName))
        viewsAttachedListener.add(key)
      }
    }

    // return True if successfully found history prediction
    private fun queryHistoryAndProcess(pathID: String, buttonText: String): Boolean {
      // not found
      val queriedEvent = PredictionHistoryManager.queryEvent(pathID) ?: return false
      if (queriedEvent != OTHER_EVENT) {
        runOnNonUiThread { processPredictedResult(queriedEvent, buttonText, floatArrayOf()) }
      }
      return true
    }

    private fun processPredictedResult(
        predictedEvent: String,
        buttonText: String,
        dense: FloatArray
    ) {
      if (isProductionEvents(predictedEvent)) {
        val logger = InternalAppEventsLogger(FacebookSdk.getApplicationContext())
        logger.logEventFromSE(predictedEvent, buttonText)
      } else if (isEligibleEvents(predictedEvent)) {
        sendPredictedResult(predictedEvent, buttonText, dense)
      }
    }

    private fun sendPredictedResult(eventToPost: String, buttonText: String, dense: FloatArray) {
      val publishParams = Bundle()
      try {
        publishParams.putString("event_name", eventToPost)
        val metadata = JSONObject()
        val denseSB = StringBuilder()
        for (f in dense) {
          denseSB.append(f).append(",")
        }
        metadata.put("dense", denseSB.toString())
        metadata.put("button_text", buttonText)
        publishParams.putString("metadata", metadata.toString())
        val postRequest =
            newPostRequest(
                null,
                String.format(Locale.US, API_ENDPOINT, FacebookSdk.getApplicationId()),
                null,
                null)
        postRequest.parameters = publishParams
        postRequest.executeAndWait()
      } catch (e: JSONException) {
        /*no op*/
      }
    }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.core

class Core


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.internal

/**
 * com.facebook.internal is solely for the use of other packages within the Facebook SDK for
 * Android. Use of any of the classes in this package is unsupported, and they may be modified or
 * removed without warning at any time.
 */
object AnalyticsEvents {
  const val EVENT_NATIVE_LOGIN_DIALOG_COMPLETE = "fb_dialogs_native_login_dialog_complete"
  const val EVENT_NATIVE_LOGIN_DIALOG_START = "fb_dialogs_native_login_dialog_start"
  const val EVENT_WEB_LOGIN_COMPLETE = "fb_dialogs_web_login_dialog_complete"
  const val EVENT_FRIEND_PICKER_USAGE = "fb_friend_picker_usage"
  const val EVENT_PLACE_PICKER_USAGE = "fb_place_picker_usage"
  const val EVENT_LOGIN_VIEW_USAGE = "fb_login_view_usage"
  const val EVENT_USER_SETTINGS_USAGE = "fb_user_settings_vc_usage"
  const val EVENT_NATIVE_DIALOG_START = "fb_native_dialog_start"
  const val EVENT_NATIVE_DIALOG_COMPLETE = "fb_native_dialog_complete"
  const val PARAMETER_WEB_LOGIN_E2E = "fb_web_login_e2e"
  const val PARAMETER_WEB_LOGIN_SWITCHBACK_TIME = "fb_web_login_switchback_time"
  const val PARAMETER_APP_ID = "app_id"
  const val PARAMETER_CALL_ID = "call_id"
  const val PARAMETER_ACTION_ID = "action_id"
  const val PARAMETER_NATIVE_LOGIN_DIALOG_START_TIME = "fb_native_login_dialog_start_time"
  const val PARAMETER_NATIVE_LOGIN_DIALOG_COMPLETE_TIME = "fb_native_login_dialog_complete_time"
  const val PARAMETER_DIALOG_OUTCOME = "fb_dialog_outcome"
  const val PARAMETER_DIALOG_OUTCOME_VALUE_COMPLETED = "Completed"
  const val PARAMETER_DIALOG_OUTCOME_VALUE_UNKNOWN = "Unknown"
  const val PARAMETER_DIALOG_OUTCOME_VALUE_CANCELLED = "Cancelled"
  const val PARAMETER_DIALOG_OUTCOME_VALUE_FAILED = "Failed"
  const val EVENT_NATIVE_DIALOG_TYPE_SHARE = "fb_dialogs_present_share"
  const val EVENT_NATIVE_DIALOG_TYPE_MESSAGE = "fb_dialogs_present_message"
  const val EVENT_NATIVE_DIALOG_TYPE_OG_SHARE = "fb_dialogs_present_share_og"
  const val EVENT_NATIVE_DIALOG_TYPE_OG_MESSAGE = "fb_dialogs_present_message_og"
  const val EVENT_NATIVE_DIALOG_TYPE_PHOTO_SHARE = "fb_dialogs_present_share_photo"
  const val EVENT_NATIVE_DIALOG_TYPE_PHOTO_MESSAGE = "fb_dialogs_present_message_photo"
  const val EVENT_NATIVE_DIALOG_TYPE_VIDEO_SHARE = "fb_dialogs_present_share_video"
  const val EVENT_NATIVE_DIALOG_TYPE_LIKE = "fb_dialogs_present_like"
  const val EVENT_LIKE_VIEW_CANNOT_PRESENT_DIALOG = "fb_like_control_cannot_present_dialog"
  const val EVENT_LIKE_VIEW_DID_LIKE = "fb_like_control_did_like"
  const val EVENT_LIKE_VIEW_DID_PRESENT_DIALOG = "fb_like_control_did_present_dialog"
  const val EVENT_LIKE_VIEW_DID_PRESENT_FALLBACK = "fb_like_control_did_present_fallback_dialog"
  const val EVENT_LIKE_VIEW_DID_UNLIKE = "fb_like_control_did_unlike"
  const val EVENT_LIKE_VIEW_DID_UNDO_QUICKLY = "fb_like_control_did_undo_quickly"
  const val EVENT_LIKE_VIEW_DIALOG_DID_SUCCEED = "fb_like_control_dialog_did_succeed"
  const val EVENT_LIKE_VIEW_ERROR = "fb_like_control_error"
  const val PARAMETER_LIKE_VIEW_STYLE = "style"
  const val PARAMETER_LIKE_VIEW_AUXILIARY_POSITION = "auxiliary_position"
  const val PARAMETER_LIKE_VIEW_HORIZONTAL_ALIGNMENT = "horizontal_alignment"
  const val PARAMETER_LIKE_VIEW_OBJECT_ID = "object_id"
  const val PARAMETER_LIKE_VIEW_OBJECT_TYPE = "object_type"
  const val PARAMETER_LIKE_VIEW_CURRENT_ACTION = "current_action"
  const val PARAMETER_LIKE_VIEW_ERROR_JSON = "error"
  const val PARAMETER_SHARE_OUTCOME = "fb_share_dialog_outcome"
  const val PARAMETER_SHARE_OUTCOME_SUCCEEDED = "succeeded"
  const val PARAMETER_SHARE_OUTCOME_CANCELLED = "cancelled"
  const val PARAMETER_SHARE_OUTCOME_ERROR = "error"
  const val PARAMETER_SHARE_OUTCOME_UNKNOWN = "unknown"
  const val PARAMETER_SHARE_ERROR_MESSAGE = "error_message"
  const val PARAMETER_SHARE_DIALOG_SHOW = "fb_share_dialog_show"
  const val PARAMETER_SHARE_DIALOG_SHOW_WEB = "web"
  const val PARAMETER_SHARE_DIALOG_SHOW_NATIVE = "native"
  const val PARAMETER_SHARE_DIALOG_SHOW_AUTOMATIC = "automatic"
  const val PARAMETER_SHARE_DIALOG_SHOW_UNKNOWN = "unknown"
  const val PARAMETER_SHARE_DIALOG_CONTENT_TYPE = "fb_share_dialog_content_type"
  const val PARAMETER_SHARE_DIALOG_CONTENT_UUID = "fb_share_dialog_content_uuid"
  const val PARAMETER_SHARE_DIALOG_CONTENT_PAGE_ID = "fb_share_dialog_content_page_id"
  const val PARAMETER_SHARE_DIALOG_CONTENT_VIDEO = "video"
  const val PARAMETER_SHARE_DIALOG_CONTENT_PHOTO = "photo"
  const val PARAMETER_SHARE_DIALOG_CONTENT_STATUS = "status"
  const val PARAMETER_SHARE_DIALOG_CONTENT_UNKNOWN = "unknown"
  const val EVENT_SHARE_RESULT = "fb_share_dialog_result"
  const val EVENT_SHARE_DIALOG_SHOW = "fb_share_dialog_show"
  const val EVENT_SHARE_MESSENGER_DIALOG_SHOW = "fb_messenger_share_dialog_show"
  const val EVENT_LIKE_BUTTON_CREATE = "fb_like_button_create"
  const val EVENT_LOGIN_BUTTON_CREATE = "fb_login_button_create"
  const val EVENT_SHARE_BUTTON_CREATE = "fb_share_button_create"
  const val EVENT_SEND_BUTTON_CREATE = "fb_send_button_create"
  const val EVENT_SHARE_BUTTON_DID_TAP = "fb_share_button_did_tap"
  const val EVENT_SEND_BUTTON_DID_TAP = "fb_send_button_did_tap"
  const val EVENT_LIKE_BUTTON_DID_TAP = "fb_like_button_did_tap"
  const val EVENT_LOGIN_BUTTON_DID_TAP = "fb_login_button_did_tap"
  const val EVENT_DEVICE_SHARE_BUTTON_CREATE = "fb_device_share_button_create"
  const val EVENT_DEVICE_SHARE_BUTTON_DID_TAP = "fb_device_share_button_did_tap"
  const val EVENT_SMART_LOGIN_SERVICE = "fb_smart_login_service"
  const val EVENT_SDK_INITIALIZE = "fb_sdk_initialize"
  const val PARAMETER_SHARE_MESSENGER_GENERIC_TEMPLATE = "GenericTemplate"
  const val PARAMETER_SHARE_MESSENGER_MEDIA_TEMPLATE = "MediaTemplate"
  const val EVENT_FOA_LOGIN_BUTTON_CREATE = "foa_login_button_create"
  const val EVENT_FOA_LOGIN_BUTTON_DID_TAP = "foa_login_button_did_tap"
  const val EVENT_FOA_DISAMBIGUATION_DIALOG_FB_DID_TAP = "foa_disambiguation_dialog_fb_did_tap"
  const val EVENT_FOA_DISAMBIGUATION_DIALOG_IG_DID_TAP = "foa_disambiguation_dialog_ig_did_tap"
  const val EVENT_FOA_DISAMBIGUATION_DIALOG_CANCELLED = "foa_disambiguation_dialog_cancelled"
  const val EVENT_FOA_FB_LOGIN_BUTTON_CREATE = "foa_fb_login_button_create"
  const val EVENT_FOA_FB_LOGIN_BUTTON_DID_TAP = "foa_fb_login_button_did_tap"
  const val EVENT_FOA_IG_LOGIN_BUTTON_CREATE = "foa_ig_login_button_create"
  const val EVENT_FOA_IG_LOGIN_BUTTON_DID_TAP = "foa_ig_login_button_did_tap"
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.internal

import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.database.Cursor
import android.net.Uri
import android.os.Binder
import android.os.IBinder
import android.os.IInterface
import android.os.Looper
import android.os.Parcel
import android.os.RemoteException
import androidx.annotation.RestrictTo
import androidx.annotation.VisibleForTesting
import com.facebook.FacebookException
import com.facebook.FacebookSdk
import com.facebook.internal.FacebookSignatureValidator.validateSignature
import com.facebook.internal.Utility.getMethodQuietly
import com.facebook.internal.Utility.invokeMethodQuietly
import com.facebook.internal.Utility.logd
import java.util.concurrent.BlockingQueue
import java.util.concurrent.LinkedBlockingDeque
import java.util.concurrent.atomic.AtomicBoolean

/**
 * com.facebook.internal is solely for the use of other packages within the Facebook SDK for
 * Android. Use of any of the classes in this package is unsupported, and they may be modified or
 * removed without warning at any time.
 */
class AttributionIdentifiers {
  private var androidAdvertiserIdValue: String? = null
  private var fetchTime: Long = 0
  var attributionId: String? = null
    private set
  var androidInstallerPackage: String? = null
    private set
  var isTrackingLimited = false
    private set

  val androidAdvertiserId: String?
    get() {
      return if (FacebookSdk.isInitialized() && FacebookSdk.getAdvertiserIDCollectionEnabled()) {
        androidAdvertiserIdValue
      } else {
        null
      }
    }

  companion object {
    private val TAG = AttributionIdentifiers::class.java.canonicalName
    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    internal const val ATTRIBUTION_ID_CONTENT_PROVIDER =
        "com.facebook.katana.provider.AttributionIdProvider"
    private const val ATTRIBUTION_ID_CONTENT_PROVIDER_WAKIZASHI =
        "com.facebook.wakizashi.provider.AttributionIdProvider"
    private const val ATTRIBUTION_ID_COLUMN_NAME = "aid"
    private const val ANDROID_ID_COLUMN_NAME = "androidid"
    private const val LIMIT_TRACKING_COLUMN_NAME = "limit_tracking"

    // com.google.android.gms.common.ConnectionResult.SUCCESS
    private const val CONNECTION_RESULT_SUCCESS = 0
    private const val IDENTIFIER_REFRESH_INTERVAL_MILLIS = (3600 * 1000).toLong()

    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    @JvmField
    internal var cachedIdentifiers: AttributionIdentifiers? = null

    private fun getAndroidId(context: Context): AttributionIdentifiers {
      var identifiers = getAndroidIdViaReflection(context)
      if (identifiers == null) {
        identifiers = getAndroidIdViaService(context)
        if (identifiers == null) {
          identifiers = AttributionIdentifiers()
        }
      }
      return identifiers
    }

    private fun getAndroidIdViaReflection(context: Context): AttributionIdentifiers? {
      try {
        if (!isGooglePlayServicesAvailable(context)) {
          return null
        }
        val getAdvertisingIdInfo =
            getMethodQuietly(
                "com.google.android.gms.ads.identifier.AdvertisingIdClient",
                "getAdvertisingIdInfo",
                Context::class.java)
                ?: return null
        val advertisingInfo =
            invokeMethodQuietly(null, getAdvertisingIdInfo, context) ?: return null
        val getId = getMethodQuietly(advertisingInfo.javaClass, "getId")
        val isLimitAdTrackingEnabled =
            getMethodQuietly(advertisingInfo.javaClass, "isLimitAdTrackingEnabled")
        if (getId == null || isLimitAdTrackingEnabled == null) {
          return null
        }
        val identifiers = AttributionIdentifiers()
        identifiers.androidAdvertiserIdValue =
            invokeMethodQuietly(advertisingInfo, getId) as String?
        identifiers.isTrackingLimited =
            (invokeMethodQuietly(advertisingInfo, isLimitAdTrackingEnabled) as Boolean?) ?: false
        return identifiers
      } catch (e: Exception) {
        logd("android_id", e)
      }
      return null
    }

    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    @JvmStatic
    fun isTrackingLimited(context: Context): Boolean {
      val attributionIdentifiers = getAttributionIdentifiers(context)
      return attributionIdentifiers != null && attributionIdentifiers.isTrackingLimited
    }

    private fun isGooglePlayServicesAvailable(context: Context): Boolean {
      val method =
          getMethodQuietly(
              "com.google.android.gms.common.GooglePlayServicesUtil",
              "isGooglePlayServicesAvailable",
              Context::class.java)
              ?: return false
      val connectionResult = invokeMethodQuietly(null, method, context)
      return !(connectionResult !is Int || connectionResult != CONNECTION_RESULT_SUCCESS)
    }

    private fun getAndroidIdViaService(context: Context): AttributionIdentifiers? {
      if (!isGooglePlayServicesAvailable(context)) {
        return null
      }

      val connection = GoogleAdServiceConnection()
      val intent = Intent("com.google.android.gms.ads.identifier.service.START")
      intent.setPackage("com.google.android.gms")
      val isBindServiceSucceed =
          try {
            context.bindService(intent, connection, Context.BIND_AUTO_CREATE)
          } catch (_: SecurityException) {
            return null
          }
      if (isBindServiceSucceed) {
        try {
          val adInfo = GoogleAdInfo(connection.binder)
          val identifiers = AttributionIdentifiers()
          identifiers.androidAdvertiserIdValue = adInfo.advertiserId
          identifiers.isTrackingLimited = adInfo.isTrackingLimited
          return identifiers
        } catch (exception: Exception) {
          logd("android_id", exception)
        } finally {
          context.unbindService(connection)
        }
      }
      return null
    }

    @JvmStatic
    fun getAttributionIdentifiers(context: Context): AttributionIdentifiers? {
      val identifiers = getAndroidId(context)
      var c: Cursor? = null
      try {
        // We can't call getAdvertisingIdInfo on the main thread or the app will potentially
        // freeze, if this is the case throw:
        if (Looper.myLooper() == Looper.getMainLooper()) {
          throw FacebookException("getAttributionIdentifiers cannot be called on the main thread.")
        }
        val cachedIdentifiers = this.cachedIdentifiers
        if (cachedIdentifiers != null &&
            System.currentTimeMillis() - cachedIdentifiers.fetchTime <
                IDENTIFIER_REFRESH_INTERVAL_MILLIS) {
          return cachedIdentifiers
        }
        val projection =
            arrayOf(ATTRIBUTION_ID_COLUMN_NAME, ANDROID_ID_COLUMN_NAME, LIMIT_TRACKING_COLUMN_NAME)
        var providerUri: Uri? = null
        val contentProviderInfo =
            context.packageManager.resolveContentProvider(ATTRIBUTION_ID_CONTENT_PROVIDER, 0)
        val wakizashiProviderInfo =
            context.packageManager.resolveContentProvider(
                ATTRIBUTION_ID_CONTENT_PROVIDER_WAKIZASHI, 0)
        if (contentProviderInfo != null &&
            validateSignature(context, contentProviderInfo.packageName)) {
          providerUri = Uri.parse("content://$ATTRIBUTION_ID_CONTENT_PROVIDER")
        } else if (wakizashiProviderInfo != null &&
            validateSignature(context, wakizashiProviderInfo.packageName)) {
          providerUri = Uri.parse("content://$ATTRIBUTION_ID_CONTENT_PROVIDER_WAKIZASHI")
        }
        val installerPackageName = getInstallerPackageName(context)
        if (installerPackageName != null) {
          identifiers.androidInstallerPackage = installerPackageName
        }
        if (providerUri == null) {
          return cacheAndReturnIdentifiers(identifiers)
        }
        c = context.contentResolver.query(providerUri, projection, null, null, null)
        if (c == null || !c.moveToFirst()) {
          return cacheAndReturnIdentifiers(identifiers)
        }
        val attributionColumnIndex = c.getColumnIndex(ATTRIBUTION_ID_COLUMN_NAME)
        val androidIdColumnIndex = c.getColumnIndex(ANDROID_ID_COLUMN_NAME)
        val limitTrackingColumnIndex = c.getColumnIndex(LIMIT_TRACKING_COLUMN_NAME)
        identifiers.attributionId = c.getString(attributionColumnIndex)

        // if we failed to call Google's APIs directly (due to improper integration by the
        // client), it may be possible for the local facebook application to relay it to us.
        if (androidIdColumnIndex > 0 &&
            limitTrackingColumnIndex > 0 &&
            identifiers.androidAdvertiserId == null) {
          identifiers.androidAdvertiserIdValue = c.getString(androidIdColumnIndex)
          identifiers.isTrackingLimited =
              java.lang.Boolean.parseBoolean(c.getString(limitTrackingColumnIndex))
        }
      } catch (e: Exception) {
        logd(TAG, "Caught unexpected exception in getAttributionId(): $e")
        return null
      } finally {
        c?.close()
      }
      return cacheAndReturnIdentifiers(identifiers)
    }

    private fun cacheAndReturnIdentifiers(
        identifiers: AttributionIdentifiers
    ): AttributionIdentifiers {
      identifiers.fetchTime = System.currentTimeMillis()
      cachedIdentifiers = identifiers
      return identifiers
    }

    private fun getInstallerPackageName(context: Context): String? {
      val packageManager = context.packageManager
      return packageManager?.getInstallerPackageName(context.packageName)
    }
  }

  private class GoogleAdServiceConnection : ServiceConnection {
    private val consumed = AtomicBoolean(false)
    private val queue: BlockingQueue<IBinder> = LinkedBlockingDeque()
    override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
      try {
        if (service != null) {
          queue.put(service)
        }
      } catch (e: InterruptedException) {}
    }

    override fun onServiceDisconnected(name: ComponentName?) = Unit

    @get:Throws(InterruptedException::class)
    val binder: IBinder
      get() {
        check(!consumed.compareAndSet(true, true)) { "Binder already consumed" }
        return queue.take()
      }
  }

  private class GoogleAdInfo constructor(private val binder: IBinder) : IInterface {
    override fun asBinder(): IBinder = binder

    @get:Throws(RemoteException::class)
    val advertiserId: String?
      get() {
        val data = Parcel.obtain()
        val reply = Parcel.obtain()
        return try {
          data.writeInterfaceToken(
              "com.google.android.gms.ads.identifier.internal.IAdvertisingIdService")
          binder.transact(FIRST_TRANSACTION_CODE, data, reply, 0)
          reply.readException()
          reply.readString()
        } finally {
          reply.recycle()
          data.recycle()
        }
      }

    @get:Throws(RemoteException::class)
    val isTrackingLimited: Boolean
      get() {
        val data = Parcel.obtain()
        val reply = Parcel.obtain()
        return try {
          data.writeInterfaceToken(
              "com.google.android.gms.ads.identifier.internal.IAdvertisingIdService")
          data.writeInt(1)
          binder.transact(SECOND_TRANSACTION_CODE, data, reply, 0)
          reply.readException()
          0 != reply.readInt()
        } finally {
          reply.recycle()
          data.recycle()
        }
      }

    companion object {
      private const val FIRST_TRANSACTION_CODE = Binder.FIRST_CALL_TRANSACTION
      private const val SECOND_TRANSACTION_CODE = FIRST_TRANSACTION_CODE + 1
    }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.internal

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Bundle
import androidx.annotation.VisibleForTesting
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.facebook.appevents.InternalAppEventsLogger
import com.facebook.internal.instrument.crashshield.AutoHandleExceptions

/**
 * com.facebook.internal is solely for the use of other packages within the Facebook SDK for
 * Android. Use of any of the classes in this package is unsupported, and they may be modified or
 * removed without warning at any time.
 */
@AutoHandleExceptions
class BoltsMeasurementEventListener private constructor(context: Context) : BroadcastReceiver() {
  private val applicationContext: Context = context.applicationContext
  companion object {
    private var singleton: BoltsMeasurementEventListener? = null

    @VisibleForTesting
    internal val MEASUREMENT_EVENT_NOTIFICATION_NAME = "com.parse.bolts.measurement_event"
    private const val MEASUREMENT_EVENT_NAME_KEY = "event_name"
    private const val MEASUREMENT_EVENT_ARGS_KEY = "event_args"
    private const val BOLTS_MEASUREMENT_EVENT_PREFIX = "bf_"

    @JvmStatic
    fun getInstance(context: Context): BoltsMeasurementEventListener? {
      if (singleton != null) {
        return singleton
      }
      val listener = BoltsMeasurementEventListener(context)
      listener.open()
      singleton = listener
      return singleton
    }
  }

  private fun open() {
    val broadcastManager = LocalBroadcastManager.getInstance(applicationContext)
    broadcastManager.registerReceiver(this, IntentFilter(MEASUREMENT_EVENT_NOTIFICATION_NAME))
  }

  private fun close() {
    val broadcastManager = LocalBroadcastManager.getInstance(applicationContext)
    broadcastManager.unregisterReceiver(this)
  }

  @Throws(Throwable::class)
  fun finalize() {
    close()
  }

  override fun onReceive(context: Context?, intent: Intent?) {
    val logger = InternalAppEventsLogger(context)
    val eventName =
        BOLTS_MEASUREMENT_EVENT_PREFIX + intent?.getStringExtra(MEASUREMENT_EVENT_NAME_KEY)
    val eventArgs = intent?.getBundleExtra(MEASUREMENT_EVENT_ARGS_KEY)
    val logData = Bundle()
    val keySet = eventArgs?.keySet()
    if (keySet != null) {
      for (key in keySet) {
        val safeKey =
            key.replace("[^0-9a-zA-Z _-]".toRegex(), "-")
                .replace("^[ -]*".toRegex(), "")
                .replace("[ -]*$".toRegex(), "")
        logData.putString(safeKey, eventArgs.get(key) as String?)
      }
    }
    logger.logEvent(eventName, logData)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.internal

import android.os.Bundle
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject

/**
 * com.facebook.internal is solely for the use of other packages within the Facebook SDK for
 * Android. Use of any of the classes in this package is unsupported, and they may be modified or
 * removed without warning at any time.
 *
 * A helper class that can round trip between JSON and Bundle objects that contains the types:
 * Boolean, Integer, Long, Double, String If other types are found, an IllegalArgumentException is
 * thrown.
 */
object BundleJSONConverter {
  private val SETTERS: MutableMap<Class<*>, Setter> = HashMap()

  @JvmStatic
  @Throws(JSONException::class)
  fun convertToJSON(bundle: Bundle): JSONObject {
    val json = JSONObject()
    for (key in bundle.keySet()) {
      val value =
          bundle[key] ?: // Null is not supported.
          continue

      // Special case List<String> as getClass would not work, since List is an interface
      if (value is List<*>) {
        val jsonArray = JSONArray()
        val listValue = value as List<String>
        for (stringValue in listValue) {
          jsonArray.put(stringValue)
        }
        json.put(key, jsonArray)
        continue
      }

      // Special case Bundle as it's one way, on the return it will be JSONObject
      if (value is Bundle) {
        json.put(key, convertToJSON(value))
        continue
      }
      val setter =
          SETTERS[value.javaClass]
              ?: throw IllegalArgumentException("Unsupported type: " + value.javaClass)
      setter.setOnJSON(json, key, value)
    }
    return json
  }

  @JvmStatic
  @Throws(JSONException::class)
  fun convertToBundle(jsonObject: JSONObject): Bundle {
    val bundle = Bundle()
    val jsonIterator = jsonObject.keys()
    while (jsonIterator.hasNext()) {
      val key = jsonIterator.next()
      val value = jsonObject[key]
      if (value === JSONObject.NULL) {
        // Null is not supported.
        continue
      }

      // Special case JSONObject as it's one way, on the return it would be Bundle.
      if (value is JSONObject) {
        bundle.putBundle(key, convertToBundle(value))
        continue
      }
      val setter =
          SETTERS[value.javaClass]
              ?: throw IllegalArgumentException("Unsupported type: " + value.javaClass)
      setter.setOnBundle(bundle, key, value)
    }
    return bundle
  }

  interface Setter {
    @Throws(JSONException::class) fun setOnBundle(bundle: Bundle, key: String, value: Any)

    @Throws(JSONException::class) fun setOnJSON(json: JSONObject, key: String, value: Any)
  }

  init {
    SETTERS[java.lang.Boolean::class.java] =
        object : Setter {
          @Throws(JSONException::class)
          override fun setOnBundle(bundle: Bundle, key: String, value: Any) {
            bundle.putBoolean(key, value as Boolean)
          }

          @Throws(JSONException::class)
          override fun setOnJSON(json: JSONObject, key: String, value: Any) {
            json.put(key, value)
          }
        }
    SETTERS[java.lang.Integer::class.java] =
        object : Setter {
          @Throws(JSONException::class)
          override fun setOnBundle(bundle: Bundle, key: String, value: Any) {
            bundle.putInt(key, value as Int)
          }

          @Throws(JSONException::class)
          override fun setOnJSON(json: JSONObject, key: String, value: Any) {
            json.put(key, value)
          }
        }
    SETTERS[java.lang.Long::class.java] =
        object : Setter {
          @Throws(JSONException::class)
          override fun setOnBundle(bundle: Bundle, key: String, value: Any) {
            bundle.putLong(key, value as Long)
          }

          @Throws(JSONException::class)
          override fun setOnJSON(json: JSONObject, key: String, value: Any) {
            json.put(key, value)
          }
        }
    SETTERS[java.lang.Double::class.java] =
        object : Setter {
          @Throws(JSONException::class)
          override fun setOnBundle(bundle: Bundle, key: String, value: Any) {
            bundle.putDouble(key, value as Double)
          }

          @Throws(JSONException::class)
          override fun setOnJSON(json: JSONObject, key: String, value: Any) {
            json.put(key, value)
          }
        }
    SETTERS[String::class.java] =
        object : Setter {
          @Throws(JSONException::class)
          override fun setOnBundle(bundle: Bundle, key: String, value: Any) {
            bundle.putString(key, value as String)
          }

          @Throws(JSONException::class)
          override fun setOnJSON(json: JSONObject, key: String, value: Any) {
            json.put(key, value)
          }
        }
    SETTERS[Array<String>::class.java] =
        object : Setter {
          @Throws(JSONException::class)
          override fun setOnBundle(bundle: Bundle, key: String, value: Any) {
            throw IllegalArgumentException("Unexpected type from JSON")
          }

          @Throws(JSONException::class)
          override fun setOnJSON(json: JSONObject, key: String, value: Any) {
            val jsonArray = JSONArray()
            for (stringValue in value as Array<String>) {
              jsonArray.put(stringValue)
            }
            json.put(key, jsonArray)
          }
        }
    SETTERS[JSONArray::class.java] =
        object : Setter {
          @Throws(JSONException::class)
          override fun setOnBundle(bundle: Bundle, key: String, value: Any) {
            val jsonArray = value as JSONArray
            val stringArrayList = ArrayList<String>()
            // Empty list, can't even figure out the type, assume an ArrayList<String>
            if (jsonArray.length() == 0) {
              bundle.putStringArrayList(key, stringArrayList)
              return
            }

            // Only strings are supported for now
            for (i in 0 until jsonArray.length()) {
              val current = jsonArray[i]
              if (current is String) {
                stringArrayList.add(current)
              } else {
                throw IllegalArgumentException("Unexpected type in an array: " + current.javaClass)
              }
            }
            bundle.putStringArrayList(key, stringArrayList)
          }

          @Throws(JSONException::class)
          override fun setOnJSON(json: JSONObject, key: String, value: Any) {
            throw IllegalArgumentException("JSONArray's are not supported in bundles.")
          }
        }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.internal

import android.content.Intent
import com.facebook.CallbackManager
import com.facebook.FacebookSdk
import java.util.HashMap

/**
 * com.facebook.internal is solely for the use of other packages within the Facebook SDK for
 * Android. Use of any of the classes in this package is unsupported, and they may be modified or
 * removed without warning at any time.
 */
class CallbackManagerImpl : CallbackManager {
  private val callbacks: MutableMap<Int, Callback> = HashMap()
  fun registerCallback(requestCode: Int, callback: Callback) {
    callbacks[requestCode] = callback
  }

  fun unregisterCallback(requestCode: Int) {
    callbacks.remove(requestCode)
  }

  override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean {
    val callback = callbacks[requestCode]
    return callback?.onActivityResult(resultCode, data)
        ?: runStaticCallback(requestCode, resultCode, data)
  }

  fun interface Callback {
    fun onActivityResult(resultCode: Int, data: Intent?): Boolean
  }

  enum class RequestCodeOffset(private val offset: Int) {
    Login(0),
    Share(1),
    Message(2),
    Like(3),
    GameRequest(4),
    AppGroupCreate(5),
    AppGroupJoin(6),
    AppInvite(7),
    DeviceShare(8),
    GamingFriendFinder(9),
    GamingGroupIntegration(10),
    Referral(11),
    GamingContextCreate(12),
    GamingContextSwitch(13),
    GamingContextChoose(14),
    TournamentShareDialog(15),
    TournamentJoinDialog(16);

    fun toRequestCode(): Int {
      return FacebookSdk.getCallbackRequestCodeOffset() + offset
    }
  }

  companion object {
    private val staticCallbacks: MutableMap<Int, Callback> = HashMap()

    /**
     * If there is no explicit callback, but we still need to call the Facebook component, because
     * it's going to update some state, e.g., login, like. Then we should register a static callback
     * that can still handle the response.
     *
     * @param requestCode The request code.
     * @param callback The callback for the feature.
     */
    @Synchronized
    @JvmStatic
    fun registerStaticCallback(requestCode: Int, callback: Callback) {
      if (staticCallbacks.containsKey(requestCode)) {
        return
      }
      staticCallbacks[requestCode] = callback
    }

    @Synchronized
    @JvmStatic
    private fun getStaticCallback(requestCode: Int): Callback? {
      return staticCallbacks[requestCode]
    }

    @JvmStatic
    private fun runStaticCallback(requestCode: Int, resultCode: Int, data: Intent?): Boolean {
      val callback = getStaticCallback(requestCode)
      return callback?.onActivityResult(resultCode, data) ?: false
    }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.internal

import android.content.ContentProvider
import android.content.ContentValues
import android.database.Cursor
import android.net.Uri
import android.util.Log
import com.facebook.FacebookSdk
import java.lang.Exception

class FacebookInitProvider : ContentProvider() {
  companion object {
    private val TAG = FacebookInitProvider::class.java.simpleName
  }
  @SuppressWarnings("deprecation")
  override fun onCreate(): Boolean {
    try {
      val context = this.context
      requireNotNull(context)
      FacebookSdk.sdkInitialize(context)
    } catch (ex: Exception) {
      Log.i(TAG, "Failed to auto initialize the Facebook SDK", ex)
    }
    return false
  }

  override fun query(
      uri: Uri,
      projection: Array<String>?,
      selection: String?,
      selectionArgs: Array<String>?,
      sortOrder: String?
  ): Cursor? {
    return null
  }

  override fun getType(uri: Uri): String? = null

  override fun insert(uri: Uri, values: ContentValues?): Uri? = null

  override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int = 0

  override fun update(
      uri: Uri,
      values: ContentValues?,
      selection: String?,
      selectionArgs: Array<String>?
  ): Int {
    return 0
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.internal

import com.facebook.FacebookRequestError
import org.json.JSONArray
import org.json.JSONObject

/**
 * com.facebook.internal is solely for the use of other packages within the Facebook SDK for
 * Android. Use of any of the classes in this package is unsupported, and they may be modified or
 * removed without warning at any time.
 */
class FacebookRequestErrorClassification
internal constructor(
    // Key is error code, value is the subcodes. Null subcodes means all subcodes are accepted.
    val otherErrors: Map<Int, Set<Int>?>?,
    val transientErrors: Map<Int, Set<Int>?>?,
    val loginRecoverableErrors: Map<Int, Set<Int>?>?,
    private val otherRecoveryMessage: String?,
    private val transientRecoveryMessage: String?,
    private val loginRecoverableRecoveryMessage: String?
) {
  fun getRecoveryMessage(category: FacebookRequestError.Category?): String? {
    return when (category) {
      FacebookRequestError.Category.OTHER -> otherRecoveryMessage
      FacebookRequestError.Category.LOGIN_RECOVERABLE -> loginRecoverableRecoveryMessage
      FacebookRequestError.Category.TRANSIENT -> transientRecoveryMessage
      else -> null
    }
  }

  fun classify(
      errorCode: Int,
      errorSubCode: Int,
      isTransient: Boolean
  ): FacebookRequestError.Category {
    if (isTransient) {
      return FacebookRequestError.Category.TRANSIENT
    }
    if (otherErrors != null && otherErrors.containsKey(errorCode)) {
      val subCodes = otherErrors[errorCode]
      if (subCodes == null || subCodes.contains(errorSubCode)) {
        return FacebookRequestError.Category.OTHER
      }
    }
    if (loginRecoverableErrors != null && loginRecoverableErrors.containsKey(errorCode)) {
      val subCodes = loginRecoverableErrors[errorCode]
      if (subCodes == null || subCodes.contains(errorSubCode)) {
        return FacebookRequestError.Category.LOGIN_RECOVERABLE
      }
    }
    if (transientErrors != null && transientErrors.containsKey(errorCode)) {
      val subCodes = transientErrors[errorCode]
      if (subCodes == null || subCodes.contains(errorSubCode)) {
        return FacebookRequestError.Category.TRANSIENT
      }
    }
    return FacebookRequestError.Category.OTHER
  }

  companion object {
    const val EC_SERVICE_UNAVAILABLE = 2
    const val EC_APP_TOO_MANY_CALLS = 4
    const val EC_RATE = 9
    const val EC_USER_TOO_MANY_CALLS = 17
    const val EC_INVALID_SESSION = 102
    const val EC_INVALID_TOKEN = 190
    const val EC_APP_NOT_INSTALLED = 412
    const val EC_TOO_MANY_USER_ACTION_CALLS = 341
    const val ESC_APP_NOT_INSTALLED = 458
    const val ESC_APP_INACTIVE = 493
    const val KEY_RECOVERY_MESSAGE = "recovery_message"
    const val KEY_NAME = "name"
    const val KEY_OTHER = "other"
    const val KEY_TRANSIENT = "transient"
    const val KEY_LOGIN_RECOVERABLE = "login_recoverable"
    private var defaultInstance: FacebookRequestErrorClassification? = null

    @JvmStatic
    val defaultErrorClassification: FacebookRequestErrorClassification
      @Synchronized
      get() {
        if (this.defaultInstance == null) {
          this.defaultInstance = defaultErrorClassificationImpl
        }
        return defaultInstance as FacebookRequestErrorClassification
      }

    private val defaultErrorClassificationImpl: FacebookRequestErrorClassification
      private get() {
        val transientErrors: HashMap<Int, Set<Int>?> =
            hashMapOf(
                EC_SERVICE_UNAVAILABLE to null,
                EC_APP_TOO_MANY_CALLS to null,
                EC_RATE to null,
                EC_USER_TOO_MANY_CALLS to null,
                EC_TOO_MANY_USER_ACTION_CALLS to null,
            )
        val loginRecoverableErrors: Map<Int, Set<Int>?> =
            hashMapOf(
                EC_INVALID_SESSION to null,
                EC_INVALID_TOKEN to null,
                EC_APP_NOT_INSTALLED to null,
            )
        return FacebookRequestErrorClassification(
            null, transientErrors, loginRecoverableErrors, null, null, null)
      }

    private fun parseJSONDefinition(definition: JSONObject): Map<Int, Set<Int>?>? {
      val itemsArray = definition.optJSONArray("items")
      if (itemsArray == null || itemsArray.length() == 0) {
        return null
      }
      val items: MutableMap<Int, Set<Int>?> = HashMap()
      for (i in 0 until itemsArray.length()) {
        val item = itemsArray.optJSONObject(i) ?: continue
        val code = item.optInt("code")
        if (code == 0) {
          continue
        }
        var subcodes: MutableSet<Int>? = null
        val subcodesArray = item.optJSONArray("subcodes")
        if (subcodesArray != null && subcodesArray.length() > 0) {
          subcodes = HashSet()
          for (j in 0 until subcodesArray.length()) {
            val subCode = subcodesArray.optInt(j)
            if (subCode != 0) {
              subcodes.add(subCode)
            }
          }
        }
        items[code] = subcodes
      }
      return items
    }

    @JvmStatic
    fun createFromJSON(jsonArray: JSONArray?): FacebookRequestErrorClassification? {
      if (jsonArray == null) {
        return null
      }
      var otherErrors: Map<Int, Set<Int>?>? = null
      var transientErrors: Map<Int, Set<Int>?>? = null
      var loginRecoverableErrors: Map<Int, Set<Int>?>? = null
      var otherRecoveryMessage: String? = null
      var transientRecoveryMessage: String? = null
      var loginRecoverableRecoveryMessage: String? = null
      for (i in 0 until jsonArray.length()) {
        val definition = jsonArray.optJSONObject(i) ?: continue
        val name = definition.optString(KEY_NAME) ?: continue
        if (name.equals(KEY_OTHER, ignoreCase = true)) {
          otherRecoveryMessage = definition.optString(KEY_RECOVERY_MESSAGE, null)
          otherErrors = parseJSONDefinition(definition)
        } else if (name.equals(KEY_TRANSIENT, ignoreCase = true)) {
          transientRecoveryMessage = definition.optString(KEY_RECOVERY_MESSAGE, null)
          transientErrors = parseJSONDefinition(definition)
        } else if (name.equals(KEY_LOGIN_RECOVERABLE, ignoreCase = true)) {
          loginRecoverableRecoveryMessage = definition.optString(KEY_RECOVERY_MESSAGE, null)
          loginRecoverableErrors = parseJSONDefinition(definition)
        }
      }
      return FacebookRequestErrorClassification(
          otherErrors,
          transientErrors,
          loginRecoverableErrors,
          otherRecoveryMessage,
          transientRecoveryMessage,
          loginRecoverableRecoveryMessage)
    }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.internal

import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.os.Build

/**
 * com.facebook.internal is solely for the use of other packages within the Facebook SDK for
 * Android. Use of any of the classes in this package is unsupported, and they may be modified or
 * removed without warning at any time.
 */
object FacebookSignatureValidator {

  private const val FBI_HASH = "a4b7452e2ed8f5f191058ca7bbfd26b0d3214bfc"
  private const val FBL_HASH = "df6b721c8b4d3b6eb44c861d4415007e5a35fc95"
  private const val FBR_HASH = "8a3c4b262d721acd49a4bf97d5213199c86fa2b9"
  private const val FBR2_HASH = "cc2751449a350f668590264ed76692694a80308a"
  private const val MSR_HASH = "9b8f518b086098de3d77736f9458a3d2f6f95a37"
  private const val FBF_HASH = "2438bce1ddb7bd026d5ff89f598b3b5e5bb824b3"
  private const val IGR_HASH = "c56fb7d591ba6704df047fd98f535372fea00211"

  private val validAppSignatureHashes =
      hashSetOf(FBR_HASH, FBR2_HASH, FBI_HASH, FBL_HASH, MSR_HASH, FBF_HASH, IGR_HASH)

  @JvmStatic
  fun validateSignature(context: Context, packageName: String): Boolean {
    val brand = Build.BRAND
    val applicationFlags = context.applicationInfo.flags
    if (brand.startsWith("generic") && applicationFlags and ApplicationInfo.FLAG_DEBUGGABLE != 0) {
      // We are debugging on an emulator, don't validate package signature.
      return true
    }

    try {
      val packageInfo =
          context.packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES)

      // just in case
      if (packageInfo.signatures == null || packageInfo.signatures.isEmpty()) {
        return false
      }

      return packageInfo.signatures.all {
        validAppSignatureHashes.contains(Utility.sha1hash(it.toByteArray()))
      }
    } catch (e: PackageManager.NameNotFoundException) {
      return false
    }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.internal

import android.content.Context
import androidx.annotation.RestrictTo
import com.facebook.FacebookSdk

/**
 * com.facebook.internal is solely for the use of other packages within the Facebook SDK for
 * Android. Use of any of the classes in this package is unsupported, and they may be modified or
 * removed without warning at any time.
 */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
object FeatureManager {
  private const val FEATURE_MANAGER_STORE = "com.facebook.internal.FEATURE_MANAGER"
  private val featureMapping: MutableMap<Feature, Array<String>> = hashMapOf()

  @JvmStatic
  fun checkFeature(feature: Feature, callback: Callback) {
    FetchedAppGateKeepersManager.loadAppGateKeepersAsync(
        object : FetchedAppGateKeepersManager.Callback {
          override fun onCompleted() {
            callback.onCompleted(isEnabled(feature))
          }
        })
  }

  @JvmStatic
  fun isEnabled(feature: Feature): Boolean {
    if (Feature.Unknown == feature) {
      return false
    }
    if (Feature.Core == feature) {
      return true
    }
    val version =
        FacebookSdk.getApplicationContext()
            .getSharedPreferences(FEATURE_MANAGER_STORE, Context.MODE_PRIVATE)
            .getString(feature.toKey(), null)
    if (version != null && version == FacebookSdk.getSdkVersion()) {
      return false
    }
    val parent = feature.parent
    return if (parent == feature) {
      getGKStatus(feature)
    } else {
      isEnabled(parent) && getGKStatus(feature)
    }
  }

  @JvmStatic
  fun disableFeature(feature: Feature) {
    FacebookSdk.getApplicationContext()
        .getSharedPreferences(FEATURE_MANAGER_STORE, Context.MODE_PRIVATE)
        .edit()
        .putString(feature.toKey(), FacebookSdk.getSdkVersion())
        .apply()
  }

  @JvmStatic
  fun getFeature(className: String): Feature {
    initializeFeatureMapping()
    for ((key, value) in featureMapping) {
      for (v in value) {
        if (className.startsWith(v)) {
          return key
        }
      }
    }
    return Feature.Unknown
  }

  @Synchronized
  private fun initializeFeatureMapping() {
    if (!featureMapping.isEmpty()) {
      return
    }
    featureMapping[Feature.AAM] = arrayOf("com.facebook.appevents.aam.")
    featureMapping[Feature.CodelessEvents] = arrayOf("com.facebook.appevents.codeless.")
    featureMapping[Feature.CloudBridge] = arrayOf("com.facebook.appevents.cloudbridge.")
    featureMapping[Feature.ErrorReport] = arrayOf("com.facebook.internal.instrument.errorreport.")
    featureMapping[Feature.AnrReport] = arrayOf("com.facebook.internal.instrument.anrreport.")
    featureMapping[Feature.PrivacyProtection] = arrayOf("com.facebook.appevents.ml.")
    featureMapping[Feature.SuggestedEvents] = arrayOf("com.facebook.appevents.suggestedevents.")
    featureMapping[Feature.RestrictiveDataFiltering] =
        arrayOf("com.facebook.appevents.restrictivedatafilter.RestrictiveDataManager")
    featureMapping[Feature.IntelligentIntegrity] =
        arrayOf("com.facebook.appevents.integrity.IntegrityManager")
    featureMapping[Feature.ProtectedMode] = arrayOf("com.facebook.appevents.integrity.ProtectedModeManager")
    featureMapping[Feature.MACARuleMatching] = arrayOf("com.facebook.appevents.integrity.MACARuleMatchingManager")
    featureMapping[Feature.EventDeactivation] = arrayOf("com.facebook.appevents.eventdeactivation.")
    featureMapping[Feature.OnDeviceEventProcessing] =
        arrayOf("com.facebook.appevents.ondeviceprocessing.")
    featureMapping[Feature.IapLogging] = arrayOf("com.facebook.appevents.iap.")
    featureMapping[Feature.Monitoring] = arrayOf("com.facebook.internal.logging.monitor")
  }

  private fun getGKStatus(feature: Feature): Boolean {
    val defaultStatus = defaultStatus(feature)
    return FetchedAppGateKeepersManager.getGateKeeperForKey(
        feature.toKey(), FacebookSdk.getApplicationId(), defaultStatus)
  }

  private fun defaultStatus(feature: Feature): Boolean {
    return when (feature) {
      Feature.RestrictiveDataFiltering,
      Feature.Instrument,
      Feature.CrashReport,
      Feature.CrashShield,
      Feature.ThreadCheck,
      Feature.ErrorReport,
      Feature.AnrReport,
      Feature.AAM,
      Feature.CloudBridge,
      Feature.PrivacyProtection,
      Feature.SuggestedEvents,
      Feature.IntelligentIntegrity,
      Feature.ModelRequest,
      Feature.EventDeactivation,
      Feature.OnDeviceEventProcessing,
      Feature.OnDevicePostInstallEventProcessing,
      Feature.IapLogging,
      Feature.IapLoggingLib2,
      Feature.ProtectedMode,
      Feature.MACARuleMatching,
      Feature.ChromeCustomTabsPrefetching,
      Feature.Monitoring,
      Feature.IgnoreAppSwitchToLoggedOut,
      Feature.BypassAppSwitch -> false
      else -> true
    }
  }

  /**
   * Feature enum Defines features in SDK
   *
   * Sample: AppEvents = 0x00010000, ^ ^ ^ ^ | | | | kit | | | feature | | sub-feature |
   * sub-sub-feature 1st byte: kit 2nd byte: feature 3rd byte: sub-feature 4th byte: sub-sub-feature
   */
  enum class Feature(private val code: Int) {
    Unknown(-1),

    // Features in CoreKit
    /** Essential of CoreKit */
    Core(0x00000000),
    AppEvents(0x00010000),
    CodelessEvents(0x00010100),
    CloudBridge(0x00010800),
    RestrictiveDataFiltering(0x00010200),
    AAM(0x00010300),
    PrivacyProtection(0x00010400),
    SuggestedEvents(0x00010401),
    IntelligentIntegrity(0x00010402),
    ModelRequest(0x00010403),
    ProtectedMode(0x00010404), /* filter out the params which are not supported legally */
    MACARuleMatching(0x00010405),
    EventDeactivation(0x00010500),
    OnDeviceEventProcessing(0x00010600),
    OnDevicePostInstallEventProcessing(0x00010601),
    IapLogging(0x00010700),
    IapLoggingLib2(0x00010701),
    Instrument(0x00020000),
    CrashReport(0x00020100),
    CrashShield(0x00020101),
    ThreadCheck(0x00020102),
    ErrorReport(0x00020200),
    AnrReport(0x00020300),
    Monitoring(0x00030000),
    ServiceUpdateCompliance(0x00030100),
    Megatron(0x00040000),
    Elora(0x00050000),

    // Features in LoginKit
    /** Essential of LoginKit */
    Login(0x01000000),
    ChromeCustomTabsPrefetching(0x01010000),
    IgnoreAppSwitchToLoggedOut(0x01020000),
    BypassAppSwitch(0x01030000),

    // Features in ShareKit
    /** Essential of ShareKit */
    Share(0x02000000);

    override fun toString(): String =
        when (this) {
          Core -> "CoreKit"
          AppEvents -> "AppEvents"
          CodelessEvents -> "CodelessEvents"
          RestrictiveDataFiltering -> "RestrictiveDataFiltering"
          Instrument -> "Instrument"
          CrashReport -> "CrashReport"
          CrashShield -> "CrashShield"
          ThreadCheck -> "ThreadCheck"
          ErrorReport -> "ErrorReport"
          AnrReport -> "AnrReport"
          AAM -> "AAM"
          CloudBridge -> "AppEventsCloudbridge"
          PrivacyProtection -> "PrivacyProtection"
          SuggestedEvents -> "SuggestedEvents"
          IntelligentIntegrity -> "IntelligentIntegrity"
          ProtectedMode -> "ProtectedMode"
          MACARuleMatching -> "MACARuleMatching"
          ModelRequest -> "ModelRequest"
          EventDeactivation -> "EventDeactivation"
          OnDeviceEventProcessing -> "OnDeviceEventProcessing"
          OnDevicePostInstallEventProcessing -> "OnDevicePostInstallEventProcessing"
          IapLogging -> "IAPLogging"
          IapLoggingLib2 -> "IAPLoggingLib2"
          Monitoring -> "Monitoring"
          Megatron -> "Megatron"
          Elora -> "Elora"
          ServiceUpdateCompliance -> "ServiceUpdateCompliance"
          Login -> "LoginKit"
          ChromeCustomTabsPrefetching -> "ChromeCustomTabsPrefetching"
          IgnoreAppSwitchToLoggedOut -> "IgnoreAppSwitchToLoggedOut"
          BypassAppSwitch -> "BypassAppSwitch"
          Share -> "ShareKit"
          else -> "unknown"
        }

    fun toKey(): String {
      return "FBSDKFeature$this"
    }

    val parent: Feature
      get() =
          when {
            this.code and 0xFF > 0 -> {
              fromInt(this.code and -0x100)
            }
            this.code and 0xFF00 > 0 -> {
              fromInt(this.code and -0x10000)
            }
            this.code and 0xFF0000 > 0 -> {
              fromInt(this.code and -0x1000000)
            }
            else -> {
              fromInt(0)
            }
          }

    companion object {
      fun fromInt(code: Int): Feature {
        for (feature in values()) {
          if (feature.code == code) {
            return feature
          }
        }
        return Unknown
      }
    }
  }

  /**
   * Callback for fetching feature status. Method [FeatureManager.checkFeature]} will call
   * GateKeeper manager to load the latest GKs first and then run the callback function.
   */
  fun interface Callback {
    /** The method that will be called when the feature status request completes. */
    fun onCompleted(enabled: Boolean)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.internal

import android.content.Context
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import androidx.annotation.RestrictTo
import androidx.annotation.VisibleForTesting
import com.facebook.FacebookSdk
import com.facebook.GraphRequest
import com.facebook.internal.Utility.isNullOrEmpty
import com.facebook.internal.gatekeeper.GateKeeper
import com.facebook.internal.gatekeeper.GateKeeperRuntimeCache
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.atomic.AtomicBoolean
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject

/**
 * com.facebook.internal is solely for the use of other packages within the Facebook SDK for
 * Android. Use of any of the classes in this package is unsupported, and they may be modified or
 * removed without warning at any time.
 */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
object FetchedAppGateKeepersManager {
  private val TAG = FetchedAppGateKeepersManager::class.simpleName
  private const val APP_GATEKEEPERS_PREFS_STORE =
      "com.facebook.internal.preferences.APP_GATEKEEPERS"
  private const val APP_GATEKEEPERS_PREFS_KEY_FORMAT = "com.facebook.internal.APP_GATEKEEPERS.%s"
  private const val APP_PLATFORM = "android"
  private const val APPLICATION_GATEKEEPER_EDGE = "mobile_sdk_gk"
  private const val APPLICATION_GATEKEEPER_FIELD = "gatekeepers"
  private const val APPLICATION_GRAPH_DATA = "data"
  private const val APPLICATION_FIELDS = GraphRequest.FIELDS_PARAM
  private const val APPLICATION_PLATFORM = "platform"
  private const val APPLICATION_SDK_VERSION = "sdk_version"
  private val isLoading = AtomicBoolean(false)
  private val callbacks = ConcurrentLinkedQueue<Callback>()
  private val fetchedAppGateKeepers: MutableMap<String, JSONObject> = ConcurrentHashMap()
  private const val APPLICATION_GATEKEEPER_CACHE_TIMEOUT = 60 * 60 * 1000.toLong()
  private var timestamp: Long? = null

  // GateKeeper values in runtime. It may be changed through the UI.
  private var gateKeeperRuntimeCache: GateKeeperRuntimeCache? = null

  fun loadAppGateKeepersAsync() {
    loadAppGateKeepersAsync(null)
  }

  @JvmStatic
  @Synchronized
  fun loadAppGateKeepersAsync(callback: Callback?) {
    if (callback != null) {
      callbacks.add(callback)
    }
    val applicationId = FacebookSdk.getApplicationId()
    if (isTimestampValid(timestamp) && fetchedAppGateKeepers.containsKey(applicationId)) {
      pollCallbacks()
      return
    }
    val context = FacebookSdk.getApplicationContext()
    val gateKeepersKey = String.format(APP_GATEKEEPERS_PREFS_KEY_FORMAT, applicationId)
    if (context == null) {
      return
    }

    // See if we had a cached copy of gatekeepers and use that immediately
    val gateKeepersSharedPrefs =
        context.getSharedPreferences(APP_GATEKEEPERS_PREFS_STORE, Context.MODE_PRIVATE)
    val gateKeepersJSONString = gateKeepersSharedPrefs.getString(gateKeepersKey, null)
    if (!Utility.isNullOrEmpty(gateKeepersJSONString)) {
      var gateKeepersJSON: JSONObject? = null
      try {
        gateKeepersJSON = JSONObject(gateKeepersJSONString)
      } catch (je: JSONException) {
        Utility.logd(Utility.LOG_TAG, je)
      }
      if (gateKeepersJSON != null) {
        parseAppGateKeepersFromJSON(applicationId, gateKeepersJSON)
      }
    }
    val executor = FacebookSdk.getExecutor() ?: return
    if (!isLoading.compareAndSet(false, true)) {
      return
    }
    executor.execute {
      val gateKeepersResultJSON = getAppGateKeepersQueryResponse(applicationId)
      if (gateKeepersResultJSON.length() != 0) {
        parseAppGateKeepersFromJSON(applicationId, gateKeepersResultJSON)
        val gateKeepersSharedPrefs =
            context.getSharedPreferences(APP_GATEKEEPERS_PREFS_STORE, Context.MODE_PRIVATE)
        gateKeepersSharedPrefs
            .edit()
            .putString(gateKeepersKey, gateKeepersResultJSON.toString())
            .apply()
        // Update timestamp only when the GKs are successfully fetched and stored
        timestamp = System.currentTimeMillis()
      }
      pollCallbacks()
      isLoading.set(false)
    }
  }

  private fun pollCallbacks() {
    val handler = Handler(Looper.getMainLooper())
    while (!callbacks.isEmpty()) {
      val callback = callbacks.poll()
      // callback can be null when function pollCallbacks is called in multiple threads
      if (callback != null) {
        handler.post { callback.onCompleted() }
      }
    }
  }

  // Note that this method makes a synchronous Graph API call, so should not be called from the
  // main thread. This call can block for long time if network is not available and network
  // timeout is long.
  @JvmStatic
  fun queryAppGateKeepers(applicationId: String, forceRequery: Boolean): JSONObject {
    // Cache the last app checked results.
    if (!forceRequery && fetchedAppGateKeepers.containsKey(applicationId)) {
      return fetchedAppGateKeepers[applicationId] ?: JSONObject()
    }
    val response = getAppGateKeepersQueryResponse(applicationId)
    val context = FacebookSdk.getApplicationContext()
    val gateKeepersKey = String.format(APP_GATEKEEPERS_PREFS_KEY_FORMAT, applicationId)
    val gateKeepersSharedPrefs =
        context.getSharedPreferences(APP_GATEKEEPERS_PREFS_STORE, Context.MODE_PRIVATE)
    gateKeepersSharedPrefs.edit().putString(gateKeepersKey, response.toString()).apply()
    return parseAppGateKeepersFromJSON(applicationId, response)
  }

  /**
   * Obtain all gatekeeper values as a map
   *
   * @param applicationId the app id that gatekeepers related to
   * @return map of all settings/gatekeepers
   */
  fun getGateKeepersForApplication(applicationId: String?): Map<String, Boolean> {
    loadAppGateKeepersAsync()
    if (applicationId == null || !fetchedAppGateKeepers.containsKey(applicationId)) {
      return HashMap()
    }
    val cacheList = gateKeeperRuntimeCache?.dumpGateKeepers(applicationId)
    return if (cacheList != null) {
      val cacheMap = HashMap<String, Boolean>()
      cacheList.forEach { cacheMap[it.name] = it.value }
      cacheMap
    } else {
      val output: MutableMap<String, Boolean> = HashMap()
      val jsonObject: JSONObject = fetchedAppGateKeepers[applicationId] ?: JSONObject()
      val jsonIterator = jsonObject.keys()
      while (jsonIterator.hasNext()) {
        val key = jsonIterator.next()
        output[key] = jsonObject.optBoolean(key)
      }
      val runtimeCache = gateKeeperRuntimeCache ?: GateKeeperRuntimeCache()
      runtimeCache.setGateKeepers(applicationId, output.map { GateKeeper(it.key, it.value) })
      gateKeeperRuntimeCache = runtimeCache
      output
    }
  }

  @JvmStatic
  fun getGateKeeperForKey(name: String, applicationId: String?, defaultValue: Boolean): Boolean {
    val map = getGateKeepersForApplication(applicationId)
    return if (!map.containsKey(name)) {
      defaultValue
    } else map[name] ?: return defaultValue
  }

  /**
   * Set GateKeeper values in the runtime cache, so that it will affect GK reading later. Only if GK
   * exists in the cache, it will be updated.
   *
   * @param applicationId Application ID
   * @param gateKeeper name-value pair of the Gate Keeper to be set
   */
  @JvmStatic
  fun setRuntimeGateKeeper(
      applicationId: String = FacebookSdk.getApplicationId(),
      gateKeeper: GateKeeper
  ) {
    if (gateKeeperRuntimeCache?.getGateKeeper(applicationId, gateKeeper.name) != null) {
      gateKeeperRuntimeCache?.setGateKeeper(applicationId, gateKeeper)
    } else {
      Log.w(TAG, "Missing gatekeeper runtime cache")
    }
  }

  /**
   * Invalid runtime GateKeeper cache so that the manager will load original GK values next time.
   */
  @JvmStatic
  fun resetRuntimeGateKeeperCache() {
    gateKeeperRuntimeCache?.resetCache()
  }

  // Note that this method makes a synchronous Graph API call, so should not be called from the
  // main thread.
  private fun getAppGateKeepersQueryResponse(applicationId: String): JSONObject {
    val appGateKeepersParams = Bundle()
    appGateKeepersParams.putString(APPLICATION_PLATFORM, APP_PLATFORM)
    appGateKeepersParams.putString(APPLICATION_SDK_VERSION, FacebookSdk.getSdkVersion())
    appGateKeepersParams.putString(APPLICATION_FIELDS, APPLICATION_GATEKEEPER_FIELD)
    val request =
        GraphRequest.newGraphPathRequest(
            null, String.format("app/%s", APPLICATION_GATEKEEPER_EDGE), null)
    request.parameters = appGateKeepersParams
    return request.executeAndWait().jsonObject ?: JSONObject()
  }

  @Synchronized
  @JvmStatic
  @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
  internal fun parseAppGateKeepersFromJSON(
      applicationId: String,
      gateKeepersJSON: JSONObject?
  ): JSONObject {
    val result = fetchedAppGateKeepers[applicationId] ?: JSONObject()
    val gateKeepers =
        gateKeepersJSON?.optJSONArray(APPLICATION_GRAPH_DATA)?.optJSONObject(0) ?: JSONObject()
    // If there does exist a valid JSON object in arr, initialize result with this JSON object
    val data = gateKeepers.optJSONArray(APPLICATION_GATEKEEPER_FIELD) ?: JSONArray()
    for (i in 0 until data.length()) {
      try {
        val gk = data.getJSONObject(i)
        result.put(gk.getString("key"), gk.getBoolean("value"))
      } catch (je: JSONException) {
        Utility.logd(Utility.LOG_TAG, je)
      }
    }
    fetchedAppGateKeepers[applicationId] = result
    return result
  }

  private fun isTimestampValid(timestamp: Long?): Boolean {
    return if (timestamp == null) {
      false
    } else System.currentTimeMillis() - timestamp < APPLICATION_GATEKEEPER_CACHE_TIMEOUT
  }

  /** Callback for fetch GK when the GK results are valid. */
  fun interface Callback {
    /** The method that will be called when the GK request completes. */
    fun onCompleted()
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.internal

import android.net.Uri
import com.facebook.internal.FetchedAppSettingsManager.getAppSettingsWithoutQuery
import java.util.EnumSet
import org.json.JSONArray
import org.json.JSONObject

/**
 * com.facebook.internal is solely for the use of other packages within the Facebook SDK for
 * Android. Use of any of the classes in this package is unsupported, and they may be modified or
 * removed without warning at any time.
 */
class FetchedAppSettings(
    private val supportsImplicitLogging: Boolean,
    val nuxContent: String,
    val nuxEnabled: Boolean,
    val sessionTimeoutInSeconds: Int,
    val smartLoginOptions: EnumSet<SmartLoginOption>,
    val dialogConfigurations: Map<String, Map<String, DialogFeatureConfig>>,
    val automaticLoggingEnabled: Boolean,
    val errorClassification: FacebookRequestErrorClassification,
    val smartLoginBookmarkIconURL: String,
    val smartLoginMenuIconURL: String,
    val iAPAutomaticLoggingEnabled: Boolean,
    val codelessEventsEnabled: Boolean,
    val eventBindings: JSONArray?,
    val sdkUpdateMessage: String,
    val trackUninstallEnabled: Boolean,
    val monitorViaDialogEnabled: Boolean,
    val rawAamRules: String?,
    val suggestedEventsSetting: String?,
    val restrictiveDataSetting: String?,
    val protectedModeStandardParamsSetting: JSONArray?,
    val MACARuleMatchingSetting: JSONArray?
) {

  fun supportsImplicitLogging(): Boolean = supportsImplicitLogging

  class DialogFeatureConfig
  private constructor(
      val dialogName: String,
      val featureName: String,
      val fallbackUrl: Uri?,
      val versionSpec: IntArray?
  ) {

    companion object {
      private const val DIALOG_CONFIG_DIALOG_NAME_FEATURE_NAME_SEPARATOR = "|"
      private const val DIALOG_CONFIG_NAME_KEY = "name"
      private const val DIALOG_CONFIG_VERSIONS_KEY = "versions"
      private const val DIALOG_CONFIG_URL_KEY = "url"
      fun parseDialogConfig(dialogConfigJSON: JSONObject): DialogFeatureConfig? {
        val dialogNameWithFeature = dialogConfigJSON.optString(DIALOG_CONFIG_NAME_KEY)
        if (Utility.isNullOrEmpty(dialogNameWithFeature)) {
          return null
        }
        val components =
            dialogNameWithFeature.split(DIALOG_CONFIG_DIALOG_NAME_FEATURE_NAME_SEPARATOR)
        if (components.size != 2) {
          // We expect the format to be dialogName|FeatureName, where both components are
          // non-empty.
          return null
        }
        val dialogName = components.first()
        val featureName = components.last()
        if (Utility.isNullOrEmpty(dialogName) || Utility.isNullOrEmpty(featureName)) {
          return null
        }
        val urlString = dialogConfigJSON.optString(DIALOG_CONFIG_URL_KEY)
        var fallbackUri: Uri? = null
        if (!Utility.isNullOrEmpty(urlString)) {
          fallbackUri = Uri.parse(urlString)
        }
        val versionsJSON = dialogConfigJSON.optJSONArray(DIALOG_CONFIG_VERSIONS_KEY)
        val featureVersionSpec = parseVersionSpec(versionsJSON)
        return DialogFeatureConfig(dialogName, featureName, fallbackUri, featureVersionSpec)
      }

      private fun parseVersionSpec(versionsJSON: JSONArray?): IntArray? {
        // Null signifies no overrides to the min-version as specified by the SDK.
        // An empty array would basically turn off the dialog (i.e no supported versions), so
        // DON'T default to that.
        var versionSpec: IntArray? = null
        if (versionsJSON != null) {
          val numVersions = versionsJSON.length()
          versionSpec = IntArray(numVersions)
          for (i in 0 until numVersions) {
            // See if the version was stored directly as an Integer
            var version = versionsJSON.optInt(i, NativeProtocol.NO_PROTOCOL_AVAILABLE)
            if (version == NativeProtocol.NO_PROTOCOL_AVAILABLE) {
              // If not, then see if it was stored as a string that can be parsed out.
              // If even that fails, then we will leave it as NO_PROTOCOL_AVAILABLE
              val versionString = versionsJSON.optString(i)
              if (!Utility.isNullOrEmpty(versionString)) {
                version =
                    try {
                      versionString.toInt()
                    } catch (nfe: NumberFormatException) {
                      Utility.logd(Utility.LOG_TAG, nfe)
                      NativeProtocol.NO_PROTOCOL_AVAILABLE
                    }
              }
            }
            versionSpec[i] = version
          }
        }
        return versionSpec
      }
    }
  }

  companion object {
    @JvmStatic
    fun getDialogFeatureConfig(
        applicationId: String,
        actionName: String,
        featureName: String
    ): DialogFeatureConfig? {
      if (actionName.isEmpty() || featureName.isEmpty()) {
        return null
      }
      val settings = getAppSettingsWithoutQuery(applicationId)
      val featureMap = settings?.dialogConfigurations?.get(actionName)
      if (featureMap != null) {
        return featureMap[featureName]
      }
      return null
    }
  }
}