summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/aapt/ResourceTable.cpp11
-rw-r--r--tools/aapt/StringPool.cpp11
-rw-r--r--tools/aapt/XMLNode.cpp7
-rw-r--r--tools/aapt2/cmd/Link.cpp6
-rw-r--r--tools/aapt2/link/ManifestFixer.cpp6
-rw-r--r--tools/aapt2/link/ManifestFixer.h9
-rw-r--r--tools/aapt2/link/ManifestFixer_test.cpp130
-rw-r--r--tools/aapt2/xml/XmlDom.cpp9
-rw-r--r--tools/aapt2/xml/XmlDom.h2
-rw-r--r--tools/bit/util.cpp2
-rw-r--r--tools/stats_log_api_gen/main.cpp92
11 files changed, 250 insertions, 35 deletions
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 734a5ab8aab4..d02f44edaa4c 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -18,13 +18,10 @@
#include <utils/TypeHelpers.h>
#include <stdarg.h>
-// SSIZE: mingw does not have signed size_t == ssize_t.
// STATUST: mingw does seem to redefine UNKNOWN_ERROR from our enum value, so a cast is necessary.
#if !defined(_WIN32)
-# define SSIZE(x) x
# define STATUST(x) x
#else
-# define SSIZE(x) (signed size_t)x
# define STATUST(x) (status_t)x
#endif
@@ -3040,7 +3037,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>&
sp<AaptFile> strFile = p->getTypeStringsData();
ssize_t amt = data->writeData(strFile->getData(), strFile->getSize());
if (kPrintStringMetrics) {
- fprintf(stderr, "**** type strings: %zd\n", SSIZE(amt));
+ fprintf(stderr, "**** type strings: %zd\n", amt);
}
strAmt += amt;
if (amt < 0) {
@@ -3050,7 +3047,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>&
strFile = p->getKeyStringsData();
amt = data->writeData(strFile->getData(), strFile->getSize());
if (kPrintStringMetrics) {
- fprintf(stderr, "**** key strings: %zd\n", SSIZE(amt));
+ fprintf(stderr, "**** key strings: %zd\n", amt);
}
strAmt += amt;
if (amt < 0) {
@@ -3322,8 +3319,8 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>&
ssize_t amt = (dest->getSize()-strStart);
strAmt += amt;
if (kPrintStringMetrics) {
- fprintf(stderr, "**** value strings: %zd\n", SSIZE(amt));
- fprintf(stderr, "**** total strings: %zd\n", SSIZE(strAmt));
+ fprintf(stderr, "**** value strings: %zd\n", amt);
+ fprintf(stderr, "**** total strings: %zd\n", amt);
}
for (pi=0; pi<flatPackages.size(); pi++) {
diff --git a/tools/aapt/StringPool.cpp b/tools/aapt/StringPool.cpp
index 866291a3b678..37b61bfdffbd 100644
--- a/tools/aapt/StringPool.cpp
+++ b/tools/aapt/StringPool.cpp
@@ -12,13 +12,6 @@
#include "ResourceTable.h"
-// SSIZE: mingw does not have signed size_t == ssize_t.
-#if !defined(_WIN32)
-# define SSIZE(x) x
-#else
-# define SSIZE(x) (signed size_t)x
-#endif
-
// Set to true for noisy debug output.
static const bool kIsDebug = false;
@@ -202,7 +195,7 @@ ssize_t StringPool::add(const String16& value,
if (kIsDebug) {
printf("Adding string %s to pool: pos=%zd eidx=%zd vidx=%zd\n",
- String8(value).string(), SSIZE(pos), SSIZE(eidx), SSIZE(vidx));
+ String8(value).string(), pos, eidx, vidx);
}
return pos;
@@ -598,7 +591,7 @@ ssize_t StringPool::offsetForString(const String16& val) const
const Vector<size_t>* indices = offsetsForString(val);
ssize_t res = indices != NULL && indices->size() > 0 ? indices->itemAt(0) : -1;
if (kIsDebug) {
- printf("Offset for string %s: %zd (%s)\n", String8(val).string(), SSIZE(res),
+ printf("Offset for string %s: %zd (%s)\n", String8(val).string(), res,
res >= 0 ? String8(mEntries[mEntryArray[res]].value).string() : String8());
}
return res;
diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp
index 15ec4afa52dd..861efd5077fe 100644
--- a/tools/aapt/XMLNode.cpp
+++ b/tools/aapt/XMLNode.cpp
@@ -16,13 +16,10 @@
#define O_BINARY 0
#endif
-// SSIZE: mingw does not have signed size_t == ssize_t.
// STATUST: mingw does seem to redefine UNKNOWN_ERROR from our enum value, so a cast is necessary.
#if !defined(_WIN32)
-# define SSIZE(x) x
# define STATUST(x) x
#else
-# define SSIZE(x) (signed size_t)x
# define STATUST(x) (status_t)x
#endif
@@ -1426,7 +1423,7 @@ status_t XMLNode::collect_attr_strings(StringPool* outPool,
idx = outPool->add(attr.name);
if (kIsDebug) {
printf("Adding attr %s (resid 0x%08x) to pool: idx=%zd\n",
- String8(attr.name).string(), id, SSIZE(idx));
+ String8(attr.name).string(), id, idx);
}
if (id != 0) {
while ((ssize_t)outResIds->size() <= idx) {
@@ -1437,7 +1434,7 @@ status_t XMLNode::collect_attr_strings(StringPool* outPool,
}
attr.namePoolIdx = idx;
if (kIsDebug) {
- printf("String %s offset=0x%08zd\n", String8(attr.name).string(), SSIZE(idx));
+ printf("String %s offset=0x%08zd\n", String8(attr.name).string(), idx);
}
}
}
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 0910040fb07a..db42e7cb3e02 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -2120,6 +2120,12 @@ int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {
.OptionalFlag("--version-name",
"Version name to inject into the AndroidManifest.xml if none is present.",
&options.manifest_fixer_options.version_name_default)
+ .OptionalSwitch("--replace-version",
+ "If --version-code and/or --version-name are specified, these\n"
+ "values will replace any value already in the manifest. By\n"
+ "default, nothing is changed if the manifest already defines\n"
+ "these attributes.",
+ &options.manifest_fixer_options.replace_version)
.OptionalFlag("--compile-sdk-version-code",
"Version code (integer) to inject into the AndroidManifest.xml if none is\n"
"present.",
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index bfff1487d4be..ee4e70288994 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -261,6 +261,9 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
manifest_action.Action(FixCoreAppAttribute);
manifest_action.Action([&](xml::Element* el) -> bool {
if (options_.version_name_default) {
+ if (options_.replace_version) {
+ el->RemoveAttribute(xml::kSchemaAndroid, "versionName");
+ }
if (el->FindAttribute(xml::kSchemaAndroid, "versionName") == nullptr) {
el->attributes.push_back(
xml::Attribute{xml::kSchemaAndroid, "versionName",
@@ -269,6 +272,9 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
}
if (options_.version_code_default) {
+ if (options_.replace_version) {
+ el->RemoveAttribute(xml::kSchemaAndroid, "versionCode");
+ }
if (el->FindAttribute(xml::kSchemaAndroid, "versionCode") == nullptr) {
el->attributes.push_back(
xml::Attribute{xml::kSchemaAndroid, "versionCode",
diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h
index 7d6fad2f0918..98d06fd776d7 100644
--- a/tools/aapt2/link/ManifestFixer.h
+++ b/tools/aapt2/link/ManifestFixer.h
@@ -44,10 +44,12 @@ struct ManifestFixerOptions {
// <instrumentation>.
Maybe<std::string> rename_instrumentation_target_package;
- // The version name to set if 'android:versionName' is not defined in <manifest>.
+ // The version name to set if 'android:versionName' is not defined in <manifest> or if
+ // replace_version is set.
Maybe<std::string> version_name_default;
- // The version code to set if 'android:versionCode' is not defined in <manifest>.
+ // The version code to set if 'android:versionCode' is not defined in <manifest> or if
+ // replace_version is set.
Maybe<std::string> version_code_default;
// The version of the framework being compiled against to set for 'android:compileSdkVersion' in
@@ -65,6 +67,9 @@ struct ManifestFixerOptions {
// Whether to inject the android:debuggable="true" flag into the manifest
bool debug_mode = false;
+
+ // Whether to replace the manifest version with the the command line version
+ bool replace_version = false;
};
// Verifies that the manifest is correctly formed and inserts defaults where specified with
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index 5f406e81f65d..5bc004d62ff2 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -349,6 +349,136 @@ TEST_F(ManifestFixerTest, UseDefaultVersionNameAndCode) {
EXPECT_THAT(attr->value, StrEq("0x10000000"));
}
+TEST_F(ManifestFixerTest, DontUseDefaultVersionNameAndCode) {
+ManifestFixerOptions options;
+options.version_name_default = std::string("Beta");
+options.version_code_default = std::string("0x10000000");
+
+std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android"
+ android:versionCode="0x20000000"
+ android:versionName="Alpha" />)EOF",
+ options);
+ASSERT_THAT(doc, NotNull());
+
+xml::Element* manifest_el = doc->root.get();
+ASSERT_THAT(manifest_el, NotNull());
+
+xml::Attribute* attr =
+ manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName");
+ASSERT_THAT(attr, NotNull());
+EXPECT_THAT(attr->value, StrEq("Alpha"));
+
+attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode");
+ASSERT_THAT(attr, NotNull());
+EXPECT_THAT(attr->value, StrEq("0x20000000"));
+}
+
+TEST_F(ManifestFixerTest, ReplaceVersionNameAndCode) {
+ManifestFixerOptions options;
+options.replace_version = true;
+options.version_name_default = std::string("Beta");
+options.version_code_default = std::string("0x10000000");
+
+std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android"
+ android:versionCode="0x20000000"
+ android:versionName="Alpha" />)EOF",
+ options);
+ASSERT_THAT(doc, NotNull());
+
+xml::Element* manifest_el = doc->root.get();
+ASSERT_THAT(manifest_el, NotNull());
+
+xml::Attribute* attr =
+ manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName");
+ASSERT_THAT(attr, NotNull());
+EXPECT_THAT(attr->value, StrEq("Beta"));
+
+attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode");
+ASSERT_THAT(attr, NotNull());
+EXPECT_THAT(attr->value, StrEq("0x10000000"));
+}
+
+TEST_F(ManifestFixerTest, ReplaceVersionName) {
+ManifestFixerOptions options;
+options.replace_version = true;
+options.version_name_default = std::string("Beta");
+
+std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android"
+ android:versionCode="0x20000000"
+ android:versionName="Alpha" />)EOF",
+ options);
+ASSERT_THAT(doc, NotNull());
+
+xml::Element* manifest_el = doc->root.get();
+ASSERT_THAT(manifest_el, NotNull());
+
+xml::Attribute* attr =
+ manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName");
+ASSERT_THAT(attr, NotNull());
+EXPECT_THAT(attr->value, StrEq("Beta"));
+
+attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode");
+ASSERT_THAT(attr, NotNull());
+EXPECT_THAT(attr->value, StrEq("0x20000000"));
+}
+
+TEST_F(ManifestFixerTest, ReplaceVersionCode) {
+ManifestFixerOptions options;
+options.replace_version = true;
+options.version_code_default = std::string("0x10000000");
+
+std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android"
+ android:versionCode="0x20000000"
+ android:versionName="Alpha" />)EOF",
+ options);
+ASSERT_THAT(doc, NotNull());
+
+xml::Element* manifest_el = doc->root.get();
+ASSERT_THAT(manifest_el, NotNull());
+
+xml::Attribute* attr =
+ manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName");
+ASSERT_THAT(attr, NotNull());
+EXPECT_THAT(attr->value, StrEq("Alpha"));
+
+attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode");
+ASSERT_THAT(attr, NotNull());
+EXPECT_THAT(attr->value, StrEq("0x10000000"));
+}
+
+TEST_F(ManifestFixerTest, DontReplaceVersionNameOrCode) {
+ManifestFixerOptions options;
+options.replace_version = true;
+
+std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android"
+ android:versionCode="0x20000000"
+ android:versionName="Alpha" />)EOF",
+ options);
+ASSERT_THAT(doc, NotNull());
+
+xml::Element* manifest_el = doc->root.get();
+ASSERT_THAT(manifest_el, NotNull());
+
+xml::Attribute* attr =
+ manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName");
+ASSERT_THAT(attr, NotNull());
+EXPECT_THAT(attr->value, StrEq("Alpha"));
+
+attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode");
+ASSERT_THAT(attr, NotNull());
+EXPECT_THAT(attr->value, StrEq("0x20000000"));
+}
+
TEST_F(ManifestFixerTest, EnsureManifestAttributesAreTyped) {
EXPECT_THAT(Verify("<manifest package=\"android\" coreApp=\"hello\" />"), IsNull());
EXPECT_THAT(Verify("<manifest package=\"android\" coreApp=\"1dp\" />"), IsNull());
diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp
index b6cd08697545..acd07c2876c8 100644
--- a/tools/aapt2/xml/XmlDom.cpp
+++ b/tools/aapt2/xml/XmlDom.cpp
@@ -423,6 +423,15 @@ const Attribute* Element::FindAttribute(const StringPiece& ns, const StringPiece
return nullptr;
}
+void Element::RemoveAttribute(const StringPiece& ns, const StringPiece& name) {
+ auto new_attr_end = std::remove_if(attributes.begin(), attributes.end(),
+ [&](const Attribute& attr) -> bool {
+ return ns == attr.namespace_uri && name == attr.name;
+ });
+
+ attributes.erase(new_attr_end, attributes.end());
+}
+
Attribute* Element::FindOrCreateAttribute(const StringPiece& ns, const StringPiece& name) {
Attribute* attr = FindAttribute(ns, name);
if (attr == nullptr) {
diff --git a/tools/aapt2/xml/XmlDom.h b/tools/aapt2/xml/XmlDom.h
index 8f3829611f30..a5b2d10fc9e0 100644
--- a/tools/aapt2/xml/XmlDom.h
+++ b/tools/aapt2/xml/XmlDom.h
@@ -102,6 +102,8 @@ class Element : public Node {
const android::StringPiece& name) const;
Attribute* FindOrCreateAttribute(const android::StringPiece& ns,
const android::StringPiece& name);
+ void RemoveAttribute(const android::StringPiece& ns,
+ const android::StringPiece& name);
Element* FindChild(const android::StringPiece& ns, const android::StringPiece& name);
const Element* FindChild(const android::StringPiece& ns, const android::StringPiece& name) const;
diff --git a/tools/bit/util.cpp b/tools/bit/util.cpp
index 922393146b10..a502a9dbe736 100644
--- a/tools/bit/util.cpp
+++ b/tools/bit/util.cpp
@@ -241,6 +241,8 @@ read_file(const string& filename)
char* buf = (char*)malloc(size);
if ((size_t) size != fread(buf, 1, size, file)) {
+ free(buf);
+ fclose(file);
return string();
}
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 638549d395b3..1f13cc274efa 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -614,23 +614,19 @@ write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl &attributio
return 0;
}
-static void write_java_usage(
- FILE* out, const string& method_name, const string& atom_code_name,
- const AtomDecl& atom, const AtomDecl &attributionDecl) {
+static void write_java_usage(FILE* out, const string& method_name, const string& atom_code_name,
+ const AtomDecl& atom) {
fprintf(out, " * Usage: StatsLog.%s(StatsLog.%s",
method_name.c_str(), atom_code_name.c_str());
for (vector<AtomField>::const_iterator field = atom.fields.begin();
field != atom.fields.end(); field++) {
if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
- for (auto chainField : attributionDecl.fields) {
- fprintf(out, ", %s[] %s",
- java_type_name(chainField.javaType), chainField.name.c_str());
- }
+ fprintf(out, ", android.os.WorkSource workSource");
} else {
fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str());
}
}
- fprintf(out, ");\n");
+ fprintf(out, ");<br>\n");
}
static void write_java_method(
@@ -656,6 +652,75 @@ static void write_java_method(
}
}
+static void write_java_work_source_method(FILE* out, const set<vector<java_type_t>>& signatures) {
+ fprintf(out, "\n // WorkSource methods.\n");
+ for (set<vector<java_type_t>>::const_iterator signature = signatures.begin();
+ signature != signatures.end(); signature++) {
+ // Determine if there is Attribution in this signature.
+ int attributionArg = -1;
+ int argIndexMax = 0;
+ for (vector<java_type_t>::const_iterator arg = signature->begin();
+ arg != signature->end(); arg++) {
+ argIndexMax++;
+ if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ if (attributionArg > -1) {
+ fprintf(stderr, "An atom contains multiple AttributionNode fields.\n");
+ fprintf(stderr, "This is not supported. Aborting WorkSource method writing.\n");
+ fprintf(out, "\n// Invalid for WorkSource: more than one attribution chain.\n");
+ return;
+ }
+ attributionArg = argIndexMax;
+ }
+ }
+ if (attributionArg < 0) {
+ continue;
+ }
+
+ // Method header (signature)
+ fprintf(out, " public static void write(int code");
+ int argIndex = 1;
+ for (vector<java_type_t>::const_iterator arg = signature->begin();
+ arg != signature->end(); arg++) {
+ if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ fprintf(out, ", WorkSource ws");
+ } else {
+ fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
+ }
+ argIndex++;
+ }
+ fprintf(out, ") {\n");
+
+ // write_non_chained() component. TODO: Remove when flat uids are no longer needed.
+ fprintf(out, " for (int i = 0; i < ws.size(); ++i) {\n");
+ fprintf(out, " write_non_chained(code");
+ for (int argIndex = 1; argIndex <= argIndexMax; argIndex++) {
+ if (argIndex == attributionArg) {
+ fprintf(out, ", ws.get(i), ws.getName(i)");
+ } else {
+ fprintf(out, ", arg%d", argIndex);
+ }
+ }
+ fprintf(out, ");\n");
+ fprintf(out, " }\n"); // close flor-loop
+
+ // write() component.
+ fprintf(out, " ArrayList<WorkSource.WorkChain> workChains = ws.getWorkChains();\n");
+ fprintf(out, " if (workChains != null) {\n");
+ fprintf(out, " for (WorkSource.WorkChain wc : workChains) {\n");
+ fprintf(out, " write(code");
+ for (int argIndex = 1; argIndex <= argIndexMax; argIndex++) {
+ if (argIndex == attributionArg) {
+ fprintf(out, ", wc.getUids(), wc.getTags()");
+ } else {
+ fprintf(out, ", arg%d", argIndex);
+ }
+ }
+ fprintf(out, ");\n");
+ fprintf(out, " }\n"); // close for-loop
+ fprintf(out, " }\n"); // close if
+ fprintf(out, " }\n"); // close method
+ }
+}
static int
write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl)
@@ -665,6 +730,9 @@ write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionD
fprintf(out, "\n");
fprintf(out, "package android.util;\n");
fprintf(out, "\n");
+ fprintf(out, "import android.os.WorkSource;\n");
+ fprintf(out, "import java.util.ArrayList;\n");
+ fprintf(out, "\n");
fprintf(out, "\n");
fprintf(out, "/**\n");
fprintf(out, " * API For logging statistics events.\n");
@@ -682,12 +750,11 @@ write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionD
string constant = make_constant_name(atom->name);
fprintf(out, "\n");
fprintf(out, " /**\n");
- fprintf(out, " * %s %s\n", atom->message.c_str(), atom->name.c_str());
- write_java_usage(out, "write", constant, *atom, attributionDecl);
+ fprintf(out, " * %s %s<br>\n", atom->message.c_str(), atom->name.c_str());
+ write_java_usage(out, "write", constant, *atom);
auto non_chained_decl = atom_code_to_non_chained_decl_map.find(atom->code);
if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
- write_java_usage(out, "write_non_chained", constant, *non_chained_decl->second,
- attributionDecl);
+ write_java_usage(out, "write_non_chained", constant, *non_chained_decl->second);
}
fprintf(out, " */\n");
fprintf(out, " public static final int %s = %d;\n", constant.c_str(), atom->code);
@@ -720,6 +787,7 @@ write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionD
fprintf(out, " // Write methods\n");
write_java_method(out, "write", atoms.signatures, attributionDecl);
write_java_method(out, "write_non_chained", atoms.non_chained_signatures, attributionDecl);
+ write_java_work_source_method(out, atoms.signatures);
fprintf(out, "}\n");