summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/aapt/Android.bp2
-rw-r--r--tools/aapt2/ResourceUtils.cpp11
-rw-r--r--tools/aapt2/ResourceUtils_test.cpp13
-rw-r--r--tools/aapt2/SdkConstants.cpp11
-rw-r--r--tools/aapt2/SdkConstants.h2
-rw-r--r--tools/aapt2/cmd/Convert_test.cpp7
-rw-r--r--tools/aapt2/io/ZipArchive.cpp10
-rw-r--r--tools/dump-coverage/Android.bp29
-rw-r--r--tools/dump-coverage/README.md50
-rw-r--r--tools/dump-coverage/dump_coverage.cc205
-rwxr-xr-xtools/hiddenapi/generate_hiddenapi_lists.py2
-rw-r--r--tools/lock_agent/Android.bp68
-rw-r--r--tools/lock_agent/agent.cpp555
-rw-r--r--tools/lock_agent/crasher.cpp (renamed from tools/preload2/src/com/android/preload/actions/ClearTableAction.java)33
-rw-r--r--tools/lock_agent/java/com/android/lock_checker/LockHook.java324
-rw-r--r--tools/lock_agent/java/com/android/lock_checker/OnThreadLockChecker.java390
-rwxr-xr-xtools/lock_agent/start_with_lockagent.sh13
-rw-r--r--tools/locked_region_code_injection/src/lockedregioncodeinjection/Main.java6
-rw-r--r--tools/preload-check/Android.bp23
-rw-r--r--tools/preload-check/AndroidTest.xml24
-rw-r--r--tools/preload-check/TEST_MAPPING7
-rw-r--r--tools/preload-check/device/Android.bp26
-rw-r--r--tools/preload-check/device/src/com/android/preload/check/Initialized.java (renamed from tools/preload2/src/com/android/preload/classdataretrieval/ClassDataRetriever.java)18
-rw-r--r--tools/preload-check/device/src/com/android/preload/check/IntegrityCheck.java40
-rw-r--r--tools/preload-check/device/src/com/android/preload/check/NotInitialized.java (renamed from tools/preload2/src/com/android/preload/ui/NullProgressMonitor.java)32
-rw-r--r--tools/preload-check/device/src/com/android/preload/check/NotInitializedRegex.java66
-rw-r--r--tools/preload-check/device/src/com/android/preload/check/Util.java125
-rw-r--r--tools/preload-check/src/com/android/preload/check/PreloadCheck.java127
-rw-r--r--tools/preload2/Android.mk30
-rw-r--r--tools/preload2/preload-tool37
-rw-r--r--tools/preload2/src/com/android/preload/ClientUtils.java224
-rw-r--r--tools/preload2/src/com/android/preload/DeviceUtils.java420
-rw-r--r--tools/preload2/src/com/android/preload/DumpData.java91
-rw-r--r--tools/preload2/src/com/android/preload/DumpDataIO.java141
-rw-r--r--tools/preload2/src/com/android/preload/DumpTableModel.java93
-rw-r--r--tools/preload2/src/com/android/preload/Main.java341
-rw-r--r--tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java39
-rw-r--r--tools/preload2/src/com/android/preload/actions/AbstractThreadedDeviceSpecificAction.java45
-rw-r--r--tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java148
-rw-r--r--tools/preload2/src/com/android/preload/actions/ComputeThresholdXAction.java41
-rw-r--r--tools/preload2/src/com/android/preload/actions/DeviceSpecific.java38
-rw-r--r--tools/preload2/src/com/android/preload/actions/ExportAction.java62
-rw-r--r--tools/preload2/src/com/android/preload/actions/ImportAction.java68
-rw-r--r--tools/preload2/src/com/android/preload/actions/ReloadListAction.java68
-rw-r--r--tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java124
-rw-r--r--tools/preload2/src/com/android/preload/actions/ScanAllPackagesAction.java63
-rw-r--r--tools/preload2/src/com/android/preload/actions/ScanPackageAction.java97
-rw-r--r--tools/preload2/src/com/android/preload/actions/ShowDataAction.java94
-rw-r--r--tools/preload2/src/com/android/preload/actions/WritePreloadedClassesAction.java60
-rw-r--r--tools/preload2/src/com/android/preload/classdataretrieval/hprof/GeneralHprofDumpHandler.java70
-rw-r--r--tools/preload2/src/com/android/preload/classdataretrieval/hprof/Hprof.java228
-rw-r--r--tools/preload2/src/com/android/preload/classdataretrieval/jdwp/JDWPClassDataRetriever.java221
-rw-r--r--tools/preload2/src/com/android/preload/classdataretrieval/jdwp/PreloadDebugeeWrapper.java40
-rw-r--r--tools/preload2/src/com/android/preload/ui/IUI.java45
-rw-r--r--tools/preload2/src/com/android/preload/ui/SequenceUI.java222
-rw-r--r--tools/preload2/src/com/android/preload/ui/SwingUI.java291
-rw-r--r--tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java54
-rw-r--r--tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java72
-rw-r--r--tools/streaming_proto/Android.bp15
-rw-r--r--tools/streaming_proto/Android.mk28
60 files changed, 2232 insertions, 3597 deletions
diff --git a/tools/aapt/Android.bp b/tools/aapt/Android.bp
index f36739730775..a594e5bf0ce1 100644
--- a/tools/aapt/Android.bp
+++ b/tools/aapt/Android.bp
@@ -71,8 +71,6 @@ cc_library_host_static {
cflags: [
"-Wno-format-y2k",
"-DSTATIC_ANDROIDFW_FOR_TOOLS",
- // Allow implicit fallthroughs in AaptAssets.cpp until they are fixed.
- "-Wno-error=implicit-fallthrough",
],
srcs: [
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index a571aee61546..e0040e486a23 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -534,17 +534,18 @@ Maybe<int> ParseSdkVersion(const StringPiece& str) {
}
// Try parsing the code name.
- std::pair<StringPiece, int> entry = GetDevelopmentSdkCodeNameAndVersion();
- if (entry.first == trimmed_str) {
- return entry.second;
+ Maybe<int> entry = GetDevelopmentSdkCodeNameVersion(trimmed_str);
+ if (entry) {
+ return entry.value();
}
// Try parsing codename from "[codename].[preview_sdk_fingerprint]" value.
const StringPiece::const_iterator begin = std::begin(trimmed_str);
const StringPiece::const_iterator end = std::end(trimmed_str);
const StringPiece::const_iterator codename_end = std::find(begin, end, '.');
- if (codename_end != end && entry.first == trimmed_str.substr(begin, codename_end)) {
- return entry.second;
+ entry = GetDevelopmentSdkCodeNameVersion(trimmed_str.substr(begin, codename_end));
+ if (entry) {
+ return entry.value();
}
return {};
}
diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp
index 3b77135a09eb..c016cb44af00 100644
--- a/tools/aapt2/ResourceUtils_test.cpp
+++ b/tools/aapt2/ResourceUtils_test.cpp
@@ -214,14 +214,15 @@ TEST(ResourceUtilsTest, ItemsWithWhitespaceAreParsedCorrectly) {
}
TEST(ResourceUtilsTest, ParseSdkVersionWithCodename) {
- const android::StringPiece codename =
- GetDevelopmentSdkCodeNameAndVersion().first;
- const int version = GetDevelopmentSdkCodeNameAndVersion().second;
+ EXPECT_THAT(ResourceUtils::ParseSdkVersion("Q"), Eq(Maybe<int>(10000)));
+ EXPECT_THAT(
+ ResourceUtils::ParseSdkVersion("Q.fingerprint"),
+ Eq(Maybe<int>(10000)));
- EXPECT_THAT(ResourceUtils::ParseSdkVersion(codename), Eq(Maybe<int>(version)));
+ EXPECT_THAT(ResourceUtils::ParseSdkVersion("R"), Eq(Maybe<int>(10000)));
EXPECT_THAT(
- ResourceUtils::ParseSdkVersion(codename.to_string() + ".fingerprint"),
- Eq(Maybe<int>(version)));
+ ResourceUtils::ParseSdkVersion("R.fingerprint"),
+ Eq(Maybe<int>(10000)));
}
TEST(ResourceUtilsTest, StringBuilderWhitespaceRemoval) {
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index f4b0124abcda..b4b6ff1daaaa 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -18,15 +18,17 @@
#include <algorithm>
#include <string>
-#include <unordered_map>
+#include <unordered_set>
#include <vector>
using android::StringPiece;
namespace aapt {
-static const char* sDevelopmentSdkCodeName = "Q";
static ApiVersion sDevelopmentSdkLevel = 10000;
+static const auto sDevelopmentSdkCodeNames = std::unordered_set<StringPiece>({
+ "Q", "R"
+});
static const std::vector<std::pair<uint16_t, ApiVersion>> sAttrIdMap = {
{0x021c, 1},
@@ -72,8 +74,9 @@ ApiVersion FindAttributeSdkLevel(const ResourceId& id) {
return iter->second;
}
-std::pair<StringPiece, ApiVersion> GetDevelopmentSdkCodeNameAndVersion() {
- return std::make_pair(StringPiece(sDevelopmentSdkCodeName), sDevelopmentSdkLevel);
+Maybe<ApiVersion> GetDevelopmentSdkCodeNameVersion(const StringPiece& code_name) {
+ return (sDevelopmentSdkCodeNames.find(code_name) == sDevelopmentSdkCodeNames.end())
+ ? Maybe<ApiVersion>() : sDevelopmentSdkLevel;
}
} // namespace aapt
diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h
index 580daabbd577..a00d978565ad 100644
--- a/tools/aapt2/SdkConstants.h
+++ b/tools/aapt2/SdkConstants.h
@@ -58,7 +58,7 @@ enum : ApiVersion {
};
ApiVersion FindAttributeSdkLevel(const ResourceId& id);
-std::pair<android::StringPiece, ApiVersion> GetDevelopmentSdkCodeNameAndVersion();
+Maybe<ApiVersion> GetDevelopmentSdkCodeNameVersion(const android::StringPiece& code_name);
} // namespace aapt
diff --git a/tools/aapt2/cmd/Convert_test.cpp b/tools/aapt2/cmd/Convert_test.cpp
index 3c0fe370c516..f35237ec25e3 100644
--- a/tools/aapt2/cmd/Convert_test.cpp
+++ b/tools/aapt2/cmd/Convert_test.cpp
@@ -123,8 +123,7 @@ TEST_F(ConvertTest, DuplicateEntriesWrittenOnce) {
void* cookie = nullptr;
- ZipString prefix("res/theme/10");
- int32_t result = StartIteration(handle, &cookie, &prefix, nullptr);
+ int32_t result = StartIteration(handle, &cookie, "res/theme/10", "");
// If this is -5, that means we've found a duplicate entry and this test has failed
EXPECT_THAT(result, Eq(0));
@@ -133,7 +132,7 @@ TEST_F(ConvertTest, DuplicateEntriesWrittenOnce) {
int count = 0;
// Can't pass nullptrs into Next()
- ZipString zip_name;
+ std::string zip_name;
ZipEntry zip_data;
while ((result = Next(cookie, &zip_data, &zip_name)) == 0) {
@@ -145,4 +144,4 @@ TEST_F(ConvertTest, DuplicateEntriesWrittenOnce) {
EXPECT_THAT(count, Eq(1));
}
-} // namespace aapt \ No newline at end of file
+} // namespace aapt
diff --git a/tools/aapt2/io/ZipArchive.cpp b/tools/aapt2/io/ZipArchive.cpp
index f6aaa1280a61..4380586b1d3c 100644
--- a/tools/aapt2/io/ZipArchive.cpp
+++ b/tools/aapt2/io/ZipArchive.cpp
@@ -114,7 +114,7 @@ std::unique_ptr<ZipFileCollection> ZipFileCollection::Create(
}
void* cookie = nullptr;
- result = StartIteration(collection->handle_, &cookie, nullptr, nullptr);
+ result = StartIteration(collection->handle_, &cookie);
if (result != 0) {
if (out_error) *out_error = ErrorCodeString(result);
return {};
@@ -123,13 +123,9 @@ std::unique_ptr<ZipFileCollection> ZipFileCollection::Create(
using IterationEnder = std::unique_ptr<void, decltype(EndIteration)*>;
IterationEnder iteration_ender(cookie, EndIteration);
- ZipString zip_entry_name;
+ std::string zip_entry_path;
ZipEntry zip_data;
- while ((result = Next(cookie, &zip_data, &zip_entry_name)) == 0) {
- std::string zip_entry_path =
- std::string(reinterpret_cast<const char*>(zip_entry_name.name),
- zip_entry_name.name_length);
-
+ while ((result = Next(cookie, &zip_data, &zip_entry_path)) == 0) {
// Do not add folders to the file collection
if (util::EndsWith(zip_entry_path, "/")) {
continue;
diff --git a/tools/dump-coverage/Android.bp b/tools/dump-coverage/Android.bp
new file mode 100644
index 000000000000..4519ce3636bf
--- /dev/null
+++ b/tools/dump-coverage/Android.bp
@@ -0,0 +1,29 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Build variants {target,host} x {32,64}
+cc_library {
+ name: "libdumpcoverage",
+ srcs: ["dump_coverage.cc"],
+ header_libs: [
+ "libopenjdkjvmti_headers",
+ ],
+
+ host_supported: true,
+ shared_libs: [
+ "libbase",
+ ],
+}
diff --git a/tools/dump-coverage/README.md b/tools/dump-coverage/README.md
new file mode 100644
index 000000000000..d1c10bc2e520
--- /dev/null
+++ b/tools/dump-coverage/README.md
@@ -0,0 +1,50 @@
+# dumpcoverage
+
+libdumpcoverage.so is a JVMTI agent designed to dump coverage information for a process, where the binaries have been instrumented by JaCoCo. JaCoCo automatically starts recording data on process start, and we need a way to trigger the resetting or dumping of this data.
+
+The JVMTI agent is used to make the calls to JaCoCo in its process.
+
+# Usage
+
+Note that these examples assume you have an instrumented build (userdebug_coverage). Here is, for example, how to dump coverage information regarding the default clock app. First some setup is necessary:
+
+```
+adb root # necessary to copy files in/out of the /data/data/{package} folder
+adb shell 'mkdir /data/data/com.android.deskclock/folder-to-use'
+```
+
+Then we can run the command to dump the data:
+
+```
+adb shell 'am attach-agent com.android.deskclock /system/lib/libdumpcoverage.so=dump:/data/data/com.android.deskclock/folder-to-use/coverage-file.ec'
+```
+
+We can also reset the coverage information with
+
+```
+adb shell 'am attach-agent com.android.deskclock /system/lib/libdumpcoverage.so=reset'
+```
+
+then perform more actions, then dump the data again. To get the files, we can get
+
+```
+adb pull /data/data/com.android.deskclock/folder-to-use/coverage-file.ec ~/path-on-your-computer
+```
+
+And you should have `coverage-file.ec` on your machine under the folder `~/path-on-your-computer`
+
+# Details
+
+In dump mode, the agent makes JNI calls equivalent to
+
+```
+Agent.getInstance().getExecutionData(/*reset = */ false);
+```
+
+and then saves the result to a file specified by the passed in directory
+
+In reset mode, it makes a JNI call equivalent to
+
+```
+Agent.getInstance().reset();
+```
diff --git a/tools/dump-coverage/dump_coverage.cc b/tools/dump-coverage/dump_coverage.cc
new file mode 100644
index 000000000000..0808e776f190
--- /dev/null
+++ b/tools/dump-coverage/dump_coverage.cc
@@ -0,0 +1,205 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <android-base/logging.h>
+#include <jni.h>
+#include <jvmti.h>
+#include <string.h>
+
+#include <fstream>
+
+using std::get;
+using std::tuple;
+
+namespace dump_coverage {
+
+#define CHECK_JVMTI(x) CHECK_EQ((x), JVMTI_ERROR_NONE)
+#define CHECK_NOTNULL(x) CHECK((x) != nullptr)
+#define CHECK_NO_EXCEPTION(env) CHECK(!(env)->ExceptionCheck());
+
+static JavaVM* java_vm = nullptr;
+
+// Get the current JNI environment.
+static JNIEnv* GetJNIEnv() {
+ JNIEnv* env = nullptr;
+ CHECK_EQ(java_vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6),
+ JNI_OK);
+ return env;
+}
+
+// Get the JaCoCo Agent class and an instance of the class, given a JNI
+// environment.
+// Will crash if the Agent isn't found or if any Java Exception occurs.
+static tuple<jclass, jobject> GetJavaAgent(JNIEnv* env) {
+ jclass java_agent_class =
+ env->FindClass("org/jacoco/agent/rt/internal/Agent");
+ CHECK_NOTNULL(java_agent_class);
+
+ jmethodID java_agent_get_instance =
+ env->GetStaticMethodID(java_agent_class, "getInstance",
+ "()Lorg/jacoco/agent/rt/internal/Agent;");
+ CHECK_NOTNULL(java_agent_get_instance);
+
+ jobject java_agent_instance =
+ env->CallStaticObjectMethod(java_agent_class, java_agent_get_instance);
+ CHECK_NO_EXCEPTION(env);
+ CHECK_NOTNULL(java_agent_instance);
+
+ return tuple(java_agent_class, java_agent_instance);
+}
+
+// Runs equivalent of Agent.getInstance().getExecutionData(false) and returns
+// the result.
+// Will crash if the Agent isn't found or if any Java Exception occurs.
+static jbyteArray GetExecutionData(JNIEnv* env) {
+ auto java_agent = GetJavaAgent(env);
+ jmethodID java_agent_get_execution_data =
+ env->GetMethodID(get<0>(java_agent), "getExecutionData", "(Z)[B");
+ CHECK_NO_EXCEPTION(env);
+ CHECK_NOTNULL(java_agent_get_execution_data);
+
+ jbyteArray java_result_array = (jbyteArray)env->CallObjectMethod(
+ get<1>(java_agent), java_agent_get_execution_data, false);
+ CHECK_NO_EXCEPTION(env);
+
+ return java_result_array;
+}
+
+// Writes the execution data to a file.
+// data, length: represent the data, as a sequence of bytes.
+// filename: file to write coverage data to.
+// returns JNI_ERR if there is an error in writing the file, otherwise JNI_OK.
+static jint WriteFile(const char* data, int length, const std::string& filename) {
+ LOG(INFO) << "Writing file of length " << length << " to '" << filename
+ << "'";
+ std::ofstream file(filename, std::ios::binary);
+
+ if (!file.is_open()) {
+ LOG(ERROR) << "Could not open file: '" << filename << "'";
+ return JNI_ERR;
+ }
+ file.write(data, length);
+ file.close();
+
+ if (!file) {
+ LOG(ERROR) << "I/O error in reading file";
+ return JNI_ERR;
+ }
+
+ LOG(INFO) << "Done writing file";
+ return JNI_OK;
+}
+
+// Grabs execution data and writes it to a file.
+// filename: file to write coverage data to.
+// returns JNI_ERR if there is an error writing the file.
+// Will crash if the Agent isn't found or if any Java Exception occurs.
+static jint Dump(const std::string& filename) {
+ LOG(INFO) << "Dumping file";
+
+ JNIEnv* env = GetJNIEnv();
+ jbyteArray java_result_array = GetExecutionData(env);
+ CHECK_NOTNULL(java_result_array);
+
+ jbyte* result_ptr = env->GetByteArrayElements(java_result_array, 0);
+ CHECK_NOTNULL(result_ptr);
+
+ int result_len = env->GetArrayLength(java_result_array);
+
+ return WriteFile((const char*) result_ptr, result_len, filename);
+}
+
+// Resets execution data, performing the equivalent of
+// Agent.getInstance().reset();
+// args: should be empty.
+// returns JNI_ERR if the arguments are invalid.
+// Will crash if the Agent isn't found or if any Java Exception occurs.
+static jint Reset(const std::string& args) {
+ if (args != "") {
+ LOG(ERROR) << "reset takes no arguments, but received '" << args << "'";
+ return JNI_ERR;
+ }
+
+ JNIEnv* env = GetJNIEnv();
+ auto java_agent = GetJavaAgent(env);
+
+ jmethodID java_agent_reset =
+ env->GetMethodID(get<0>(java_agent), "reset", "()V");
+ CHECK_NOTNULL(java_agent_reset);
+
+ env->CallVoidMethod(get<1>(java_agent), java_agent_reset);
+ CHECK_NO_EXCEPTION(env);
+ return JNI_OK;
+}
+
+// Given a string of the form "<a>:<b>" returns (<a>, <b>).
+// Given a string <a> that doesn't contain a colon, returns (<a>, "").
+static tuple<std::string, std::string> SplitOnColon(const std::string& options) {
+ size_t loc_delim = options.find(':');
+ std::string command, args;
+
+ if (loc_delim == std::string::npos) {
+ command = options;
+ } else {
+ command = options.substr(0, loc_delim);
+ args = options.substr(loc_delim + 1, options.length());
+ }
+ return tuple(command, args);
+}
+
+// Parses and executes a command specified by options of the form
+// "<command>:<args>" where <command> is either "dump" or "reset".
+static jint ParseOptionsAndExecuteCommand(const std::string& options) {
+ auto split = SplitOnColon(options);
+ auto command = get<0>(split), args = get<1>(split);
+
+ LOG(INFO) << "command: '" << command << "' args: '" << args << "'";
+
+ if (command == "dump") {
+ return Dump(args);
+ }
+
+ if (command == "reset") {
+ return Reset(args);
+ }
+
+ LOG(ERROR) << "Invalid command: expected 'dump' or 'reset' but was '"
+ << command << "'";
+ return JNI_ERR;
+}
+
+static jint AgentStart(JavaVM* vm, char* options) {
+ android::base::InitLogging(/* argv= */ nullptr);
+ java_vm = vm;
+
+ return ParseOptionsAndExecuteCommand(options);
+}
+
+// Late attachment (e.g. 'am attach-agent').
+extern "C" JNIEXPORT jint JNICALL
+Agent_OnAttach(JavaVM* vm, char* options, void* reserved ATTRIBUTE_UNUSED) {
+ return AgentStart(vm, options);
+}
+
+// Early attachment.
+extern "C" JNIEXPORT jint JNICALL
+Agent_OnLoad(JavaVM* jvm ATTRIBUTE_UNUSED, char* options ATTRIBUTE_UNUSED, void* reserved ATTRIBUTE_UNUSED) {
+ LOG(ERROR)
+ << "The dumpcoverage agent will not work on load,"
+ << " as it does not have access to the runtime.";
+ return JNI_ERR;
+}
+
+} // namespace dump_coverage
diff --git a/tools/hiddenapi/generate_hiddenapi_lists.py b/tools/hiddenapi/generate_hiddenapi_lists.py
index c856cc36d6f6..e883c6bed755 100755
--- a/tools/hiddenapi/generate_hiddenapi_lists.py
+++ b/tools/hiddenapi/generate_hiddenapi_lists.py
@@ -29,6 +29,7 @@ FLAG_GREYLIST = "greylist"
FLAG_BLACKLIST = "blacklist"
FLAG_GREYLIST_MAX_O = "greylist-max-o"
FLAG_GREYLIST_MAX_P = "greylist-max-p"
+FLAG_GREYLIST_MAX_Q = "greylist-max-q"
FLAG_CORE_PLATFORM_API = "core-platform-api"
FLAG_PUBLIC_API = "public-api"
FLAG_SYSTEM_API = "system-api"
@@ -41,6 +42,7 @@ FLAGS_API_LIST = [
FLAG_BLACKLIST,
FLAG_GREYLIST_MAX_O,
FLAG_GREYLIST_MAX_P,
+ FLAG_GREYLIST_MAX_Q,
]
ALL_FLAGS = FLAGS_API_LIST + [
FLAG_CORE_PLATFORM_API,
diff --git a/tools/lock_agent/Android.bp b/tools/lock_agent/Android.bp
new file mode 100644
index 000000000000..79dce4a8ce09
--- /dev/null
+++ b/tools/lock_agent/Android.bp
@@ -0,0 +1,68 @@
+cc_library {
+ name: "liblockagent",
+ host_supported: false,
+ srcs: ["agent.cpp"],
+ static_libs: [
+ "libbase_ndk",
+ "slicer_ndk_no_rtti",
+ ],
+ shared_libs: [
+ "libz", // for slicer (using adler32).
+ "liblog",
+ ],
+ sdk_version: "current",
+ stl: "c++_static",
+ header_libs: [
+ // Use ScopedUtfChars.
+ "libnativehelper_header_only",
+ "libopenjdkjvmti_headers",
+ ],
+ compile_multilib: "both",
+}
+
+cc_binary_host {
+ name: "lockagenttest",
+ srcs: ["agent.cpp"],
+ static_libs: [
+ "libbase",
+ "libz",
+ "slicer",
+ ],
+ header_libs: [
+ // Use ScopedUtfChars.
+ "libnativehelper_header_only",
+ "libopenjdkjvmti_headers",
+ ],
+}
+
+java_library {
+ name: "lockagent",
+ srcs: ["java/**/*.java"],
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+ installable: true,
+}
+
+cc_binary {
+ name: "lockagent_crasher",
+ srcs: ["crasher.cpp"],
+ static_libs: ["libbase_ndk"],
+ shared_libs: ["liblog"],
+ sdk_version: "current",
+ stl: "c++_static",
+ compile_multilib: "first",
+}
+
+sh_binary {
+ name: "start_with_lockagent",
+ src: "start_with_lockagent.sh",
+ required: [
+ "liblockagent",
+ "lockagent",
+ "lockagent_crasher",
+ ],
+}
diff --git a/tools/lock_agent/agent.cpp b/tools/lock_agent/agent.cpp
new file mode 100644
index 000000000000..40293b690180
--- /dev/null
+++ b/tools/lock_agent/agent.cpp
@@ -0,0 +1,555 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstring>
+#include <iostream>
+#include <memory>
+#include <sstream>
+
+#include <unistd.h>
+
+#include <jni.h>
+
+#include <jvmti.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <nativehelper/scoped_utf_chars.h>
+
+// We need dladdr.
+#if !defined(__APPLE__) && !defined(_WIN32)
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#define DEFINED_GNU_SOURCE
+#endif
+#include <dlfcn.h>
+#ifdef DEFINED_GNU_SOURCE
+#undef _GNU_SOURCE
+#undef DEFINED_GNU_SOURCE
+#endif
+#endif
+
+// Slicer's headers have code that triggers these warnings. b/65298177
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-parameter"
+#pragma clang diagnostic ignored "-Wsign-compare"
+
+#include <slicer/dex_ir.h>
+#include <slicer/code_ir.h>
+#include <slicer/dex_bytecode.h>
+#include <slicer/dex_ir_builder.h>
+#include <slicer/writer.h>
+#include <slicer/reader.h>
+
+#pragma clang diagnostic pop
+
+namespace {
+
+JavaVM* gJavaVM = nullptr;
+bool gForkCrash = false;
+bool gJavaCrash = false;
+
+// Converts a class name to a type descriptor
+// (ex. "java.lang.String" to "Ljava/lang/String;")
+std::string classNameToDescriptor(const char* className) {
+ std::stringstream ss;
+ ss << "L";
+ for (auto p = className; *p != '\0'; ++p) {
+ ss << (*p == '.' ? '/' : *p);
+ }
+ ss << ";";
+ return ss.str();
+}
+
+using namespace dex;
+using namespace lir;
+
+class Transformer {
+public:
+ explicit Transformer(std::shared_ptr<ir::DexFile> dexIr) : dexIr_(dexIr) {}
+
+ bool transform() {
+ bool classModified = false;
+
+ std::unique_ptr<ir::Builder> builder;
+
+ for (auto& method : dexIr_->encoded_methods) {
+ // Do not look into abstract/bridge/native/synthetic methods.
+ if ((method->access_flags & (kAccAbstract | kAccBridge | kAccNative | kAccSynthetic))
+ != 0) {
+ continue;
+ }
+
+ struct HookVisitor: public Visitor {
+ HookVisitor(Transformer* transformer, CodeIr* c_ir)
+ : transformer(transformer), cIr(c_ir) {
+ }
+
+ bool Visit(Bytecode* bytecode) override {
+ if (bytecode->opcode == OP_MONITOR_ENTER) {
+ insertHook(bytecode, true,
+ reinterpret_cast<VReg*>(bytecode->operands[0])->reg);
+ return true;
+ }
+ if (bytecode->opcode == OP_MONITOR_EXIT) {
+ insertHook(bytecode, false,
+ reinterpret_cast<VReg*>(bytecode->operands[0])->reg);
+ return true;
+ }
+ return false;
+ }
+
+ void insertHook(lir::Instruction* before, bool pre, u4 reg) {
+ transformer->preparePrePost();
+ transformer->addCall(cIr, before, OP_INVOKE_STATIC_RANGE,
+ transformer->hookType_, pre ? "preLock" : "postLock",
+ transformer->voidType_, transformer->objectType_, reg);
+ myModified = true;
+ }
+
+ Transformer* transformer;
+ CodeIr* cIr;
+ bool myModified = false;
+ };
+
+ CodeIr c(method.get(), dexIr_);
+ bool methodModified = false;
+
+ HookVisitor visitor(this, &c);
+ for (auto it = c.instructions.begin(); it != c.instructions.end(); ++it) {
+ lir::Instruction* fi = *it;
+ fi->Accept(&visitor);
+ }
+ methodModified |= visitor.myModified;
+
+ if (methodModified) {
+ classModified = true;
+ c.Assemble();
+ }
+ }
+
+ return classModified;
+ }
+
+private:
+ void preparePrePost() {
+ // Insert "void LockHook.(pre|post)(Object o)."
+
+ prepareBuilder();
+
+ if (voidType_ == nullptr) {
+ voidType_ = builder_->GetType("V");
+ }
+ if (hookType_ == nullptr) {
+ hookType_ = builder_->GetType("Lcom/android/lock_checker/LockHook;");
+ }
+ if (objectType_ == nullptr) {
+ objectType_ = builder_->GetType("Ljava/lang/Object;");
+ }
+ }
+
+ void prepareBuilder() {
+ if (builder_ == nullptr) {
+ builder_ = std::unique_ptr<ir::Builder>(new ir::Builder(dexIr_));
+ }
+ }
+
+ static void addInst(CodeIr* cIr, lir::Instruction* instructionAfter, Opcode opcode,
+ const std::list<Operand*>& operands) {
+ auto instruction = cIr->Alloc<Bytecode>();
+
+ instruction->opcode = opcode;
+
+ for (auto it = operands.begin(); it != operands.end(); it++) {
+ instruction->operands.push_back(*it);
+ }
+
+ cIr->instructions.InsertBefore(instructionAfter, instruction);
+ }
+
+ void addCall(CodeIr* cIr, lir::Instruction* instructionAfter, Opcode opcode, ir::Type* type,
+ const char* methodName, ir::Type* returnType,
+ const std::vector<ir::Type*>& types, const std::list<int>& regs) {
+ auto proto = builder_->GetProto(returnType, builder_->GetTypeList(types));
+ auto method = builder_->GetMethodDecl(builder_->GetAsciiString(methodName), proto, type);
+
+ VRegList* paramRegs = cIr->Alloc<VRegList>();
+ for (auto it = regs.begin(); it != regs.end(); it++) {
+ paramRegs->registers.push_back(*it);
+ }
+
+ addInst(cIr, instructionAfter, opcode,
+ { paramRegs, cIr->Alloc<Method>(method, method->orig_index) });
+ }
+
+ void addCall(CodeIr* cIr, lir::Instruction* instructionAfter, Opcode opcode, ir::Type* type,
+ const char* methodName, ir::Type* returnType, ir::Type* paramType,
+ u4 paramVReg) {
+ auto proto = builder_->GetProto(returnType, builder_->GetTypeList( { paramType }));
+ auto method = builder_->GetMethodDecl(builder_->GetAsciiString(methodName), proto, type);
+
+ VRegRange* args = cIr->Alloc<VRegRange>(paramVReg, 1);
+
+ addInst(cIr, instructionAfter, opcode,
+ { args, cIr->Alloc<Method>(method, method->orig_index) });
+ }
+
+ std::shared_ptr<ir::DexFile> dexIr_;
+ std::unique_ptr<ir::Builder> builder_;
+
+ ir::Type* voidType_ = nullptr;
+ ir::Type* hookType_ = nullptr;
+ ir::Type* objectType_ = nullptr;
+};
+
+std::pair<dex::u1*, size_t> maybeTransform(const char* name, size_t classDataLen,
+ const unsigned char* classData, dex::Writer::Allocator* allocator) {
+ // Isolate byte code of class class. This is needed as Android usually gives us more
+ // than the class we need.
+ dex::Reader reader(classData, classDataLen);
+
+ dex::u4 index = reader.FindClassIndex(classNameToDescriptor(name).c_str());
+ CHECK_NE(index, kNoIndex);
+ reader.CreateClassIr(index);
+ std::shared_ptr<ir::DexFile> ir = reader.GetIr();
+
+ {
+ Transformer transformer(ir);
+ if (!transformer.transform()) {
+ return std::make_pair(nullptr, 0);
+ }
+ }
+
+ size_t new_size;
+ dex::Writer writer(ir);
+ dex::u1* newClassData = writer.CreateImage(allocator, &new_size);
+ return std::make_pair(newClassData, new_size);
+}
+
+void transformHook(jvmtiEnv* jvmtiEnv, JNIEnv* env ATTRIBUTE_UNUSED,
+ jclass classBeingRedefined ATTRIBUTE_UNUSED, jobject loader, const char* name,
+ jobject protectionDomain ATTRIBUTE_UNUSED, jint classDataLen,
+ const unsigned char* classData, jint* newClassDataLen, unsigned char** newClassData) {
+ // Even reading the classData array is expensive as the data is only generated when the
+ // memory is touched. Hence call JvmtiAgent#shouldTransform to check if we need to transform
+ // the class.
+
+ // Skip bootclasspath classes. TODO: Make this configurable.
+ if (loader == nullptr) {
+ return;
+ }
+
+ // Do not look into java.* classes. Should technically be filtered by above, but when that's
+ // configurable have this.
+ if (strncmp("java", name, 4) == 0) {
+ return;
+ }
+
+ // Do not look into our Java classes.
+ if (strncmp("com/android/lock_checker", name, 24) == 0) {
+ return;
+ }
+
+ class JvmtiAllocator: public dex::Writer::Allocator {
+ public:
+ explicit JvmtiAllocator(::jvmtiEnv* jvmti) :
+ jvmti_(jvmti) {
+ }
+
+ void* Allocate(size_t size) override {
+ unsigned char* res = nullptr;
+ jvmti_->Allocate(size, &res);
+ return res;
+ }
+
+ void Free(void* ptr) override {
+ jvmti_->Deallocate(reinterpret_cast<unsigned char*>(ptr));
+ }
+
+ private:
+ ::jvmtiEnv* jvmti_;
+ };
+ JvmtiAllocator allocator(jvmtiEnv);
+ std::pair<dex::u1*, size_t> result = maybeTransform(name, classDataLen, classData,
+ &allocator);
+
+ if (result.second > 0) {
+ *newClassData = result.first;
+ *newClassDataLen = static_cast<jint>(result.second);
+ }
+}
+
+void dataDumpRequestHook(jvmtiEnv* jvmtiEnv ATTRIBUTE_UNUSED) {
+ if (gJavaVM == nullptr) {
+ LOG(ERROR) << "No JavaVM for dump";
+ return;
+ }
+ JNIEnv* env;
+ if (gJavaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ LOG(ERROR) << "Could not get env for dump";
+ return;
+ }
+ jclass lockHookClass = env->FindClass("com/android/lock_checker/LockHook");
+ if (lockHookClass == nullptr) {
+ env->ExceptionClear();
+ LOG(ERROR) << "Could not find LockHook class";
+ return;
+ }
+ jmethodID dumpId = env->GetStaticMethodID(lockHookClass, "dump", "()V");
+ if (dumpId == nullptr) {
+ env->ExceptionClear();
+ LOG(ERROR) << "Could not find LockHook.dump";
+ return;
+ }
+ env->CallStaticVoidMethod(lockHookClass, dumpId);
+ env->ExceptionClear();
+}
+
+// A function for dladdr to search.
+extern "C" __attribute__ ((visibility ("default"))) void lock_agent_tag_fn() {
+}
+
+bool fileExists(const std::string& path) {
+ struct stat statBuf;
+ int rc = stat(path.c_str(), &statBuf);
+ return rc == 0;
+}
+
+std::string findLockAgentJar() {
+ // Check whether the jar is located next to the agent's so.
+#ifndef __APPLE__
+ {
+ Dl_info info;
+ if (dladdr(reinterpret_cast<const void*>(&lock_agent_tag_fn), /* out */ &info) != 0) {
+ std::string lockAgentSoPath = info.dli_fname;
+ std::string dir = android::base::Dirname(lockAgentSoPath);
+ std::string lockAgentJarPath = dir + "/" + "lockagent.jar";
+ if (fileExists(lockAgentJarPath)) {
+ return lockAgentJarPath;
+ }
+ } else {
+ LOG(ERROR) << "dladdr failed";
+ }
+ }
+#endif
+
+ std::string sysFrameworkPath = "/system/framework/lockagent.jar";
+ if (fileExists(sysFrameworkPath)) {
+ return sysFrameworkPath;
+ }
+
+ std::string relPath = "lockagent.jar";
+ if (fileExists(relPath)) {
+ return relPath;
+ }
+
+ return "";
+}
+
+void prepareHook(jvmtiEnv* env) {
+ // Inject the agent Java code.
+ {
+ std::string path = findLockAgentJar();
+ if (path.empty()) {
+ LOG(FATAL) << "Could not find lockagent.jar";
+ }
+ LOG(INFO) << "Will load Java parts from " << path;
+ jvmtiError res = env->AddToBootstrapClassLoaderSearch(path.c_str());
+ if (res != JVMTI_ERROR_NONE) {
+ LOG(FATAL) << "Could not add lockagent from " << path << " to boot classpath: " << res;
+ }
+ }
+
+ jvmtiCapabilities caps;
+ memset(&caps, 0, sizeof(caps));
+ caps.can_retransform_classes = 1;
+
+ if (env->AddCapabilities(&caps) != JVMTI_ERROR_NONE) {
+ LOG(FATAL) << "Could not add caps";
+ }
+
+ jvmtiEventCallbacks cb;
+ memset(&cb, 0, sizeof(cb));
+ cb.ClassFileLoadHook = transformHook;
+ cb.DataDumpRequest = dataDumpRequestHook;
+
+ if (env->SetEventCallbacks(&cb, sizeof(cb)) != JVMTI_ERROR_NONE) {
+ LOG(FATAL) << "Could not set cb";
+ }
+
+ if (env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, nullptr)
+ != JVMTI_ERROR_NONE) {
+ LOG(FATAL) << "Could not enable events";
+ }
+ if (env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_DATA_DUMP_REQUEST, nullptr)
+ != JVMTI_ERROR_NONE) {
+ LOG(FATAL) << "Could not enable events";
+ }
+}
+
+jint attach(JavaVM* vm, char* options, void* reserved ATTRIBUTE_UNUSED) {
+ gJavaVM = vm;
+
+ jvmtiEnv* env;
+ jint jvmError = vm->GetEnv(reinterpret_cast<void**>(&env), JVMTI_VERSION_1_2);
+ if (jvmError != JNI_OK) {
+ return jvmError;
+ }
+
+ prepareHook(env);
+
+ std::vector<std::string> config = android::base::Split(options, ",");
+ for (const std::string& c : config) {
+ if (c == "native_crash") {
+ gForkCrash = true;
+ } else if (c == "java_crash") {
+ gJavaCrash = true;
+ }
+ }
+
+ return JVMTI_ERROR_NONE;
+}
+
+extern "C" JNIEXPORT
+jboolean JNICALL Java_com_android_lock_1checker_LockHook_getNativeHandlingConfig(JNIEnv*, jclass) {
+ return gForkCrash ? JNI_TRUE : JNI_FALSE;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_android_lock_1checker_LockHook_getSimulateCrashConfig(JNIEnv*, jclass) {
+ return gJavaCrash ? JNI_TRUE : JNI_FALSE;
+}
+
+extern "C" JNIEXPORT void JNICALL Java_com_android_lock_1checker_LockHook_nWtf(JNIEnv* env, jclass,
+ jstring msg) {
+ if (!gForkCrash || msg == nullptr) {
+ return;
+ }
+
+ // Create a native crash with the given message. Decouple from the current crash to create a
+ // tombstone but continue on.
+ //
+ // TODO: Once there are not so many reports, consider making this fatal for the calling process.
+ ScopedUtfChars utf(env, msg);
+ if (utf.c_str() == nullptr) {
+ return;
+ }
+ const char* args[] = {
+ "/system/bin/lockagent_crasher",
+ utf.c_str(),
+ nullptr
+ };
+ pid_t pid = fork();
+ if (pid < 0) {
+ return;
+ }
+ if (pid == 0) {
+ // Double fork so we return quickly. Leave init to deal with the zombie.
+ pid_t pid2 = fork();
+ if (pid2 == 0) {
+ execv(args[0], const_cast<char* const*>(args));
+ _exit(1);
+ __builtin_unreachable();
+ }
+ _exit(0);
+ __builtin_unreachable();
+ }
+ int status;
+ waitpid(pid, &status, 0); // Ignore any results.
+}
+
+extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved) {
+ return attach(vm, options, reserved);
+}
+
+extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char* options, void* reserved) {
+ return attach(vm, options, reserved);
+}
+
+int locktest_main(int argc, char *argv[]) {
+ if (argc != 3) {
+ LOG(FATAL) << "Need two arguments: dex-file class-name";
+ }
+ struct stat statBuf;
+ int rc = stat(argv[1], &statBuf);
+ if (rc != 0) {
+ PLOG(FATAL) << "Could not get file size for " << argv[1];
+ }
+ std::unique_ptr<char[]> data(new char[statBuf.st_size]);
+ {
+ android::base::unique_fd fd(open(argv[1], O_RDONLY));
+ if (fd.get() == -1) {
+ PLOG(FATAL) << "Could not open file " << argv[1];
+ }
+ if (!android::base::ReadFully(fd.get(), data.get(), statBuf.st_size)) {
+ PLOG(FATAL) << "Could not read file " << argv[1];
+ }
+ }
+
+ class NewDeleteAllocator: public dex::Writer::Allocator {
+ public:
+ explicit NewDeleteAllocator() {
+ }
+
+ void* Allocate(size_t size) override {
+ return new char[size];
+ }
+
+ void Free(void* ptr) override {
+ delete[] reinterpret_cast<char*>(ptr);
+ }
+ };
+ NewDeleteAllocator allocator;
+
+ std::pair<dex::u1*, size_t> result = maybeTransform(argv[2], statBuf.st_size,
+ reinterpret_cast<unsigned char*>(data.get()), &allocator);
+
+ if (result.second == 0) {
+ LOG(INFO) << "No transformation";
+ return 0;
+ }
+
+ std::string newName(argv[1]);
+ newName.append(".new");
+
+ {
+ android::base::unique_fd fd(
+ open(newName.c_str(), O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR));
+ if (fd.get() == -1) {
+ PLOG(FATAL) << "Could not open file " << newName;
+ }
+ if (!android::base::WriteFully(fd.get(), result.first, result.second)) {
+ PLOG(FATAL) << "Could not write file " << newName;
+ }
+ }
+ LOG(INFO) << "Transformed file written to " << newName;
+
+ return 0;
+}
+
+} // namespace
+
+int main(int argc, char *argv[]) {
+ return locktest_main(argc, argv);
+}
diff --git a/tools/preload2/src/com/android/preload/actions/ClearTableAction.java b/tools/lock_agent/crasher.cpp
index c0e4795b6d90..2829d6d9447c 100644
--- a/tools/preload2/src/com/android/preload/actions/ClearTableAction.java
+++ b/tools/lock_agent/crasher.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,24 +14,17 @@
* limitations under the License.
*/
-package com.android.preload.actions;
+#include <android-base/logging.h>
-import com.android.preload.DumpTableModel;
-
-import java.awt.event.ActionEvent;
-
-import javax.swing.AbstractAction;
-
-public class ClearTableAction extends AbstractAction {
- private final DumpTableModel dataTableModel;
-
- public ClearTableAction(DumpTableModel dataTableModel) {
- super("Clear");
- this.dataTableModel = dataTableModel;
- }
-
- @Override
- public void actionPerformed(ActionEvent e) {
- dataTableModel.clear();
+// Simple binary that will just crash with the message given as the first parameter.
+//
+// This is helpful in cases the caller does not want to crash itself, e.g., fork+crash
+// instead, as LOG(FATAL) might not be safe (for example in a multi-threaded environment).
+int main(int argc, char *argv[]) {
+ if (argc != 2) {
+ LOG(FATAL) << "Need one argument for abort message";
+ __builtin_unreachable();
}
-} \ No newline at end of file
+ LOG(FATAL) << argv[1];
+ __builtin_unreachable();
+}
diff --git a/tools/lock_agent/java/com/android/lock_checker/LockHook.java b/tools/lock_agent/java/com/android/lock_checker/LockHook.java
new file mode 100644
index 000000000000..35c75cbbae7c
--- /dev/null
+++ b/tools/lock_agent/java/com/android/lock_checker/LockHook.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.lock_checker;
+
+import android.app.ActivityThread;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.util.Log;
+import android.util.LogWriter;
+
+import com.android.internal.os.RuntimeInit;
+import com.android.internal.os.SomeArgs;
+import com.android.internal.util.StatLogger;
+
+import dalvik.system.AnnotatedStackTraceElement;
+
+import libcore.util.HexEncoding;
+
+import java.io.PrintWriter;
+import java.nio.charset.Charset;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Entry class for lock inversion infrastructure. The agent will inject calls to preLock
+ * and postLock, and the hook will call the checker, and store violations.
+ */
+public class LockHook {
+ private static final String TAG = "LockHook";
+
+ private static final Charset sFilenameCharset = Charset.forName("UTF-8");
+
+ private static final HandlerThread sHandlerThread;
+ private static final WtfHandler sHandler;
+
+ private static final AtomicInteger sTotalObtainCount = new AtomicInteger();
+ private static final AtomicInteger sTotalReleaseCount = new AtomicInteger();
+ private static final AtomicInteger sDeepestNest = new AtomicInteger();
+
+ /**
+ * Whether to do the lock check on this thread.
+ */
+ private static final ThreadLocal<Boolean> sDoCheck = ThreadLocal.withInitial(() -> true);
+
+ interface Stats {
+ int ON_THREAD = 0;
+ }
+
+ static final StatLogger sStats = new StatLogger(new String[] { "on-thread", });
+
+ private static final ConcurrentLinkedQueue<Violation> sViolations =
+ new ConcurrentLinkedQueue<>();
+ private static final int MAX_VIOLATIONS = 50;
+
+ private static final LockChecker[] sCheckers;
+
+ private static boolean sNativeHandling = false;
+ private static boolean sSimulateCrash = false;
+
+ static {
+ sHandlerThread = new HandlerThread("LockHook:wtf", Process.THREAD_PRIORITY_BACKGROUND);
+ sHandlerThread.start();
+ sHandler = new WtfHandler(sHandlerThread.getLooper());
+
+ sCheckers = new LockChecker[] { new OnThreadLockChecker() };
+
+ sNativeHandling = getNativeHandlingConfig();
+ sSimulateCrash = getSimulateCrashConfig();
+ }
+
+ private static native boolean getNativeHandlingConfig();
+ private static native boolean getSimulateCrashConfig();
+
+ static <T> boolean shouldDumpStacktrace(StacktraceHasher hasher, Map<String, T> dumpedSet,
+ T val, AnnotatedStackTraceElement[] st, int from, int to) {
+ final String stacktraceHash = hasher.stacktraceHash(st, from, to);
+ if (dumpedSet.containsKey(stacktraceHash)) {
+ return false;
+ }
+ dumpedSet.put(stacktraceHash, val);
+ return true;
+ }
+
+ static void updateDeepestNest(int nest) {
+ for (;;) {
+ final int knownDeepest = sDeepestNest.get();
+ if (knownDeepest >= nest) {
+ return;
+ }
+ if (sDeepestNest.compareAndSet(knownDeepest, nest)) {
+ return;
+ }
+ }
+ }
+
+ static void wtf(Violation v) {
+ sHandler.wtf(v);
+ }
+
+ static void doCheckOnThisThread(boolean check) {
+ sDoCheck.set(check);
+ }
+
+ /**
+ * This method is called when a lock is about to be held. (Except if it's a
+ * synchronized, the lock is already held.)
+ */
+ public static void preLock(Object lock) {
+ if (Thread.currentThread() != sHandlerThread && sDoCheck.get()) {
+ sDoCheck.set(false);
+ try {
+ sTotalObtainCount.incrementAndGet();
+ for (LockChecker checker : sCheckers) {
+ checker.pre(lock);
+ }
+ } finally {
+ sDoCheck.set(true);
+ }
+ }
+ }
+
+ /**
+ * This method is called when a lock is about to be released.
+ */
+ public static void postLock(Object lock) {
+ if (Thread.currentThread() != sHandlerThread && sDoCheck.get()) {
+ sDoCheck.set(false);
+ try {
+ sTotalReleaseCount.incrementAndGet();
+ for (LockChecker checker : sCheckers) {
+ checker.post(lock);
+ }
+ } finally {
+ sDoCheck.set(true);
+ }
+ }
+ }
+
+ private static class WtfHandler extends Handler {
+ private static final int MSG_WTF = 1;
+
+ WtfHandler(Looper looper) {
+ super(looper);
+ }
+
+ public void wtf(Violation v) {
+ sDoCheck.set(false);
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = v;
+ obtainMessage(MSG_WTF, args).sendToTarget();
+ sDoCheck.set(true);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_WTF:
+ SomeArgs args = (SomeArgs) msg.obj;
+ handleViolation((Violation) args.arg1);
+ args.recycle();
+ break;
+ }
+ }
+ }
+
+ private static void handleViolation(Violation v) {
+ String msg = v.toString();
+ Log.wtf(TAG, msg);
+ if (sNativeHandling) {
+ nWtf(msg); // Also send to native.
+ }
+ if (sSimulateCrash) {
+ RuntimeInit.logUncaught("LockAgent",
+ ActivityThread.isSystem() ? "system_server"
+ : ActivityThread.currentProcessName(),
+ Process.myPid(), v.getException());
+ }
+ }
+
+ private static native void nWtf(String msg);
+
+ /**
+ * Generates a hash for a given stacktrace of a {@link Throwable}.
+ */
+ static class StacktraceHasher {
+ private byte[] mLineNumberBuffer = new byte[4];
+ private final MessageDigest mHash;
+
+ StacktraceHasher() {
+ try {
+ mHash = MessageDigest.getInstance("MD5");
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public String stacktraceHash(Throwable t) {
+ mHash.reset();
+ for (StackTraceElement e : t.getStackTrace()) {
+ hashStackTraceElement(e);
+ }
+ return HexEncoding.encodeToString(mHash.digest());
+ }
+
+ public String stacktraceHash(AnnotatedStackTraceElement[] annotatedStack, int from,
+ int to) {
+ mHash.reset();
+ for (int i = from; i <= to; i++) {
+ hashStackTraceElement(annotatedStack[i].getStackTraceElement());
+ }
+ return HexEncoding.encodeToString(mHash.digest());
+ }
+
+ private void hashStackTraceElement(StackTraceElement e) {
+ if (e.getFileName() != null) {
+ mHash.update(sFilenameCharset.encode(e.getFileName()).array());
+ } else {
+ if (e.getClassName() != null) {
+ mHash.update(sFilenameCharset.encode(e.getClassName()).array());
+ }
+ if (e.getMethodName() != null) {
+ mHash.update(sFilenameCharset.encode(e.getMethodName()).array());
+ }
+ }
+
+ final int line = e.getLineNumber();
+ mLineNumberBuffer[0] = (byte) ((line >> 24) & 0xff);
+ mLineNumberBuffer[1] = (byte) ((line >> 16) & 0xff);
+ mLineNumberBuffer[2] = (byte) ((line >> 8) & 0xff);
+ mLineNumberBuffer[3] = (byte) ((line >> 0) & 0xff);
+ mHash.update(mLineNumberBuffer);
+ }
+ }
+
+ static void addViolation(Violation v) {
+ wtf(v);
+
+ sViolations.offer(v);
+ while (sViolations.size() > MAX_VIOLATIONS) {
+ sViolations.poll();
+ }
+ }
+
+ /**
+ * Dump stats to the given PrintWriter.
+ */
+ public static void dump(PrintWriter pw, String indent) {
+ final int oc = LockHook.sTotalObtainCount.get();
+ final int rc = LockHook.sTotalReleaseCount.get();
+ final int dn = LockHook.sDeepestNest.get();
+ pw.print("Lock stats: oc=");
+ pw.print(oc);
+ pw.print(" rc=");
+ pw.print(rc);
+ pw.print(" dn=");
+ pw.print(dn);
+ pw.println();
+
+ for (LockChecker checker : sCheckers) {
+ pw.print(indent);
+ pw.print(" ");
+ checker.dump(pw);
+ pw.println();
+ }
+
+ sStats.dump(pw, indent);
+
+ pw.print(indent);
+ pw.println("Violations:");
+ for (Object v : sViolations) {
+ pw.print(indent); // This won't really indent a multiline string,
+ // though.
+ pw.println(v);
+ }
+ }
+
+ /**
+ * Dump stats to logcat.
+ */
+ public static void dump() {
+ // Dump to logcat.
+ PrintWriter out = new PrintWriter(new LogWriter(Log.WARN, TAG), true);
+ dump(out, "");
+ out.close();
+ }
+
+ interface LockChecker {
+ void pre(Object lock);
+
+ void post(Object lock);
+
+ int getNumDetected();
+
+ int getNumDetectedUnique();
+
+ String getCheckerName();
+
+ void dump(PrintWriter pw);
+ }
+
+ interface Violation {
+ Throwable getException();
+ }
+}
diff --git a/tools/lock_agent/java/com/android/lock_checker/OnThreadLockChecker.java b/tools/lock_agent/java/com/android/lock_checker/OnThreadLockChecker.java
new file mode 100644
index 000000000000..e74ccf9d5069
--- /dev/null
+++ b/tools/lock_agent/java/com/android/lock_checker/OnThreadLockChecker.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.lock_checker;
+
+import android.util.Log;
+
+import dalvik.system.AnnotatedStackTraceElement;
+import dalvik.system.VMStack;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.atomic.AtomicInteger;
+
+
+class OnThreadLockChecker implements LockHook.LockChecker {
+ private static final String TAG = "LockCheckOnThread";
+
+ private static final boolean SKIP_RECURSIVE = true;
+
+ private final Thread mChecker;
+
+ private final AtomicInteger mNumDetected = new AtomicInteger();
+
+ private final AtomicInteger mNumDetectedUnique = new AtomicInteger();
+
+ // Queue for possible violations, to handle them on the sChecker thread.
+ private final LinkedBlockingQueue<Violation> mQueue = new LinkedBlockingQueue<>();
+
+ // The stack of locks held on the current thread.
+ private final ThreadLocal<List<Object>> mHeldLocks = ThreadLocal
+ .withInitial(() -> new ArrayList<>(10));
+
+ // A cached stacktrace hasher for each thread. The hasher caches internal objects and is not
+ // thread-safe.
+ private final ThreadLocal<LockHook.StacktraceHasher> mStacktraceHasher = ThreadLocal
+ .withInitial(() -> new LockHook.StacktraceHasher());
+
+ // A map of stacktrace hashes we have seen.
+ private final ConcurrentMap<String, Boolean> mDumpedStacktraceHashes =
+ new ConcurrentHashMap<>();
+
+ OnThreadLockChecker() {
+ mChecker = new Thread(() -> checker());
+ mChecker.setName(TAG);
+ mChecker.setPriority(Thread.MIN_PRIORITY);
+ mChecker.start();
+ }
+
+ private static class LockPair {
+ // Consider WeakReference. It will require also caching the String
+ // description for later reporting, though.
+ Object mFirst;
+ Object mSecond;
+
+ private int mCachedHashCode;
+
+ LockPair(Object first, Object second) {
+ mFirst = first;
+ mSecond = second;
+ computeHashCode();
+ }
+
+ public void set(Object newFirst, Object newSecond) {
+ mFirst = newFirst;
+ mSecond = newSecond;
+ computeHashCode();
+ }
+
+ private void computeHashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((mFirst == null) ? 0 : System.identityHashCode(mFirst));
+ result = prime * result + ((mSecond == null) ? 0 : System.identityHashCode(mSecond));
+ mCachedHashCode = result;
+ }
+
+ @Override
+ public int hashCode() {
+ return mCachedHashCode;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ LockPair other = (LockPair) obj;
+ return mFirst == other.mFirst && mSecond == other.mSecond;
+ }
+ }
+
+ private static class OrderData {
+ final int mTid;
+ final String mThreadName;
+ final AnnotatedStackTraceElement[] mStack;
+
+ OrderData(int tid, String threadName, AnnotatedStackTraceElement[] stack) {
+ this.mTid = tid;
+ this.mThreadName = threadName;
+ this.mStack = stack;
+ }
+ }
+
+ private static ConcurrentMap<LockPair, OrderData> sLockOrderMap = new ConcurrentHashMap<>();
+
+ @Override
+ public void pre(Object lock) {
+ handlePre(Thread.currentThread(), lock);
+ }
+
+ @Override
+ public void post(Object lock) {
+ handlePost(Thread.currentThread(), lock);
+ }
+
+ private void handlePre(Thread self, Object lock) {
+ List<Object> heldLocks = mHeldLocks.get();
+
+ LockHook.updateDeepestNest(heldLocks.size() + 1);
+
+ heldLocks.add(lock);
+ if (heldLocks.size() == 1) {
+ return;
+ }
+
+ // Data about this location. Cached and lazily initialized.
+ AnnotatedStackTraceElement[] annotatedStack = null;
+ OrderData orderData = null;
+
+ // Reused tmp pair;
+ LockPair tmp = new LockPair(lock, lock);
+
+ int size = heldLocks.size() - 1;
+ for (int i = 0; i < size; i++) {
+ Object alreadyHeld = heldLocks.get(i);
+ if (SKIP_RECURSIVE && lock == alreadyHeld) {
+ return;
+ }
+
+ // Check if we've already seen alreadyHeld -> lock.
+ tmp.set(alreadyHeld, lock);
+ if (sLockOrderMap.containsKey(tmp)) {
+ continue; // Already seen.
+ }
+
+ // Note: could insert the OrderData now. This would mean we only
+ // report one instance for each order violation, but it avoids
+ // the expensive hashing in handleViolation for duplicate stacks.
+
+ // Locking alreadyHeld -> lock, check whether the inverse exists.
+ tmp.set(lock, alreadyHeld);
+
+ // We technically need a critical section here. Add synchronized and
+ // skip
+ // instrumenting this class. For now, a concurrent hash map is good
+ // enough.
+
+ OrderData oppositeData = sLockOrderMap.getOrDefault(tmp, null);
+ if (oppositeData != null) {
+ if (annotatedStack == null) {
+ annotatedStack = VMStack.getAnnotatedThreadStackTrace(self);
+ }
+ postViolation(self, alreadyHeld, lock, annotatedStack, oppositeData);
+ continue;
+ }
+
+ // Enter our occurrence.
+ if (annotatedStack == null) {
+ annotatedStack = VMStack.getAnnotatedThreadStackTrace(self);
+ }
+ if (orderData == null) {
+ orderData = new OrderData((int) self.getId(), self.getName(), annotatedStack);
+ }
+ sLockOrderMap.putIfAbsent(new LockPair(alreadyHeld, lock), orderData);
+
+ // Check again whether we might have raced with the opposite.
+ oppositeData = sLockOrderMap.getOrDefault(tmp, null);
+ if (oppositeData != null) {
+ postViolation(self, alreadyHeld, lock, annotatedStack, oppositeData);
+ }
+ }
+ }
+
+ private void handlePost(Thread self, Object lock) {
+ List<Object> heldLocks = mHeldLocks.get();
+ if (heldLocks.isEmpty()) {
+ Log.wtf("LockCheckMine", "Empty thread list on post()");
+ return;
+ }
+ int index = heldLocks.size() - 1;
+ if (heldLocks.get(index) != lock) {
+ Log.wtf("LockCheckMine", "post(" + Violation.describeLock(lock) + ") vs [..., "
+ + Violation.describeLock(heldLocks.get(index)) + "]");
+ return;
+ }
+ heldLocks.remove(index);
+ }
+
+ private static class Violation implements LockHook.Violation {
+ int mSelfTid;
+ String mSelfName;
+ Object mAlreadyHeld;
+ Object mLock;
+ AnnotatedStackTraceElement[] mStack;
+ OrderData mOppositeData;
+
+ private static final int STACK_OFFSET = 4;
+
+ Violation(Thread self, Object alreadyHeld, Object lock,
+ AnnotatedStackTraceElement[] stack, OrderData oppositeData) {
+ this.mSelfTid = (int) self.getId();
+ this.mSelfName = self.getName();
+ this.mAlreadyHeld = alreadyHeld;
+ this.mLock = lock;
+ this.mStack = stack;
+ this.mOppositeData = oppositeData;
+ }
+
+ private static String getAnnotatedStackString(AnnotatedStackTraceElement[] stackTrace,
+ int skip, String extra, int prefixAfter, String prefix) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = skip; i < stackTrace.length; i++) {
+ AnnotatedStackTraceElement element = stackTrace[i];
+ sb.append(" ").append(i >= prefixAfter ? prefix : "").append("at ")
+ .append(element.getStackTraceElement()).append('\n');
+ if (i == skip && extra != null) {
+ sb.append(" ").append(extra).append('\n');
+ }
+ if (element.getHeldLocks() != null) {
+ for (Object held : element.getHeldLocks()) {
+ sb.append(" ").append(i >= prefixAfter ? prefix : "")
+ .append(describeLocking(held, "locked")).append('\n');
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+ private static String describeLocking(Object lock, String action) {
+ return String.format("- %s %s", action, describeLock(lock));
+ }
+
+ private static int getTo(AnnotatedStackTraceElement[] stack, Object searchFor) {
+ // Extract the range of the annotated stack.
+ int to = stack.length - 1;
+ for (int i = 0; i < stack.length; i++) {
+ Object[] locks = stack[i].getHeldLocks();
+ if (locks != null) {
+ for (Object heldLock : locks) {
+ if (heldLock == searchFor) {
+ to = i;
+ break;
+ }
+ }
+ }
+ }
+ return to;
+ }
+
+ private static String describeLock(Object lock) {
+ return String.format("<0x%08x> (a %s)", System.identityHashCode(lock),
+ lock.getClass().getName());
+ }
+
+ // Synthesize an exception.
+ public Throwable getException() {
+ RuntimeException inner = new RuntimeException("Previously locked");
+ inner.setStackTrace(synthesizeStackTrace(mOppositeData.mStack));
+
+ RuntimeException outer = new RuntimeException(toString(), inner);
+ outer.setStackTrace(synthesizeStackTrace(mStack));
+
+ return outer;
+ }
+
+ private StackTraceElement[] synthesizeStackTrace(AnnotatedStackTraceElement[] stack) {
+
+ StackTraceElement[] out = new StackTraceElement[stack.length - STACK_OFFSET];
+ for (int i = 0; i < out.length; i++) {
+ out[i] = stack[i + STACK_OFFSET].getStackTraceElement();
+ }
+ return out;
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Lock inversion detected!\n");
+ sb.append(" Locked ");
+ sb.append(describeLock(mLock));
+ sb.append(" -> ");
+ sb.append(describeLock(mAlreadyHeld));
+ sb.append(" on thread ").append(mOppositeData.mTid).append(" (")
+ .append(mOppositeData.mThreadName).append(")");
+ sb.append(" at:\n");
+ sb.append(getAnnotatedStackString(mOppositeData.mStack, STACK_OFFSET,
+ describeLocking(mAlreadyHeld, "will lock"), getTo(mOppositeData.mStack, mLock)
+ + 1, " | "));
+ sb.append(" Locking ");
+ sb.append(describeLock(mAlreadyHeld));
+ sb.append(" -> ");
+ sb.append(describeLock(mLock));
+ sb.append(" on thread ").append(mSelfTid).append(" (").append(mSelfName).append(")");
+ sb.append(" at:\n");
+ sb.append(getAnnotatedStackString(mStack, STACK_OFFSET,
+ describeLocking(mLock, "will lock"),
+ getTo(mStack, mAlreadyHeld) + 1, " | "));
+
+ return sb.toString();
+ }
+ }
+
+ private void postViolation(Thread self, Object alreadyHeld, Object lock,
+ AnnotatedStackTraceElement[] annotatedStack, OrderData oppositeData) {
+ mQueue.offer(new Violation(self, alreadyHeld, lock, annotatedStack, oppositeData));
+ }
+
+ private void handleViolation(Violation v) {
+ mNumDetected.incrementAndGet();
+ // Extract the range of the annotated stack.
+ int to = Violation.getTo(v.mStack, v.mAlreadyHeld);
+
+ if (LockHook.shouldDumpStacktrace(mStacktraceHasher.get(), mDumpedStacktraceHashes,
+ Boolean.TRUE, v.mStack, 0, to)) {
+ mNumDetectedUnique.incrementAndGet();
+ LockHook.addViolation(v);
+ }
+ }
+
+ private void checker() {
+ LockHook.doCheckOnThisThread(false);
+
+ for (;;) {
+ try {
+ Violation v = mQueue.take();
+ handleViolation(v);
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+
+ @Override
+ public int getNumDetected() {
+ return mNumDetected.get();
+ }
+
+ @Override
+ public int getNumDetectedUnique() {
+ return mNumDetectedUnique.get();
+ }
+
+ @Override
+ public String getCheckerName() {
+ return "Standard LockChecker";
+ }
+
+ @Override
+ public void dump(PrintWriter pw) {
+ pw.print(getCheckerName());
+ pw.print(": d=");
+ pw.print(getNumDetected());
+ pw.print(" du=");
+ pw.print(getNumDetectedUnique());
+ }
+}
diff --git a/tools/lock_agent/start_with_lockagent.sh b/tools/lock_agent/start_with_lockagent.sh
new file mode 100755
index 000000000000..70ed5c5e1dd7
--- /dev/null
+++ b/tools/lock_agent/start_with_lockagent.sh
@@ -0,0 +1,13 @@
+#!/system/bin/sh
+
+AGENT_OPTIONS=
+if [[ "$1" == --agent-options ]] ; then
+ shift
+ AGENT_OPTIONS="=$1"
+ shift
+fi
+
+APP=$1
+shift
+
+$APP -Xplugin:libopenjdkjvmti.so "-agentpath:liblockagent.so$AGENT_OPTIONS" $@
diff --git a/tools/locked_region_code_injection/src/lockedregioncodeinjection/Main.java b/tools/locked_region_code_injection/src/lockedregioncodeinjection/Main.java
index edb9a49f4106..828cce72dda9 100644
--- a/tools/locked_region_code_injection/src/lockedregioncodeinjection/Main.java
+++ b/tools/locked_region_code_injection/src/lockedregioncodeinjection/Main.java
@@ -13,6 +13,9 @@
*/
package lockedregioncodeinjection;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+
import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -24,8 +27,6 @@ import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
-import org.objectweb.asm.ClassReader;
-import org.objectweb.asm.ClassWriter;
public class Main {
public static void main(String[] args) throws IOException {
@@ -74,6 +75,7 @@ public class Main {
while (srcEntries.hasMoreElements()) {
ZipEntry entry = srcEntries.nextElement();
ZipEntry newEntry = new ZipEntry(entry.getName());
+ newEntry.setTime(entry.getTime());
zos.putNextEntry(newEntry);
BufferedInputStream bis = new BufferedInputStream(zipSrc.getInputStream(entry));
diff --git a/tools/preload-check/Android.bp b/tools/preload-check/Android.bp
new file mode 100644
index 000000000000..87b31d22af32
--- /dev/null
+++ b/tools/preload-check/Android.bp
@@ -0,0 +1,23 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+java_test_host {
+ name: "PreloadCheck",
+ srcs: ["src/**/*.java"],
+ java_resources: [":preloaded-classes-blacklist"],
+ libs: ["tradefed"],
+ test_suites: ["general-tests"],
+ required: ["preload-check-device"],
+ data: [":preload-check-device"],
+}
diff --git a/tools/preload-check/AndroidTest.xml b/tools/preload-check/AndroidTest.xml
new file mode 100644
index 000000000000..a0645d5b1051
--- /dev/null
+++ b/tools/preload-check/AndroidTest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for PreloadCheck">
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="preload-check-device.jar->/data/local/tmp/preload-check-device.jar" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.HostTest" >
+ <option name="class" value="com.android.preload.check.PreloadCheck" />
+ </test>
+</configuration>
diff --git a/tools/preload-check/TEST_MAPPING b/tools/preload-check/TEST_MAPPING
new file mode 100644
index 000000000000..d09805ec08b6
--- /dev/null
+++ b/tools/preload-check/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "PreloadCheck"
+ }
+ ]
+}
diff --git a/tools/preload-check/device/Android.bp b/tools/preload-check/device/Android.bp
new file mode 100644
index 000000000000..f40d8ba5a287
--- /dev/null
+++ b/tools/preload-check/device/Android.bp
@@ -0,0 +1,26 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+java_test_helper_library {
+ name: "preload-check-device",
+ host_supported: false,
+ device_supported: true,
+ compile_dex: true,
+
+ sdk_version: "current",
+ srcs: ["src/**/*.java"],
+ dex_preopt: {
+ enabled: false,
+ },
+}
diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/ClassDataRetriever.java b/tools/preload-check/device/src/com/android/preload/check/Initialized.java
index f04360fc1942..81c074c04d4c 100644
--- a/tools/preload2/src/com/android/preload/classdataretrieval/ClassDataRetriever.java
+++ b/tools/preload-check/device/src/com/android/preload/check/Initialized.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,14 @@
* limitations under the License.
*/
-package com.android.preload.classdataretrieval;
-
-import com.android.ddmlib.Client;
-
-import java.util.Map;
+package com.android.preload.check;
/**
- * Retrieve a class-to-classloader map for loaded classes from the client.
+ * Test that the given boot classpath class is initialized.
*/
-public interface ClassDataRetriever {
-
- public Map<String, String> getClassData(Client client);
+public class Initialized {
+ public static void main(String[] args) throws Exception {
+ Util.assertInitialized(args[0], null);
+ System.out.println("OK");
+ }
}
diff --git a/tools/preload-check/device/src/com/android/preload/check/IntegrityCheck.java b/tools/preload-check/device/src/com/android/preload/check/IntegrityCheck.java
new file mode 100644
index 000000000000..1c1e927c54c4
--- /dev/null
+++ b/tools/preload-check/device/src/com/android/preload/check/IntegrityCheck.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.preload.check;
+
+/**
+ * Test that a helper class is first seen as uninitialized, then initialized after forced.
+ */
+public class IntegrityCheck {
+ public static void main(String[] args) throws Exception {
+ ClassLoader loader = IntegrityCheck.class.getClassLoader();
+
+ Util.assertNotInitialized("com.android.preload.check.IntegrityCheck$StatusHelper", loader);
+
+ Class.forName("com.android.preload.check.IntegrityCheck$StatusHelper",
+ /* initialize */ true, loader);
+
+ Util.assertInitialized("com.android.preload.check.IntegrityCheck$StatusHelper", loader);
+
+ System.out.println("OK");
+ }
+
+ @SuppressWarnings("unused")
+ private static class StatusHelper {
+ private final static Object defer = new Object();
+ }
+}
diff --git a/tools/preload2/src/com/android/preload/ui/NullProgressMonitor.java b/tools/preload-check/device/src/com/android/preload/check/NotInitialized.java
index f45aad06ac6b..c3d2c7737c7d 100644
--- a/tools/preload2/src/com/android/preload/ui/NullProgressMonitor.java
+++ b/tools/preload-check/device/src/com/android/preload/check/NotInitialized.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,26 +14,14 @@
* limitations under the License.
*/
-package com.android.preload.ui;
+package com.android.preload.check;
-import com.android.ddmlib.SyncService.ISyncProgressMonitor;
-
-public class NullProgressMonitor implements ISyncProgressMonitor {
-
- @Override
- public void advance(int arg0) {}
-
- @Override
- public boolean isCanceled() {
- return false;
+/**
+ * Test that the given boot classpath class is not initialized.
+ */
+public class NotInitialized {
+ public static void main(String[] args) throws Exception {
+ Util.assertNotInitialized(args[0], null);
+ System.out.println("OK");
}
-
- @Override
- public void start(int arg0) {}
-
- @Override
- public void startSubTask(String arg0) {}
-
- @Override
- public void stop() {}
-} \ No newline at end of file
+}
diff --git a/tools/preload-check/device/src/com/android/preload/check/NotInitializedRegex.java b/tools/preload-check/device/src/com/android/preload/check/NotInitializedRegex.java
new file mode 100644
index 000000000000..d942bad9b6a8
--- /dev/null
+++ b/tools/preload-check/device/src/com/android/preload/check/NotInitializedRegex.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.preload.check;
+
+import dalvik.system.DexFile;
+
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Test boot classpath classes that satisfy a given regular expression to be not initialized.
+ * Optionally check that at least one class was matched.
+ */
+public class NotInitializedRegex {
+ /**
+ * First arg (mandatory): regular exception. Second arg (optional): boolean to denote a
+ * required match.
+ */
+ public static void main(String[] args) throws Exception {
+ Matcher m = Pattern.compile(args[0]).matcher("");
+ boolean requiresMatch = args.length > 1 ? Boolean.parseBoolean(args[1]) : false;
+
+ Collection<DexFile> dexFiles = Util.getBootDexFiles();
+ int matched = 0, notMatched = 0;
+ for (DexFile dexFile : dexFiles) {
+ Enumeration<String> entries = dexFile.entries();
+ while (entries.hasMoreElements()) {
+ String entry = entries.nextElement();
+ m.reset(entry);
+ if (m.matches()) {
+ System.out.println(entry + ": match");
+ matched++;
+ check(entry);
+ } else {
+ System.out.println(entry + ": no match");
+ notMatched++;
+ }
+ }
+ }
+ System.out.println("Matched: " + matched + " Not-Matched: " + notMatched);
+ if (requiresMatch && matched == 0) {
+ throw new RuntimeException("Did not find match");
+ }
+ System.out.println("OK");
+ }
+
+ private static void check(String name) {
+ Util.assertNotInitialized(name, null);
+ }
+}
diff --git a/tools/preload-check/device/src/com/android/preload/check/Util.java b/tools/preload-check/device/src/com/android/preload/check/Util.java
new file mode 100644
index 000000000000..fccea0a0c107
--- /dev/null
+++ b/tools/preload-check/device/src/com/android/preload/check/Util.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.preload.check;
+
+import dalvik.system.DexFile;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+public class Util {
+ private static Field statusField;
+
+ static {
+ try {
+ Class<?> klass = Class.class;
+ statusField = klass.getDeclaredField("status");
+ statusField.setAccessible(true);
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ // Reset the framework's kill handler.
+ Thread.setDefaultUncaughtExceptionHandler(null);
+ }
+
+ public static Collection<DexFile> getBootDexFiles() throws Exception {
+ Class<?> vmClassLoaderClass = Class.forName("java.lang.VMClassLoader");
+ Method getResources = vmClassLoaderClass.getDeclaredMethod("getResources", String.class);
+ getResources.setAccessible(true);
+ LinkedList<DexFile> res = new LinkedList<>();
+ for (int i = 1;; i++) {
+ try {
+ String name = "classes" + (i > 1 ? String.valueOf(i) : "") + ".dex";
+ @SuppressWarnings("unchecked")
+ List<URL> urls = (List<URL>) getResources.invoke(null, name);
+ if (urls.isEmpty()) {
+ break;
+ }
+ for (URL url : urls) {
+ // Make temp copy, so we can use public API. Would be nice to use in-memory, but
+ // those are unstable.
+ String tmp = "/data/local/tmp/tmp.dex";
+ try (BufferedInputStream in = new BufferedInputStream(url.openStream());
+ BufferedOutputStream out = new BufferedOutputStream(
+ new FileOutputStream(tmp))) {
+ byte[] buf = new byte[4096];
+ for (;;) {
+ int r = in.read(buf);
+ if (r == -1) {
+ break;
+ }
+ out.write(buf, 0, r);
+ }
+ }
+ try {
+ res.add(new DexFile(tmp));
+ } catch (Exception dexError) {
+ dexError.printStackTrace(System.out);
+ }
+ new File(tmp).delete();
+ }
+ } catch (Exception ignored) {
+ break;
+ }
+ }
+ return res;
+ }
+
+ public static boolean isInitialized(Class<?> klass) throws Exception {
+ Object val = statusField.get(klass);
+ if (val == null || !(val instanceof Integer)) {
+ throw new IllegalStateException(String.valueOf(val));
+ }
+ int intVal = (int)val;
+ intVal = (intVal >> (32-4)) & 0xf;
+ return intVal >= 14;
+ }
+
+ public static void assertTrue(boolean val, String msg) {
+ if (!val) {
+ throw new RuntimeException(msg);
+ }
+ }
+
+ public static void assertInitializedState(String className, boolean expected,
+ ClassLoader loader) {
+ boolean initialized;
+ try {
+ Class<?> klass = Class.forName(className, /* initialize */ false, loader);
+ initialized = isInitialized(klass);
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ assertTrue(expected == initialized, className);
+ }
+
+ public static void assertNotInitialized(String className, ClassLoader loader) {
+ assertInitializedState(className, false, loader);
+ }
+
+ public static void assertInitialized(String className, ClassLoader loader) {
+ assertInitializedState(className, true, loader);
+ }
+}
diff --git a/tools/preload-check/src/com/android/preload/check/PreloadCheck.java b/tools/preload-check/src/com/android/preload/check/PreloadCheck.java
new file mode 100644
index 000000000000..00fd414e3ee2
--- /dev/null
+++ b/tools/preload-check/src/com/android/preload/check/PreloadCheck.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.preload.check;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.IDeviceTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class PreloadCheck implements IDeviceTest {
+ private ITestDevice mTestDevice;
+
+ private static final String TEST_CLASSPATH = "/data/local/tmp/preload-check-device.jar";
+
+ @Override
+ public void setDevice(ITestDevice testDevice) {
+ mTestDevice = testDevice;
+ }
+
+ @Override
+ public ITestDevice getDevice() {
+ return mTestDevice;
+ }
+
+ /**
+ * Test that checks work as expected.
+ */
+ @Test
+ public void testStatus() throws Exception {
+ run("com.android.preload.check.IntegrityCheck");
+ }
+
+ /**
+ * b/130206915.
+ */
+ @Test
+ public void testAsyncTask() throws Exception {
+ run("com.android.preload.check.NotInitialized", "android.os.AsyncTask");
+ }
+
+ /**
+ * Just a check for something we expect to see initialized.
+ */
+ @Test
+ public void testAnimator() throws Exception {
+ run("com.android.preload.check.Initialized", "android.animation.Animator");
+ }
+
+ /**
+ * Test the classes mentioned in the embedded preloaded-classes blacklist.
+ */
+ @Test
+ public void testBlackList() throws Exception {
+ StringBuilder sb = new StringBuilder();
+ try (BufferedReader br = new BufferedReader(new InputStreamReader(getClass()
+ .getResourceAsStream("/preloaded-classes-blacklist")))) {
+ String s;
+ while ((s = br.readLine()) != null) {
+ s = s.trim();
+ if (s.startsWith("#") || s.isEmpty()) {
+ continue;
+ }
+ try {
+ run("com.android.preload.check.NotInitialized", s);
+ } catch (Throwable t) {
+ if (sb.length() > 0) {
+ sb.append('\n');
+ }
+ sb.append(t.getMessage());
+ }
+ }
+ }
+ if (sb.length() > 0) {
+ throw new RuntimeException(sb.toString());
+ }
+ }
+
+ /**
+ * Test the classes ending in NoPreloadHolder are not initialized.
+ */
+ @Test
+ public void testNoPreloadHolder() throws Exception {
+ run("com.android.preload.check.NotInitializedRegex", ".*NoPreloadHolder$", "true");
+ }
+
+ private void run(String cmd, String... args) throws Exception {
+ StringBuilder sb = new StringBuilder();
+ sb.append("app_process ")
+ .append("-cp ").append(TEST_CLASSPATH)
+ .append(" /system/bin ")
+ .append(cmd);
+ for (String arg : args) {
+ sb.append(' ').append(escape(arg));
+ }
+ String res = mTestDevice.executeShellCommand(sb.toString());
+ assertTrue(sb.toString() + "\n===\n" + res, res.trim().endsWith("OK"));
+ }
+
+ private static String escape(String input) {
+ if (input.indexOf('$') == -1) {
+ return input;
+ }
+ return input.replace("$", "\\$");
+ }
+}
diff --git a/tools/preload2/Android.mk b/tools/preload2/Android.mk
deleted file mode 100644
index d3ee1d370855..000000000000
--- a/tools/preload2/Android.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-
-# To connect to devices (and take hprof dumps).
-LOCAL_STATIC_JAVA_LIBRARIES := ddmlib-prebuilt tools-common-prebuilt
-
-# To process hprof dumps.
-LOCAL_STATIC_JAVA_LIBRARIES += perflib-prebuilt trove-prebuilt guavalib
-
-# For JDWP access we use the framework in the JDWP tests from Apache Harmony, for
-# convenience (and to not depend on internal JDK APIs).
-LOCAL_STATIC_JAVA_LIBRARIES += apache-harmony-jdwp-tests-host junit-host
-
-LOCAL_MODULE:= preload2
-
-include $(BUILD_HOST_JAVA_LIBRARY)
-# Copy to build artifacts
-$(call dist-for-goals,dist_files,$(LOCAL_BUILT_MODULE):$(LOCAL_MODULE).jar)
-
-# Copy the preload-tool shell script to the host's bin directory.
-include $(CLEAR_VARS)
-LOCAL_IS_HOST_MODULE := true
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_MODULE := preload-tool
-LOCAL_SRC_FILES := preload-tool
-LOCAL_REQUIRED_MODULES := preload2
-include $(BUILD_PREBUILT)
diff --git a/tools/preload2/preload-tool b/tools/preload2/preload-tool
deleted file mode 100644
index 322b62fda071..000000000000
--- a/tools/preload2/preload-tool
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This script is used on the host only. It uses a common subset
-# shell dialect that should work well. It is partially derived
-# from art/tools/art.
-
-function follow_links() {
- if [ z"$BASH_SOURCE" != z ]; then
- file="$BASH_SOURCE"
- else
- file="$0"
- fi
- while [ -h "$file" ]; do
- # On Mac OS, readlink -f doesn't work.
- file="$(readlink "$file")"
- done
- echo "$file"
-}
-
-
-PROG_NAME="$(follow_links)"
-PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
-ANDROID_ROOT=$PROG_DIR/..
-
-java -cp $ANDROID_ROOT/framework/preload2.jar com.android.preload.Main $@
diff --git a/tools/preload2/src/com/android/preload/ClientUtils.java b/tools/preload2/src/com/android/preload/ClientUtils.java
deleted file mode 100644
index 71ef025d6587..000000000000
--- a/tools/preload2/src/com/android/preload/ClientUtils.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.preload;
-
-import com.android.ddmlib.AndroidDebugBridge;
-import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
-import com.android.ddmlib.Client;
-import com.android.ddmlib.IDevice;
-
-/**
- * Helper class for common communication with a Client (the ddms name for a running application).
- *
- * Instances take a default timeout parameter that's applied to all functions without explicit
- * timeout. Timeouts are in milliseconds.
- */
-public class ClientUtils {
-
- private int defaultTimeout;
-
- public ClientUtils() {
- this(10000);
- }
-
- public ClientUtils(int defaultTimeout) {
- this.defaultTimeout = defaultTimeout;
- }
-
- /**
- * Shortcut for findClient with default timeout.
- */
- public Client findClient(IDevice device, String processName, int processPid) {
- return findClient(device, processName, processPid, defaultTimeout);
- }
-
- /**
- * Find the client with the given process name or process id. The name takes precedence over
- * the process id (if valid). Stop looking after the given timeout.
- *
- * @param device The device to communicate with.
- * @param processName The name of the process. May be null.
- * @param processPid The pid of the process. Values less than or equal to zero are ignored.
- * @param timeout The amount of milliseconds to wait, at most.
- * @return The client, if found. Otherwise null.
- */
- public Client findClient(IDevice device, String processName, int processPid, int timeout) {
- WaitForClient wfc = new WaitForClient(device, processName, processPid, timeout);
- return wfc.get();
- }
-
- /**
- * Shortcut for findAllClients with default timeout.
- */
- public Client[] findAllClients(IDevice device) {
- return findAllClients(device, defaultTimeout);
- }
-
- /**
- * Retrieve all clients known to the given device. Wait at most the given timeout.
- *
- * @param device The device to investigate.
- * @param timeout The amount of milliseconds to wait, at most.
- * @return An array of clients running on the given device. May be null depending on the
- * device implementation.
- */
- public Client[] findAllClients(IDevice device, int timeout) {
- if (device.hasClients()) {
- return device.getClients();
- }
- WaitForClients wfc = new WaitForClients(device, timeout);
- return wfc.get();
- }
-
- private static class WaitForClient implements IClientChangeListener {
-
- private IDevice device;
- private String processName;
- private int processPid;
- private long timeout;
- private Client result;
-
- public WaitForClient(IDevice device, String processName, int processPid, long timeout) {
- this.device = device;
- this.processName = processName;
- this.processPid = processPid;
- this.timeout = timeout;
- this.result = null;
- }
-
- public Client get() {
- synchronized (this) {
- AndroidDebugBridge.addClientChangeListener(this);
-
- // Maybe it's already there.
- if (result == null) {
- result = searchForClient(device);
- }
-
- if (result == null) {
- try {
- wait(timeout);
- } catch (InterruptedException e) {
- // Note: doesn't guard for spurious wakeup.
- }
- }
- }
-
- AndroidDebugBridge.removeClientChangeListener(this);
- return result;
- }
-
- private Client searchForClient(IDevice device) {
- if (processName != null) {
- Client tmp = device.getClient(processName);
- if (tmp != null) {
- return tmp;
- }
- }
- if (processPid > 0) {
- String name = device.getClientName(processPid);
- if (name != null && !name.isEmpty()) {
- Client tmp = device.getClient(name);
- if (tmp != null) {
- return tmp;
- }
- }
- }
- if (processPid > 0) {
- // Try manual search.
- for (Client cl : device.getClients()) {
- if (cl.getClientData().getPid() == processPid
- && cl.getClientData().getClientDescription() != null) {
- return cl;
- }
- }
- }
- return null;
- }
-
- private boolean isTargetClient(Client c) {
- if (processPid > 0 && c.getClientData().getPid() == processPid) {
- return true;
- }
- if (processName != null
- && processName.equals(c.getClientData().getClientDescription())) {
- return true;
- }
- return false;
- }
-
- @Override
- public void clientChanged(Client arg0, int arg1) {
- synchronized (this) {
- if ((arg1 & Client.CHANGE_INFO) != 0 && (arg0.getDevice() == device)) {
- if (isTargetClient(arg0)) {
- result = arg0;
- notifyAll();
- }
- }
- }
- }
- }
-
- private static class WaitForClients implements IClientChangeListener {
-
- private IDevice device;
- private long timeout;
-
- public WaitForClients(IDevice device, long timeout) {
- this.device = device;
- this.timeout = timeout;
- }
-
- public Client[] get() {
- synchronized (this) {
- AndroidDebugBridge.addClientChangeListener(this);
-
- if (device.hasClients()) {
- return device.getClients();
- }
-
- try {
- wait(timeout); // Note: doesn't guard for spurious wakeup.
- } catch (InterruptedException exc) {
- }
-
- // We will be woken up when the first client data arrives. Sleep a little longer
- // to give (hopefully all of) the rest of the clients a chance to become available.
- // Note: a loop with timeout is brittle as well and complicated, just accept this
- // for now.
- try {
- Thread.sleep(500);
- } catch (InterruptedException exc) {
- }
- }
-
- AndroidDebugBridge.removeClientChangeListener(this);
-
- return device.getClients();
- }
-
- @Override
- public void clientChanged(Client arg0, int arg1) {
- synchronized (this) {
- if ((arg1 & Client.CHANGE_INFO) != 0 && (arg0.getDevice() == device)) {
- notifyAll();
- }
- }
- }
- }
-}
diff --git a/tools/preload2/src/com/android/preload/DeviceUtils.java b/tools/preload2/src/com/android/preload/DeviceUtils.java
deleted file mode 100644
index 18cab7bee12d..000000000000
--- a/tools/preload2/src/com/android/preload/DeviceUtils.java
+++ /dev/null
@@ -1,420 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.preload;
-
-import com.android.ddmlib.AdbCommandRejectedException;
-import com.android.ddmlib.AndroidDebugBridge;
-import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener;
-import com.android.preload.classdataretrieval.hprof.Hprof;
-import com.android.ddmlib.DdmPreferences;
-import com.android.ddmlib.IDevice;
-import com.android.ddmlib.IShellOutputReceiver;
-import com.android.ddmlib.SyncException;
-import com.android.ddmlib.TimeoutException;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Date;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Helper class for some device routines.
- */
-public class DeviceUtils {
-
- // Locations
- private static final String PRELOADED_CLASSES_FILE = "/etc/preloaded-classes";
- // Shell commands
- private static final String CREATE_EMPTY_PRELOADED_CMD = "touch " + PRELOADED_CLASSES_FILE;
- private static final String DELETE_CACHE_CMD = "rm /data/dalvik-cache/*/*boot.art";
- private static final String DELETE_PRELOADED_CMD = "rm " + PRELOADED_CLASSES_FILE;
- private static final String READ_PRELOADED_CMD = "cat " + PRELOADED_CLASSES_FILE;
- private static final String START_SHELL_CMD = "start";
- private static final String STOP_SHELL_CMD = "stop";
- private static final String REMOUNT_SYSTEM_CMD = "mount -o rw,remount /system";
- private static final String UNSET_BOOTCOMPLETE_CMD = "setprop dev.bootcomplete \"0\"";
-
- public static void init(int debugPort) {
- DdmPreferences.setSelectedDebugPort(debugPort);
-
- Hprof.init();
-
- AndroidDebugBridge.init(true);
-
- AndroidDebugBridge.createBridge();
- }
-
- /**
- * Run a command in the shell on the device.
- */
- public static void doShell(IDevice device, String cmdline, long timeout, TimeUnit unit) {
- doShell(device, cmdline, new NullShellOutputReceiver(), timeout, unit);
- }
-
- /**
- * Run a command in the shell on the device. Collects and returns the console output.
- */
- public static String doShellReturnString(IDevice device, String cmdline, long timeout,
- TimeUnit unit) {
- CollectStringShellOutputReceiver rec = new CollectStringShellOutputReceiver();
- doShell(device, cmdline, rec, timeout, unit);
- return rec.toString();
- }
-
- /**
- * Run a command in the shell on the device, directing all output to the given receiver.
- */
- public static void doShell(IDevice device, String cmdline, IShellOutputReceiver receiver,
- long timeout, TimeUnit unit) {
- try {
- device.executeShellCommand(cmdline, receiver, timeout, unit);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- /**
- * Run am start on the device.
- */
- public static void doAMStart(IDevice device, String name, String activity) {
- doShell(device, "am start -n " + name + " /." + activity, 30, TimeUnit.SECONDS);
- }
-
- /**
- * Find the device with the given serial. Give up after the given timeout (in milliseconds).
- */
- public static IDevice findDevice(String serial, int timeout) {
- WaitForDevice wfd = new WaitForDevice(serial, timeout);
- return wfd.get();
- }
-
- /**
- * Get all devices ddms knows about. Wait at most for the given timeout.
- */
- public static IDevice[] findDevices(int timeout) {
- WaitForDevice wfd = new WaitForDevice(null, timeout);
- wfd.get();
- return AndroidDebugBridge.getBridge().getDevices();
- }
-
- /**
- * Return the build type of the given device. This is the value of the "ro.build.type"
- * system property.
- */
- public static String getBuildType(IDevice device) {
- try {
- Future<String> buildType = device.getSystemProperty("ro.build.type");
- return buildType.get(500, TimeUnit.MILLISECONDS);
- } catch (Exception e) {
- }
- return null;
- }
-
- /**
- * Check whether the given device has a pre-optimized boot image. More precisely, checks
- * whether /system/framework/ * /boot.art exists.
- */
- public static boolean hasPrebuiltBootImage(IDevice device) {
- String ret =
- doShellReturnString(device, "ls /system/framework/*/boot.art", 500, TimeUnit.MILLISECONDS);
-
- return !ret.contains("No such file or directory");
- }
-
- /**
- * Write over the preloaded-classes file with an empty or existing file and regenerate the boot
- * image as necessary.
- *
- * @param device
- * @param pcFile
- * @param bootTimeout
- * @throws AdbCommandRejectedException
- * @throws IOException
- * @throws TimeoutException
- * @throws SyncException
- * @return true if successfully overwritten, false otherwise
- */
- public static boolean overwritePreloaded(IDevice device, File pcFile, long bootTimeout)
- throws AdbCommandRejectedException, IOException, TimeoutException, SyncException {
- boolean writeEmpty = (pcFile == null);
- if (writeEmpty) {
- // Check if the preloaded-classes file is already empty.
- String oldContent =
- doShellReturnString(device, READ_PRELOADED_CMD, 1, TimeUnit.SECONDS);
- if (oldContent.trim().equals("")) {
- System.out.println("Preloaded-classes already empty.");
- return true;
- }
- }
-
- // Stop the system server etc.
- doShell(device, STOP_SHELL_CMD, 1, TimeUnit.SECONDS);
- // Remount the read-only system partition
- doShell(device, REMOUNT_SYSTEM_CMD, 1, TimeUnit.SECONDS);
- // Delete the preloaded-classes file
- doShell(device, DELETE_PRELOADED_CMD, 1, TimeUnit.SECONDS);
- // Delete the dalvik cache files
- doShell(device, DELETE_CACHE_CMD, 1, TimeUnit.SECONDS);
- if (writeEmpty) {
- // Write an empty preloaded-classes file
- doShell(device, CREATE_EMPTY_PRELOADED_CMD, 500, TimeUnit.MILLISECONDS);
- } else {
- // Push the new preloaded-classes file
- device.pushFile(pcFile.getAbsolutePath(), PRELOADED_CLASSES_FILE);
- }
- // Manually reset the boot complete flag
- doShell(device, UNSET_BOOTCOMPLETE_CMD, 1, TimeUnit.SECONDS);
- // Restart system server on the device
- doShell(device, START_SHELL_CMD, 1, TimeUnit.SECONDS);
- // Wait for the boot complete flag and return the outcome.
- return waitForBootComplete(device, bootTimeout);
- }
-
- private static boolean waitForBootComplete(IDevice device, long timeout) {
- // Do a loop checking each second whether bootcomplete. Wait for at most the given
- // threshold.
- Date startDate = new Date();
- for (;;) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- // Ignore spurious wakeup.
- }
- // Check whether bootcomplete.
- String ret =
- doShellReturnString(device, "getprop dev.bootcomplete", 500, TimeUnit.MILLISECONDS);
- if (ret.trim().equals("1")) {
- break;
- }
- System.out.println("Still not booted: " + ret);
-
- // Check whether we timed out. This is a simplistic check that doesn't take into account
- // things like switches in time.
- Date endDate = new Date();
- long seconds =
- TimeUnit.SECONDS.convert(endDate.getTime() - startDate.getTime(), TimeUnit.MILLISECONDS);
- if (seconds > timeout) {
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * Enable method-tracing on device. The system should be restarted after this.
- */
- public static void enableTracing(IDevice device) {
- // Disable selinux.
- doShell(device, "setenforce 0", 100, TimeUnit.MILLISECONDS);
-
- // Make the profile directory world-writable.
- doShell(device, "chmod 777 /data/dalvik-cache/profiles", 100, TimeUnit.MILLISECONDS);
-
- // Enable streaming method tracing with a small 1K buffer.
- doShell(device, "setprop dalvik.vm.method-trace true", 100, TimeUnit.MILLISECONDS);
- doShell(device, "setprop dalvik.vm.method-trace-file "
- + "/data/dalvik-cache/profiles/zygote.trace.bin", 100, TimeUnit.MILLISECONDS);
- doShell(device, "setprop dalvik.vm.method-trace-file-siz 1024", 100, TimeUnit.MILLISECONDS);
- doShell(device, "setprop dalvik.vm.method-trace-stream true", 100, TimeUnit.MILLISECONDS);
- }
-
- private static class NullShellOutputReceiver implements IShellOutputReceiver {
- @Override
- public boolean isCancelled() {
- return false;
- }
-
- @Override
- public void flush() {}
-
- @Override
- public void addOutput(byte[] arg0, int arg1, int arg2) {}
- }
-
- private static class CollectStringShellOutputReceiver implements IShellOutputReceiver {
-
- private StringBuilder builder = new StringBuilder();
-
- @Override
- public String toString() {
- String ret = builder.toString();
- // Strip trailing newlines. They are especially ugly because adb uses DOS line endings.
- while (ret.endsWith("\r") || ret.endsWith("\n")) {
- ret = ret.substring(0, ret.length() - 1);
- }
- return ret;
- }
-
- @Override
- public void addOutput(byte[] arg0, int arg1, int arg2) {
- builder.append(new String(arg0, arg1, arg2));
- }
-
- @Override
- public void flush() {}
-
- @Override
- public boolean isCancelled() {
- return false;
- }
- }
-
- private static class WaitForDevice {
-
- private String serial;
- private long timeout;
- private IDevice device;
-
- public WaitForDevice(String serial, long timeout) {
- this.serial = serial;
- this.timeout = timeout;
- device = null;
- }
-
- public IDevice get() {
- if (device == null) {
- WaitForDeviceListener wfdl = new WaitForDeviceListener(serial);
- synchronized (wfdl) {
- AndroidDebugBridge.addDeviceChangeListener(wfdl);
-
- // Check whether we already know about this device.
- IDevice[] devices = AndroidDebugBridge.getBridge().getDevices();
- if (serial != null) {
- for (IDevice d : devices) {
- if (serial.equals(d.getSerialNumber())) {
- // Only accept if there are clients already. Else wait for the callback informing
- // us that we now have clients.
- if (d.hasClients()) {
- device = d;
- }
-
- break;
- }
- }
- } else {
- if (devices.length > 0) {
- device = devices[0];
- }
- }
-
- if (device == null) {
- try {
- wfdl.wait(timeout);
- } catch (InterruptedException e) {
- // Ignore spurious wakeups.
- }
- device = wfdl.getDevice();
- }
-
- AndroidDebugBridge.removeDeviceChangeListener(wfdl);
- }
- }
-
- if (device != null) {
- // Wait for clients.
- WaitForClientsListener wfcl = new WaitForClientsListener(device);
- synchronized (wfcl) {
- AndroidDebugBridge.addDeviceChangeListener(wfcl);
-
- if (!device.hasClients()) {
- try {
- wfcl.wait(timeout);
- } catch (InterruptedException e) {
- // Ignore spurious wakeups.
- }
- }
-
- AndroidDebugBridge.removeDeviceChangeListener(wfcl);
- }
- }
-
- return device;
- }
-
- private static class WaitForDeviceListener implements IDeviceChangeListener {
-
- private String serial;
- private IDevice device;
-
- public WaitForDeviceListener(String serial) {
- this.serial = serial;
- }
-
- public IDevice getDevice() {
- return device;
- }
-
- @Override
- public void deviceChanged(IDevice arg0, int arg1) {
- // We may get a device changed instead of connected. Handle like a connection.
- deviceConnected(arg0);
- }
-
- @Override
- public void deviceConnected(IDevice arg0) {
- if (device != null) {
- // Ignore updates.
- return;
- }
-
- if (serial == null || serial.equals(arg0.getSerialNumber())) {
- device = arg0;
- synchronized (this) {
- notifyAll();
- }
- }
- }
-
- @Override
- public void deviceDisconnected(IDevice arg0) {
- // Ignore disconnects.
- }
-
- }
-
- private static class WaitForClientsListener implements IDeviceChangeListener {
-
- private IDevice myDevice;
-
- public WaitForClientsListener(IDevice myDevice) {
- this.myDevice = myDevice;
- }
-
- @Override
- public void deviceChanged(IDevice arg0, int arg1) {
- if (arg0 == myDevice && (arg1 & IDevice.CHANGE_CLIENT_LIST) != 0) {
- // Got a client list, done here.
- synchronized (this) {
- notifyAll();
- }
- }
- }
-
- @Override
- public void deviceConnected(IDevice arg0) {
- }
-
- @Override
- public void deviceDisconnected(IDevice arg0) {
- }
-
- }
- }
-
-}
diff --git a/tools/preload2/src/com/android/preload/DumpData.java b/tools/preload2/src/com/android/preload/DumpData.java
deleted file mode 100644
index d99722416a1d..000000000000
--- a/tools/preload2/src/com/android/preload/DumpData.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.preload;
-
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Holds the collected data for a process.
- */
-public class DumpData {
- /**
- * Name of the package (=application).
- */
- String packageName;
-
- /**
- * A map of class name to a string for the classloader. This may be a toString equivalent,
- * or just a unique ID.
- */
- Map<String, String> dumpData;
-
- /**
- * The Date when this data was captured. Mostly for display purposes.
- */
- Date date;
-
- /**
- * A cached value for the number of boot classpath classes (classloader value in dumpData is
- * null).
- */
- int bcpClasses;
-
- public DumpData(String packageName, Map<String, String> dumpData, Date date) {
- this.packageName = packageName;
- this.dumpData = dumpData;
- this.date = date;
-
- countBootClassPath();
- }
-
- public String getPackageName() {
- return packageName;
- }
-
- public Date getDate() {
- return date;
- }
-
- public Map<String, String> getDumpData() {
- return dumpData;
- }
-
- public void countBootClassPath() {
- bcpClasses = 0;
- for (Map.Entry<String, String> e : dumpData.entrySet()) {
- if (e.getValue() == null) {
- bcpClasses++;
- }
- }
- }
-
- // Return an inverted mapping.
- public Map<String, Set<String>> invertData() {
- Map<String, Set<String>> ret = new HashMap<>();
- for (Map.Entry<String, String> e : dumpData.entrySet()) {
- if (!ret.containsKey(e.getValue())) {
- ret.put(e.getValue(), new HashSet<String>());
- }
- ret.get(e.getValue()).add(e.getKey());
- }
- return ret;
- }
-} \ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/DumpDataIO.java b/tools/preload2/src/com/android/preload/DumpDataIO.java
deleted file mode 100644
index 28625c5531e4..000000000000
--- a/tools/preload2/src/com/android/preload/DumpDataIO.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.preload;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-import org.xml.sax.XMLReader;
-import org.xml.sax.helpers.DefaultHandler;
-
-import java.io.File;
-import java.io.FileReader;
-import java.text.DateFormat;
-import java.util.Collection;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.Map;
-
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-
-/**
- * Helper class for serialization and deserialization of a collection of DumpData objects to XML.
- */
-public class DumpDataIO {
-
- /**
- * Serialize the given collection to an XML document. Returns the produced string.
- */
- public static String serialize(Collection<DumpData> data) {
- // We'll do this by hand, constructing a DOM or similar is too complicated for our simple
- // use case.
-
- StringBuilder sb = new StringBuilder();
- sb.append("<preloaded-classes-data>\n");
-
- for (DumpData d : data) {
- serialize(d, sb);
- }
-
- sb.append("</preloaded-classes-data>\n");
- return sb.toString();
- }
-
- private static void serialize(DumpData d, StringBuilder sb) {
- sb.append("<data package=\"" + d.packageName + "\" date=\"" +
- DateFormat.getDateTimeInstance().format(d.date) +"\">\n");
-
- for (Map.Entry<String, String> e : d.dumpData.entrySet()) {
- sb.append("<class name=\"" + e.getKey() + "\" classloader=\"" + e.getValue() + "\"/>\n");
- }
-
- sb.append("</data>\n");
- }
-
- /**
- * Load a collection of DumpData objects from the given file.
- */
- public static Collection<DumpData> deserialize(File f) throws Exception {
- // Use SAX parsing. Our format is very simple. Don't do any schema validation or such.
-
- SAXParserFactory spf = SAXParserFactory.newInstance();
- spf.setNamespaceAware(false);
- SAXParser saxParser = spf.newSAXParser();
-
- XMLReader xmlReader = saxParser.getXMLReader();
- DumpDataContentHandler ddch = new DumpDataContentHandler();
- xmlReader.setContentHandler(ddch);
- xmlReader.parse(new InputSource(new FileReader(f)));
-
- return ddch.data;
- }
-
- private static class DumpDataContentHandler extends DefaultHandler {
- Collection<DumpData> data = new LinkedList<DumpData>();
- DumpData openData = null;
-
- @Override
- public void startElement(String uri, String localName, String qName, Attributes attributes)
- throws SAXException {
- if (qName.equals("data")) {
- if (openData != null) {
- throw new IllegalStateException();
- }
- String pkg = attributes.getValue("package");
- String dateString = attributes.getValue("date");
-
- if (pkg == null || dateString == null) {
- throw new IllegalArgumentException();
- }
-
- try {
- Date date = DateFormat.getDateTimeInstance().parse(dateString);
- openData = new DumpData(pkg, new HashMap<String, String>(), date);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- } else if (qName.equals("class")) {
- if (openData == null) {
- throw new IllegalStateException();
- }
- String className = attributes.getValue("name");
- String classLoader = attributes.getValue("classloader");
-
- if (className == null || classLoader == null) {
- throw new IllegalArgumentException();
- }
-
- openData.dumpData.put(className, classLoader.equals("null") ? null : classLoader);
- }
- }
-
- @Override
- public void endElement(String uri, String localName, String qName) throws SAXException {
- if (qName.equals("data")) {
- if (openData == null) {
- throw new IllegalStateException();
- }
- openData.countBootClassPath();
-
- data.add(openData);
- openData = null;
- }
- }
- }
-}
diff --git a/tools/preload2/src/com/android/preload/DumpTableModel.java b/tools/preload2/src/com/android/preload/DumpTableModel.java
deleted file mode 100644
index d97cbf0df5e5..000000000000
--- a/tools/preload2/src/com/android/preload/DumpTableModel.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.preload;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.swing.table.AbstractTableModel;
-
-/**
- * A table model for collected DumpData. This is both the internal storage as well as the model
- * for display.
- */
-public class DumpTableModel extends AbstractTableModel {
-
- private List<DumpData> data = new ArrayList<DumpData>();
-
- public void addData(DumpData d) {
- data.add(d);
- fireTableRowsInserted(data.size() - 1, data.size() - 1);
- }
-
- public void clear() {
- int size = data.size();
- if (size > 0) {
- data.clear();
- fireTableRowsDeleted(0, size - 1);
- }
- }
-
- public List<DumpData> getData() {
- return data;
- }
-
- @Override
- public int getRowCount() {
- return data.size();
- }
-
- @Override
- public int getColumnCount() {
- return 4;
- }
-
- @Override
- public String getColumnName(int column) {
- switch (column) {
- case 0:
- return "Package";
- case 1:
- return "Date";
- case 2:
- return "# All Classes";
- case 3:
- return "# Boot Classpath Classes";
-
- default:
- throw new IndexOutOfBoundsException(String.valueOf(column));
- }
- }
-
- @Override
- public Object getValueAt(int rowIndex, int columnIndex) {
- DumpData d = data.get(rowIndex);
- switch (columnIndex) {
- case 0:
- return d.packageName;
- case 1:
- return d.date;
- case 2:
- return d.dumpData.size();
- case 3:
- return d.bcpClasses;
-
- default:
- throw new IndexOutOfBoundsException(String.valueOf(columnIndex));
- }
- }
-} \ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/Main.java b/tools/preload2/src/com/android/preload/Main.java
deleted file mode 100644
index 2265e9557c4b..000000000000
--- a/tools/preload2/src/com/android/preload/Main.java
+++ /dev/null
@@ -1,341 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.preload;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.IDevice;
-import com.android.preload.actions.ClearTableAction;
-import com.android.preload.actions.ComputeThresholdAction;
-import com.android.preload.actions.ComputeThresholdXAction;
-import com.android.preload.actions.DeviceSpecific;
-import com.android.preload.actions.ExportAction;
-import com.android.preload.actions.ImportAction;
-import com.android.preload.actions.ReloadListAction;
-import com.android.preload.actions.RunMonkeyAction;
-import com.android.preload.actions.ScanAllPackagesAction;
-import com.android.preload.actions.ScanPackageAction;
-import com.android.preload.actions.ShowDataAction;
-import com.android.preload.actions.WritePreloadedClassesAction;
-import com.android.preload.classdataretrieval.ClassDataRetriever;
-import com.android.preload.classdataretrieval.hprof.Hprof;
-import com.android.preload.classdataretrieval.jdwp.JDWPClassDataRetriever;
-import com.android.preload.ui.IUI;
-import com.android.preload.ui.SequenceUI;
-import com.android.preload.ui.SwingUI;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
-
-import javax.swing.Action;
-import javax.swing.DefaultListModel;
-
-public class Main {
-
- /**
- * Enable tracing mode. This is a work-in-progress to derive compiled-methods data, so it is
- * off for now.
- */
- public final static boolean ENABLE_TRACING = false;
-
- /**
- * Ten-second timeout.
- */
- public final static int DEFAULT_TIMEOUT_MILLIS = 10 * 1000;
-
- /**
- * Hprof timeout. Two minutes.
- */
- public final static int HPROF_TIMEOUT_MILLIS = 120 * 1000;
-
- private IDevice device;
- private static ClientUtils clientUtils;
-
- private DumpTableModel dataTableModel;
- private DefaultListModel<Client> clientListModel;
-
- private IUI ui;
-
- // Actions that need to be updated once a device is selected.
- private Collection<DeviceSpecific> deviceSpecificActions;
-
- // Current main instance.
- private static Main top;
- private static boolean useJdwpClassDataRetriever = false;
-
- public final static String CLASS_PRELOAD_BLACKLIST = "android.app.AlarmManager$" + "|"
- + "android.app.SearchManager$" + "|" + "android.os.FileObserver$" + "|"
- + "com.android.server.PackageManagerService\\$AppDirObserver$" + "|" +
-
-
- // Threads
- "android.os.AsyncTask$" + "|" + "android.pim.ContactsAsyncHelper$" + "|"
- + "android.webkit.WebViewClassic\\$1$" + "|" + "java.lang.ProcessManager$" + "|"
- + "(.*\\$NoPreloadHolder$)";
-
- public final static String SCAN_ALL_CMD = "scan-all";
- public final static String SCAN_PACKAGE_CMD = "scan";
- public final static String COMPUTE_FILE_CMD = "comp";
- public final static String EXPORT_CMD = "export";
- public final static String IMPORT_CMD = "import";
- public final static String WRITE_CMD = "write";
-
- /**
- * @param args
- */
- public static void main(String[] args) {
- Main m;
- if (args.length > 0 && args[0].equals("--seq")) {
- m = createSequencedMain(args);
- } else {
- m = new Main(new SwingUI());
- }
-
- top = m;
- m.startUp();
- }
-
- public Main(IUI ui) {
- this.ui = ui;
-
- clientListModel = new DefaultListModel<Client>();
- dataTableModel = new DumpTableModel();
-
- clientUtils = new ClientUtils(DEFAULT_TIMEOUT_MILLIS); // Client utils with 10s timeout.
-
- List<Action> actions = new ArrayList<Action>();
- actions.add(new ReloadListAction(clientUtils, null, clientListModel));
- actions.add(new ClearTableAction(dataTableModel));
- actions.add(new RunMonkeyAction(null, dataTableModel));
- actions.add(new ScanPackageAction(clientUtils, null, dataTableModel));
- actions.add(new ScanAllPackagesAction(clientUtils, null, dataTableModel));
- actions.add(new ComputeThresholdAction("Compute preloaded-classes", dataTableModel, 2,
- CLASS_PRELOAD_BLACKLIST));
- actions.add(new ComputeThresholdAction("Compute compiled-classes", dataTableModel, 1,
- null));
- actions.add(new ComputeThresholdXAction("Compute(X)", dataTableModel,
- CLASS_PRELOAD_BLACKLIST));
- actions.add(new WritePreloadedClassesAction(clientUtils, null, dataTableModel));
- actions.add(new ShowDataAction(dataTableModel));
- actions.add(new ImportAction(dataTableModel));
- actions.add(new ExportAction(dataTableModel));
-
- deviceSpecificActions = new ArrayList<DeviceSpecific>();
- for (Action a : actions) {
- if (a instanceof DeviceSpecific) {
- deviceSpecificActions.add((DeviceSpecific)a);
- }
- }
-
- ui.prepare(clientListModel, dataTableModel, actions);
- }
-
- /**
- * @param args
- * @return
- */
- private static Main createSequencedMain(String[] args) {
- SequenceUI ui = new SequenceUI();
- Main main = new Main(ui);
-
- Iterator<String> it = Arrays.asList(args).iterator();
- it.next(); // --seq
- // Setup
- ui.choice("#" + it.next()); // Device.
- ui.confirmNo(); // Prepare: no.
- // Actions
- try {
- while (it.hasNext()) {
- String op = it.next();
- // Operation: Scan a single package
- if (SCAN_PACKAGE_CMD.equals(op)) {
- System.out.println("Scanning package.");
- ui.action(ScanPackageAction.class);
- ui.client(it.next());
- // Operation: Scan all packages
- } else if (SCAN_ALL_CMD.equals(op)) {
- System.out.println("Scanning all packages.");
- ui.action(ScanAllPackagesAction.class);
- // Operation: Export the output to a file
- } else if (EXPORT_CMD.equals(op)) {
- System.out.println("Exporting data.");
- ui.action(ExportAction.class);
- ui.output(new File(it.next()));
- // Operation: Import the input from a file or directory
- } else if (IMPORT_CMD.equals(op)) {
- System.out.println("Importing data.");
- File file = new File(it.next());
- if (!file.exists()) {
- throw new RuntimeException(
- String.format("File does not exist, %s.", file.getAbsolutePath()));
- } else if (file.isFile()) {
- ui.action(ImportAction.class);
- ui.input(file);
- } else if (file.isDirectory()) {
- for (File content : file.listFiles()) {
- ui.action(ImportAction.class);
- ui.input(content);
- }
- }
- // Operation: Compute preloaded classes with specific threshold
- } else if (COMPUTE_FILE_CMD.equals(op)) {
- System.out.println("Compute preloaded classes.");
- ui.action(ComputeThresholdXAction.class);
- ui.input(it.next());
- ui.confirmYes();
- ui.output(new File(it.next()));
- // Operation: Write preloaded classes from a specific file
- } else if (WRITE_CMD.equals(op)) {
- System.out.println("Writing preloaded classes.");
- ui.action(WritePreloadedClassesAction.class);
- ui.input(new File(it.next()));
- }
- }
- } catch (NoSuchElementException e) {
- System.out.println("Failed to parse action sequence correctly.");
- throw e;
- }
-
- return main;
- }
-
- public static IUI getUI() {
- return top.ui;
- }
-
- public static ClassDataRetriever getClassDataRetriever() {
- if (useJdwpClassDataRetriever) {
- return new JDWPClassDataRetriever();
- } else {
- return new Hprof(HPROF_TIMEOUT_MILLIS);
- }
- }
-
- public IDevice getDevice() {
- return device;
- }
-
- public void setDevice(IDevice device) {
- this.device = device;
- for (DeviceSpecific ds : deviceSpecificActions) {
- ds.setDevice(device);
- }
- }
-
- public DefaultListModel<Client> getClientListModel() {
- return clientListModel;
- }
-
- static class DeviceWrapper {
- IDevice device;
-
- public DeviceWrapper(IDevice d) {
- device = d;
- }
-
- @Override
- public String toString() {
- return device.getName() + " (#" + device.getSerialNumber() + ")";
- }
- }
-
- private void startUp() {
- getUI().showWaitDialog();
- initDevice();
-
- // Load clients.
- new ReloadListAction(clientUtils, getDevice(), clientListModel).run();
-
- getUI().hideWaitDialog();
- getUI().ready();
- }
-
- private void initDevice() {
- DeviceUtils.init(DEFAULT_TIMEOUT_MILLIS);
-
- IDevice devices[] = DeviceUtils.findDevices(DEFAULT_TIMEOUT_MILLIS);
- if (devices == null || devices.length == 0) {
- throw new RuntimeException("Could not find any devices...");
- }
-
- getUI().hideWaitDialog();
-
- DeviceWrapper deviceWrappers[] = new DeviceWrapper[devices.length];
- for (int i = 0; i < devices.length; i++) {
- deviceWrappers[i] = new DeviceWrapper(devices[i]);
- }
-
- DeviceWrapper ret = Main.getUI().showChoiceDialog("Choose a device", "Choose device",
- deviceWrappers);
- if (ret != null) {
- setDevice(ret.device);
- } else {
- System.exit(0);
- }
-
- boolean prepare = Main.getUI().showConfirmDialog("Prepare device?",
- "Do you want to prepare the device? This is highly recommended.");
- if (prepare) {
- String buildType = DeviceUtils.getBuildType(device);
- if (buildType == null || (!buildType.equals("userdebug") && !buildType.equals("eng"))) {
- Main.getUI().showMessageDialog("Need a userdebug or eng build! (Found " + buildType
- + ")");
- return;
- }
- if (DeviceUtils.hasPrebuiltBootImage(device)) {
- Main.getUI().showMessageDialog("Cannot prepare a device with pre-optimized boot "
- + "image!");
- return;
- }
-
- if (ENABLE_TRACING) {
- DeviceUtils.enableTracing(device);
- }
-
- Main.getUI().showMessageDialog("The device will reboot. This will potentially take a "
- + "long time. Please be patient.");
- boolean success = false;
- try {
- success = DeviceUtils.overwritePreloaded(device, null, 15 * 60);
- } catch (Exception e) {
- System.err.println(e);
- } finally {
- if (!success) {
- Main.getUI().showMessageDialog(
- "Removing preloaded-classes failed unexpectedly!");
- }
- }
- }
- }
-
- public static Map<String, String> findAndGetClassData(IDevice device, String packageName)
- throws Exception {
- Client client = clientUtils.findClient(device, packageName, -1);
- if (client == null) {
- throw new RuntimeException("Could not find client...");
- }
- System.out.println("Found client: " + client);
-
- return getClassDataRetriever().getClassData(client);
- }
-
-}
diff --git a/tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java b/tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java
deleted file mode 100644
index 5787d8507230..000000000000
--- a/tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.preload.actions;
-
-import com.android.preload.Main;
-import java.awt.event.ActionEvent;
-
-import javax.swing.AbstractAction;
-
-public abstract class AbstractThreadedAction extends AbstractAction implements Runnable {
-
- protected AbstractThreadedAction(String title) {
- super(title);
- }
-
- @Override
- public void actionPerformed(ActionEvent e) {
- if (Main.getUI().isSingleThreaded()) {
- run();
- } else {
- new Thread(this).start();
- }
- }
-
-}
diff --git a/tools/preload2/src/com/android/preload/actions/AbstractThreadedDeviceSpecificAction.java b/tools/preload2/src/com/android/preload/actions/AbstractThreadedDeviceSpecificAction.java
deleted file mode 100644
index 7906417b7a8d..000000000000
--- a/tools/preload2/src/com/android/preload/actions/AbstractThreadedDeviceSpecificAction.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.preload.actions;
-
-import com.android.ddmlib.IDevice;
-
-import java.awt.event.ActionEvent;
-
-public abstract class AbstractThreadedDeviceSpecificAction extends AbstractThreadedAction
- implements DeviceSpecific {
-
- protected IDevice device;
-
- protected AbstractThreadedDeviceSpecificAction(String title, IDevice device) {
- super(title);
- this.device = device;
- }
-
- @Override
- public void setDevice(IDevice device) {
- this.device = device;
- }
-
- @Override
- public void actionPerformed(ActionEvent e) {
- if (device == null) {
- return;
- }
- super.actionPerformed(e);
- }
-}
diff --git a/tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java b/tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java
deleted file mode 100644
index 3a7f7f74d755..000000000000
--- a/tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.preload.actions;
-
-import com.android.preload.DumpData;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-import java.awt.event.ActionEvent;
-import java.io.File;
-import java.io.PrintWriter;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.regex.Pattern;
-
-import javax.swing.AbstractAction;
-
-/**
- * Compute an intersection of classes from the given data. A class is in the intersection if it
- * appears in at least the number of threshold given packages. An optional blacklist can be
- * used to filter classes from the intersection.
- */
-public class ComputeThresholdAction extends AbstractThreadedAction {
- protected int threshold;
- private Pattern blacklist;
- private DumpTableModel dataTableModel;
-
- /**
- * Create an action with the given parameters. The blacklist is a regular expression
- * that filters classes.
- */
- public ComputeThresholdAction(String name, DumpTableModel dataTableModel, int threshold,
- String blacklist) {
- super(name);
- this.dataTableModel = dataTableModel;
- this.threshold = threshold;
- if (blacklist != null) {
- this.blacklist = Pattern.compile(blacklist);
- }
- }
-
- @Override
- public void actionPerformed(ActionEvent e) {
- List<DumpData> data = dataTableModel.getData();
- if (data.size() == 0) {
- Main.getUI().showMessageDialog("No data available, please scan packages or run "
- + "monkeys.");
- return;
- }
- if (data.size() == 1) {
- Main.getUI().showMessageDialog("Cannot compute list from only one data set, please "
- + "scan packages or run monkeys.");
- return;
- }
-
- super.actionPerformed(e);
- }
-
- @Override
- public void run() {
- Main.getUI().showWaitDialog();
-
- Map<String, Set<String>> uses = new HashMap<String, Set<String>>();
- for (DumpData d : dataTableModel.getData()) {
- Main.getUI().updateWaitDialog("Merging " + d.getPackageName());
- updateClassUse(d.getPackageName(), uses, getBootClassPathClasses(d.getDumpData()));
- }
-
- Main.getUI().updateWaitDialog("Computing thresholded set");
- Set<String> result = fromThreshold(uses, blacklist, threshold);
- Main.getUI().hideWaitDialog();
-
- boolean ret = Main.getUI().showConfirmDialog("Computed a set with " + result.size()
- + " classes, would you like to save to disk?", "Save?");
- if (ret) {
- File f = Main.getUI().showSaveDialog();
- if (f != null) {
- saveSet(result, f);
- }
- }
- }
-
- private Set<String> fromThreshold(Map<String, Set<String>> classUses, Pattern blacklist,
- int threshold) {
- TreeSet<String> ret = new TreeSet<>(); // TreeSet so it's nicely ordered by name.
-
- for (Map.Entry<String, Set<String>> e : classUses.entrySet()) {
- if (e.getValue().size() >= threshold) {
- if (blacklist == null || !blacklist.matcher(e.getKey()).matches()) {
- ret.add(e.getKey());
- }
- }
- }
-
- return ret;
- }
-
- private static void updateClassUse(String pkg, Map<String, Set<String>> classUses,
- Set<String> classes) {
- for (String className : classes) {
- Set<String> old = classUses.get(className);
- if (old == null) {
- classUses.put(className, new HashSet<String>());
- }
- classUses.get(className).add(pkg);
- }
- }
-
- private static Set<String> getBootClassPathClasses(Map<String, String> source) {
- Set<String> ret = new HashSet<>();
- for (Map.Entry<String, String> e : source.entrySet()) {
- if (e.getValue() == null) {
- ret.add(e.getKey());
- }
- }
- return ret;
- }
-
- private static void saveSet(Set<String> result, File f) {
- try {
- PrintWriter out = new PrintWriter(f);
- for (String s : result) {
- out.println(s);
- }
- out.close();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-} \ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ComputeThresholdXAction.java b/tools/preload2/src/com/android/preload/actions/ComputeThresholdXAction.java
deleted file mode 100644
index 3ec0a4c18db1..000000000000
--- a/tools/preload2/src/com/android/preload/actions/ComputeThresholdXAction.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.preload.actions;
-
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-public class ComputeThresholdXAction extends ComputeThresholdAction {
-
- public ComputeThresholdXAction(String name, DumpTableModel dataTableModel,
- String blacklist) {
- super(name, dataTableModel, 1, blacklist);
- }
-
- @Override
- public void run() {
- String value = Main.getUI().showInputDialog("Threshold?");
-
- if (value != null) {
- try {
- threshold = Integer.parseInt(value);
- super.run();
- } catch (Exception exc) {
- }
- }
- }
-} \ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/DeviceSpecific.java b/tools/preload2/src/com/android/preload/actions/DeviceSpecific.java
deleted file mode 100644
index 35a8f26a99fe..000000000000
--- a/tools/preload2/src/com/android/preload/actions/DeviceSpecific.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.preload.actions;
-
-import com.android.ddmlib.IDevice;
-
-/**
- * Marks an action as being device-specific. The user must set the device through the specified
- * method if the device selection changes.
- *
- * Implementors must tolerate a null device (for example, with a no-op). This includes calling
- * any methods before setDevice has been called.
- */
-public interface DeviceSpecific {
-
- /**
- * Set the device that should be used. Note that there is no restriction on calling other
- * methods of the implementor before a setDevice call. Neither is device guaranteed to be
- * non-null.
- *
- * @param device The device to use going forward.
- */
- public void setDevice(IDevice device);
-}
diff --git a/tools/preload2/src/com/android/preload/actions/ExportAction.java b/tools/preload2/src/com/android/preload/actions/ExportAction.java
deleted file mode 100644
index 848a56826788..000000000000
--- a/tools/preload2/src/com/android/preload/actions/ExportAction.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.preload.actions;
-
-import com.android.preload.DumpDataIO;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-import java.awt.event.ActionEvent;
-import java.io.File;
-import java.io.PrintWriter;
-
-public class ExportAction extends AbstractThreadedAction {
- private File lastSaveFile;
- private DumpTableModel dataTableModel;
-
- public ExportAction(DumpTableModel dataTableModel) {
- super("Export data");
- this.dataTableModel = dataTableModel;
- }
-
- @Override
- public void actionPerformed(ActionEvent e) {
- lastSaveFile = Main.getUI().showSaveDialog();
- if (lastSaveFile != null) {
- super.actionPerformed(e);
- }
- }
-
- @Override
- public void run() {
- Main.getUI().showWaitDialog();
-
- String serialized = DumpDataIO.serialize(dataTableModel.getData());
-
- if (serialized != null) {
- try {
- PrintWriter out = new PrintWriter(lastSaveFile);
- out.println(serialized);
- out.close();
-
- Main.getUI().hideWaitDialog();
- } catch (Exception e) {
- Main.getUI().hideWaitDialog();
- Main.getUI().showMessageDialog("Failed writing: " + e.getMessage());
- }
- }
- }
-} \ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ImportAction.java b/tools/preload2/src/com/android/preload/actions/ImportAction.java
deleted file mode 100644
index bfeeb836fd45..000000000000
--- a/tools/preload2/src/com/android/preload/actions/ImportAction.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.preload.actions;
-
-import com.android.preload.DumpData;
-import com.android.preload.DumpDataIO;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-import java.awt.event.ActionEvent;
-import java.io.File;
-import java.util.Collection;
-
-import javax.swing.AbstractAction;
-
-public class ImportAction extends AbstractThreadedAction {
- private File[] lastOpenFiles;
- private DumpTableModel dataTableModel;
-
- public ImportAction(DumpTableModel dataTableModel) {
- super("Import data");
- this.dataTableModel = dataTableModel;
- }
-
- @Override
- public void actionPerformed(ActionEvent e) {
- lastOpenFiles = Main.getUI().showOpenDialog(true);
- if (lastOpenFiles != null) {
- super.actionPerformed(e);
- }
- }
-
- @Override
- public void run() {
- Main.getUI().showWaitDialog();
-
- try {
- for (File f : lastOpenFiles) {
- try {
- Collection<DumpData> data = DumpDataIO.deserialize(f);
-
- for (DumpData d : data) {
- dataTableModel.addData(d);
- }
- } catch (Exception e) {
- Main.getUI().showMessageDialog("Failed reading: " + e.getMessage());
- }
- }
- } finally {
- Main.getUI().hideWaitDialog();
- }
-
- }
-} \ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ReloadListAction.java b/tools/preload2/src/com/android/preload/actions/ReloadListAction.java
deleted file mode 100644
index 29f055700dfa..000000000000
--- a/tools/preload2/src/com/android/preload/actions/ReloadListAction.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.preload.actions;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.IDevice;
-import com.android.preload.ClientUtils;
-
-import java.util.Arrays;
-import java.util.Comparator;
-
-import javax.swing.DefaultListModel;
-
-public class ReloadListAction extends AbstractThreadedDeviceSpecificAction {
-
- private ClientUtils clientUtils;
- private final DefaultListModel<Client> clientListModel;
-
- public ReloadListAction(ClientUtils utils, IDevice device,
- DefaultListModel<Client> clientListModel) {
- super("Reload", device);
- this.clientUtils = utils;
- this.clientListModel = clientListModel;
- }
-
- @Override
- public void run() {
- Client[] clients = clientUtils.findAllClients(device);
- if (clients != null) {
- Arrays.sort(clients, new ClientComparator());
- }
- clientListModel.removeAllElements();
- for (Client c : clients) {
- clientListModel.addElement(c);
- }
- }
-
- private static class ClientComparator implements Comparator<Client> {
-
- @Override
- public int compare(Client o1, Client o2) {
- String s1 = o1.getClientData().getClientDescription();
- String s2 = o2.getClientData().getClientDescription();
-
- if (s1 == null || s2 == null) {
- // Not good, didn't get all data?
- return (s1 == null) ? -1 : 1;
- }
-
- return s1.compareTo(s2);
- }
-
- }
-} \ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java b/tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java
deleted file mode 100644
index 29464fc7abdf..000000000000
--- a/tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.preload.actions;
-
-import com.android.ddmlib.IDevice;
-import com.android.preload.DeviceUtils;
-import com.android.preload.DumpData;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-import java.awt.event.ActionEvent;
-import java.util.Date;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-import javax.swing.AbstractAction;
-
-public class RunMonkeyAction extends AbstractAction implements DeviceSpecific {
-
- private final static String DEFAULT_MONKEY_PACKAGES =
- "com.android.calendar,com.android.gallery3d";
-
- private IDevice device;
- private DumpTableModel dataTableModel;
-
- public RunMonkeyAction(IDevice device, DumpTableModel dataTableModel) {
- super("Run monkey");
- this.device = device;
- this.dataTableModel = dataTableModel;
- }
-
- @Override
- public void setDevice(IDevice device) {
- this.device = device;
- }
-
- @Override
- public void actionPerformed(ActionEvent e) {
- String packages = Main.getUI().showInputDialog("Please enter packages name to run with"
- + " the monkey, or leave empty for default.");
- if (packages == null) {
- return;
- }
- if (packages.isEmpty()) {
- packages = DEFAULT_MONKEY_PACKAGES;
- }
- Runnable r = new RunMonkeyRunnable(packages);
- if (Main.getUI().isSingleThreaded()) {
- r.run();
- } else {
- new Thread(r).start();
- }
- }
-
- private class RunMonkeyRunnable implements Runnable {
-
- private String packages;
- private final static int ITERATIONS = 1000;
-
- public RunMonkeyRunnable(String packages) {
- this.packages = packages;
- }
-
- @Override
- public void run() {
- Main.getUI().showWaitDialog();
-
- try {
- String pkgs[] = packages.split(",");
-
- for (String pkg : pkgs) {
- Main.getUI().updateWaitDialog("Running monkey on " + pkg);
-
- try {
- // Stop running app.
- forceStop(pkg);
-
- // Little bit of breather here.
- try {
- Thread.sleep(1000);
- } catch (Exception e) {
- }
-
- DeviceUtils.doShell(device, "monkey -p " + pkg + " " + ITERATIONS, 1,
- TimeUnit.MINUTES);
-
- Main.getUI().updateWaitDialog("Retrieving heap data for " + pkg);
- Map<String, String> data = Main.findAndGetClassData(device, pkg);
- DumpData dumpData = new DumpData(pkg, data, new Date());
- dataTableModel.addData(dumpData);
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- // Stop running app.
- forceStop(pkg);
- }
- }
- } finally {
- Main.getUI().hideWaitDialog();
- }
- }
-
- private void forceStop(String packageName) {
- // Stop running app.
- DeviceUtils.doShell(device, "force-stop " + packageName, 5, TimeUnit.SECONDS);
- DeviceUtils.doShell(device, "kill " + packageName, 5, TimeUnit.SECONDS);
- DeviceUtils.doShell(device, "kill `pid " + packageName + "`", 5, TimeUnit.SECONDS);
- }
- }
-} \ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ScanAllPackagesAction.java b/tools/preload2/src/com/android/preload/actions/ScanAllPackagesAction.java
deleted file mode 100644
index d74b8a3f6cfb..000000000000
--- a/tools/preload2/src/com/android/preload/actions/ScanAllPackagesAction.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.preload.actions;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.IDevice;
-import com.android.preload.ClientUtils;
-import com.android.preload.DumpData;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-import java.util.Date;
-import java.util.Map;
-
-public class ScanAllPackagesAction extends AbstractThreadedDeviceSpecificAction {
-
- private ClientUtils clientUtils;
- private DumpTableModel dataTableModel;
-
- public ScanAllPackagesAction(ClientUtils utils, IDevice device, DumpTableModel dataTableModel) {
- super("Scan all packages", device);
- this.clientUtils = utils;
- this.dataTableModel = dataTableModel;
- }
-
- @Override
- public void run() {
- Main.getUI().showWaitDialog();
-
- try {
- Client[] clients = clientUtils.findAllClients(device);
- for (Client c : clients) {
- String pkg = c.getClientData().getClientDescription();
- Main.getUI().showWaitDialog();
- Main.getUI().updateWaitDialog("Retrieving heap data for " + pkg);
-
- try {
- Map<String, String> data = Main.getClassDataRetriever().getClassData(c);
- DumpData dumpData = new DumpData(pkg, data, new Date());
- dataTableModel.addData(dumpData);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- } finally {
- Main.getUI().hideWaitDialog();
- }
- }
-} \ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ScanPackageAction.java b/tools/preload2/src/com/android/preload/actions/ScanPackageAction.java
deleted file mode 100644
index 98492bd951bf..000000000000
--- a/tools/preload2/src/com/android/preload/actions/ScanPackageAction.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.preload.actions;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.IDevice;
-import com.android.preload.ClientUtils;
-import com.android.preload.DumpData;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-import java.util.Date;
-import java.util.Map;
-
-public class ScanPackageAction extends AbstractThreadedDeviceSpecificAction {
-
- private ClientUtils clientUtils;
- private DumpTableModel dataTableModel;
-
- public ScanPackageAction(ClientUtils utils, IDevice device, DumpTableModel dataTableModel) {
- super("Scan package", device);
- this.clientUtils = utils;
- this.dataTableModel = dataTableModel;
- }
-
- @Override
- public void run() {
- Main.getUI().showWaitDialog();
-
- try {
- Client client = Main.getUI().getSelectedClient();
- if (client != null) {
- work(client);
- } else {
- Client[] clients = clientUtils.findAllClients(device);
- if (clients.length > 0) {
- ClientWrapper[] clientWrappers = new ClientWrapper[clients.length];
- for (int i = 0; i < clientWrappers.length; i++) {
- clientWrappers[i] = new ClientWrapper(clients[i]);
- }
- Main.getUI().hideWaitDialog();
-
- ClientWrapper ret = Main.getUI().showChoiceDialog("Choose a package to scan",
- "Choose package",
- clientWrappers);
- if (ret != null) {
- work(ret.client);
- }
- }
- }
- } finally {
- Main.getUI().hideWaitDialog();
- }
- }
-
- private void work(Client c) {
- String pkg = c.getClientData().getClientDescription();
- Main.getUI().showWaitDialog();
- Main.getUI().updateWaitDialog("Retrieving heap data for " + pkg);
-
- try {
- Map<String, String> data = Main.findAndGetClassData(device, pkg);
- DumpData dumpData = new DumpData(pkg, data, new Date());
- dataTableModel.addData(dumpData);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- private static class ClientWrapper {
- private Client client;
-
- public ClientWrapper(Client c) {
- client = c;
- }
-
- @Override
- public String toString() {
- return client.getClientData().getClientDescription() + " (pid "
- + client.getClientData().getPid() + ")";
- }
- }
-} \ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ShowDataAction.java b/tools/preload2/src/com/android/preload/actions/ShowDataAction.java
deleted file mode 100644
index 2bb175f60772..000000000000
--- a/tools/preload2/src/com/android/preload/actions/ShowDataAction.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.preload.actions;
-
-import com.android.preload.DumpData;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-import java.awt.BorderLayout;
-import java.awt.event.ActionEvent;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import javax.swing.AbstractAction;
-import javax.swing.JFrame;
-import javax.swing.JScrollPane;
-import javax.swing.JTextArea;
-
-public class ShowDataAction extends AbstractAction {
- private DumpTableModel dataTableModel;
-
- public ShowDataAction(DumpTableModel dataTableModel) {
- super("Show data");
- this.dataTableModel = dataTableModel;
- }
-
- @Override
- public void actionPerformed(ActionEvent e) {
- // TODO(agampe): Auto-generated method stub
- int selRow = Main.getUI().getSelectedDataTableRow();
- if (selRow != -1) {
- DumpData data = dataTableModel.getData().get(selRow);
- Map<String, Set<String>> inv = data.invertData();
-
- StringBuilder builder = new StringBuilder();
-
- // First bootclasspath.
- add(builder, "Boot classpath:", inv.get(null));
-
- // Now everything else.
- for (String k : inv.keySet()) {
- if (k != null) {
- builder.append("==================\n\n");
- add(builder, k, inv.get(k));
- }
- }
-
- JFrame newFrame = new JFrame(data.getPackageName() + " " + data.getDate());
- newFrame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
- newFrame.getContentPane().add(new JScrollPane(new JTextArea(builder.toString())),
- BorderLayout.CENTER);
- newFrame.setSize(800, 600);
- newFrame.setLocationRelativeTo(null);
- newFrame.setVisible(true);
- }
- }
-
- private void add(StringBuilder builder, String head, Set<String> set) {
- builder.append(head);
- builder.append('\n');
- addSet(builder, set);
- builder.append('\n');
- }
-
- private void addSet(StringBuilder builder, Set<String> set) {
- if (set == null) {
- builder.append(" NONE\n");
- return;
- }
- List<String> sorted = new ArrayList<>(set);
- Collections.sort(sorted);
- for (String s : sorted) {
- builder.append(s);
- builder.append('\n');
- }
- }
-} \ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/WritePreloadedClassesAction.java b/tools/preload2/src/com/android/preload/actions/WritePreloadedClassesAction.java
deleted file mode 100644
index 9b97f1168df9..000000000000
--- a/tools/preload2/src/com/android/preload/actions/WritePreloadedClassesAction.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.preload.actions;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.IDevice;
-import com.android.preload.ClientUtils;
-import com.android.preload.DeviceUtils;
-import com.android.preload.DumpData;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-import java.awt.event.ActionEvent;
-import java.io.File;
-import java.util.Date;
-import java.util.Map;
-
-public class WritePreloadedClassesAction extends AbstractThreadedDeviceSpecificAction {
- private File preloadedClassFile;
-
- public WritePreloadedClassesAction(ClientUtils utils, IDevice device, DumpTableModel dataTableModel) {
- super("Write preloaded classes action", device);
- }
-
- @Override
- public void actionPerformed(ActionEvent e) {
- File[] files = Main.getUI().showOpenDialog(true);
- if (files != null && files.length > 0) {
- preloadedClassFile = files[0];
- super.actionPerformed(e);
- }
- }
-
- @Override
- public void run() {
- Main.getUI().showWaitDialog();
- try {
- // Write the new file with a 5-minute timeout
- DeviceUtils.overwritePreloaded(device, preloadedClassFile, 5 * 60);
- } catch (Exception e) {
- System.err.println(e);
- } finally {
- Main.getUI().hideWaitDialog();
- }
- }
-}
diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/hprof/GeneralHprofDumpHandler.java b/tools/preload2/src/com/android/preload/classdataretrieval/hprof/GeneralHprofDumpHandler.java
deleted file mode 100644
index 8d797ee00128..000000000000
--- a/tools/preload2/src/com/android/preload/classdataretrieval/hprof/GeneralHprofDumpHandler.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.preload.classdataretrieval.hprof;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.ClientData.IHprofDumpHandler;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class GeneralHprofDumpHandler implements IHprofDumpHandler {
-
- private List<IHprofDumpHandler> handlers = new ArrayList<>();
-
- public void addHandler(IHprofDumpHandler h) {
- synchronized (handlers) {
- handlers.add(h);
- }
- }
-
- public void removeHandler(IHprofDumpHandler h) {
- synchronized (handlers) {
- handlers.remove(h);
- }
- }
-
- private List<IHprofDumpHandler> getIterationList() {
- synchronized (handlers) {
- return new ArrayList<>(handlers);
- }
- }
-
- @Override
- public void onEndFailure(Client arg0, String arg1) {
- List<IHprofDumpHandler> iterList = getIterationList();
- for (IHprofDumpHandler h : iterList) {
- h.onEndFailure(arg0, arg1);
- }
- }
-
- @Override
- public void onSuccess(String arg0, Client arg1) {
- List<IHprofDumpHandler> iterList = getIterationList();
- for (IHprofDumpHandler h : iterList) {
- h.onSuccess(arg0, arg1);
- }
- }
-
- @Override
- public void onSuccess(byte[] arg0, Client arg1) {
- List<IHprofDumpHandler> iterList = getIterationList();
- for (IHprofDumpHandler h : iterList) {
- h.onSuccess(arg0, arg1);
- }
- }
- } \ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/hprof/Hprof.java b/tools/preload2/src/com/android/preload/classdataretrieval/hprof/Hprof.java
deleted file mode 100644
index 84ec8b7d0fdd..000000000000
--- a/tools/preload2/src/com/android/preload/classdataretrieval/hprof/Hprof.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.preload.classdataretrieval.hprof;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.ClientData;
-import com.android.ddmlib.ClientData.IHprofDumpHandler;
-import com.android.preload.classdataretrieval.ClassDataRetriever;
-import com.android.preload.ui.NullProgressMonitor;
-import com.android.tools.perflib.captures.MemoryMappedFileBuffer;
-import com.android.tools.perflib.heap.ClassObj;
-import com.android.tools.perflib.heap.Queries;
-import com.android.tools.perflib.heap.Snapshot;
-
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
-public class Hprof implements ClassDataRetriever {
-
- private static GeneralHprofDumpHandler hprofHandler;
-
- public static void init() {
- synchronized(Hprof.class) {
- if (hprofHandler == null) {
- ClientData.setHprofDumpHandler(hprofHandler = new GeneralHprofDumpHandler());
- }
- }
- }
-
- public static File doHprof(Client client, int timeout) {
- GetHprof gh = new GetHprof(client, timeout);
- return gh.get();
- }
-
- /**
- * Return a map of class names to class-loader names derived from the hprof dump.
- *
- * @param hprofLocalFile
- */
- public static Map<String, String> analyzeHprof(File hprofLocalFile) throws Exception {
- Snapshot snapshot = Snapshot.createSnapshot(new MemoryMappedFileBuffer(hprofLocalFile));
-
- Map<String, Set<ClassObj>> classes = Queries.classes(snapshot, null);
- Map<String, String> retValue = new HashMap<String, String>();
- for (Map.Entry<String, Set<ClassObj>> e : classes.entrySet()) {
- for (ClassObj c : e.getValue()) {
- String cl = c.getClassLoader() == null ? null : c.getClassLoader().toString();
- String cName = c.getClassName();
- int aDepth = 0;
- while (cName.endsWith("[]")) {
- cName = cName.substring(0, cName.length()-2);
- aDepth++;
- }
- String newName = transformPrimitiveClass(cName);
- if (aDepth > 0) {
- // Need to use kind-a descriptor syntax. If it was transformed, it is primitive.
- if (newName.equals(cName)) {
- newName = "L" + newName + ";";
- }
- for (int i = 0; i < aDepth; i++) {
- newName = "[" + newName;
- }
- }
- retValue.put(newName, cl);
- }
- }
-
- // Free up memory.
- snapshot.dispose();
-
- return retValue;
- }
-
- private static Map<String, String> primitiveMapping;
-
- static {
- primitiveMapping = new HashMap<>();
- primitiveMapping.put("boolean", "Z");
- primitiveMapping.put("byte", "B");
- primitiveMapping.put("char", "C");
- primitiveMapping.put("double", "D");
- primitiveMapping.put("float", "F");
- primitiveMapping.put("int", "I");
- primitiveMapping.put("long", "J");
- primitiveMapping.put("short", "S");
- primitiveMapping.put("void", "V");
- }
-
- private static String transformPrimitiveClass(String name) {
- String rep = primitiveMapping.get(name);
- if (rep != null) {
- return rep;
- }
- return name;
- }
-
- private static class GetHprof implements IHprofDumpHandler {
-
- private File target;
- private long timeout;
- private Client client;
-
- public GetHprof(Client client, long timeout) {
- this.client = client;
- this.timeout = timeout;
- }
-
- public File get() {
- synchronized (this) {
- hprofHandler.addHandler(this);
- client.dumpHprof();
- if (target == null) {
- try {
- wait(timeout);
- } catch (Exception e) {
- System.out.println(e);
- }
- }
- }
-
- hprofHandler.removeHandler(this);
- return target;
- }
-
- private void wakeUp() {
- synchronized (this) {
- notifyAll();
- }
- }
-
- @Override
- public void onEndFailure(Client arg0, String arg1) {
- System.out.println("GetHprof.onEndFailure");
- if (client == arg0) {
- wakeUp();
- }
- }
-
- private static File createTargetFile() {
- try {
- return File.createTempFile("ddms", ".hprof");
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public void onSuccess(String arg0, Client arg1) {
- System.out.println("GetHprof.onSuccess");
- if (client == arg1) {
- try {
- target = createTargetFile();
- arg1.getDevice().getSyncService().pullFile(arg0,
- target.getAbsoluteFile().toString(), new NullProgressMonitor());
- } catch (Exception e) {
- if (target != null) {
- target.delete();
- }
- e.printStackTrace();
- target = null;
- }
- wakeUp();
- }
- }
-
- @Override
- public void onSuccess(byte[] arg0, Client arg1) {
- System.out.println("GetHprof.onSuccess");
- if (client == arg1) {
- try {
- target = createTargetFile();
- BufferedOutputStream out =
- new BufferedOutputStream(new FileOutputStream(target));
- out.write(arg0);
- out.close();
- } catch (Exception e) {
- if (target != null) {
- target.delete();
- }
- e.printStackTrace();
- target = null;
- }
- wakeUp();
- }
- }
- }
-
- private int timeout;
-
- public Hprof(int timeout) {
- this.timeout = timeout;
- }
-
- @Override
- public Map<String, String> getClassData(Client client) {
- File hprofLocalFile = Hprof.doHprof(client, timeout);
- if (hprofLocalFile == null) {
- throw new RuntimeException("Failed getting dump...");
- }
- System.out.println("Dump file is " + hprofLocalFile);
-
- try {
- return analyzeHprof(hprofLocalFile);
- } catch (Exception e) {
- throw new RuntimeException(e);
- } finally {
- hprofLocalFile.delete();
- }
- }
-}
diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/JDWPClassDataRetriever.java b/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/JDWPClassDataRetriever.java
deleted file mode 100644
index dbd4c89b27cf..000000000000
--- a/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/JDWPClassDataRetriever.java
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.preload.classdataretrieval.jdwp;
-
-import com.android.ddmlib.Client;
-import com.android.preload.classdataretrieval.ClassDataRetriever;
-
-import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket;
-import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands;
-import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants;
-import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket;
-import org.apache.harmony.jpda.tests.jdwp.share.JDWPTestCase;
-import org.apache.harmony.jpda.tests.jdwp.share.JDWPUnitDebuggeeWrapper;
-import org.apache.harmony.jpda.tests.share.JPDALogWriter;
-import org.apache.harmony.jpda.tests.share.JPDATestOptions;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public class JDWPClassDataRetriever extends JDWPTestCase implements ClassDataRetriever {
-
- private final Client client;
-
- public JDWPClassDataRetriever() {
- this(null);
- }
-
- public JDWPClassDataRetriever(Client client) {
- this.client = client;
- }
-
-
- @Override
- protected String getDebuggeeClassName() {
- return "<unset>";
- }
-
- @Override
- public Map<String, String> getClassData(Client client) {
- return new JDWPClassDataRetriever(client).retrieve();
- }
-
- private Map<String, String> retrieve() {
- if (client == null) {
- throw new IllegalStateException();
- }
-
- settings = createTestOptions("localhost:" + String.valueOf(client.getDebuggerListenPort()));
- settings.setDebuggeeSuspend("n");
-
- logWriter = new JPDALogWriter(System.out, "", false);
-
- try {
- internalSetUp();
-
- return retrieveImpl();
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- } finally {
- internalTearDown();
- }
- }
-
- private Map<String, String> retrieveImpl() {
- try {
- // Suspend the app.
- {
- CommandPacket packet = new CommandPacket(
- JDWPCommands.VirtualMachineCommandSet.CommandSetID,
- JDWPCommands.VirtualMachineCommandSet.SuspendCommand);
- ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
- if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
- return null;
- }
- }
-
- // List all classes.
- CommandPacket packet = new CommandPacket(
- JDWPCommands.VirtualMachineCommandSet.CommandSetID,
- JDWPCommands.VirtualMachineCommandSet.AllClassesCommand);
- ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
-
- if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
- return null;
- }
-
- int classCount = reply.getNextValueAsInt();
- System.out.println("Runtime reported " + classCount + " classes.");
-
- Map<Long, String> classes = new HashMap<Long, String>();
- Map<Long, String> arrayClasses = new HashMap<Long, String>();
-
- for (int i = 0; i < classCount; i++) {
- byte refTypeTag = reply.getNextValueAsByte();
- long typeID = reply.getNextValueAsReferenceTypeID();
- String signature = reply.getNextValueAsString();
- /* int status = */ reply.getNextValueAsInt();
-
- switch (refTypeTag) {
- case JDWPConstants.TypeTag.CLASS:
- case JDWPConstants.TypeTag.INTERFACE:
- classes.put(typeID, signature);
- break;
-
- case JDWPConstants.TypeTag.ARRAY:
- arrayClasses.put(typeID, signature);
- break;
- }
- }
-
- Map<String, String> result = new HashMap<String, String>();
-
- // Parse all classes.
- for (Map.Entry<Long, String> entry : classes.entrySet()) {
- long typeID = entry.getKey();
- String signature = entry.getValue();
-
- if (!checkClass(typeID, signature, result)) {
- System.err.println("Issue investigating " + signature);
- }
- }
-
- // For arrays, look at the leaf component type.
- for (Map.Entry<Long, String> entry : arrayClasses.entrySet()) {
- long typeID = entry.getKey();
- String signature = entry.getValue();
-
- if (!checkArrayClass(typeID, signature, result)) {
- System.err.println("Issue investigating " + signature);
- }
- }
-
- return result;
- } finally {
- // Resume the app.
- {
- CommandPacket packet = new CommandPacket(
- JDWPCommands.VirtualMachineCommandSet.CommandSetID,
- JDWPCommands.VirtualMachineCommandSet.ResumeCommand);
- /* ReplyPacket reply = */ debuggeeWrapper.vmMirror.performCommand(packet);
- }
- }
- }
-
- private boolean checkClass(long typeID, String signature, Map<String, String> result) {
- CommandPacket packet = new CommandPacket(
- JDWPCommands.ReferenceTypeCommandSet.CommandSetID,
- JDWPCommands.ReferenceTypeCommandSet.ClassLoaderCommand);
- packet.setNextValueAsReferenceTypeID(typeID);
- ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
- if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
- return false;
- }
-
- long classLoaderID = reply.getNextValueAsObjectID();
-
- // TODO: Investigate the classloader to have a better string?
- String classLoaderString = (classLoaderID == 0) ? null : String.valueOf(classLoaderID);
-
- result.put(getClassName(signature), classLoaderString);
-
- return true;
- }
-
- private boolean checkArrayClass(long typeID, String signature, Map<String, String> result) {
- // Classloaders of array classes are the same as the component class'.
- CommandPacket packet = new CommandPacket(
- JDWPCommands.ReferenceTypeCommandSet.CommandSetID,
- JDWPCommands.ReferenceTypeCommandSet.ClassLoaderCommand);
- packet.setNextValueAsReferenceTypeID(typeID);
- ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
- if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
- return false;
- }
-
- long classLoaderID = reply.getNextValueAsObjectID();
-
- // TODO: Investigate the classloader to have a better string?
- String classLoaderString = (classLoaderID == 0) ? null : String.valueOf(classLoaderID);
-
- // For array classes, we *need* the signature directly.
- result.put(signature, classLoaderString);
-
- return true;
- }
-
- private static String getClassName(String signature) {
- String withoutLAndSemicolon = signature.substring(1, signature.length() - 1);
- return withoutLAndSemicolon.replace('/', '.');
- }
-
-
- private static JPDATestOptions createTestOptions(String address) {
- JPDATestOptions options = new JPDATestOptions();
- options.setAttachConnectorKind();
- options.setTimeout(1000);
- options.setWaitingTime(1000);
- options.setTransportAddress(address);
- return options;
- }
-
- @Override
- protected JDWPUnitDebuggeeWrapper createDebuggeeWrapper() {
- return new PreloadDebugeeWrapper(settings, logWriter);
- }
-}
diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/PreloadDebugeeWrapper.java b/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/PreloadDebugeeWrapper.java
deleted file mode 100644
index b9df6d0aeb93..000000000000
--- a/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/PreloadDebugeeWrapper.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.preload.classdataretrieval.jdwp;
-
-import org.apache.harmony.jpda.tests.framework.LogWriter;
-import org.apache.harmony.jpda.tests.jdwp.share.JDWPManualDebuggeeWrapper;
-import org.apache.harmony.jpda.tests.share.JPDATestOptions;
-
-import java.io.IOException;
-
-public class PreloadDebugeeWrapper extends JDWPManualDebuggeeWrapper {
-
- public PreloadDebugeeWrapper(JPDATestOptions options, LogWriter writer) {
- super(options, writer);
- }
-
- @Override
- protected Process launchProcess(String cmdLine) throws IOException {
- return null;
- }
-
- @Override
- protected void WaitForProcessExit(Process process) {
- }
-
-}
diff --git a/tools/preload2/src/com/android/preload/ui/IUI.java b/tools/preload2/src/com/android/preload/ui/IUI.java
deleted file mode 100644
index 9371463e9a79..000000000000
--- a/tools/preload2/src/com/android/preload/ui/IUI.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.android.preload.ui;
-
-import com.android.ddmlib.Client;
-import java.io.File;
-import java.util.List;
-import javax.swing.Action;
-import javax.swing.ListModel;
-import javax.swing.table.TableModel;
-
-/**
- * UI abstraction for the tool. This allows a graphical mode, command line mode,
- * or silent mode.
- */
-public interface IUI {
-
- void prepare(ListModel<Client> clientListModel, TableModel dataTableModel,
- List<Action> actions);
-
- void ready();
-
- boolean isSingleThreaded();
-
- Client getSelectedClient();
-
- int getSelectedDataTableRow();
-
- void showWaitDialog();
-
- void updateWaitDialog(String s);
-
- void hideWaitDialog();
-
- void showMessageDialog(String s);
-
- boolean showConfirmDialog(String title, String message);
-
- String showInputDialog(String message);
-
- <T> T showChoiceDialog(String title, String message, T[] choices);
-
- File showSaveDialog();
-
- File[] showOpenDialog(boolean multi);
-
-}
diff --git a/tools/preload2/src/com/android/preload/ui/SequenceUI.java b/tools/preload2/src/com/android/preload/ui/SequenceUI.java
deleted file mode 100644
index dc6a4f389b10..000000000000
--- a/tools/preload2/src/com/android/preload/ui/SequenceUI.java
+++ /dev/null
@@ -1,222 +0,0 @@
-package com.android.preload.ui;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.ClientData;
-import java.io.File;
-import java.util.LinkedList;
-import java.util.List;
-import javax.swing.Action;
-import javax.swing.ListModel;
-import javax.swing.table.TableModel;
-
-public class SequenceUI implements IUI {
-
- private ListModel<Client> clientListModel;
- @SuppressWarnings("unused")
- private TableModel dataTableModel;
- private List<Action> actions;
-
- private List<Object> sequence = new LinkedList<>();
-
- public SequenceUI() {
- }
-
- @Override
- public boolean isSingleThreaded() {
- return true;
- }
-
- @Override
- public void prepare(ListModel<Client> clientListModel, TableModel dataTableModel,
- List<Action> actions) {
- this.clientListModel = clientListModel;
- this.dataTableModel = dataTableModel;
- this.actions = actions;
- }
-
- public SequenceUI action(Action a) {
- sequence.add(a);
- return this;
- }
-
- public SequenceUI action(Class<? extends Action> actionClass) {
- for (Action a : actions) {
- if (actionClass.equals(a.getClass())) {
- sequence.add(a);
- return this;
- }
- }
- throw new IllegalArgumentException("No action of class " + actionClass + " found.");
- }
-
- public SequenceUI confirmYes() {
- sequence.add(Boolean.TRUE);
- return this;
- }
-
- public SequenceUI confirmNo() {
- sequence.add(Boolean.FALSE);
- return this;
- }
-
- public SequenceUI input(String input) {
- sequence.add(input);
- return this;
- }
-
- public SequenceUI input(File... f) {
- sequence.add(f);
- return this;
- }
-
- public SequenceUI output(File f) {
- sequence.add(f);
- return this;
- }
-
- public SequenceUI tableRow(int i) {
- sequence.add(i);
- return this;
- }
-
- private class ClientSelector {
- private String pkg;
-
- public ClientSelector(String pkg) {
- this.pkg = pkg;
- }
-
- public Client getClient() {
- for (int i = 0; i < clientListModel.getSize(); i++) {
- ClientData cd = clientListModel.getElementAt(i).getClientData();
- if (cd != null) {
- String s = cd.getClientDescription();
- if (pkg.equals(s)) {
- return clientListModel.getElementAt(i);
- }
- }
- }
- throw new RuntimeException("Didn't find client " + pkg);
- }
- }
-
- public SequenceUI client(String pkg) {
- sequence.add(new ClientSelector(pkg));
- return this;
- }
-
- public SequenceUI choice(String pattern) {
- sequence.add(pattern);
- return this;
- }
-
- @Override
- public void ready() {
- // Run the actions.
- // No iterator or foreach loop as the sequence will be emptied while running.
- try {
- while (!sequence.isEmpty()) {
- Object next = sequence.remove(0);
- if (next instanceof Action) {
- ((Action)next).actionPerformed(null);
- } else {
- throw new IllegalStateException("Didn't expect a non-action: " + next);
- }
- }
- } catch (Exception e) {
- e.printStackTrace(System.out);
- }
-
- // Now shut down.
- System.exit(0);
- }
-
- @Override
- public Client getSelectedClient() {
- Object next = sequence.remove(0);
- if (next instanceof ClientSelector) {
- return ((ClientSelector)next).getClient();
- }
- throw new IllegalStateException("Unexpected: " + next);
- }
-
- @Override
- public int getSelectedDataTableRow() {
- Object next = sequence.remove(0);
- if (next instanceof Integer) {
- return ((Integer)next).intValue();
- }
- throw new IllegalStateException("Unexpected: " + next);
- }
-
- @Override
- public void showWaitDialog() {
- }
-
- @Override
- public void updateWaitDialog(String s) {
- System.out.println(s);
- }
-
- @Override
- public void hideWaitDialog() {
- }
-
- @Override
- public void showMessageDialog(String s) {
- System.out.println(s);
- }
-
- @Override
- public boolean showConfirmDialog(String title, String message) {
- Object next = sequence.remove(0);
- if (next instanceof Boolean) {
- return ((Boolean)next).booleanValue();
- }
- throw new IllegalStateException("Unexpected: " + next);
- }
-
- @Override
- public String showInputDialog(String message) {
- Object next = sequence.remove(0);
- if (next instanceof String) {
- return (String)next;
- }
- throw new IllegalStateException("Unexpected: " + next);
- }
-
- @Override
- public <T> T showChoiceDialog(String title, String message, T[] choices) {
- Object next = sequence.remove(0);
- if (next instanceof String) {
- String s = (String)next;
- for (T t : choices) {
- if (t.toString().contains(s)) {
- return t;
- }
- }
- return null;
- }
- throw new IllegalStateException("Unexpected: " + next);
- }
-
- @Override
- public File showSaveDialog() {
- Object next = sequence.remove(0);
- if (next instanceof File) {
- System.out.println(next);
- return (File)next;
- }
- throw new IllegalStateException("Unexpected: " + next);
- }
-
- @Override
- public File[] showOpenDialog(boolean multi) {
- Object next = sequence.remove(0);
- if (next instanceof File[]) {
- return (File[])next;
- }
- throw new IllegalStateException("Unexpected: " + next);
- }
-
-}
diff --git a/tools/preload2/src/com/android/preload/ui/SwingUI.java b/tools/preload2/src/com/android/preload/ui/SwingUI.java
deleted file mode 100644
index cab3744ad74c..000000000000
--- a/tools/preload2/src/com/android/preload/ui/SwingUI.java
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.preload.ui;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.ClientData;
-
-import java.awt.BorderLayout;
-import java.awt.Component;
-import java.awt.Dimension;
-import java.io.File;
-import java.util.List;
-
-import javax.swing.Action;
-import javax.swing.DefaultListCellRenderer;
-import javax.swing.JDialog;
-import javax.swing.JFileChooser;
-import javax.swing.JFrame;
-import javax.swing.JLabel;
-import javax.swing.JList;
-import javax.swing.JOptionPane;
-import javax.swing.JProgressBar;
-import javax.swing.JScrollPane;
-import javax.swing.JTable;
-import javax.swing.JToolBar;
-import javax.swing.ListModel;
-import javax.swing.SwingUtilities;
-import javax.swing.table.TableModel;
-
-public class SwingUI extends JFrame implements IUI {
-
- private JList<Client> clientList;
- private JTable dataTable;
-
- // Shared file chooser, means the directory is retained.
- private JFileChooser jfc;
-
- public SwingUI() {
- super("Preloaded-classes computation");
- }
-
- @Override
- public boolean isSingleThreaded() {
- return false;
- }
-
- @Override
- public void prepare(ListModel<Client> clientListModel, TableModel dataTableModel,
- List<Action> actions) {
- getContentPane().add(new JScrollPane(clientList = new JList<Client>(clientListModel)),
- BorderLayout.WEST);
- clientList.setCellRenderer(new ClientListCellRenderer());
- // clientList.addListSelectionListener(listener);
-
- dataTable = new JTable(dataTableModel);
- getContentPane().add(new JScrollPane(dataTable), BorderLayout.CENTER);
-
- JToolBar toolbar = new JToolBar(JToolBar.HORIZONTAL);
- for (Action a : actions) {
- if (a == null) {
- toolbar.addSeparator();
- } else {
- toolbar.add(a);
- }
- }
- getContentPane().add(toolbar, BorderLayout.PAGE_START);
-
- setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- setBounds(100, 100, 800, 600);
-
- setVisible(true);
- }
-
- @Override
- public void ready() {
- }
-
- @Override
- public Client getSelectedClient() {
- return clientList.getSelectedValue();
- }
-
- @Override
- public int getSelectedDataTableRow() {
- return dataTable.getSelectedRow();
- }
-
- private JDialog currentWaitDialog = null;
-
- @Override
- public void showWaitDialog() {
- if (currentWaitDialog == null) {
- currentWaitDialog = new JDialog(this, "Please wait...", true);
- currentWaitDialog.getContentPane().add(new JLabel("Please be patient."),
- BorderLayout.CENTER);
- JProgressBar progress = new JProgressBar(JProgressBar.HORIZONTAL);
- progress.setIndeterminate(true);
- currentWaitDialog.getContentPane().add(progress, BorderLayout.SOUTH);
- currentWaitDialog.setSize(200, 100);
- currentWaitDialog.setLocationRelativeTo(null);
- showWaitDialogLater();
- }
- }
-
- private void showWaitDialogLater() {
- SwingUtilities.invokeLater(new Runnable() {
- @Override
- public void run() {
- if (currentWaitDialog != null) {
- currentWaitDialog.setVisible(true); // This is blocking.
- }
- }
- });
- }
-
- @Override
- public void updateWaitDialog(String s) {
- if (currentWaitDialog != null) {
- ((JLabel) currentWaitDialog.getContentPane().getComponent(0)).setText(s);
- Dimension prefSize = currentWaitDialog.getPreferredSize();
- Dimension curSize = currentWaitDialog.getSize();
- if (prefSize.width > curSize.width || prefSize.height > curSize.height) {
- currentWaitDialog.setSize(Math.max(prefSize.width, curSize.width),
- Math.max(prefSize.height, curSize.height));
- currentWaitDialog.invalidate();
- }
- }
- }
-
- @Override
- public void hideWaitDialog() {
- if (currentWaitDialog != null) {
- currentWaitDialog.setVisible(false);
- currentWaitDialog = null;
- }
- }
-
- @Override
- public void showMessageDialog(String s) {
- // Hide the wait dialog...
- if (currentWaitDialog != null) {
- currentWaitDialog.setVisible(false);
- }
-
- try {
- JOptionPane.showMessageDialog(this, s);
- } finally {
- // And reshow it afterwards...
- if (currentWaitDialog != null) {
- showWaitDialogLater();
- }
- }
- }
-
- @Override
- public boolean showConfirmDialog(String title, String message) {
- // Hide the wait dialog...
- if (currentWaitDialog != null) {
- currentWaitDialog.setVisible(false);
- }
-
- try {
- return JOptionPane.showConfirmDialog(this, title, message, JOptionPane.YES_NO_OPTION)
- == JOptionPane.YES_OPTION;
- } finally {
- // And reshow it afterwards...
- if (currentWaitDialog != null) {
- showWaitDialogLater();
- }
- }
- }
-
- @Override
- public String showInputDialog(String message) {
- // Hide the wait dialog...
- if (currentWaitDialog != null) {
- currentWaitDialog.setVisible(false);
- }
-
- try {
- return JOptionPane.showInputDialog(message);
- } finally {
- // And reshow it afterwards...
- if (currentWaitDialog != null) {
- showWaitDialogLater();
- }
- }
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public <T> T showChoiceDialog(String title, String message, T[] choices) {
- // Hide the wait dialog...
- if (currentWaitDialog != null) {
- currentWaitDialog.setVisible(false);
- }
-
- try{
- return (T)JOptionPane.showInputDialog(this,
- title,
- message,
- JOptionPane.QUESTION_MESSAGE,
- null,
- choices,
- choices[0]);
- } finally {
- // And reshow it afterwards...
- if (currentWaitDialog != null) {
- showWaitDialogLater();
- }
- }
- }
-
- @Override
- public File showSaveDialog() {
- // Hide the wait dialog...
- if (currentWaitDialog != null) {
- currentWaitDialog.setVisible(false);
- }
-
- try{
- if (jfc == null) {
- jfc = new JFileChooser();
- }
-
- int ret = jfc.showSaveDialog(this);
- if (ret == JFileChooser.APPROVE_OPTION) {
- return jfc.getSelectedFile();
- } else {
- return null;
- }
- } finally {
- // And reshow it afterwards...
- if (currentWaitDialog != null) {
- showWaitDialogLater();
- }
- }
- }
-
- @Override
- public File[] showOpenDialog(boolean multi) {
- // Hide the wait dialog...
- if (currentWaitDialog != null) {
- currentWaitDialog.setVisible(false);
- }
-
- try{
- if (jfc == null) {
- jfc = new JFileChooser();
- }
-
- jfc.setMultiSelectionEnabled(multi);
- int ret = jfc.showOpenDialog(this);
- if (ret == JFileChooser.APPROVE_OPTION) {
- return jfc.getSelectedFiles();
- } else {
- return null;
- }
- } finally {
- // And reshow it afterwards...
- if (currentWaitDialog != null) {
- showWaitDialogLater();
- }
- }
- }
-
- private class ClientListCellRenderer extends DefaultListCellRenderer {
-
- @Override
- public Component getListCellRendererComponent(JList<?> list, Object value, int index,
- boolean isSelected, boolean cellHasFocus) {
- ClientData cd = ((Client) value).getClientData();
- String s = cd.getClientDescription() + " (pid " + cd.getPid() + ")";
- return super.getListCellRendererComponent(list, s, index, isSelected, cellHasFocus);
- }
- }
-}
diff --git a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java
index ef2914610f80..5a5703ed520c 100644
--- a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java
+++ b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java
@@ -20,12 +20,13 @@ import static javax.lang.model.element.ElementKind.PACKAGE;
import static javax.tools.Diagnostic.Kind.ERROR;
import static javax.tools.Diagnostic.Kind.WARNING;
-import android.annotation.UnsupportedAppUsage;
-
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.sun.tools.javac.code.Type;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -69,6 +70,7 @@ public class SignatureBuilder {
public SignatureBuilderException(String message) {
super(message);
}
+
public void report(Element offendingElement) {
mMessager.printMessage(ERROR, getMessage(), offendingElement);
}
@@ -153,7 +155,7 @@ public class SignatureBuilder {
/**
* Get the signature for an executable, either a method or a constructor.
*
- * @param name "<init>" for constructor, else the method name
+ * @param name "<init>" for constructor, else the method name
* @param method The executable element in question.
*/
private String getExecutableSignature(CharSequence name, ExecutableElement method)
@@ -191,8 +193,13 @@ public class SignatureBuilder {
return sig.toString();
}
- public String buildSignature(Element element) {
- UnsupportedAppUsage uba = element.getAnnotation(UnsupportedAppUsage.class);
+ /**
+ * Creates the signature for an annotated element.
+ *
+ * @param annotationType type of annotation being processed.
+ * @param element element for which we want to create a signature.
+ */
+ public String buildSignature(Class<? extends Annotation> annotationType, Element element) {
try {
String signature;
switch (element.getKind()) {
@@ -208,18 +215,35 @@ public class SignatureBuilder {
default:
return null;
}
- // if we have an expected signature on the annotation, warn if it doesn't match.
- if (!Strings.isNullOrEmpty(uba.expectedSignature())) {
- if (!signature.equals(uba.expectedSignature())) {
- mMessager.printMessage(
- WARNING,
- String.format("Expected signature doesn't match generated signature.\n"
- + " Expected: %s\n Generated: %s",
- uba.expectedSignature(), signature),
- element);
+ // Obtain annotation objects
+ Annotation annotation = element.getAnnotation(annotationType);
+ if (annotation == null) {
+ throw new IllegalStateException(
+ "Element doesn't have any UnsupportedAppUsage annotation");
+ }
+ try {
+ Method expectedSignatureMethod = annotationType.getMethod("expectedSignature");
+ // If we have an expected signature on the annotation, warn if it doesn't match.
+ String expectedSignature = expectedSignatureMethod.invoke(annotation).toString();
+ if (!Strings.isNullOrEmpty(expectedSignature)) {
+ if (!signature.equals(expectedSignature)) {
+ mMessager.printMessage(
+ WARNING,
+ String.format(
+ "Expected signature doesn't match generated signature.\n"
+ + " Expected: %s\n Generated: %s",
+ expectedSignature, signature),
+ element);
+ }
}
+ return signature;
+ } catch (NoSuchMethodException e) {
+ throw new IllegalStateException(
+ "Annotation type does not have expectedSignature parameter", e);
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ throw new IllegalStateException(
+ "Could not get expectedSignature parameter for annotation", e);
}
- return signature;
} catch (SignatureBuilderException problem) {
problem.report(element);
return null;
diff --git a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java
index d368136c7081..5bb956a1fea2 100644
--- a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java
+++ b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java
@@ -18,9 +18,8 @@ package android.processor.unsupportedappusage;
import static javax.tools.StandardLocation.CLASS_OUTPUT;
-import android.annotation.UnsupportedAppUsage;
-
import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableSet;
import com.sun.tools.javac.model.JavacElements;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.util.Pair;
@@ -28,6 +27,7 @@ import com.sun.tools.javac.util.Position;
import java.io.IOException;
import java.io.PrintStream;
+import java.lang.annotation.Annotation;
import java.net.URLEncoder;
import java.util.Map;
import java.util.Set;
@@ -47,14 +47,14 @@ import javax.lang.model.element.TypeElement;
/**
* Annotation processor for {@link UnsupportedAppUsage} annotations.
*
- * This processor currently outputs two things:
- * 1. A greylist.txt containing dex signatures of all annotated elements.
- * 2. A CSV file with a mapping of dex signatures to corresponding source positions.
+ * This processor currently outputs a CSV file with a mapping of dex signatures to corresponding
+ * source positions.
*
- * The first will be used at a later stage of the build to add access flags to the dex file. The
- * second is used for automating updates to the annotations themselves.
+ * This is used for automating updates to the annotations themselves.
*/
-@SupportedAnnotationTypes({"android.annotation.UnsupportedAppUsage"})
+@SupportedAnnotationTypes({"android.annotation.UnsupportedAppUsage",
+ "dalvik.annotation.compat.UnsupportedAppUsage"
+})
public class UnsupportedAppUsageProcessor extends AbstractProcessor {
// Package name for writing output. Output will be written to the "class output" location within
@@ -62,6 +62,13 @@ public class UnsupportedAppUsageProcessor extends AbstractProcessor {
private static final String PACKAGE = "unsupportedappusage";
private static final String INDEX_CSV = "unsupportedappusage_index.csv";
+ private static final ImmutableSet<Class<? extends Annotation>> SUPPORTED_ANNOTATIONS =
+ ImmutableSet.of(android.annotation.UnsupportedAppUsage.class,
+ dalvik.annotation.compat.UnsupportedAppUsage.class);
+ private static final ImmutableSet<String> SUPPORTED_ANNOTATION_NAMES =
+ SUPPORTED_ANNOTATIONS.stream().map(annotation -> annotation.getCanonicalName()).collect(
+ ImmutableSet.toImmutableSet());
+
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
@@ -92,8 +99,7 @@ public class UnsupportedAppUsageProcessor extends AbstractProcessor {
private AnnotationMirror getUnsupportedAppUsageAnnotationMirror(Element e) {
for (AnnotationMirror m : e.getAnnotationMirrors()) {
TypeElement type = (TypeElement) m.getAnnotationType().asElement();
- if (type.getQualifiedName().toString().equals(
- UnsupportedAppUsage.class.getCanonicalName())) {
+ if (SUPPORTED_ANNOTATION_NAMES.contains(type.getQualifiedName().toString())) {
return m;
}
}
@@ -133,12 +139,12 @@ public class UnsupportedAppUsageProcessor extends AbstractProcessor {
/**
* Maps an annotated element to the source position of the @UnsupportedAppUsage annotation
* attached to it. It returns CSV in the format:
- * dex-signature,filename,start-line,start-col,end-line,end-col
+ * dex-signature,filename,start-line,start-col,end-line,end-col
*
* The positions refer to the annotation itself, *not* the annotated member. This can therefore
* be used to read just the annotation from the file, and to perform in-place edits on it.
*
- * @param signature the dex signature for the element.
+ * @param signature the dex signature for the element.
* @param annotatedElement The annotated element
* @return A single line of CSV text
*/
@@ -164,28 +170,34 @@ public class UnsupportedAppUsageProcessor extends AbstractProcessor {
*/
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
- Set<? extends Element> annotated = roundEnv.getElementsAnnotatedWith(
- UnsupportedAppUsage.class);
- if (annotated.size() == 0) {
- return true;
- }
- // build signatures for each annotated member, and put them in a map of signature to member
Map<String, Element> signatureMap = new TreeMap<>();
SignatureBuilder sb = new SignatureBuilder(processingEnv.getMessager());
- for (Element e : annotated) {
- String sig = sb.buildSignature(e);
- if (sig != null) {
- signatureMap.put(sig, e);
+ for (Class<? extends Annotation> supportedAnnotation : SUPPORTED_ANNOTATIONS) {
+ Set<? extends Element> annotated = roundEnv.getElementsAnnotatedWith(
+ supportedAnnotation);
+ if (annotated.size() == 0) {
+ continue;
+ }
+ // Build signatures for each annotated member and put them in a map from signature to
+ // member.
+ for (Element e : annotated) {
+ String sig = sb.buildSignature(supportedAnnotation, e);
+ if (sig != null) {
+ signatureMap.put(sig, e);
+ }
}
}
- try {
- writeToFile(INDEX_CSV,
- getCsvHeaders(),
- signatureMap.entrySet()
- .stream()
- .map(e -> getAnnotationIndex(e.getKey() ,e.getValue())));
- } catch (IOException e) {
- throw new RuntimeException("Failed to write output", e);
+
+ if (!signatureMap.isEmpty()) {
+ try {
+ writeToFile(INDEX_CSV,
+ getCsvHeaders(),
+ signatureMap.entrySet()
+ .stream()
+ .map(e -> getAnnotationIndex(e.getKey(), e.getValue())));
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to write output", e);
+ }
}
return true;
}
diff --git a/tools/streaming_proto/Android.bp b/tools/streaming_proto/Android.bp
index 1121eadca967..14eead853f50 100644
--- a/tools/streaming_proto/Android.bp
+++ b/tools/streaming_proto/Android.bp
@@ -49,3 +49,18 @@ cc_binary_host {
defaults: ["protoc-gen-stream-defaults"],
}
+
+// ==========================================================
+// Build the java test
+// ==========================================================
+java_library {
+ name: "StreamingProtoTest",
+ srcs: [
+ "test/**/*.java",
+ "test/**/*.proto",
+ ],
+ proto: {
+ plugin: "javastream",
+ },
+ static_libs: ["libprotobuf-java-lite"],
+}
diff --git a/tools/streaming_proto/Android.mk b/tools/streaming_proto/Android.mk
deleted file mode 100644
index ebb77a197883..000000000000
--- a/tools/streaming_proto/Android.mk
+++ /dev/null
@@ -1,28 +0,0 @@
-#
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-LOCAL_PATH:= $(call my-dir)
-
-# ==========================================================
-# Build the java test
-# ==========================================================
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, test) \
- $(call all-proto-files-under, test)
-LOCAL_MODULE := StreamingProtoTest
-LOCAL_PROTOC_OPTIMIZE_TYPE := stream
-include $(BUILD_JAVA_LIBRARY)
-