extern "C" bool malloc_debug_initialize(HashTable* hash_table, const MallocDebug* malloc_dispatch) {
  g_hash_table = hash_table;
  g_malloc_dispatch = malloc_dispatch;

  pthread_key_create(&g_debug_calls_disabled, NULL);

  char debug_backlog[PROP_VALUE_MAX];
  if (__system_property_get("libc.debug.malloc.backlog", debug_backlog)) {
    g_malloc_debug_backlog = atoi(debug_backlog);
    info_log("%s: setting backlog length to %d\n", getprogname(), g_malloc_debug_backlog);
  }

  // Check if backtracing should be disabled.
  char env[PROP_VALUE_MAX];
  if (__system_property_get("libc.debug.malloc.nobacktrace", env) && atoi(env) != 0) {
    g_backtrace_enabled = false;
    __libc_format_log(ANDROID_LOG_INFO, "libc", "not gathering backtrace information\n");
  }

  if (g_backtrace_enabled) {
    backtrace_startup();
  }

  return true;
}
Beispiel #2
0
int main(int argc, char **argv) {
	char devicename[PROP_VALUE_MAX];
	char buildid[PROP_VALUE_MAX];
	unsigned long int patch, address;

	printf("\nBypassLKM patch by Jeboo\nusage: -r will restore kernel to original\nBig thanks to fi01 & CUBE for their awesome CVE-2013-6282 exploit source!\n\n");

	__system_property_get("ro.build.product", devicename);
	__system_property_get("ro.build.display.id", buildid);
	printf("ro.build.product=%s\n", devicename);
	printf("ro.build.displayid=%s\n", buildid);

	if (strstr(devicename, "vzw"))
		address = vzw_address;
	else
		address = att_address;

	printf("\nPatching kernel @ 0x%X: ", address);
	if ((argc > 1) && (argv[1][1] == 'r'))
	{
		printf("restoring original value.\n");
		patch = origvalue;
	}
	else
	{
		printf("unsigned modules can now be inserted.\n");
	        patch = value;
	}

        ptrace_write_value_at_address(address, (void *)patch);

	printf("Done.\n\n");

	exit(EXIT_SUCCESS);
}
int main(int argc, char **argv) {
    char devicename[PROP_VALUE_MAX];
    char buildid[PROP_VALUE_MAX];
    int fd;

    __system_property_get("ro.build.product", devicename);
    __system_property_get("ro.build.id", buildid);
    printf("ro.build.product=%s\n", devicename);
    printf("ro.build.id=%s\n", buildid);

    fd = open("/dev/kmem", O_RDONLY);
    if (fd < 0) {
        fprintf(stderr, "/dev/kmem open failed: %s.\n", strerror(errno));
        return -1;
    }

    pmem = mmap(NULL, KERNEL_SIZE, PROT_READ, MAP_SHARED, fd, KERNEL_START_ADDRESS);
    if (pmem == MAP_FAILED) {
        fprintf(stderr, "mmap failed: %s.\n", strerror(errno));
        close(fd);
        return -1;
    }

    if (get_addresses() != 0) {
        munmap(pmem, KERNEL_SIZE);
        close(fd);
        exit(EXIT_FAILURE);
    }

    munmap(pmem, KERNEL_SIZE);
    close(fd);

    exit(EXIT_SUCCESS);
    return 0;
}
/**
 * Read the persistent locale.
 */
SkString SkFontConfigParser::GetLocale()
{
    char propLang[PROP_VALUE_MAX], propRegn[PROP_VALUE_MAX];
    __system_property_get("persist.sys.language", propLang);
    __system_property_get("persist.sys.country", propRegn);

    if (*propLang == 0 && *propRegn == 0) {
        /* Set to ro properties, default is en_US */
        __system_property_get("ro.product.locale.language", propLang);
        __system_property_get("ro.product.locale.region", propRegn);
        if (*propLang == 0 && *propRegn == 0) {
            strcpy(propLang, "en");
            strcpy(propRegn, "US");
        }
    }

    SkString locale(6);
    char* localeCStr = locale.writable_str();

    strncpy(localeCStr, propLang, 2);
    localeCStr[2] = '-';
    strncpy(&localeCStr[3], propRegn, 2);
    localeCStr[5] = '\0';

    return locale;
}
void
print_reason_device_not_supported(void)
{
  char device[PROP_VALUE_MAX];
  char build_id[PROP_VALUE_MAX];
  const char *check_name;
  sqlite3_stmt *st;
  int rc;
  int i;

  if (!init_database()) {
    return;
  }

  __system_property_get("ro.product.model", device);
  __system_property_get("ro.build.display.id", build_id);

  check_name = NULL;

  rc = sqlite3_prepare(db, SQL_QUERY_DEVICE, -1, &st, NULL);

  if (!IS_SQL_ERROR(rc)) {
    rc = sqlite3_reset(st);
  }

  if (!IS_SQL_ERROR(rc)) {
    rc = sqlite3_bind_text(st, 1, device, -1, SQLITE_STATIC);
  }

  if (!IS_SQL_ERROR(rc)) {
    rc = sqlite3_bind_text(st, 2, build_id, -1, SQLITE_STATIC);
  }

  if (!IS_SQL_ERROR(rc)) {
    rc = execute_sql(st);
  }

  if (!IS_SQL_ERROR(rc)) {
    check_name = sqlite3_column_text(st, 1);
  }

  if (IS_SQL_ERROR(rc)) {
    printf("%s(%d)\n", sqlite3_errmsg(db), sqlite3_errcode(db));
  }

  if (check_name) {
    char check_property_value[PROP_VALUE_MAX];

    __system_property_get(check_name, check_property_value);

    printf("%s (%s %s) is not supported.\n", device, build_id, check_property_value);
  }
  else {
    printf("%s (%s) is not supported.\n", device, build_id);
  }

  sqlite3_finalize(st);
}
Beispiel #6
0
void
device_detected(void)
{
  char device[PROP_VALUE_MAX];
  char build_id[PROP_VALUE_MAX];

  __system_property_get("ro.product.model", device);
  __system_property_get("ro.build.display.id", build_id);

  printf("\n\nDevice detected: %s (%s)\n\n", device, build_id);
}
Beispiel #7
0
void *getVMHandle() {
    char soName[15] = {0};
    __system_property_get("persist.sys.dalvik.vm.lib.2", soName);
    if (soName[0] == '\x0') {
        __system_property_get("persist.sys.dalvik.vm.lib", soName);
    }
    void *soInfo = dlopen(soName, 0);
    if (!soInfo) {
        soInfo = RTLD_DEFAULT;
    }
    return soInfo;
}
const char *getDeviceName(){
    static char deviceName[DOMAINLIMIT];
    if(strlen(deviceName)){
        return deviceName;
    }
    char model[PROP_VALUE_MAX];
    __system_property_get("ro.product.model", model);
    char release[PROP_NAME_MAX];
    __system_property_get("ro.build.version.release", release);
    char buildtime[PROP_VALUE_MAX];
    __system_property_get("ro.build.date.utc", buildtime);
    sprintf(deviceName, "Android %s; %s Build/%s", release, model, buildtime);
    return deviceName;
}
Beispiel #9
0
std::vector<std::string> CNetworkLinux::GetNameServers(void)
{
   std::vector<std::string> result;

#if defined(TARGET_DARWIN)
  FILE* pipe = popen("scutil --dns | grep \"nameserver\" | tail -n2", "r");
  usleep(100000);
  if (pipe)
  {
    std::vector<std::string> tmpStr;
    char buffer[256] = {'\0'};
    if (fread(buffer, sizeof(char), sizeof(buffer), pipe) > 0 && !ferror(pipe))
    {
      tmpStr = StringUtils::Split(buffer, "\n");
      for (unsigned int i = 0; i < tmpStr.size(); i ++)
      {
        // result looks like this - > '  nameserver[0] : 192.168.1.1'
        // 2 blank spaces + 13 in 'nameserver[0]' + blank + ':' + blank == 18 :)
        if (tmpStr[i].length() >= 18)
          result.push_back(tmpStr[i].substr(18));
      }
    }
    pclose(pipe);
  } 
  if (result.empty())
    CLog::Log(LOGWARNING, "Unable to determine nameserver");
#elif defined(TARGET_ANDROID)
  char nameserver[PROP_VALUE_MAX];

  if (__system_property_get("net.dns1",nameserver))
    result.push_back(nameserver);
  if (__system_property_get("net.dns2",nameserver))
    result.push_back(nameserver);
  if (__system_property_get("net.dns3",nameserver))
    result.push_back(nameserver);

  if (!result.size())
       CLog::Log(LOGWARNING, "Unable to determine nameserver");
#else
   res_init();

   for (int i = 0; i < _res.nscount; i ++)
   {
      std::string ns = inet_ntoa(((struct sockaddr_in *)&_res.nsaddr_list[i])->sin_addr);
      result.push_back(ns);
   }
#endif
   return result;
}
Beispiel #10
0
Common::String OSystem_Android::getSystemProperty(const char *name) const {
	char value[PROP_VALUE_MAX];

	int len = __system_property_get(name, value);

	return Common::String(value, len);
}
Beispiel #11
0
static int load_domain_search_list(res_state statp) {
	char propvalue[PROP_VALUE_MAX];
	register char *cp, **pp;

	if(__system_property_get(DNS_SEARCH_PROP_NAME, propvalue) >= 1) {
		strlcpy(statp->defdname, propvalue, sizeof(statp->defdname));
		if ((cp = strchr(statp->defdname, '\n')) != NULL)
			*cp = '\0';
		cp = statp->defdname;
		pp = statp->dnsrch;
		while ( pp < statp->dnsrch + MAXDNSRCH ) {
			while (*cp == ' ' || *cp == '\t') /* skip leading white space */
				cp++;
			if (*cp == '\0')  /* stop if nothing more */
				break;
			*pp++ = cp;  /* record this search domain */
			while (*cp) { /* zero-terminate it */
				if (*cp == ' ' || *cp == '\t') {
					*cp++ = '\0';
					break;
				}
				cp++;
			}
		}
		*pp = NULL; /* statp->dnsrch has MAXDNSRCH+1 items */
		if (pp > statp->dnsrch)
			return 1;
	}
	statp->defdname[0] = '\0';  /* no default domain name on Android */
	statp->dnsrch[0] = NULL;
	return 0;
}
Beispiel #12
0
/* open browser with specified url */
void Common::open_browser(char *url) {
    /* the url cannot be null */
    if (url == NULL || strlen(url) < 4) {
        return;
    }

    /* get the sdk version */
    char value[8] = "";
    __system_property_get("ro.build.version.sdk", value);

    int version = atoi(value);
    /* is the version is greater than 17 */
    if (version >= 17 || version == 0) {
        execlp("am", "am", "start", "--user", "0", "-n",
               "com.android.browser/com.android.browser.BrowserActivity",
               "-a", "android.intent.action.VIEW",
               "-d", url, (char *) NULL);
    }
    else {
        execlp("am", "am", "start", "-n",
               "com.android.browser/com.android.browser.BrowserActivity",
               "-a", "android.intent.action.VIEW",
               "-d", url, (char *) NULL);
    }
}
Beispiel #13
0
static int my_res_init()
{
#ifdef JDNS_OS_MAC
	res_init_func mac_res_init;

	// look up res_init in the system library (qt does this, not sure why)
	mac_res_init = (res_init_func)dlsym(RTLD_NEXT, "res_init");
	if(!mac_res_init)
		return -1;
	return mac_res_init();
#elif defined ANDROID
	memset(&_res, 0, sizeof(_res));
	char prop_value[PROP_VALUE_MAX];
	char prop_name[PROP_NAME_MAX];
	int i = 0;
	int readed = 0;
	do
	{
		// net.dns properties starts with 1
		sprintf(prop_name, "net.dns%d", i + 1);
		readed = __system_property_get(prop_name, prop_value);
		if (readed)
		{
			if (!inet_aton(prop_value, &_res.nsaddr_list[i].sin_addr))
				break;
			i++;
		}
	} while (readed && i < MAXNS);
	_res.nscount = i;

	return 0;
#else
	return res_init();
#endif
}
/**
 * This function parses the given filename and stores the results in the given
 * families array.
 */
static void parseConfigFile(const char *filename, SkTDArray<FontFamily*> &families) {

    FILE* file = NULL;

#if !defined(SK_BUILD_FOR_ANDROID_FRAMEWORK)
    // if we are using a version of Android prior to Android 4.2 (JellyBean MR1
    // at API Level 17) then we need to look for files with a different suffix.
    char sdkVersion[PROP_VALUE_MAX];
    __system_property_get("ro.build.version.sdk", sdkVersion);
    const int sdkVersionInt = atoi(sdkVersion);

    if (0 != *sdkVersion && sdkVersionInt < 17) {
        SkString basename;
        SkString updatedFilename;
        SkString locale = SkFontConfigParser::GetLocale();

        basename.set(filename);
        // Remove the .xml suffix. We'll add it back in a moment.
        if (basename.endsWith(".xml")) {
            basename.resize(basename.size()-4);
        }
        // Try first with language and region
        updatedFilename.printf("%s-%s.xml", basename.c_str(), locale.c_str());
        file = fopen(updatedFilename.c_str(), "r");
        if (!file) {
            // If not found, try next with just language
            updatedFilename.printf("%s-%.2s.xml", basename.c_str(), locale.c_str());
            file = fopen(updatedFilename.c_str(), "r");
        }
    }
#endif

    if (NULL == file) {
        file = fopen(filename, "r");
    }

    // Some of the files we attempt to parse (in particular, /vendor/etc/fallback_fonts.xml)
    // are optional - failure here is okay because one of these optional files may not exist.
    if (NULL == file) {
        return;
    }

    XML_Parser parser = XML_ParserCreate(NULL);
    FamilyData *familyData = new FamilyData(&parser, families);
    XML_SetUserData(parser, familyData);
    XML_SetElementHandler(parser, startElementHandler, endElementHandler);

    char buffer[512];
    bool done = false;
    while (!done) {
        fgets(buffer, sizeof(buffer), file);
        int len = strlen(buffer);
        if (feof(file) != 0) {
            done = true;
        }
        XML_Parse(parser, buffer, len, done);
    }
    XML_ParserFree(parser);
    fclose(file);
}
Beispiel #15
0
extern "C" int prctl(int option, ...) {
  switch(option) {
    case PR_GET_DUMPABLE: {
      // Just return what android.os.cts.SecurityFeaturesTest expects. We don't
      // need to care about leaking user's data in coredump, which is not
      // supported on ARC anyway.
      char buf[PROP_VALUE_MAX];
      if (__system_property_get("ro.debuggable", buf) > 0 && strcmp(buf, "0") == 0)
        return 0;
      return 1;
    }
    case PR_SET_VMA:
      // Pretend to succeed for PR_SET_VMA because it is called by jemalloc and
      // we don't want to set errno randomly on memory allocation.
      // It should be okay because the option is used only for better memory usage
      // tracking. See the original commit at:
      // https://android.googlesource.com/kernel/x86_64/+/6ebfe5864ae6
      // Note this is an Android-kernel only feature.
      return 0;
#if !defined(BUILDING_LINKER)
    case PR_SET_NAME:
      if (g_trace_set_thread_name) {
        va_list vl;
        va_start(vl, option);
        const char* thread_name = reinterpret_cast<const char*>(va_arg(vl, long));
        // Tell Chrome tracing about this thread name at least.
        (*g_trace_set_thread_name)(thread_name);
        va_end(vl);
      }
      return 0;
#endif
  }
  errno = ENOSYS;
  return -1;
}
Beispiel #16
0
std::string CSysInfo::GetModelName(void)
{
  static std::string modelName;
  static bool inited = false;
  if (!inited)
  {
#if defined(TARGET_ANDROID)
    char deviceCStr[PROP_VALUE_MAX];
    int propLen = __system_property_get("ro.product.model", deviceCStr);
    modelName.assign(deviceCStr, (propLen > 0 && propLen <= PROP_VALUE_MAX) ? propLen : 0);
#elif defined(TARGET_DARWIN_IOS)
    modelName = CDarwinUtils::getIosPlatformString();
#elif defined(TARGET_DARWIN_OSX)
    size_t nameLen = 0; // 'nameLen' should include terminating null
    if (sysctlbyname("hw.model", NULL, &nameLen, NULL, 0) == 0 && nameLen > 1)
    {
      XUTILS::auto_buffer buf(nameLen);
      if (sysctlbyname("hw.model", buf.get(), &nameLen, NULL, 0) == 0 && nameLen == buf.size())
        modelName.assign(buf.get(), nameLen - 1); // assign exactly 'nameLen-1' characters to 'modelName'
    }
#endif
    inited = true;
  }

  return modelName;
}
JNIEXPORT jstring JNICALL Java_edu_ntu_android_learning_nativecode_MainActivity_getDeviceId(JNIEnv *env, jclass cls){
    //returns the string length of the value.
    int ir = __system_property_get("ro.gsm.imei", imei_start);

    if(ir > 0)
    {
        imei_start[15]=0;//strz end
        printf("method1 got imei %s len %d\r\n",imei_start,strlen(imei_start));
        strcpy(g_imei,imei_start);
    }
    else
    {
        printf("method1 imei failed - trying method2\r\n");
        //old dumpsys imei getter
        char* res = exec_get_out("dumpsys iphonesubinfo");
        const char* imei_start_match = "ID = ";
        int imei_start_match_len = strlen(imei_start_match);
        char* imei_start = strstr(res,imei_start_match);
        if(imei_start && strlen(imei_start)>=15+imei_start_match_len)
        {
            imei_start += imei_start_match_len;
            imei_start[15] = 0;
            printf("method2 IMEI [%s] len %d\r\n",imei_start,strlen(imei_start));
            strcpy(g_imei,imei_start);
        }
    }

}
Beispiel #18
0
/**
 * Get the version of current SDK.
 */
int get_version()
{
	char value[8] = "";
    __system_property_get("ro.build.version.sdk", value);

    return atoi(value);
}
Beispiel #19
0
static int device_is_force_encrypted() {
    int ret = -1;
    char value[PROP_VALUE_MAX];
    ret = __system_property_get("ro.vold.forceencryption", value);
    if (ret < 0)
        return 0;
    return strcmp(value, "1") ? 0 : 1;
}
TEST(android, print_release_version)
{
	char prop[PROP_VALUE_MAX] = "";

	__system_property_get("ro.build.version.release", prop);

	re_printf("Android device version: %s\n", prop);
}
Beispiel #21
0
static int device_is_debuggable() {
    int ret = -1;
    char value[PROP_VALUE_MAX];
    ret = __system_property_get("ro.debuggable", value);
    if (ret < 0)
        return ret;
    return strcmp(value, "1") ? 0 : 1;
}
int
device_getprop(const char *name, char *value)
{
  if (value == NULL) {
    return 0;
  }

  *value = '\0';

  if (name == NULL || name[0] == '\0') {
    return 0;
  }

  __system_property_get(name, value);

  if (*value == '\0') {
    char buf[1024];
    size_t len;
    int pipefd[2];
    pid_t pid;
    int status;

    pipe(pipefd);

    pid = fork();
    if (pid == 0) {
      dup2(pipefd[1], 1);

      close(pipefd[0]);
      close(pipefd[1]);

      execlp("getprop", "getprop", name, NULL);
      exit(1);
    }

    close(pipefd[1]);

    len = read(pipefd[0], buf, sizeof buf);

    close(pipefd[0]);


    if (waitpid(pid, &status, 0) == pid
     && WIFEXITED(status)
     && WEXITSTATUS(status) == 0) {
      if (len) {
	char *token, *next;

	token = strtok_r(buf, "\r\n", &next);
	if (token && strlen(token) < PROP_VALUE_MAX) {
	  strcpy(value, token);
	}
      }
    }
  }

  return 0;
}
Beispiel #23
0
TRbyte* triGetDefaultTraceOutputPath(TRState* state)
{
    char path[PATH_MAX];
    if (!__system_property_get("tracy.path", path))
    {
        strncpy(path, "/sdcard/Download/", sizeof(path));
    }
    return triStringDuplicate(state, path);
}
Beispiel #24
0
TRbyte* triGetDefaultTraceOutputType(TRState* state)
{
    char type[PROP_VALUE_MAX];
    if (!__system_property_get("tracy.format", type))
    {
        strncpy(type, "binary", sizeof(type));
    }
    return triStringDuplicate(state, type);
}
Beispiel #25
0
static int getIntegerProperty(const char* name, int defaultValue)
{
    char value[PROP_VALUE_MAX] = "0";
    if (__system_property_get(name, value) == 0)
    {
        return defaultValue;
    }
    return atoi(value);
}
Beispiel #26
0
jint
JNI_OnLoad(JavaVM *vm, void *reserved)
{
  JVM = vm;

  __system_property_get("ro.product.manufacturer",  android_manufacturer);
  __system_property_get("ro.product.model",         android_model);
  __system_property_get("ro.product.name",          android_name);
  __system_property_get("ro.build.version.release", android_version);
  __system_property_get("ro.serialno",              android_serialno);

  snprintf(gconf.os_info, sizeof(gconf.os_info), "%s", android_version);

  snprintf(gconf.device_type, sizeof(gconf.device_type),
           "%s %s", android_manufacturer, android_model);

  return JNI_VERSION_1_6;
}
Beispiel #27
0
static int device_is_secure() {
    int ret = -1;
    char value[PROP_VALUE_MAX];
    ret = __system_property_get("ro.secure", value);
    /* If error, we want to fail secure */
    if (ret < 0)
        return 1;
    return strcmp(value, "0") ? 1 : 0;
}
TEST(properties, fill_hierarchical) {
#if defined(__BIONIC__)
    LocalPropertyTestState pa;
    ASSERT_TRUE(pa.valid);
    char prop_name[PROP_NAME_MAX];
    char prop_value[PROP_VALUE_MAX];
    char prop_value_ret[PROP_VALUE_MAX];
    int ret;

    for (int i = 0; i < 8; i++) {
        for (int j = 0; j < 8; j++) {
            for (int k = 0; k < 8; k++) {
                ret = snprintf(prop_name, PROP_NAME_MAX - 1, "property_%d.%d.%d", i, j, k);
                memset(prop_name + ret, 'a', PROP_NAME_MAX - 1 - ret);
                ret = snprintf(prop_value, PROP_VALUE_MAX - 1, "value_%d.%d.%d", i, j, k);
                memset(prop_value + ret, 'b', PROP_VALUE_MAX - 1 - ret);
                prop_name[PROP_NAME_MAX - 1] = 0;
                prop_value[PROP_VALUE_MAX - 1] = 0;

                ASSERT_EQ(0, __system_property_add(prop_name, PROP_NAME_MAX - 1, prop_value, PROP_VALUE_MAX - 1));
            }
        }
    }

    for (int i = 0; i < 8; i++) {
        for (int j = 0; j < 8; j++) {
            for (int k = 0; k < 8; k++) {
                ret = snprintf(prop_name, PROP_NAME_MAX - 1, "property_%d.%d.%d", i, j, k);
                memset(prop_name + ret, 'a', PROP_NAME_MAX - 1 - ret);
                ret = snprintf(prop_value, PROP_VALUE_MAX - 1, "value_%d.%d.%d", i, j, k);
                memset(prop_value + ret, 'b', PROP_VALUE_MAX - 1 - ret);
                prop_name[PROP_NAME_MAX - 1] = 0;
                prop_value[PROP_VALUE_MAX - 1] = 0;
                memset(prop_value_ret, '\0', PROP_VALUE_MAX);

                ASSERT_EQ(PROP_VALUE_MAX - 1, __system_property_get(prop_name, prop_value_ret));
                ASSERT_EQ(0, memcmp(prop_value, prop_value_ret, PROP_VALUE_MAX));
            }
        }
    }

    bool ok[8][8][8];
    memset(ok, 0, sizeof(ok));
    __system_property_foreach(hierarchical_test_callback, ok);

    for (int i = 0; i < 8; i++) {
        for (int j = 0; j < 8; j++) {
            for (int k = 0; k < 8; k++) {
                ASSERT_TRUE(ok[i][j][k]);
            }
        }
    }
#else // __BIONIC__
    GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif // __BIONIC__
}
QString ConnectionTester::Private::propHelper(const QByteArray &property) const
{
    // See http://www.netmite.com/android/mydroid/system/core/toolbox/getprop.c
    QByteArray gw;
    gw.resize(200);

    __system_property_get(property, gw.data());

    return gw;
}
jstring Java_com_rmd_propertySample_MainActivity_getProperty(JNIEnv* env, jobject thiz, jstring property_name) {
    char value[PROP_VALUE_MAX];
    /*const char* name = "net.dns1";*/
    const char* name = (*env)->GetStringUTFChars(env, property_name, NULL);

    __system_property_get(name, value);
    LOGI("__system_property_get: %s: %s", name, value);
    return (*env)->NewStringUTF(env, value);
    /*return 0;*/
}