/*
 * 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.FacebookSdk
import com.facebook.FacebookTestCase
import java.security.SecureRandom
import java.util.concurrent.Executor
import java.util.concurrent.Executors
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
import org.assertj.core.api.Assertions.assertThat
import org.junit.Assert
import org.junit.Before
import org.junit.Test

class WorkQueueTest : FacebookTestCase() {
  @Before
  fun init() {
    FacebookSdk.setExecutor(Executors.newFixedThreadPool(5))
  }

  @Test
  fun `test empty validate`() {
    val manager = WorkQueue()
    manager.validate()
  }

  @Test
  fun `test run something`() {
    val run = CountingRunnable()
    Assert.assertEquals(0, run.runCount)
    val executor = ScriptableExecutor()
    Assert.assertEquals(0, executor.pendingCount)
    val manager = WorkQueue(1, executor)
    addActiveWorkItem(manager, run)
    Assert.assertEquals(1, executor.pendingCount)
    Assert.assertEquals(0, run.runCount)
    executeNext(manager, executor)
    Assert.assertEquals(0, executor.pendingCount)
    Assert.assertEquals(1, run.runCount)
  }

  @Test
  fun `test run sequence`() {
    val workTotal = 100
    val run = CountingRunnable()
    val executor = ScriptableExecutor()
    val manager = WorkQueue(1, executor)
    for (i in 0 until workTotal) {
      addActiveWorkItem(manager, run)
      Assert.assertEquals(1, executor.pendingCount)
    }
    for (i in 0 until workTotal) {
      Assert.assertEquals(1, executor.pendingCount)
      Assert.assertEquals(i, run.runCount)
      executeNext(manager, executor)
    }
    Assert.assertEquals(0, executor.pendingCount)
    Assert.assertEquals(workTotal, run.runCount)
  }

  @Test
  fun `test run parallel`() {
    val workTotal = 100
    val run = CountingRunnable()
    val executor = ScriptableExecutor()
    val manager = WorkQueue(workTotal, executor)
    for (i in 0 until workTotal) {
      Assert.assertEquals(i, executor.pendingCount)
      addActiveWorkItem(manager, run)
    }
    for (i in 0 until workTotal) {
      Assert.assertEquals(workTotal - i, executor.pendingCount)
      Assert.assertEquals(i, run.runCount)
      executeNext(manager, executor)
    }
    Assert.assertEquals(0, executor.pendingCount)
    Assert.assertEquals(workTotal, run.runCount)
  }

  @Test
  fun `test simple cancel`() {
    val run = CountingRunnable()
    val executor = ScriptableExecutor()
    val manager = WorkQueue(1, executor)
    addActiveWorkItem(manager, run)
    val work1 = addActiveWorkItem(manager, run)
    cancelWork(manager, work1)
    Assert.assertEquals(1, executor.pendingCount)
    executeNext(manager, executor)
    Assert.assertEquals(0, executor.pendingCount)
  }

  @Test
  fun `test move to front`() {
    val firstCount = 8
    val highCount = 17
    val highWorkItems = ArrayList<WorkQueue.WorkItem>()
    val highRun = CountingRunnable()
    val firstRun = CountingRunnable()
    val lowRun = CountingRunnable()
    val executor = ScriptableExecutor()
    val manager = WorkQueue(firstCount, executor)
    for (i in 0 until firstCount) {
      addActiveWorkItem(manager, firstRun)
    }
    var lowCount = 0
    for (h in 0 until highCount) {
      highWorkItems.add(addActiveWorkItem(manager, highRun))
      repeat(h) {
        addActiveWorkItem(manager, lowRun)
        lowCount++
      }
    }
    Assert.assertEquals(firstCount, executor.pendingCount)
    for (highItem in highWorkItems) {
      prioritizeWork(manager, highItem)
    }
    for (i in 0 until firstCount) {
      Assert.assertEquals(i, firstRun.runCount)
      executeNext(manager, executor)
    }
    for (i in 0 until highCount) {
      Assert.assertEquals(i, highRun.runCount)
      executeNext(manager, executor)
    }
    for (i in 0 until lowCount) {
      Assert.assertEquals(i, lowRun.runCount)
      executeNext(manager, executor)
    }
    Assert.assertEquals(firstCount, firstRun.runCount)
    Assert.assertEquals(highCount, highRun.runCount)
    Assert.assertEquals(lowCount, lowRun.runCount)
  }

  // Test cancelling running work item, completed work item
  @Test
  fun `test thread stress`() {
    val manager = WorkQueue()
    val runnables = ArrayList<StressRunnable>()
    val threadCount = 20
    for (i in 0 until threadCount) {
      runnables.add(StressRunnable(manager, 20))
    }
    for (i in 0 until threadCount) {
      manager.addActiveWorkItem(runnables[i])
    }
    for (i in 0 until threadCount) {
      runnables[i].waitForDone()
    }
  }

  private fun addActiveWorkItem(manager: WorkQueue, runnable: Runnable): WorkQueue.WorkItem {
    manager.validate()
    val workItem = manager.addActiveWorkItem(runnable)
    manager.validate()
    return workItem
  }

  private fun executeNext(manager: WorkQueue, executor: ScriptableExecutor) {
    manager.validate()
    executor.runNext()
    manager.validate()
  }

  private fun cancelWork(manager: WorkQueue, workItem: WorkQueue.WorkItem) {
    manager.validate()
    workItem.cancel()
    manager.validate()
  }

  private fun prioritizeWork(manager: WorkQueue, workItem: WorkQueue.WorkItem) {
    manager.validate()
    workItem.moveToFront()
    manager.validate()
  }

  internal class StressRunnable(val manager: WorkQueue, val iterationCount: Int) : Runnable {
    private val random = SecureRandom()
    private val lock = ReentrantLock()
    private val condition = lock.newCondition()
    var iterationIndex = 0
    var isDone = false
    override fun run() {
      // Each iteration runs a random action against the WorkQueue.
      if (iterationIndex++ < iterationCount) {
        val sleepWeight = 80
        val trackThisWeight = 10
        val prioritizeTrackedWeight = 6
        val validateWeight = 2
        var weight = 0
        val n =
            random.nextInt(sleepWeight + trackThisWeight + prioritizeTrackedWeight + validateWeight)
        val workItem = manager.addActiveWorkItem(this)
        if (n <
            sleepWeight.let {
              weight += it
              weight
            }) {
          // Sleep
          try {
            Thread.sleep(n / 4L)
          } catch (e: InterruptedException) {}
        } else if (n <
            trackThisWeight.let {
              weight += it
              weight
            }) {
          // Track this work item to activate later
          synchronized(tracked) { tracked.add(workItem) }
        } else if (n <
            prioritizeTrackedWeight.let {
              weight += it
              weight
            }) {
          // Background all pending items, prioritize tracked items, and clear tracked list
          val items = ArrayList<WorkQueue.WorkItem>()
          synchronized(tracked) {
            items.addAll(tracked)
            tracked.clear()
          }
          for (item in items) {
            item.moveToFront()
          }
        } else {
          // Validate
          manager.validate()
        }
      } else {
        // Also have all threads validate once they are done.
        manager.validate()
        lock.withLock {
          isDone = true
          condition.signalAll()
        }
      }
    }

    fun waitForDone() {
      lock.withLock {
        while (!isDone) {
          try {
            condition.await()
          } catch (e: InterruptedException) {}
        }
      }
    }

    companion object {
      var tracked = ArrayList<WorkQueue.WorkItem>()
    }
  }

  internal class ScriptableExecutor : Executor {
    private val runnables = ArrayList<Runnable>()
    val pendingCount: Int
      get() = runnables.size

    fun runNext() {
      assertThat(runnables.size).isGreaterThan(0)
      runnables[0].run()
      runnables.removeAt(0)
    }

    fun runLast() {
      assertThat(runnables.size).isGreaterThan(0)
      val index = runnables.size - 1
      runnables[index].run()
      runnables.removeAt(index)
    }

    override fun execute(runnable: Runnable) {
      synchronized(this) { runnables.add(runnable) }
    }
  }

  internal class CountingRunnable : Runnable {
    @get:Synchronized
    var runCount = 0
      private set

    override fun run() {
      synchronized(this) { runCount++ }
      try {
        Thread.sleep(1)
      } catch (e: InterruptedException) {}
    }
  }
}


/*
 * 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.gatekeeper

import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito.mockStatic
import org.powermock.core.classloader.annotations.PrepareForTest

@PrepareForTest(FacebookSdk::class)
class GateKeeperRuntimeCacheTest : FacebookPowerMockTestCase() {

  companion object {
    const val APPLICATION_ID = "123456789"
    const val APPLICATION_ID2 = "987654321"
    const val GK1 = "FBSDKFeatureInstrument"
    const val GK2 = "app_events_killswitch"
  }

  @Test
  fun `test map dump`() {
    val cache = GateKeeperRuntimeCache()
    cache.setGateKeeperValue(APPLICATION_ID, GK1, true)
    cache.setGateKeeperValue(APPLICATION_ID, GK2, false)

    val dumpMap = HashMap<String, Boolean>()
    cache.dumpGateKeepers(APPLICATION_ID)?.forEach { dumpMap[it.name] = it.value }
    assertThat(dumpMap.getValue(GK1)).isTrue
    assertThat(dumpMap.getValue(GK2)).isFalse
  }

  @Test
  fun `test empty gate keeper`() {
    val cache = GateKeeperRuntimeCache()

    assertThat(cache.getGateKeeperValue(APPLICATION_ID, GK1, true)).isTrue
    assertThat(cache.getGateKeeperValue(APPLICATION_ID, GK1, false)).isFalse
  }

  @Test
  fun `test multiple applications`() {
    val cache = GateKeeperRuntimeCache()
    cache.setGateKeeperValue(APPLICATION_ID, GK1, true)
    cache.setGateKeeperValue(APPLICATION_ID2, GK1, false)
    assertThat(cache.getGateKeeperValue(APPLICATION_ID, GK1, false)).isTrue
    assertThat(cache.getGateKeeperValue(APPLICATION_ID2, GK1, true)).isFalse
  }

  @Test
  fun `test write to default app Id`() {
    mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getApplicationId()).thenReturn(APPLICATION_ID2)
    val cache = GateKeeperRuntimeCache()
    cache.setGateKeeperValue(name = GK1, value = true)
    assertThat(cache.getGateKeeperValue(name = GK1, defaultValue = false)).isTrue
    assertThat(cache.getGateKeeperValue(appId = APPLICATION_ID, name = GK1, defaultValue = false))
        .isFalse
  }
}


/*
 * 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.instrument

import android.content.Context
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.GraphRequestBatch
import com.facebook.MockSharedPreference
import com.facebook.internal.Utility
import java.io.File
import java.util.UUID
import java.util.concurrent.atomic.AtomicBoolean
import org.json.JSONObject
import org.junit.After
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.reflect.Whitebox

@PrepareForTest(
    FacebookSdk::class,
    ExceptionAnalyzer::class,
    InstrumentUtility::class,
    Utility::class,
    GraphRequestBatch::class)
class ExceptionAnalyzerTest : FacebookPowerMockTestCase() {
  private var instrumentDataWritten = "{}"
  private val preference = MockSharedPreference()
  private lateinit var directory: File
  private lateinit var root: File

  @Before
  fun init() {
    val rootName = UUID.randomUUID().toString()
    directory = File(rootName, "instrument")
    directory.mkdirs()
    root = File(rootName)

    val context = PowerMockito.mock(Context::class.java)
    PowerMockito.`when`(context.getSharedPreferences(anyString(), anyInt())).thenReturn(preference)
    PowerMockito.spy(FacebookSdk::class.java)
    PowerMockito.spy(ExceptionAnalyzer::class.java)
    PowerMockito.`when`(ExceptionAnalyzer.isDebug()).thenReturn(false)
    Whitebox.setInternalState(FacebookSdk::class.java, "sdkInitialized", AtomicBoolean(true))
    Whitebox.setInternalState(FacebookSdk::class.java, "applicationId", "123456789")
    Whitebox.setInternalState(FacebookSdk::class.java, "applicationContext", context)
    Whitebox.setInternalState(ExceptionAnalyzer::class.java, "enabled", true)
    PowerMockito.spy(InstrumentUtility::class.java)
    PowerMockito.spy(Utility::class.java)
    PowerMockito.doReturn(false).`when`(Utility::class.java, "isDataProcessingRestricted")
    PowerMockito.doReturn(directory).`when`(InstrumentUtility::class.java, "getInstrumentReportDir")
  }

  @After
  fun clean() {
    root.deleteRecursively()
  }

  @Test
  fun `test execute`() {
    PowerMockito.doReturn(true).`when`(FacebookSdk::class.java, "getAutoLogAppEventsEnabled")
    PowerMockito.doAnswer {
          instrumentDataWritten = it.arguments[1] as String
          return@doAnswer Unit
        }
        .`when`(InstrumentUtility::class.java, "writeFile", anyString(), anyString())

    val e = Exception()
    val trace =
        arrayOf(
            StackTraceElement(
                "com.facebook.appevents.codeless.CodelessManager", "onActivityResumed", "file", 10))
    e.stackTrace = trace
    ExceptionAnalyzer.execute(e)
    Assert.assertEquals(
        FacebookSdk.getSdkVersion(), preference.getString("FBSDKFeatureCodelessEvents", null))
    val instrumentData = JSONObject(instrumentDataWritten)
    val featureNames = instrumentData.getJSONArray("feature_names")
    val featureToExam = featureNames.getString(0)
    Assert.assertNotNull(featureToExam)
    Assert.assertEquals("CodelessEvents", featureToExam)
  }

  @Test
  fun `test send error reports`() {
    InstrumentUtility.writeFile("analysis_log_1.json", "{\"feature_names\":[],\"timestamp\":1}")
    val mockGraphRequestBatch = PowerMockito.mock(GraphRequestBatch::class.java)
    PowerMockito.whenNew(GraphRequestBatch::class.java)
        .withAnyArguments()
        .thenReturn(mockGraphRequestBatch)
    ExceptionAnalyzer.sendExceptionAnalysisReports()
    verify(mockGraphRequestBatch, times(1)).executeAsync()
  }
}


/*
 * 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.instrument

import com.facebook.FacebookPowerMockTestCase
import com.facebook.internal.Utility
import java.io.File
import java.lang.RuntimeException
import org.assertj.core.api.Assertions.assertThat
import org.json.JSONArray
import org.json.JSONObject
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers.isA
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito.mockStatic
import org.powermock.core.classloader.annotations.PrepareForTest

@PrepareForTest(InstrumentUtility::class, Utility::class)
class InstrumentDataTest : FacebookPowerMockTestCase() {

  private val validJson =
      "{\n" +
          "  \"timestamp\": 10,\n" +
          "  \"app_version\": \"0.0.1\",\n" +
          "  \"reason\": \"i do not know\",\n" +
          "  \"callstack\": \"placeholder\"\n" +
          "}"

  @Before
  fun init() {
    val fileData = JSONObject(validJson)
    mockStatic(InstrumentUtility::class.java)
    whenever(InstrumentUtility.readFile(isA(String::class.java), isA(Boolean::class.java)))
        .thenReturn(fileData)
    whenever(InstrumentUtility.getCause(isA(Throwable::class.java))).thenCallRealMethod()
    whenever(InstrumentUtility.getStackTrace(isA(Throwable::class.java))).thenCallRealMethod()

    mockStatic(Utility::class.java)
    whenever(Utility.getAppVersion()).thenReturn("0.0.1")
  }

  @Test
  fun `test creating instrument data with an anr`() {
    val cause = "test_cause"
    val stacktrace = "test_st"
    val data = InstrumentData.Builder.build(cause, stacktrace)
    assertThat(data.isValid).isTrue
    val parameterString = data.toString()
    assertNotNull(parameterString)
    val parameters = JSONObject(parameterString)
    assertEquals(parameters.get("type"), InstrumentData.Type.AnrReport.toString())
    assertEquals(parameters.get("reason"), cause)
    assertEquals(parameters.get("callstack"), stacktrace)
  }

  @Test
  fun `test creating instrument data with an exception`() {
    val ex = NotImplementedError()
    val data = InstrumentData.Builder.build(ex, InstrumentData.Type.CrashReport)
    assertThat(data.isValid).isTrue
    val parameterString = data.toString()
    assertNotNull(parameterString)
    val parameters = JSONObject(parameterString)
    assertEquals(parameters.get("type"), InstrumentData.Type.CrashReport.toString())
    assertEquals(parameters.get("reason"), InstrumentUtility.getCause(ex))
    assertEquals(parameters.get("callstack"), InstrumentUtility.getStackTrace(ex))
  }

  @Test
  fun `test creating instrument data with file`() {
    val testFile = File("thread_check_log_001.json")
    val data = InstrumentData.Builder.load(testFile)
    assertThat(data.isValid).isTrue
    val parameterString = data.toString()
    assertNotNull(parameterString)
    val parameters = JSONObject(parameterString)
    assertEquals(parameters.get("type"), InstrumentData.Type.ThreadCheck.toString())
    assertEquals(parameters.get("timestamp"), 10)
    assertEquals(parameters.get("app_version"), "0.0.1")
    assertEquals(parameters.get("reason"), "i do not know")
    assertEquals(parameters.get("callstack"), "placeholder")
  }

  @Test
  fun `test creating instrument data with a features array`() {
    val featureArray = arrayOf("a", "b", "c")
    val features = JSONArray(featureArray)
    val data = InstrumentData.Builder.build(features)
    assertThat(data.isValid).isTrue
    val parameterString = data.toString()
    assertNotNull(parameterString)
    val parameters = JSONObject(parameterString)
    assertEquals(parameters.get("feature_names"), features)
  }

  @Test
  fun `test invalid instrument analysis data`() {
    val testFile = File("analysis_log_1.json")
    val data = InstrumentData.Builder.load(testFile)
    assertThat(data.isValid).isFalse
  }

  @Test
  fun `test save with exception report`() {
    var didWriteFile = false
    whenever(InstrumentUtility.writeFile(isA(String::class.java), isA(String::class.java))).then {
      didWriteFile = true
      return@then Unit
    }
    InstrumentData.Builder.build(RuntimeException(), InstrumentData.Type.CrashShield).save()
    assertThat(didWriteFile).isTrue
  }
}


/*
 * 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.instrument

import androidx.test.core.app.ApplicationProvider
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.internal.FeatureManager
import com.facebook.internal.instrument.anrreport.ANRHandler
import com.facebook.internal.instrument.crashreport.CrashHandler
import com.facebook.internal.instrument.crashshield.CrashShieldHandler
import com.facebook.internal.instrument.errorreport.ErrorReportHandler
import com.facebook.internal.instrument.threadcheck.ThreadCheckHandler
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.reflect.Whitebox

@PrepareForTest(
    FacebookSdk::class,
    FeatureManager::class,
    CrashHandler::class,
    ExceptionAnalyzer::class,
    CrashShieldHandler::class,
    ThreadCheckHandler::class,
    ErrorReportHandler::class,
    ANRHandler::class,
)
class InstrumentManagerTest : FacebookPowerMockTestCase() {
  private var isLogAppEventsEnable = false
  private var isCrashHandlerEnable = false
  private var isExceptionAnalyzerEnable = false
  private var isErrorReportHandlerEnable = false
  private var isThreadCheckHandlerEnable = false
  private var isCrashShieldHandlerEnable = false
  private var isAnrHandlerEnable = false

  private lateinit var listOfCallbacks: ArrayList<FeatureManager.Callback>

  @Before
  fun init() {
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getApplicationContext())
        .thenReturn(ApplicationProvider.getApplicationContext())
    whenever(FacebookSdk.getAutoLogAppEventsEnabled()).thenAnswer {
      return@thenAnswer isLogAppEventsEnable
    }
    listOfCallbacks = arrayListOf()
    PowerMockito.mockStatic(FeatureManager::class.java)
    whenever(FeatureManager.checkFeature(any(), any())).then {
      val callback = it.arguments[1] as FeatureManager.Callback
      listOfCallbacks.add(callback)
      return@then Unit
    }

    PowerMockito.mockStatic(CrashHandler::class.java)
    val mockCrashHandlerCompanion = mock<CrashHandler.Companion>()
    Whitebox.setInternalState(CrashHandler::class.java, "Companion", mockCrashHandlerCompanion)
    PowerMockito.doAnswer {
          isCrashHandlerEnable = true
          Unit
        }
        .`when`(mockCrashHandlerCompanion)
        .enable()

    PowerMockito.mockStatic(ExceptionAnalyzer::class.java)
    whenever(ExceptionAnalyzer.enable()).thenAnswer {
      isExceptionAnalyzerEnable = true
      Unit
    }
    PowerMockito.mockStatic(CrashShieldHandler::class.java)

    whenever(CrashShieldHandler.enable()).thenAnswer {
      isCrashShieldHandlerEnable = true
      Unit
    }
    PowerMockito.mockStatic(ThreadCheckHandler::class.java)
    whenever(ThreadCheckHandler.enable()).thenAnswer {
      isThreadCheckHandlerEnable = true
      Unit
    }
    PowerMockito.mockStatic(ErrorReportHandler::class.java)
    whenever(ErrorReportHandler.enable()).thenAnswer {
      isErrorReportHandlerEnable = true
      Unit
    }
    PowerMockito.mockStatic(ANRHandler::class.java)
    whenever(ANRHandler.enable()).thenAnswer {
      isAnrHandlerEnable = true
      Unit
    }
  }

  @Test
  fun `test start with auto logging events disable`() {
    isLogAppEventsEnable = false
    InstrumentManager.start()
    assertThat(listOfCallbacks.isEmpty()).isTrue
  }

  @Test
  fun `test start with all features enable`() {
    isLogAppEventsEnable = true
    whenever(FeatureManager.isEnabled(any())).thenReturn(true)
    InstrumentManager.start()
    listOfCallbacks.forEach { it.onCompleted(true) }
    assertThat(isCrashHandlerEnable).isTrue
    assertThat(isExceptionAnalyzerEnable).isTrue
    assertThat(isErrorReportHandlerEnable).isTrue
    assertThat(isThreadCheckHandlerEnable).isTrue
    assertThat(isCrashShieldHandlerEnable).isTrue
    assertThat(isAnrHandlerEnable).isTrue
  }
}


/*
 * 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.instrument

import android.content.Context
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import java.io.File
import java.util.UUID
import org.assertj.core.api.Assertions.assertThat
import org.json.JSONArray
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito.mock
import org.powermock.api.mockito.PowerMockito.mockStatic
import org.powermock.core.classloader.annotations.PrepareForTest

@PrepareForTest(FacebookSdk::class)
class InstrumentUtilityTest : FacebookPowerMockTestCase() {

  private lateinit var rootDirectory: File

  @Before
  fun init() {
    val rootName = UUID.randomUUID().toString()
    rootDirectory = File(rootName)
    rootDirectory.mkdir()
    mockStatic(FacebookSdk::class.java)
    val mockContext = mock(Context::class.java)
    whenever(mockContext.cacheDir).thenReturn(rootDirectory)
    whenever(FacebookSdk.getApplicationContext()).thenReturn(mockContext)
  }

  @After
  fun tearDown() {
    rootDirectory.deleteRecursively()
  }

  @Test
  fun `writing and reading json string`() {
    InstrumentUtility.writeFile("error_log_1337.json", "{\"anything\":\"swag\"}")
    val result = InstrumentUtility.readFile("error_log_1337.json", false)
    assertEquals("{\"anything\":\"swag\"}", result.toString())
  }

  @Test
  fun `writing and reading non-json string`() {
    InstrumentUtility.writeFile("error_log_1337.json", "anything")
    val result = InstrumentUtility.readFile("error_log_1337.json", false)
    assertNull(result)
  }

  @Test
  fun `getting stack trace from a specific thread`() {
    val trace =
        arrayOf(
            StackTraceElement(
                "com.facebook.appevents.codeless.CodelessManager", "onActivityResumed", "file", 10))
    mockStatic(Thread::class.java)
    val thread: Thread = mock(Thread::class.java)
    whenever(thread.stackTrace).thenReturn(trace)
    val result = InstrumentUtility.getStackTrace(thread)
    val expected = JSONArray()
    expected.put("com.facebook.appevents.codeless.CodelessManager.onActivityResumed(file:10)")
    assertEquals(expected.toString(), result)
  }

  @Test
  fun `Checking if the thread is SDK related`() {
    mockStatic(Thread::class.java)
    val thread: Thread = mock(Thread::class.java)

    var trace =
        arrayOf(StackTraceElement("com.cfsample.coffeeshop.AnrActivity", "onClick", "file", 10))
    whenever(thread.stackTrace).thenReturn(trace)
    assertThat(InstrumentUtility.isSDKRelatedThread(thread)).isFalse

    // Exclude onClick(), onItemClick() or onTouch() when they are calling app itself's click
    // listeners
    trace =
        arrayOf(
            StackTraceElement("com.cfsample.coffeeshop.AnrActivity", "onClick", "file", 10),
            StackTraceElement(
                "com.facebook.appevents.suggestedevents.ViewOnClickListener",
                "onClick",
                "ViewOnClickListener.java",
                10),
            StackTraceElement(
                "com.facebook.appevents.codeless.CodelessLoggingEventListener",
                "onItemClick",
                "CodelessLoggingEventListener.java",
                10),
            StackTraceElement(
                "com.facebook.appevents.codeless.RCTCodelessLoggingEventListener",
                "onTouch",
                "RCTCodelessLoggingEventListener.java",
                10),
        )
    whenever(thread.stackTrace).thenReturn(trace)
    assertThat(InstrumentUtility.isSDKRelatedThread(thread)).isFalse

    // If onClick() calls process() and there is an ANR in process(), it's SDK related
    trace =
        arrayOf(
            StackTraceElement(
                "com.facebook.appevents.suggestedevents.ViewOnClickListener",
                "process",
                "ViewOnClickListener.java",
                10),
            StackTraceElement(
                "com.facebook.appevents.suggestedevents.ViewOnClickListener",
                "onClick",
                "ViewOnClickListener.java",
                10),
            StackTraceElement("org.mockito.kotlin.any", "onClick", "ViewOnClickListener.java", 10),
        )
    whenever(thread.stackTrace).thenReturn(trace)
    assertThat(InstrumentUtility.isSDKRelatedThread(thread)).isTrue
  }

  @Test
  fun `listing anr report files`() {
    InstrumentUtility.writeFile("anr_log_1.json", "{\"anything\":\"swag\"}")
    InstrumentUtility.writeFile("anr_log_2.json", "{\"anything\":\"swag\"}")
    InstrumentUtility.writeFile("shouldbeignored_1.json", "{\"anything\":\"swag\"}")
    val result = InstrumentUtility.listAnrReportFiles()
    assertEquals(2, result.size)

    InstrumentUtility.deleteFile("anr_log_1.json")
    InstrumentUtility.deleteFile("anr_log_2.json")
    val result1 = InstrumentUtility.listAnrReportFiles()
    assertEquals(0, result1.size)
  }

  @Test
  fun `listing exception report files`() {
    InstrumentUtility.writeFile("crash_log_1.json", "{\"anything\":\"swag\"}")
    InstrumentUtility.writeFile("shield_log_1.json", "{\"anything\":\"swag\"}")
    InstrumentUtility.writeFile("thread_check_log_1.json", "{\"anything\":\"swag\"}")
    InstrumentUtility.writeFile("shouldbeignored_1.json", "{\"anything\":\"swag\"}")
    val result = InstrumentUtility.listExceptionReportFiles()
    assertEquals(3, result.size)

    InstrumentUtility.deleteFile("crash_log_1.json")
    InstrumentUtility.deleteFile("shield_log_1.json")
    InstrumentUtility.deleteFile("thread_check_log_1.json")
    val result1 = InstrumentUtility.listExceptionReportFiles()
    assertEquals(0, result1.size)
  }

  @Test
  fun `listing exception analysis files`() {
    InstrumentUtility.writeFile("analysis_log_1.json", "{\"anything\":\"swag\"}")
    InstrumentUtility.writeFile("analysis_log_2.json", "{\"anything\":\"swag\"}")
    InstrumentUtility.writeFile("shouldbeignored_1.json", "{\"anything\":\"swag\"}")
    val result = InstrumentUtility.listExceptionAnalysisReportFiles()
    assertEquals(2, result.size)

    InstrumentUtility.deleteFile("analysis_log_1.json")
    InstrumentUtility.deleteFile("analysis_log_2.json")
    val result1 = InstrumentUtility.listExceptionAnalysisReportFiles()
    assertEquals(0, result1.size)
  }
}


/*
 * 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.instrument.anrreport

import android.app.ActivityManager
import android.os.Process.myUid
import com.facebook.FacebookPowerMockTestCase
import com.facebook.internal.instrument.InstrumentData
import com.facebook.internal.instrument.InstrumentUtility
import java.lang.reflect.Field
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers
import org.mockito.Mockito
import org.mockito.kotlin.any
import org.mockito.kotlin.never
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest

@PrepareForTest(
    ActivityManager::class,
    ActivityManager.ProcessErrorStateInfo::class,
    InstrumentUtility::class,
    InstrumentData.Builder::class)
class ANRDetectorTest : FacebookPowerMockTestCase() {
  private lateinit var am: ActivityManager
  private lateinit var info: ActivityManager.ProcessErrorStateInfo

  @Before
  fun init() {
    am = PowerMockito.mock(ActivityManager::class.java)
    info = PowerMockito.mock(ActivityManager.ProcessErrorStateInfo::class.java)

    PowerMockito.mockStatic(InstrumentData.Builder::class.java)

    val stacktrace = "com.facebook.appevents.codeless.CodelessManager.onActivityResumed(file:10)"
    PowerMockito.mockStatic(InstrumentUtility::class.java)
    whenever(InstrumentUtility.getStackTrace(any<Thread>())).thenReturn(stacktrace)
    PowerMockito.`when`(
            InstrumentUtility.writeFile(
                ArgumentMatchers.isA(String::class.java), ArgumentMatchers.isA(String::class.java)))
        .thenCallRealMethod()
    PowerMockito.`when`(
            InstrumentUtility.readFile(
                ArgumentMatchers.isA(String::class.java),
                ArgumentMatchers.isA(Boolean::class.java)))
        .thenCallRealMethod()

    // reset previousStackTrace
    val field: Field = ANRDetector::class.java.getDeclaredField("previousStackTrace")
    field.isAccessible = true
    field[null] = ""
  }

  @Test
  fun `test Anr is not detected when process list is null`() {
    val mockInstrumentData = PowerMockito.mock(InstrumentData::class.java)
    whenever(InstrumentData.Builder.build(any<String>(), any<String>()))
        .thenReturn(mockInstrumentData)
    whenever(InstrumentUtility.isSDKRelatedThread(any<Thread>())).thenReturn(true)
    whenever(am.processesInErrorState).thenReturn(null)
    ANRDetector.checkProcessError(am)
    Mockito.verify(mockInstrumentData, never()).save()
  }

  @Test
  fun `test Anr is not detected when process list does not have right cause`() {
    val mockInstrumentData = PowerMockito.mock(InstrumentData::class.java)
    whenever(InstrumentData.Builder.build(any<String>(), any<String>()))
        .thenReturn(mockInstrumentData)
    whenever(InstrumentUtility.isSDKRelatedThread(any<Thread>())).thenReturn(true)

    val infoList: MutableList<ActivityManager.ProcessErrorStateInfo> = ArrayList()
    info.condition = ActivityManager.ProcessErrorStateInfo.CRASHED
    info.uid = myUid()
    info.shortMsg = "testCause"
    infoList.add(info)
    whenever(am.processesInErrorState).thenReturn(infoList)

    ANRDetector.checkProcessError(am)
    Mockito.verify(mockInstrumentData, never()).save()
  }

  @Test
  fun `test Anr is not detected when the ANR is not SDK related`() {
    val mockInstrumentData = PowerMockito.mock(InstrumentData::class.java)
    whenever(InstrumentData.Builder.build(any<String>(), any<String>()))
        .thenReturn(mockInstrumentData)
    whenever(InstrumentUtility.isSDKRelatedThread(any<Thread>())).thenReturn(false)

    val infoList: MutableList<ActivityManager.ProcessErrorStateInfo> = ArrayList()
    info.condition = ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING
    info.uid = myUid()
    info.shortMsg = "testCause"
    infoList.add(info)
    whenever(am.processesInErrorState).thenReturn(infoList)

    ANRDetector.checkProcessError(am)
    Mockito.verify(mockInstrumentData, never()).save()
  }

  @Test
  fun `test Anr is detected when there is an SDK related ANR`() {
    val mockInstrumentData = PowerMockito.mock(InstrumentData::class.java)
    whenever(InstrumentData.Builder.build(any<String>(), any<String>()))
        .thenReturn(mockInstrumentData)
    whenever(InstrumentUtility.isSDKRelatedThread(any<Thread>())).thenReturn(true)

    val infoList: MutableList<ActivityManager.ProcessErrorStateInfo> = ArrayList()
    info.condition = ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING
    info.uid = myUid()
    info.shortMsg = "testCause"
    infoList.add(info)
    whenever(am.processesInErrorState).thenReturn(infoList)

    ANRDetector.checkProcessError(am)
    verify(mockInstrumentData).save()
  }

  @Test
  fun `test Anr is saved only once when detecting the same ANR`() {
    val mockInstrumentData = PowerMockito.mock(InstrumentData::class.java)
    whenever(InstrumentData.Builder.build(any<String>(), any<String>()))
        .thenReturn(mockInstrumentData)
    whenever(InstrumentUtility.isSDKRelatedThread(any<Thread>())).thenReturn(true)

    val infoList: MutableList<ActivityManager.ProcessErrorStateInfo> = ArrayList()
    info.condition = ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING
    info.uid = myUid()
    info.shortMsg = "testCause"
    infoList.add(info)
    whenever(am.processesInErrorState).thenReturn(infoList)

    ANRDetector.checkProcessError(am)
    ANRDetector.checkProcessError(am)
    ANRDetector.checkProcessError(am)
    Mockito.verify(mockInstrumentData, times(1)).save()
  }
}


/*
 * 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.instrument.anrreport

import androidx.test.core.app.ApplicationProvider
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.GraphRequest
import com.facebook.internal.instrument.InstrumentUtility
import java.io.File
import java.util.UUID
import org.json.JSONArray
import org.json.JSONObject
import org.junit.After
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest

@PrepareForTest(ANRDetector::class, InstrumentUtility::class, FacebookSdk::class)
class ANRHandlerTest : FacebookPowerMockTestCase() {
  private lateinit var root: File
  private lateinit var directory: File

  @Before
  fun init() {
    val rootName = UUID.randomUUID().toString()
    directory = File(rootName, "instrument")
    directory.mkdirs()
    root = File(rootName)

    PowerMockito.mockStatic(ANRDetector::class.java)

    PowerMockito.mockStatic(FacebookSdk::class.java)
    PowerMockito.`when`(FacebookSdk.isInitialized()).thenReturn(true)
    PowerMockito.`when`(FacebookSdk.getApplicationContext())
        .thenReturn(ApplicationProvider.getApplicationContext())
    PowerMockito.spy(InstrumentUtility::class.java)
    PowerMockito.doReturn(directory).`when`(InstrumentUtility::class.java, "getInstrumentReportDir")
  }

  @After
  fun cleanTestDirectory() {
    root.deleteRecursively()
  }

  @Test
  fun `test to only execute enable() once`() {
    var hitSendReports = 0
    var hitStartANRDetection = 0
    PowerMockito.`when`(FacebookSdk.getAutoLogAppEventsEnabled()).thenReturn(true)
    PowerMockito.doAnswer {
          hitSendReports += 1
          null
        }
        .`when`(
            InstrumentUtility::class.java,
            "sendReports",
            anyOrNull<String>(),
            any<JSONArray>(),
            anyOrNull<GraphRequest.Callback>())
    PowerMockito.`when`(ANRDetector.start()).then {
      hitStartANRDetection += 1
      return@then Unit
    }

    ANRHandler.enable()
    ANRHandler.enable()
    ANRHandler.enable()
    Assert.assertEquals(1, hitSendReports)
    Assert.assertEquals(1, hitStartANRDetection)
  }

  @Test
  fun `test not to send report if app events disabled or data processing restricted`() {
    var hitSendReports = 0
    PowerMockito.`when`(FacebookSdk.getAutoLogAppEventsEnabled()).thenReturn(false)
    PowerMockito.doAnswer {
          hitSendReports += 1
          null
        }
        .`when`(
            InstrumentUtility::class.java,
            "sendReports",
            anyOrNull<String>(),
            any<JSONArray>(),
            anyOrNull<GraphRequest.Callback>())

    ANRHandler.enable()
    ANRHandler.enable()
    ANRHandler.enable()
    Assert.assertEquals(0, hitSendReports)
  }

  @Test
  fun `test send reports`() {
    PowerMockito.`when`(FacebookSdk.getAutoLogAppEventsEnabled()).thenReturn(true)

    InstrumentUtility.writeFile(
        "anr_log_1.json", "{\"reason\":\"yoloswag\",\"callstack\":[],\"timestamp\":1}")
    InstrumentUtility.writeFile(
        "anr_log_2.json", "{\"reason\":\"yoloswag\",\"callstack\":[],\"timestamp\":2}")
    InstrumentUtility.writeFile(
        "anr_log_3.json", "{\"reason\":\"yoloswag\",\"callstack\":[],\"timestamp\":3}")
    var anrLogs: JSONArray? = null
    PowerMockito.doAnswer {
          anrLogs = it.arguments[1] as JSONArray
          null
        }
        .`when`(
            InstrumentUtility::class.java,
            "sendReports",
            anyOrNull<String>(),
            any<JSONArray>(),
            anyOrNull<GraphRequest.Callback>())

    ANRHandler.sendANRReports()
    // check the order of the reports is correct
    val anrLogsTimeStamps =
        (0 until (anrLogs?.length() ?: 0)).map {
          JSONObject(anrLogs?.getString(it) ?: "{}").getInt("timestamp")
        }
    Assert.assertArrayEquals(intArrayOf(3, 2, 1), anrLogsTimeStamps.toIntArray())
  }
}


/*
 * 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.instrument.crashreport

import android.content.Context
import android.content.SharedPreferences
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.GraphRequest
import com.facebook.internal.instrument.InstrumentData
import com.facebook.internal.instrument.InstrumentUtility
import java.io.File
import java.util.UUID
import org.assertj.core.api.Assertions.assertThat
import org.json.JSONArray
import org.json.JSONObject
import org.json.JSONTokener
import org.junit.After
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.isNull
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.reflect.Whitebox

@PrepareForTest(InstrumentData::class, FacebookSdk::class)
class CrashHandlerTest : FacebookPowerMockTestCase() {
  private lateinit var root: File
  private lateinit var mockGraphRequestCompanionObject: GraphRequest.Companion

  @Before
  fun init() {
    val rootName = UUID.randomUUID().toString()
    root = File(rootName)
    root.mkdir()

    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getApplicationId()).thenReturn("123")

    val mockContext: Context = mock()
    val mockSharedPreferences: SharedPreferences = mock()
    whenever(mockSharedPreferences.getString(FacebookSdk.DATA_PROCESSION_OPTIONS, null))
        .thenReturn(getDataProcessingOptions())
    whenever(mockContext.cacheDir).thenReturn(root)
    PowerMockito.`when`(
            mockContext.getSharedPreferences(
                FacebookSdk.DATA_PROCESSING_OPTIONS_PREFERENCES, Context.MODE_PRIVATE))
        .thenReturn(mockSharedPreferences)
    whenever(FacebookSdk.getApplicationContext()).thenReturn(mockContext)

    PowerMockito.mockStatic(InstrumentData::class.java)
    mockGraphRequestCompanionObject = mock()
    Whitebox.setInternalState(
        GraphRequest::class.java, "Companion", mockGraphRequestCompanionObject)
  }

  @After
  fun cleanTestDirectory() {
    root.deleteRecursively()
  }

  @Test
  fun `test not to send report if app events disabled or data processing restricted`() {
    var hitSendReports = false
    whenever(FacebookSdk.getAutoLogAppEventsEnabled()).thenReturn(false)
    whenever(mockGraphRequestCompanionObject.newPostRequest(any(), any(), any(), any()))
        .thenAnswer {
          hitSendReports = true
          null
        }
    CrashHandler.enable()
    assertThat(hitSendReports).isFalse
  }

  @Test
  fun `test enable CrashHandler will set to be default handler`() {
    CrashHandler.enable()
    val handler = Thread.getDefaultUncaughtExceptionHandler()
    Assert.assertNotNull(handler)
    assertThat(handler is CrashHandler).isTrue
  }

  @Test
  fun `test crash handler save the exception report`() {
    val mockInstrumentData = PowerMockito.mock(InstrumentData::class.java)
    PowerMockito.whenNew(InstrumentData::class.java)
        .withAnyArguments()
        .thenReturn(mockInstrumentData)
    val e = Exception()
    val trace =
        arrayOf(
            StackTraceElement(
                "com.facebook.appevents.codeless.CodelessManager", "onActivityResumed", "file", 10))
    e.stackTrace = trace

    CrashHandler.enable()

    val handler = Thread.getDefaultUncaughtExceptionHandler()
    handler?.uncaughtException(Thread.currentThread(), e)
    verify(mockInstrumentData).save()
  }

  @Test
  fun `test send reports`() {
    whenever(FacebookSdk.getAutoLogAppEventsEnabled()).thenReturn(true)

    InstrumentUtility.writeFile("crash_log_1.json", "{\"callstack\":[],\"timestamp\":1}")
    InstrumentUtility.writeFile("crash_log_2.json", "{\"callstack\":[],\"timestamp\":2}")
    InstrumentUtility.writeFile("crash_log_3.json", "{\"callstack\":[],\"timestamp\":3}")
    var crashLogs: JSONObject? = null
    val mockRequest: GraphRequest = mock()
    PowerMockito.`when`(
            mockGraphRequestCompanionObject.newPostRequest(isNull(), any(), any(), any()))
        .thenAnswer {
          crashLogs = it.arguments[2] as JSONObject?
          mockRequest
        }
    CrashHandler.enable()
    val options = crashLogs?.get(FacebookSdk.DATA_PROCESSION_OPTIONS) as JSONArray
    val country = crashLogs?.get(FacebookSdk.DATA_PROCESSION_OPTIONS_COUNTRY) as Int
    val state = crashLogs?.get(FacebookSdk.DATA_PROCESSION_OPTIONS_STATE) as Int
    val tokener = JSONTokener(crashLogs?.get("crash_reports") as String)
    val logArray = JSONArray(tokener)
    val crashLogsTimeStamps =
        (0 until logArray?.length()).map {
          JSONObject(logArray?.getString(it) ?: "{}").getInt("timestamp")
        }
    Assert.assertArrayEquals(intArrayOf(3, 2, 1), crashLogsTimeStamps.toIntArray())
    Assert.assertEquals(options, JSONArray("[\"ABC\"]"))
    Assert.assertEquals(country, 1)
    Assert.assertEquals(state, 100)
  }

  fun getDataProcessingOptions(): String {
    val dataProcessingOptions = JSONObject()
    dataProcessingOptions.put(FacebookSdk.DATA_PROCESSION_OPTIONS, JSONArray("[\"ABC\"]"))
    dataProcessingOptions.put(FacebookSdk.DATA_PROCESSION_OPTIONS_COUNTRY, 1)
    dataProcessingOptions.put(FacebookSdk.DATA_PROCESSION_OPTIONS_STATE, 100)
    return dataProcessingOptions.toString()
  }
}


/*
 * 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.instrument.crashshield

import android.os.Handler
import android.os.Looper
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.internal.instrument.InstrumentData
import com.facebook.util.common.anyObject
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest

@PrepareForTest(
    FacebookSdk::class,
    InstrumentData::class,
    InstrumentData.Builder::class,
    CrashShieldHandler::class,
    Looper::class,
    Handler::class)
class CrashShieldHandlerDebugTest : FacebookPowerMockTestCase() {
  private lateinit var mockInstrumentData: InstrumentData
  private lateinit var mockLooper: Looper
  private lateinit var mockHandler: Handler

  @Before
  fun init() {
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getAutoLogAppEventsEnabled()).thenReturn(true)

    mockInstrumentData = PowerMockito.mock(InstrumentData::class.java)
    whenever(mockInstrumentData.save()).then {
      return@then Unit
    }
    PowerMockito.mockStatic(InstrumentData.Builder::class.java)
    whenever(InstrumentData.Builder.build(any<Throwable>(), any<InstrumentData.Type>()))
        .thenReturn(mockInstrumentData)
    PowerMockito.spy(CrashShieldHandler::class.java)
    whenever(CrashShieldHandler.isDebug()).thenReturn(true)
    mockLooper = PowerMockito.mock(Looper::class.java)
    mockHandler = PowerMockito.mock(Handler::class.java)
    PowerMockito.mockStatic(Looper::class.java)
    whenever(Looper.getMainLooper()).thenReturn(mockLooper)
    PowerMockito.whenNew(Handler::class.java).withAnyArguments().thenReturn(mockHandler)
  }

  @Test(expected = RuntimeException::class)
  fun `test throw exceptions in debug mode`() {
    val probe = Object()
    val exception = RuntimeException()
    var runnableToBePosted: Runnable? = null
    PowerMockito.doAnswer {
          runnableToBePosted = it.arguments[0] as Runnable
          return@doAnswer true
        }
        .`when`(mockHandler)
        .post(anyObject())
    CrashShieldHandler.enable()
    CrashShieldHandler.handleThrowable(exception, probe)

    Assert.assertNotNull(runnableToBePosted)
    try {
      runnableToBePosted?.run()
    } catch (e: RuntimeException) {
      Assert.assertEquals(exception, e.cause)
      throw e
    }
  }
}


/*
 * 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.instrument.crashshield

import android.os.Handler
import android.os.Looper
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.internal.instrument.InstrumentData
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest

@PrepareForTest(
    FacebookSdk::class,
    InstrumentData::class,
    InstrumentData.Builder::class,
    CrashShieldHandler::class,
    Looper::class,
    Handler::class)
class CrashShieldHandlerTest : FacebookPowerMockTestCase() {
  private lateinit var mockInstrumentData: InstrumentData

  @Before
  fun init() {
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getAutoLogAppEventsEnabled()).thenReturn(true)

    mockInstrumentData = PowerMockito.mock(InstrumentData::class.java)
    whenever(mockInstrumentData.save()).then {
      return@then Unit
    }
    PowerMockito.mockStatic(InstrumentData.Builder::class.java)
    whenever(InstrumentData.Builder.build(any<Throwable>(), any<InstrumentData.Type>()))
        .thenReturn(mockInstrumentData)
    PowerMockito.spy(CrashShieldHandler::class.java)
    whenever(CrashShieldHandler.isDebug()).thenReturn(false)
  }

  @Test
  fun `test handler disabled by default`() {
    val probe = Object()
    val exception = RuntimeException()
    CrashShieldHandler.disable()
    CrashShieldHandler.handleThrowable(exception, probe)
    assertThat(CrashShieldHandler.isObjectCrashing(probe)).isFalse
  }

  @Test
  fun `test handler enable`() {
    val probe = Object()
    val exception = RuntimeException()
    CrashShieldHandler.enable()
    CrashShieldHandler.handleThrowable(exception, probe)
    assertThat(CrashShieldHandler.isObjectCrashing(probe)).isTrue
    verify(mockInstrumentData, times(1)).save()
  }

  @Test
  fun `test reset`() {
    val probe = Object()
    val exception = RuntimeException()
    CrashShieldHandler.enable()
    CrashShieldHandler.handleThrowable(exception, probe)
    assertThat(CrashShieldHandler.isObjectCrashing(probe)).isTrue
    CrashShieldHandler.reset()
    assertThat(CrashShieldHandler.isObjectCrashing(probe)).isFalse
  }
}


/*
 * 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.instrument.errorreport

import com.facebook.FacebookPowerMockTestCase
import com.facebook.internal.instrument.InstrumentUtility
import java.io.File
import org.json.JSONObject
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers.isA
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito.mockStatic
import org.powermock.core.classloader.annotations.PrepareForTest

@PrepareForTest(InstrumentUtility::class)
class ErrorReportDataTest : FacebookPowerMockTestCase() {

  private val validJson =
      "{\n" + "  \"timestamp\": 10,\n" + "  \"error_message\": \"yoloswag\"\n" + "}"
  private val jsonWithoutTimestamp = "{\n" + "  \"error_message\": \"error\"\n" + "}"

  @Before
  fun init() {
    mockStatic(InstrumentUtility::class.java)
    val jsonObject = JSONObject(validJson)
    whenever(InstrumentUtility.readFile(isA(String::class.java), isA(Boolean::class.java)))
        .thenReturn(jsonObject)
  }

  @Test
  fun `test params from json`() {
    val data = ErrorReportData(File("swag"))
    val result = data.parameters
    assertEquals("yoloswag", result?.optString("error_message"))
    assertEquals(10L, result?.optLong("timestamp"))
  }

  @Test
  fun `test compare to`() {
    whenever(InstrumentUtility.readFile(isA(String::class.java), isA(Boolean::class.java)))
        .thenReturn(JSONObject(validJson), JSONObject(jsonWithoutTimestamp))
    val data1 = ErrorReportData(File("swag1"))
    val data2 = ErrorReportData(File("swag2"))
    assertEquals(-1, data1.compareTo(data2))
  }
}


/*
 * 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.instrument.errorreport

import android.content.Context
import android.content.SharedPreferences
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.internal.instrument.InstrumentUtility
import com.facebook.util.common.anyObject
import java.io.File
import java.util.UUID
import org.json.JSONArray
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito.mock
import org.powermock.api.mockito.PowerMockito.mockStatic
import org.powermock.core.classloader.annotations.PrepareForTest

@PrepareForTest(
    InstrumentUtility::class,
    FacebookSdk::class,
)
class ErrorReportHandlerTest : FacebookPowerMockTestCase() {

  private lateinit var directory: File
  private lateinit var rootDirectory: File
  private val validJson1 =
      "{\n" + "  \"timestamp\": 10,\n" + "  \"error_message\": \"yoloswag\"\n" + "}"
  private val validJson2 =
      "{\n" + "  \"timestamp\": 20,\n" + "  \"error_message\": \"yoloswag\"\n" + "}"
  private val inValidJson = "{\n" + "  \"timestamp\": 30,\n" + "}"

  @Before
  fun init() {
    val rootName = UUID.randomUUID().toString()
    directory = File(rootName, "instrument")
    directory.mkdirs()
    rootDirectory = File(rootName)
    mockStatic(InstrumentUtility::class.java)
    whenever(InstrumentUtility.getInstrumentReportDir()).thenReturn(directory)
    whenever(
            InstrumentUtility.writeFile(
                ArgumentMatchers.isA(String::class.java), ArgumentMatchers.isA(String::class.java)))
        .thenCallRealMethod()
    whenever(
            InstrumentUtility.readFile(
                ArgumentMatchers.isA(String::class.java),
                ArgumentMatchers.isA(Boolean::class.java)))
        .thenCallRealMethod()

    InstrumentUtility.writeFile("error_log_1.json", validJson1)
    InstrumentUtility.writeFile("error_log_2.json", validJson2)
    InstrumentUtility.writeFile("error_log_3.json", inValidJson)
    InstrumentUtility.writeFile("log_1337.json", "{\"anything\":\"swag\"}")

    mockStatic(FacebookSdk::class.java)
    val mockContext = mock(Context::class.java)
    val mockSharedPreferences = mock(SharedPreferences::class.java)
    whenever(mockSharedPreferences.getString(FacebookSdk.DATA_PROCESSION_OPTIONS, null))
        .thenReturn(null)
    whenever(
            mockContext.getSharedPreferences(
                FacebookSdk.DATA_PROCESSING_OPTIONS_PREFERENCES, Context.MODE_PRIVATE))
        .thenReturn(mockSharedPreferences)
    whenever(FacebookSdk.getApplicationContext()).thenReturn(mockContext)
  }

  @After
  fun tearDown() {
    rootDirectory.deleteRecursively()
  }

  @Test
  fun `test error report files`() {
    val files = ErrorReportHandler.listErrorReportFiles()
    assertEquals(3, files.size)
  }

  @Test
  fun `test send error log`() {
    var errorLogs: JSONArray? = null
    whenever(InstrumentUtility.sendReports(anyObject(), anyObject(), anyObject())).thenAnswer {
      errorLogs = it.getArgument(1) as JSONArray
      null
    }
    ErrorReportHandler.sendErrorReports()
    assertNotNull(errorLogs)
    assertEquals(2, errorLogs?.length())
    assertEquals(20, (errorLogs?.get(0) as ErrorReportData).parameters?.getInt("timestamp"))
    assertEquals(10, (errorLogs?.get(1) as ErrorReportData).parameters?.getInt("timestamp"))
  }
}


/*
 * 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.instrument.threadcheck

import android.util.Log
import com.facebook.FacebookPowerMockTestCase
import com.facebook.internal.instrument.InstrumentData
import org.assertj.core.api.Assertions.assertThat
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers.anyString
import org.mockito.kotlin.any
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest

@PrepareForTest(InstrumentData::class, InstrumentData.Builder::class, Log::class)
class ThreadCheckHandlerTest : FacebookPowerMockTestCase() {
  private lateinit var mockInstrumentData: InstrumentData
  @Before
  fun init() {
    mockInstrumentData = PowerMockito.mock(InstrumentData::class.java)
    PowerMockito.mockStatic(InstrumentData.Builder::class.java)
    whenever(InstrumentData.Builder.build(any<Throwable>(), any<InstrumentData.Type>()))
        .thenReturn(mockInstrumentData)
    PowerMockito.mockStatic(Log::class.java)
  }

  @Test
  fun `test ui thread violation`() {
    var logMessage: String = ""
    whenever(Log.e(anyString(), anyString(), any())).then {
      logMessage = it.arguments[1] as String
      return@then 0
    }
    ThreadCheckHandler.enable()
    ThreadCheckHandler.uiThreadViolationDetected(this.javaClass, "testMethod", "testMethod()")
    verify(mockInstrumentData, times(1)).save()
    Assert.assertNotNull(logMessage)
    assertThat(logMessage).contains("@UiThread")
  }

  @Test
  fun `test worker thread violation`() {
    var logMessage: String = ""
    whenever(Log.e(anyString(), anyString(), any())).then {
      logMessage = it.arguments[1] as String
      return@then 0
    }
    ThreadCheckHandler.enable()
    ThreadCheckHandler.workerThreadViolationDetected(this.javaClass, "testMethod", "testMethod()")
    verify(mockInstrumentData, times(1)).save()
    Assert.assertNotNull(logMessage)
    assertThat(logMessage).contains("@WorkerThread")
  }
}


/*
 * 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.security

import android.content.Context
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.pm.Signature
import com.facebook.FacebookPowerMockTestCase
import org.assertj.core.api.Assertions.assertThat
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito.mock

class CertificateUtilTest : FacebookPowerMockTestCase() {
  private lateinit var mockContext: Context
  private lateinit var mockPackageManager: PackageManager
  private lateinit var mockPackageInfo: PackageInfo
  private lateinit var mockSignature1: Signature
  private lateinit var mockSignature2: Signature

  companion object {
    private const val PACKAGE_NAME = "com.facebook.orca"
  }

  @Before
  fun init() {
    mockSignature1 = mock(Signature::class.java)
    whenever(mockSignature1.toByteArray()).thenReturn(byteArrayOf(1))
    mockSignature2 = mock(Signature::class.java)
    whenever(mockSignature2.toByteArray()).thenReturn(byteArrayOf(2))

    mockContext = mock(Context::class.java)
    mockPackageManager = mock(PackageManager::class.java)
    mockPackageInfo = PackageInfo()
    whenever(mockContext.packageManager).thenReturn(mockPackageManager)
    whenever(mockContext.packageName).thenReturn(PACKAGE_NAME)
    whenever(mockPackageManager.getPackageInfo(anyString(), anyInt())).thenReturn(mockPackageInfo)
  }

  @Test
  fun `test no signature is provided`() {
    mockPackageInfo.signatures = emptyArray()
    val certificateHash = CertificateUtil.getCertificateHash(mockContext)
    assertEquals("", certificateHash)
  }

  @Test
  fun `test exception in access package info`() {
    whenever(mockPackageManager.getPackageInfo(anyString(), anyInt()))
        .thenThrow(PackageManager.NameNotFoundException(PACKAGE_NAME))
    val certificateHash = CertificateUtil.getCertificateHash(mockContext)
    assertEquals("", certificateHash)
  }

  @Test
  fun `test with sample signature`() {
    mockPackageInfo.signatures = arrayOf(mockSignature1)
    val certificateHash = CertificateUtil.getCertificateHash(mockContext)
    assertNotEquals("", certificateHash)
  }

  @Test
  fun `test with two sample signatures`() {
    mockPackageInfo.signatures = arrayOf(mockSignature1)
    val certificateHash1 = CertificateUtil.getCertificateHash(mockContext)
    mockPackageInfo.signatures = arrayOf(mockSignature1, mockSignature2)
    val certificateHash = CertificateUtil.getCertificateHash(mockContext)
    assertThat(certificateHash).contains(certificateHash1)
    assertThat(certificateHash).contains(CertificateUtil.DELIMITER)
    assertThat(certificateHash.endsWith(CertificateUtil.DELIMITER)).isFalse
  }
}


/*
 * 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.login

import com.facebook.FacebookTestCase
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test

class LoginTargetAppTest : FacebookTestCase() {
  @Test
  fun `test conversion to string`() {
    assertThat(LoginTargetApp.FACEBOOK.toString()).isEqualTo("facebook")
    assertThat(LoginTargetApp.INSTAGRAM.toString()).isEqualTo("instagram")
  }

  @Test
  fun `test conversion from string`() {
    assertThat(LoginTargetApp.fromString("facebook")).isEqualTo(LoginTargetApp.FACEBOOK)
    assertThat(LoginTargetApp.fromString("instagram")).isEqualTo(LoginTargetApp.INSTAGRAM)
  }
}


/*
 * 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.util.common

import android.text.format.DateUtils
import com.facebook.AuthenticationToken
import com.facebook.AuthenticationTokenClaims
import com.facebook.AuthenticationTokenHeader

object AuthenticationTokenTestUtil {

  const val JTI = "jti"
  const val APP_ID = "123456789" // aud
  const val ISS = "https://facebook.com/dialog/oauth"
  const val SUB = "1234"
  const val NONCE = "nonce"
  val IAT = System.currentTimeMillis() / 1000
  val EXPIRATION_DATE =
      (System.currentTimeMillis() + DateUtils.HOUR_IN_MILLIS) / 1000 // valid only within 60 minutes

  @JvmField
  val AUTH_TOKEN_CLAIMS_FOR_TEST =
      AuthenticationTokenClaims(
          JTI,
          ISS,
          APP_ID,
          NONCE,
          EXPIRATION_DATE, // exp
          IAT, // iat
          SUB,
          "name",
          "givenName",
          "middleName",
          "familyName",
          "email",
          "picture",
          listOf("friend1", "friend2"),
          "userBirthday",
          hashMapOf("min" to 20),
          hashMapOf("id" to "112724962075996", "name" to "Martinez, California"),
          hashMapOf("id" to "110843418940484", "name" to "Seattle, Washington"),
          "male",
          "facebook.com")

  @JvmField
  val AUTH_TOKEN_CLAIMS_WITH_EMPTY_OPTIONAL_FIELDS =
      AuthenticationTokenClaims(
          JTI,
          ISS,
          APP_ID,
          NONCE,
          EXPIRATION_DATE, // exp
          IAT, // iat
          SUB,
          "",
          "",
          "",
          "",
          "",
          "",
          listOf(),
          "",
          hashMapOf(),
          hashMapOf(),
          hashMapOf(),
          "",
          "")

  @JvmField val AUTH_TOKEN_HEADER_FOR_TEST = AuthenticationTokenHeader("RS256", "token_type", "abc")

  val VALID_HEADER_STRING = AUTH_TOKEN_HEADER_FOR_TEST.toEnCodedString()

  @JvmField
  val authenticationTokenClaimsWithRequiredFieldsOnly =
      AuthenticationTokenClaims(JTI, ISS, APP_ID, NONCE, EXPIRATION_DATE, IAT, SUB)

  @JvmStatic
  fun getAuthenticationTokenForTest(): AuthenticationToken {
    return AuthenticationToken(getEncodedAuthTokenStringForTest(), NONCE)
  }

  @JvmStatic
  fun getAuthenticationTokenEmptyOptionalClaimsForTest(): AuthenticationToken {
    return AuthenticationToken(getEncodedAuthTokenStringWithEmptyOptionalClaimsForTest(), NONCE)
  }

  @JvmStatic
  fun getEncodedAuthTokenStringForTest(): String {
    return buildString {
      append(VALID_HEADER_STRING)
      append(".")
      append(AUTH_TOKEN_CLAIMS_FOR_TEST.toEnCodedString())
      append(".")
      append("Signature")
    }
  }

  @JvmStatic
  fun getEncodedAuthTokenStringWithEmptyOptionalClaimsForTest(): String {
    return buildString {
      append(VALID_HEADER_STRING)
      append(".")
      append(AUTH_TOKEN_CLAIMS_WITH_EMPTY_OPTIONAL_FIELDS.toEnCodedString())
      append(".")
      append("Signature")
    }
  }
}


/*
 * 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.util.common

import android.net.Uri
import com.facebook.Profile
import org.junit.Assert

object ProfileTestHelper {
  const val ID = "ID"
  const val ANOTHER_ID = "ANOTHER_ID"
  const val FIRST_NAME = "FIRST_NAME"
  const val MIDDLE_NAME = "MIDDLE_NAME"
  const val LAST_NAME = "LAST_NAME"
  const val NAME = "NAME"
  const val LINK_URI = "https://www.facebook.com/name"
  const val PICTURE_URI = "https://scontent.cdninstagram.com"
  @JvmStatic
  fun createDefaultProfile(): Profile {
    return Profile(ID, FIRST_NAME, MIDDLE_NAME, LAST_NAME, NAME, Uri.parse(LINK_URI))
  }

  @JvmStatic
  fun createProfileWithPictureUri(): Profile {
    return Profile(
        ID, FIRST_NAME, MIDDLE_NAME, LAST_NAME, NAME, Uri.parse(LINK_URI), Uri.parse(PICTURE_URI))
  }

  @JvmStatic
  fun assertDefaultObjectGetters(profile: Profile?) {
    Assert.assertNotNull(profile)
    Assert.assertEquals(ID, profile?.id)
    Assert.assertEquals(FIRST_NAME, profile?.firstName)
    Assert.assertEquals(MIDDLE_NAME, profile?.middleName)
    Assert.assertEquals(LAST_NAME, profile?.lastName)
    Assert.assertEquals(NAME, profile?.name)
    Assert.assertEquals(Uri.parse(LINK_URI), profile?.linkUri)
  }

  @JvmStatic
  fun createMostlyNullsProfile(): Profile {
    return Profile(ANOTHER_ID, null, null, null, null, null)
  }

  @JvmStatic
  fun assertMostlyNullsObjectGetters(profile: Profile?) {
    Assert.assertNotNull(profile)
    Assert.assertEquals(ANOTHER_ID, profile?.id)
    Assert.assertNull(profile?.firstName)
    Assert.assertNull(profile?.middleName)
    Assert.assertNull(profile?.lastName)
    Assert.assertNull(profile?.name)
    Assert.assertNull(profile?.linkUri)
  }
}


/*
 * 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.gamingservices

import android.app.Activity
import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.Fragment
import com.facebook.AccessToken
import com.facebook.FacebookCallback
import com.facebook.FacebookException
import com.facebook.FacebookSdk
import com.facebook.GraphResponse
import com.facebook.gamingservices.cloudgaming.CloudGameLoginHandler
import com.facebook.gamingservices.cloudgaming.DaemonRequest
import com.facebook.gamingservices.cloudgaming.internal.SDKConstants
import com.facebook.gamingservices.cloudgaming.internal.SDKMessageEnum
import com.facebook.gamingservices.model.ContextChooseContent
import com.facebook.internal.*
import com.facebook.share.internal.ResultProcessor
import com.facebook.share.internal.ShareInternalUtility
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject

class ContextChooseDialog : FacebookDialogBase<ContextChooseContent, ContextChooseDialog.Result> {
  private var callback: FacebookCallback<Result>? = null

  /**
   * Constructs a new ContextChooseDialog.
   *
   * @param activity Activity to use to trigger this Dialog.
   */
  constructor(activity: Activity) : super(activity, DEFAULT_REQUEST_CODE)

  /**
   * Constructs a new ContextChooseDialog.
   *
   * @param fragment fragment to use to trigger this Dialog.
   */
  constructor(fragment: Fragment) : this(FragmentWrapper(fragment))

  /**
   * Constructs a new ContextChooseDialog.
   *
   * @param fragment fragment to use to trigger this Dialog.
   */
  constructor(fragment: android.app.Fragment) : this(FragmentWrapper(fragment))

  private constructor(
      fragmentWrapper: FragmentWrapper
  ) : super(fragmentWrapper, DEFAULT_REQUEST_CODE)

  /**
   * Indicates whether the context choose dialog can be shown.
   *
   * @return true if the dialog can be shown
   */
  override fun canShow(content: ContextChooseContent): Boolean =
      CloudGameLoginHandler.isRunningInCloud() ||
          FacebookAppHandler().canShow(content, true) ||
          ChromeCustomTabHandler().canShow(content, true)

  override fun showImpl(content: ContextChooseContent, mode: Any) =
      if (CloudGameLoginHandler.isRunningInCloud()) {
        showForCloud(content)
      } else {
        super.showImpl(content, mode)
      }

  private fun showForCloud(content: ContextChooseContent) {
    val currentAccessToken = AccessToken.getCurrentAccessToken()
    if (currentAccessToken == null || currentAccessToken.isExpired) {
      throw FacebookException("Attempted to open ContextChooseContent with an invalid access token")
    }
    val requestCallback =
        DaemonRequest.Callback { response ->
          callback?.let { callback ->
            response.error?.let { error -> callback.onError(FacebookException(error.errorMessage)) }
                ?: callback.onSuccess(Result(response))
          }
        }
    val parameters = JSONObject()
    try {
      parameters.put(SDKConstants.PARAM_CONTEXT_FILTERS, content.getFilters())
      parameters.put(SDKConstants.PARAM_CONTEXT_MIN_SIZE, content.minSize)
      content.getFilters()?.let {
        val filtersArray = JSONArray()
        for (filter in it) {
          filtersArray.put(filter)
        }
        parameters.put(SDKConstants.PARAM_CONTEXT_FILTERS, filtersArray)
      }
      DaemonRequest.executeAsync(
          activityContext, parameters, requestCallback, SDKMessageEnum.CONTEXT_CHOOSE)
    } catch (e: JSONException) {
      callback?.onError(FacebookException("Couldn't prepare Context Choose Dialog"))
    }
  }

  override fun registerCallbackImpl(
      callbackManager: CallbackManagerImpl,
      callback: FacebookCallback<Result>
  ) {
    this.callback = callback
    val resultProcessor =
        object : ResultProcessor(callback) {
          override fun onSuccess(appCall: AppCall, results: Bundle?) {
            if (results != null) {
              if (results.getString("error_message") != null) {
                callback.onError(FacebookException(results.getString("error_message")))
                return
              }
              val contextId = results.getString(SDKConstants.PARAM_CONTEXT_ID)
              if (contextId != null) {
                GamingContext.setCurrentGamingContext(GamingContext(contextId))
                callback.onSuccess(Result(results))
              }
              callback.onError(
                  FacebookException(results.getString("Invalid response received from server.")))
            } else {
              onCancel(appCall)
            }
          }
        }

    callbackManager.registerCallback(requestCode) { resultCode, data ->
      ShareInternalUtility.handleActivityResult(requestCode, resultCode, data, resultProcessor)
    }
  }

  override val orderedModeHandlers: List<ModeHandler>
    get() = listOf(FacebookAppHandler(), ChromeCustomTabHandler())

  override fun createBaseAppCall(): AppCall = AppCall(requestCode)

  /*
   * Describes the result of a Context Choose Dialog.
   */
  class Result {
    /**
     * Returns the context ID.
     *
     * @return the context ID.
     */
    var contextID: String? = null

    constructor(results: Bundle) {
      contextID = results.getString(SDKConstants.PARAM_CONTEXT_ID)
    }

    constructor(response: GraphResponse) {
      try {
        response.getJSONObject()?.let { data ->
          data.optJSONObject("data")?.let { contextID = it.getString("id") }
        }
      } catch (e: JSONException) {
        contextID = null
      }
    }
  }

  private inner class FacebookAppHandler : ModeHandler() {
    override fun canShow(content: ContextChooseContent, isBestEffort: Boolean): Boolean {
      val packageManager = activityContext?.packageManager
      val intent = Intent("com.facebook.games.gaming_services.DEEPLINK")
      intent.type = "text/plain"
      val fbAppCanShow = packageManager?.let { intent.resolveActivity(it) } != null
      val currentToken = AccessToken.getCurrentAccessToken()
      val isGamingLoggedIn =
          currentToken?.graphDomain != null && FacebookSdk.GAMING == currentToken.graphDomain
      return fbAppCanShow && isGamingLoggedIn
    }

    override fun createAppCall(content: ContextChooseContent): AppCall {
      val appCall = createBaseAppCall()
      val intent = Intent("com.facebook.games.gaming_services.DEEPLINK")
      intent.type = "text/plain"
      val accessToken = AccessToken.getCurrentAccessToken()
      val args = Bundle()
      args.putString("deeplink", "CONTEXT_CHOOSE")
      if (accessToken != null) {
        args.putString("game_id", accessToken.applicationId)
      } else {
        args.putString("game_id", FacebookSdk.getApplicationId())
      }

      // TODO: Pass along context key
      if (content.minSize != null) {
        args.putString("min_thread_size", content.minSize.toString())
      }
      if (content.maxSize != null) {
        args.putString("max_thread_size", content.maxSize.toString())
      }
      if (content.getFilters() != null) {
        val jsonList = JSONArray(content.getFilters())
        args.putString("filters", jsonList.toString())
      }
      NativeProtocol.setupProtocolRequestIntent(
          intent, appCall.callId.toString(), "", NativeProtocol.getLatestKnownVersion(), args)
      appCall.requestIntent = intent
      return appCall
    }
  }

  private inner class ChromeCustomTabHandler : ModeHandler() {
    override fun canShow(content: ContextChooseContent, isBestEffort: Boolean): Boolean =
        CustomTabUtils.getChromePackage() != null

    override fun createAppCall(content: ContextChooseContent): AppCall {
      val appCall = createBaseAppCall()
      val accessToken = AccessToken.getCurrentAccessToken()
      val contextChooseParams = Bundle()
      val payload = Bundle()
      val filters = Bundle()
      contextChooseParams.putString(
          ServerProtocol.DIALOG_PARAM_APP_ID,
          accessToken?.applicationId ?: FacebookSdk.getApplicationId())
      if (content.minSize != null) {
        filters.putString("min_size", content.minSize.toString())
      }
      if (content.maxSize != null) {
        filters.putString("max_size", content.maxSize.toString())
      }
      if (content.getFilters() != null) {
        val filterArray = JSONArray(content.getFilters())
        filters.putString("filters", filterArray.toString())
      }
      payload.putString("filters", filters.toString())
      contextChooseParams.putString("payload", payload.toString())
      contextChooseParams.putString(
          ServerProtocol.DIALOG_PARAM_REDIRECT_URI, CustomTabUtils.getDefaultRedirectURI())
      DialogPresenter.setupAppCallForCustomTabDialog(
          appCall, CONTEXT_CHOOSE_DIALOG, contextChooseParams)
      return appCall
    }
  }

  companion object {
    private val DEFAULT_REQUEST_CODE =
        CallbackManagerImpl.RequestCodeOffset.GamingContextChoose.toRequestCode()
    private const val CONTEXT_CHOOSE_DIALOG = "context_choose"
  }
}


/*
 * 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.gamingservices

import android.app.Activity
import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.Fragment
import com.facebook.*
import com.facebook.gamingservices.cloudgaming.CloudGameLoginHandler
import com.facebook.gamingservices.cloudgaming.DaemonRequest
import com.facebook.gamingservices.cloudgaming.internal.SDKConstants
import com.facebook.gamingservices.cloudgaming.internal.SDKMessageEnum
import com.facebook.gamingservices.model.ContextCreateContent
import com.facebook.internal.*
import com.facebook.share.internal.ResultProcessor
import com.facebook.share.internal.ShareInternalUtility
import org.json.JSONException
import org.json.JSONObject

class ContextCreateDialog : FacebookDialogBase<ContextCreateContent, ContextCreateDialog.Result> {
  private var callback: FacebookCallback<Result>? = null

  /**
   * Constructs a new ContextCreateDialog.
   *
   * @param activity Activity to use to trigger this Dialog.
   */
  constructor(activity: Activity) : super(activity, DEFAULT_REQUEST_CODE)

  /**
   * Constructs a new ContextCreateDialog.
   *
   * @param fragment fragment to use to trigger this Dialog.
   */
  constructor(fragment: Fragment) : this(FragmentWrapper(fragment))

  /**
   * Constructs a new ContextCreateDialog.
   *
   * @param fragment fragment to use to trigger this Dialog.
   */
  constructor(fragment: android.app.Fragment) : this(FragmentWrapper(fragment))

  private constructor(
      fragmentWrapper: FragmentWrapper
  ) : super(fragmentWrapper, DEFAULT_REQUEST_CODE)

  /**
   * Indicates whether the context create dialog can be shown.
   *
   * @return true if the dialog can be shown
   */
  override fun canShow(content: ContextCreateContent): Boolean =
      CloudGameLoginHandler.isRunningInCloud() ||
          FacebookAppHandler().canShow(content, true) ||
          WebHandler().canShow(content, true)

  override fun showImpl(content: ContextCreateContent, mode: Any) =
      if (CloudGameLoginHandler.isRunningInCloud()) {
        showForCloud(content)
      } else {
        super.showImpl(content, mode)
      }

  private fun showForCloud(content: ContextCreateContent) {
    val currentAccessToken = AccessToken.getCurrentAccessToken()
    if (currentAccessToken == null || currentAccessToken.isExpired) {
      throw FacebookException("Attempted to open ContextCreateContent with an invalid access token")
    }
    val requestCallback =
        DaemonRequest.Callback { response ->
          callback?.let { callback ->
            response.error?.let { error -> callback.onError(FacebookException(error.errorMessage)) }
                ?: callback.onSuccess(Result(response))
          }
        }
    val parameters = JSONObject()
    try {
      if (content.suggestedPlayerID != null) {
        parameters.put(SDKConstants.PARAM_CONTEXT_ID, content.suggestedPlayerID)
      }
      DaemonRequest.executeAsync(
          activityContext, parameters, requestCallback, SDKMessageEnum.CONTEXT_CREATE)
    } catch (e: JSONException) {
      callback?.onError(FacebookException("Couldn't prepare Context Create Dialog"))
    }
  }

  override fun registerCallbackImpl(
      callbackManager: CallbackManagerImpl,
      callback: FacebookCallback<Result>
  ) {
    this.callback = callback
    val resultProcessor: ResultProcessor =
        object : ResultProcessor(callback) {
          override fun onSuccess(appCall: AppCall, results: Bundle?) {
            if (results != null) {
              if (results.getString("error_message") != null) {
                callback.onError(FacebookException(results.getString("error_message")))
                return
              }
              val contextId = results.getString(SDKConstants.PARAM_CONTEXT_ID)
              val contextContextId = results.getString(SDKConstants.PARAM_CONTEXT_CONTEXT_ID)
              if (contextId != null) {
                GamingContext.setCurrentGamingContext(GamingContext(contextId))
                callback.onSuccess(Result(contextId))
              } else if (contextContextId != null) {
                GamingContext.setCurrentGamingContext(GamingContext(contextContextId))
                callback.onSuccess(Result(contextContextId))
              }
              callback.onError(
                  FacebookException(results.getString("Invalid response received from server.")))
            } else {
              onCancel(appCall)
            }
          }
        }
    callbackManager.registerCallback(requestCode) { resultCode, data ->
      ShareInternalUtility.handleActivityResult(requestCode, resultCode, data, resultProcessor)
    }
  }

  override val orderedModeHandlers: List<ModeHandler>
    get() = listOf(FacebookAppHandler(), WebHandler())

  override fun createBaseAppCall(): AppCall = AppCall(requestCode)

  /*
   * Describes the result of a Context Create Dialog.
   */
  class Result {
    /**
     * Returns the context ID.
     *
     * @return the context ID.
     */
    var contextID: String? = null

    internal constructor(contextID: String) {
      this.contextID = contextID
    }

    internal constructor(response: GraphResponse) {
      try {
        response.getJSONObject()?.let { data ->
          data.optJSONObject("data")?.let { contextID = it.getString("id") }
        }
      } catch (e: JSONException) {
        contextID = null
      }
    }
  }

  private inner class WebHandler : ModeHandler() {
    override fun canShow(content: ContextCreateContent, isBestEffort: Boolean): Boolean = true

    override fun createAppCall(content: ContextCreateContent): AppCall {
      val appCall = createBaseAppCall()
      val webParams = Bundle()
      webParams.putString("player_id", content.suggestedPlayerID)
      val currentToken = AccessToken.getCurrentAccessToken()
      if (currentToken != null) {
        webParams.putString("dialog_access_token", currentToken.token)
      }
      DialogPresenter.setupAppCallForWebDialog(appCall, "context", webParams)
      return appCall
    }
  }

  private inner class FacebookAppHandler : ModeHandler() {
    override fun canShow(content: ContextCreateContent, isBestEffort: Boolean): Boolean {
      val packageManager = activityContext?.packageManager
      val intent = Intent("com.facebook.games.gaming_services.DEEPLINK")
      intent.type = "text/plain"
      val fbAppCanShow = packageManager?.let { intent.resolveActivity(it) } != null
      val currentToken = AccessToken.getCurrentAccessToken()
      val isGamingLoggedIn =
          currentToken?.graphDomain != null && FacebookSdk.GAMING == currentToken.graphDomain
      return fbAppCanShow && isGamingLoggedIn
    }

    override fun createAppCall(content: ContextCreateContent): AppCall {
      val appCall = createBaseAppCall()
      val intent = Intent("com.facebook.games.gaming_services.DEEPLINK")
      intent.type = "text/plain"
      val accessToken = AccessToken.getCurrentAccessToken()
      val args = Bundle()
      args.putString("deeplink", "CONTEXT_CREATE")
      if (accessToken != null) {
        args.putString("game_id", accessToken.applicationId)
      } else {
        args.putString("game_id", FacebookSdk.getApplicationId())
      }
      if (content.suggestedPlayerID != null) {
        args.putString("player_id", content.suggestedPlayerID)
      }
      NativeProtocol.setupProtocolRequestIntent(
          intent, appCall.callId.toString(), "", NativeProtocol.getLatestKnownVersion(), args)
      appCall.requestIntent = intent
      return appCall
    }
  }

  companion object {
    private val DEFAULT_REQUEST_CODE =
        CallbackManagerImpl.RequestCodeOffset.GamingContextCreate.toRequestCode()
  }
}


/*
 * 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.gamingservices

import android.app.Activity
import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.Fragment
import com.facebook.*
import com.facebook.gamingservices.cloudgaming.CloudGameLoginHandler
import com.facebook.gamingservices.cloudgaming.DaemonRequest
import com.facebook.gamingservices.cloudgaming.internal.SDKConstants
import com.facebook.gamingservices.cloudgaming.internal.SDKMessageEnum
import com.facebook.gamingservices.model.ContextSwitchContent
import com.facebook.internal.*
import com.facebook.share.internal.ResultProcessor
import com.facebook.share.internal.ShareInternalUtility
import org.json.JSONException
import org.json.JSONObject

/** Provides functionality to switch contexts in games. */
class ContextSwitchDialog : FacebookDialogBase<ContextSwitchContent, ContextSwitchDialog.Result> {
  private var callback: FacebookCallback<Result>? = null

  /**
   * Constructs a new ContextSwitchDialog.
   *
   * @param activity Activity to use to trigger this Dialog.
   */
  constructor(activity: Activity) : super(activity, DEFAULT_REQUEST_CODE)

  /**
   * Constructs a new ContextSwitchDialog.
   *
   * @param fragment fragment to use to trigger this Dialog.
   */
  constructor(fragment: Fragment) : this(FragmentWrapper(fragment))

  /**
   * Constructs a new ContextSwitchDialog.
   *
   * @param fragment fragment to use to trigger this Dialog.
   */
  constructor(fragment: android.app.Fragment) : this(FragmentWrapper(fragment))

  private constructor(
      fragmentWrapper: FragmentWrapper
  ) : super(fragmentWrapper, DEFAULT_REQUEST_CODE)

  /**
   * Indicates whether the context switch dialog can be shown.
   *
   * @return true if the dialog can be shown
   */
  override fun canShow(content: ContextSwitchContent): Boolean =
      CloudGameLoginHandler.isRunningInCloud() ||
          FacebookAppHandler().canShow(content, true) ||
          WebHandler().canShow(content, true)

  override fun showImpl(content: ContextSwitchContent, mode: Any) =
      if (CloudGameLoginHandler.isRunningInCloud()) {
        showForCloud(content)
      } else {
        super.showImpl(content, mode)
      }

  private fun showForCloud(content: ContextSwitchContent) {
    val currentAccessToken = AccessToken.getCurrentAccessToken()
    if (currentAccessToken == null || currentAccessToken.isExpired) {
      throw FacebookException("Attempted to open ContextSwitchContent with an invalid access token")
    }
    val requestCallback =
        DaemonRequest.Callback { response ->
          callback?.let { callback ->
            response.error?.let { error -> callback.onError(FacebookException(error.errorMessage)) }
                ?: callback.onSuccess(Result(response))
          }
        }
    val contextID = content.contextID
    if (contextID == null) {
      callback?.onError(FacebookException("Required string contextID not provided."))
      return
    }
    val parameters = JSONObject()
    try {
      parameters.put(SDKConstants.PARAM_CONTEXT_ID, contextID)
      DaemonRequest.executeAsync(
          activityContext, parameters, requestCallback, SDKMessageEnum.CONTEXT_SWITCH)
    } catch (e: JSONException) {
      callback?.onError(FacebookException("Couldn't prepare Context Switch Dialog"))
    }
  }

  override fun registerCallbackImpl(
      callbackManager: CallbackManagerImpl,
      callback: FacebookCallback<Result>
  ) {
    this.callback = callback
    val resultProcessor: ResultProcessor =
        object : ResultProcessor(callback) {
          override fun onSuccess(appCall: AppCall, results: Bundle?) {
            if (results != null) {
              if (results.getString("error_message") != null) {
                callback.onError(FacebookException(results.getString("error_message")))
                return
              }
              val contextId = results.getString(SDKConstants.PARAM_CONTEXT_ID)
              val contextContextId = results.getString(SDKConstants.PARAM_CONTEXT_CONTEXT_ID)
              if (contextId != null) {
                GamingContext.setCurrentGamingContext(GamingContext(contextId))
                callback.onSuccess(Result(contextId))
              } else if (contextContextId != null) {
                GamingContext.setCurrentGamingContext(GamingContext(contextContextId))
                callback.onSuccess(Result(contextContextId))
              }
              callback.onError(
                  FacebookException(results.getString("Invalid response received from server.")))
            } else {
              onCancel(appCall)
            }
          }
        }
    callbackManager.registerCallback(requestCode) { resultCode, data ->
      ShareInternalUtility.handleActivityResult(requestCode, resultCode, data, resultProcessor)
    }
  }

  override val orderedModeHandlers: List<ModeHandler>
    get() = listOf(FacebookAppHandler(), WebHandler())

  override fun createBaseAppCall(): AppCall = AppCall(requestCode)

  /*
   * Describes the result of a Context Switch Dialog.
   */
  class Result {
    /**
     * Returns the context ID.
     *
     * @return the context ID.
     */
    var contextID: String? = null

    internal constructor(contextID: String) {
      this.contextID = contextID
    }

    internal constructor(response: GraphResponse) {
      try {
        response.getJSONObject()?.let { data ->
          data.optJSONObject("data")?.let { contextID = it.getString("id") }
        }
      } catch (e: JSONException) {
        contextID = null
      }
    }
  }

  private inner class WebHandler : ModeHandler() {
    override fun canShow(content: ContextSwitchContent, isBestEffort: Boolean): Boolean = true

    override fun createAppCall(content: ContextSwitchContent): AppCall {
      val appCall = createBaseAppCall()
      val webParams = Bundle()
      webParams.putString("context_id", content.contextID)
      AccessToken.getCurrentAccessToken()?.let {
        webParams.putString("dialog_access_token", it.token)
      }
      DialogPresenter.setupAppCallForWebDialog(appCall, "context", webParams)
      return appCall
    }
  }

  private inner class FacebookAppHandler : ModeHandler() {
    override fun canShow(content: ContextSwitchContent, isBestEffort: Boolean): Boolean {
      val packageManager = activityContext?.packageManager
      val intent = Intent("com.facebook.games.gaming_services.DEEPLINK")
      intent.type = "text/plain"
      val fbAppCanShow = packageManager?.let { intent.resolveActivity(it) } != null
      val currentToken = AccessToken.getCurrentAccessToken()
      val isGamingLoggedIn =
          currentToken?.graphDomain != null && FacebookSdk.GAMING == currentToken.graphDomain
      return fbAppCanShow && isGamingLoggedIn
    }

    override fun createAppCall(content: ContextSwitchContent): AppCall {
      val appCall = createBaseAppCall()
      val intent = Intent("com.facebook.games.gaming_services.DEEPLINK")
      intent.type = "text/plain"
      val accessToken = AccessToken.getCurrentAccessToken()
      val args = Bundle()
      args.putString("deeplink", "CONTEXT_SWITCH")
      if (accessToken != null) {
        args.putString("game_id", accessToken.applicationId)
      } else {
        args.putString("game_id", FacebookSdk.getApplicationId())
      }
      if (content.contextID != null) {
        args.putString("context_token_id", content.contextID)
      }
      NativeProtocol.setupProtocolRequestIntent(
          intent, appCall.callId.toString(), "", NativeProtocol.getLatestKnownVersion(), args)
      appCall.requestIntent = intent
      return appCall
    }
  }

  companion object {
    private val DEFAULT_REQUEST_CODE =
        CallbackManagerImpl.RequestCodeOffset.GamingContextSwitch.toRequestCode()
  }
}


/*
 * 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.gamingservices

import com.facebook.AccessToken
import com.facebook.GraphRequest
import com.facebook.gamingservices.model.CustomUpdateContent

object CustomUpdate {
  /**
   * Returns a request to perform a CustomUpdate
   *
   * This informs facebook that the player has taken an action and will notify other players in the
   * same GamingContext. Please check CustomUpdateContent.Builder for details on all the fields that
   * allow customizing the update.
   *
   * @returns A GraphRequest that is ready to execute
   */
  @JvmStatic
  fun newCustomUpdateRequest(
      content: CustomUpdateContent,
      callback: GraphRequest.Callback?
  ): GraphRequest {
    return GraphRequest.newPostRequest(
        AccessToken.getCurrentAccessToken(),
        "me/custom_update",
        content.toGraphRequestContent(),
        callback)
  }
}


/*
 * 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.gamingservices

import com.facebook.FacebookSdk
import com.facebook.gamingservices.cloudgaming.CloudGameLoginHandler
import com.facebook.gamingservices.cloudgaming.DaemonRequest
import com.facebook.gamingservices.cloudgaming.internal.SDKMessageEnum

data class GamingContext(val contextID: String) {

  companion object {
    private const val DEFAULT_TIMEOUT: Int = 5
    private var currentContext: GamingContext? = null

    @JvmStatic
    fun getCurrentGamingContext(): GamingContext? {
      if (CloudGameLoginHandler.isRunningInCloud()) {
        val response =
            DaemonRequest.executeAndWait(
                FacebookSdk.getApplicationContext(),
                null,
                SDKMessageEnum.CONTEXT_GET_ID,
                DEFAULT_TIMEOUT)
        val contextId = response?.getJSONObject()?.getString("id") ?: return null
        return GamingContext(contextId)
      }
      return currentContext
    }

    @JvmStatic
    fun setCurrentGamingContext(ctx: GamingContext) {
      if (CloudGameLoginHandler.isRunningInCloud()) {
        return
      }
      currentContext = ctx
    }
  }
}


/*
 * 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.gamingservices

class GamingServices


/*
 * 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.gamingservices

import android.os.Build
import android.os.Parcel
import android.os.Parcelable
import com.facebook.gamingservices.internal.DateFormatter
import com.facebook.share.model.ShareModel
import com.facebook.share.model.ShareModelBuilder
import com.google.gson.annotations.SerializedName
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter

class Tournament : ShareModel {
  @JvmField @SerializedName("id") val identifier: String
  @JvmField @SerializedName("tournament_title") val title: String?
  @JvmField @SerializedName("tournament_payload") val payload: String?
  @JvmField @SerializedName("tournament_end_time") internal var endTime: String? = null

  var expiration: ZonedDateTime?
    get() {
      return this.endTime?.let {
        DateFormatter.format(it)
      }
    }
    private set(newValue) {
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        if (newValue != null) {
          this.endTime = newValue.format(DateTimeFormatter.ISO_DATE_TIME)
          this.expiration = newValue
        }
      }
    }

  constructor(
      identifier: String,
      endTime: String?,
      title: String?,
      payload: String?,
  ) {
    this.identifier = identifier
    this.endTime = endTime
    this.title = title
    this.payload = payload
    this.expiration = this.endTime?.let { DateFormatter.format(it) }
  }

  constructor(
      parcel: Parcel
  ) : this(parcel.toString(), parcel.toString(), parcel.toString(), parcel.toString())

  internal data class Builder(
      var identifier: String,
      var expiration: ZonedDateTime? = null,
      var title: String? = null,
      var payload: String? = null,
  ) : ShareModelBuilder<Tournament, Builder> {
    var endTime: String? = null
    fun identifier(identifier: String): Builder = apply { this.identifier = identifier }
    fun expiration(expiration: ZonedDateTime?): Builder = apply {
      this.expiration = expiration
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        if (expiration != null) {
          this.endTime = expiration.format(DateTimeFormatter.ISO_DATE_TIME)
        }
      }
    }
    fun title(title: String?): Builder = apply { this.title = title }
    fun payload(payload: String?): Builder = apply { this.payload = payload }

    override fun build(): Tournament {
      return Tournament(identifier, endTime, title, payload)
    }

    override fun readFrom(tournament: Tournament?): Builder =
        tournament?.let {
          this.identifier(it.identifier)
              .expiration(it.expiration)
              .title(it.title)
              .payload(it.payload)
        }
            ?: run { this }
  }

  override fun describeContents(): Int = 0

  override fun writeToParcel(out: Parcel, flags: Int) {
    out.writeString(this.identifier)
    out.writeString(this.endTime)
    out.writeString(this.title)
    out.writeString(this.payload)
  }

  companion object CREATOR : Parcelable.Creator<Tournament> {
    override fun createFromParcel(parcel: Parcel): Tournament {
      return Tournament(parcel)
    }

    override fun newArray(size: Int): Array<Tournament?> {
      return arrayOfNulls(size)
    }
  }
}

class InvalidScoreTypeException : IllegalArgumentException()

class InvalidExpirationDateException : IllegalArgumentException()