#include <gtest/gtest.h>

#include <stdint.h>
#include <linux/limits.h>
#include <adb.h>

static const unsigned kWaitInstallingApk = 20;

static bool HasFilePaId(const char *path) {
  const char kXattrTemplate[] = "xattr_manager -l %s | grep user.pa";

  char command_buffer[4096];

  snprintf(command_buffer, sizeof(command_buffer) - 1, kXattrTemplate, path);

  return Adb::Shell(command_buffer) == 0;
}

/**
 * @brief To check file existence on device
 * @param [in] buffer File name
 * @return true if file exists
 */
static bool CheckInstalledApk(const std::string& name) {
  int seconds = kWaitInstallingApk;

  while (seconds--) {
    if (Adb::Shell("ls -la " + name, false) == 0) {
      return true;
    }
    sleep(1);
  }

  return false;
}

static int Install(const char *file) {
  char ss_apk_path[PATH_MAX];
  snprintf(ss_apk_path, sizeof(ss_apk_path) - 1,"%s/data/%s", getenv("PWD"), file);
  return Adb::Install(ss_apk_path);
}

class ApkSignerTest : public ::testing::Test {
 protected:

  static void SetUpTestCase() {
    Adb::Uninstall("com.sec.android.secureclient", true);
    Adb::Uninstall("com.sec.android.securestoragetestmu1", true);
  }

  virtual void SetUp() {
    // Guard pause before any test cases
    sleep(1);
  }
  virtual void TearDown() {
  }

  static void TearDownTestCase() {
    Adb::Uninstall("com.sec.android.secureclient", true);
    Adb::Uninstall("com.sec.android.securestoragetestmu1", true);
  }
};

TEST_F(ApkSignerTest, 01_ApplicationWithSS_PaIdIsAdded) {
  int res = Install("SecureStorageClient.apk");
  ASSERT_EQ(res, 0) << "Android application with SS permission can not be installed";

  bool check = CheckInstalledApk("/data/app/*/com.sec.android.secureclient*/base.apk");
  ASSERT_TRUE(check) << "Apk was not installed!";

  bool has = HasFilePaId("/data/app/*/com.sec.android.secureclient*/base.apk");
  ASSERT_TRUE(has) << "user.pa xattr SHOULD be added to APK file if application has SecureStorage permissions";
}

TEST_F(ApkSignerTest, 02_ApplicationWithoutSS_PaIdIsAbsent) {
  // This APK file does not have SS permissions
  int res = Install("SecureStorageTestMU1-4.apk");
  ASSERT_EQ(res, 0) << "Android application without SS permission can not be installed";

  bool has = HasFilePaId("/data/app/*/com.sec.android.securestoragetestmu1*/base.apk");
  ASSERT_FALSE(has) << "user.pa xattr SHOULD NOT be added to APK file if application has not SecureStorage permissions";
}

TEST_F(ApkSignerTest, 03_ApplicationWithSS_OdexPaIdIsAdded) {
  int res = Install("SecureStorageClient.apk");
  ASSERT_EQ(res, 0) << "Android application with SS permission can not be installed";

  res = Adb::Shell("cmd package compile -m interpret-only -f com.sec.android.secureclient");
  ASSERT_EQ(res, 0) << "dex2oat can not optimize APK file";

  bool has = HasFilePaId("/data/app/*/com.sec.android.secureclient*/oat/arm*/base.odex");
  ASSERT_TRUE(has) << "user.pa xattr SHOULD be added to ODEX file if application has SecureStorage permissions";
}

TEST_F(ApkSignerTest, 04_ApplicationWithoutSS_OdexPaIdIsAbsent) {
  // This APK file does not have SS permissions
  int res = Install("SecureStorageTestMU1-4.apk");
  ASSERT_EQ(res, 0) << "Android application without SS permission can not be installed";

  res = Adb::Shell("cmd package compile -m interpret-only -f com.sec.android.securestoragetestmu1");
  ASSERT_EQ(res, 0) << "dex2oat can not optimize APK file";

  bool has = HasFilePaId("/data/app/*/com.sec.android.securestoragetestmu1*/oat/arm*/base.odex");
  ASSERT_FALSE(has) << "user.pa xattr SHOULD NOT be added to ODEX file if application has not SecureStorage permissions";
}

TEST_F(ApkSignerTest, 05_ApplicationWithSSReoptimization_OdexPaIdIsAdded) {
  int res = Install("SecureStorageClient.apk");
  ASSERT_EQ(res, 0) << "Android application with SS permission can not be installed";

  res = Adb::Shell("cmd package compile -m speed -f com.sec.android.secureclient");
  ASSERT_EQ(res, 0) << "dex2oat can not optimize APK file";

  // Second run of optimization
  res = Adb::Shell("cmd package compile -m interpret-only -f com.sec.android.secureclient");
  ASSERT_EQ(res, 0) << "dex2oat can not optimize APK file";

  bool has = HasFilePaId("/data/app/*/com.sec.android.secureclient*/oat/arm*/base.odex");
  ASSERT_TRUE(has) << "user.pa xattr SHOULD be added to ODEX file if application has SecureStorage permissions";
}

TEST_F(ApkSignerTest, 06_SystemFrameworkServicesJar_PaIdIsAdded) {
  // Cover FR-PROV-10
  bool has = HasFilePaId("/system/framework/services.jar");
  ASSERT_TRUE(has) << "user.pa xattr SHOULD be added to services.jar";
}

TEST_F(ApkSignerTest, 07_SystemFrameworkServicesJarOdex_PaIdIsAdded) {
  // Cover FR-PROV-11
  const std::string kTargetDex = "/data/dalvik-cache/arm*/system@framework@services.jar@classes.dex";
  if (Adb::isFileExists(kTargetDex)) {
    bool has = HasFilePaId(kTargetDex.c_str());
    EXPECT_TRUE(has) << "user.pa xattr SHOULD be added to optimized DEX services.jar";
  }

  const std::string kTargetVdex = "/data/dalvik-cache/arm*/system@framework@services.jar@classes.vdex";
  if (Adb::isFileExists(kTargetVdex)) {
    bool has = HasFilePaId(kTargetVdex.c_str());
    EXPECT_TRUE(has) << "user.pa xattr SHOULD be added to optimized DEX services.jar";
  }
}

TEST_F(ApkSignerTest, 08_NotSignedJarOdex_PaIdIsAbsent) {
  // Cover FR-PROV-11
  bool has = HasFilePaId("/system/framework/samsung-services.jar");
  EXPECT_FALSE(has) << "user.pa xattr SHOULD NOT be added to samsung-services.jar";

  has = HasFilePaId("/data/dalvik-cache/arm*/system@framework@samsung-services.jar@classes.vdex");
  EXPECT_FALSE(has) << "user.pa xattr SHOULD NOT be added to optimized samsung-services.jar";
}
