summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--adbconnection/adbconnection.cc257
-rw-r--r--adbconnection/adbconnection.h42
-rw-r--r--runtime/native/dalvik_system_VMDebug.cc65
-rw-r--r--runtime/runtime_callbacks.cc40
-rw-r--r--runtime/runtime_callbacks.h23
5 files changed, 372 insertions, 55 deletions
diff --git a/adbconnection/adbconnection.cc b/adbconnection/adbconnection.cc
index 55eac932d3..2ef7a842fa 100644
--- a/adbconnection/adbconnection.cc
+++ b/adbconnection/adbconnection.cc
@@ -16,6 +16,7 @@
#include "adbconnection.h"
+#include <dlfcn.h>
#include <jni.h>
#include <sys/eventfd.h>
#include <sys/ioctl.h>
@@ -74,8 +75,7 @@ using dt_fd_forward::kSkipHandshakeMessage;
using android::base::StringPrintf;
static constexpr const char kJdwpHandshake[14] = {
- 'J', 'D', 'W', 'P', '-', 'H', 'a', 'n', 'd', 's', 'h', 'a', 'k', 'e'
-};
+ 'J', 'D', 'W', 'P', '-', 'H', 'a', 'n', 'd', 's', 'h', 'a', 'k', 'e'};
static constexpr int kEventfdLocked = 0;
static constexpr int kEventfdUnlocked = 1;
@@ -92,8 +92,88 @@ static constexpr uint8_t kDdmChunkCommand = 1;
static std::optional<AdbConnectionState> gState;
static std::optional<pthread_t> gPthread;
-static bool IsDebuggingPossible() {
- return art::Dbg::IsJdwpAllowed();
+// ADB apex method v2
+using AdbApexProcessName = void (*)(const char*);
+AdbApexProcessName apex_adbconnection_client_set_current_process_name = nullptr;
+using AdbApexPackageName = void (*)(const char*);
+AdbApexPackageName apex_adbconnection_client_add_application = nullptr;
+AdbApexPackageName apex_adbconnection_client_remove_application = nullptr;
+using AdbApexWaitingForDebugger = void (*)(bool);
+AdbApexWaitingForDebugger apex_adbconnection_client_set_waiting_for_debugger = nullptr;
+using AdbApexSendUpdate = void (*)(AdbConnectionClientContext*);
+AdbApexSendUpdate apex_adbconnection_client_send_update = nullptr;
+using AdbApexHasPendingUpdate = bool (*)();
+AdbApexHasPendingUpdate apex_adbconnection_client_has_pending_update = nullptr;
+using AdbApexSetUserId = void (*)(int);
+AdbApexSetUserId apex_adbconnection_client_set_user_id = nullptr;
+
+void apex_adbconnection_client_set_current_process_name_noop(const char*) {}
+void apex_adbconnection_client_add_application_noop(const char*) {}
+void apex_adbconnection_client_remove_application_noop(const char*) {}
+void apex_adbconnection_client_set_waiting_for_debugger_noop(bool) {}
+void apex_adbconnection_client_send_update_noop(AdbConnectionClientContext*) {}
+bool apex_adbconnection_client_has_pending_update_noop() { return false; }
+void apex_adbconnection_client_set_user_id_noop(int) {}
+
+static void RetrieveApexPointers() {
+ apex_adbconnection_client_set_current_process_name =
+ (AdbApexProcessName)dlsym(RTLD_DEFAULT, "adbconnection_client_set_current_process_name");
+ if (!apex_adbconnection_client_set_current_process_name) {
+ VLOG(jdwp) << "Unable to dlsym adbconnection_client_set_current_process_name";
+ apex_adbconnection_client_set_current_process_name =
+ apex_adbconnection_client_set_current_process_name_noop;
+ }
+
+ apex_adbconnection_client_add_application =
+ (AdbApexPackageName)dlsym(RTLD_DEFAULT, "adbconnection_client_add_application");
+ if (!apex_adbconnection_client_add_application) {
+ VLOG(jdwp) << "Unable to dlsym adbconnection_client_add_application";
+ apex_adbconnection_client_add_application = apex_adbconnection_client_add_application_noop;
+ }
+
+ apex_adbconnection_client_remove_application =
+ (AdbApexPackageName)dlsym(RTLD_DEFAULT, "adbconnection_client_remove_application");
+ if (!apex_adbconnection_client_remove_application) {
+ VLOG(jdwp) << "Unable to dlsym adbconnection_client_remove_application";
+ apex_adbconnection_client_remove_application =
+ apex_adbconnection_client_remove_application_noop;
+ }
+
+ apex_adbconnection_client_set_waiting_for_debugger = (AdbApexWaitingForDebugger)dlsym(
+ RTLD_DEFAULT, "adbconnection_client_set_waiting_for_debugger");
+ if (!apex_adbconnection_client_set_waiting_for_debugger) {
+ VLOG(jdwp) << "Unable to dlsym adbconnection_client_set_waiting_for_debugger";
+ apex_adbconnection_client_set_waiting_for_debugger =
+ apex_adbconnection_client_set_waiting_for_debugger_noop;
+ }
+
+ apex_adbconnection_client_send_update =
+ (AdbApexSendUpdate)dlsym(RTLD_DEFAULT, "adbconnection_client_send_update");
+ if (!apex_adbconnection_client_send_update) {
+ VLOG(jdwp) << "Unable to dlsym adbconnection_client_send_update";
+ apex_adbconnection_client_send_update = apex_adbconnection_client_send_update_noop;
+ }
+
+ apex_adbconnection_client_has_pending_update =
+ (AdbApexHasPendingUpdate)dlsym(RTLD_DEFAULT, "adbconnection_client_has_pending_update");
+ if (!apex_adbconnection_client_has_pending_update) {
+ VLOG(jdwp) << "Unable to dlsym adbconnection_client_has_pending_update";
+ apex_adbconnection_client_has_pending_update =
+ apex_adbconnection_client_has_pending_update_noop;
+ }
+
+ apex_adbconnection_client_set_user_id =
+ (AdbApexSetUserId)dlsym(RTLD_DEFAULT, "adbconnection_client_set_user_id");
+ if (!apex_adbconnection_client_set_user_id) {
+ VLOG(jdwp) << "Unable to dlsym adbconnection_client_set_user_id";
+ apex_adbconnection_client_set_user_id = apex_adbconnection_client_set_user_id_noop;
+ }
+}
+
+static bool IsDebuggingPossible() { return art::Dbg::IsJdwpAllowed(); }
+
+static bool IsDebuggableOrProfilable() {
+ return IsDebuggingPossible() || art::Runtime::Current()->IsProfileableFromShell();
}
// Begin running the debugger.
@@ -101,7 +181,7 @@ void AdbConnectionDebuggerController::StartDebugger() {
// The debugger thread is started for a debuggable or profileable-from-shell process.
// The pid will be send to adbd for adb's "track-jdwp" and "track-app" services.
// The thread will also set up the jdwp tunnel if the process is debuggable.
- if (IsDebuggingPossible() || art::Runtime::Current()->IsProfileableFromShell()) {
+ if (IsDebuggableOrProfilable()) {
connection_->StartDebuggerThreads();
} else {
LOG(ERROR) << "Not starting debugger since process cannot load the jdwp agent.";
@@ -135,15 +215,31 @@ void AdbConnectionDdmCallback::DdmPublishChunk(uint32_t type,
connection_->PublishDdmData(type, data);
}
+void AdbConnectionAppInfoCallback::SetCurrentProcessName(const std::string& process_name) {
+ connection_->SetCurrentProcessName(process_name);
+}
+
+void AdbConnectionAppInfoCallback::AddApplication(const std::string& package_name) {
+ connection_->AddApplication(package_name);
+}
+
+void AdbConnectionAppInfoCallback::RemoveApplication(const std::string& package_name) {
+ connection_->RemoveApplication(package_name);
+}
+
+void AdbConnectionAppInfoCallback::SetWaitingForDebugger(bool waiting) {
+ connection_->SetWaitingForDebugger(waiting);
+}
+
+void AdbConnectionAppInfoCallback::SetUserId(int user_id) { connection_->SetUserId(user_id); }
+
class ScopedEventFdLock {
public:
explicit ScopedEventFdLock(int fd) : fd_(fd), data_(0) {
TEMP_FAILURE_RETRY(read(fd_, &data_, sizeof(data_)));
}
- ~ScopedEventFdLock() {
- TEMP_FAILURE_RETRY(write(fd_, &data_, sizeof(data_)));
- }
+ ~ScopedEventFdLock() { TEMP_FAILURE_RETRY(write(fd_, &data_, sizeof(data_))); }
private:
int fd_;
@@ -151,24 +247,25 @@ class ScopedEventFdLock {
};
AdbConnectionState::AdbConnectionState(const std::string& agent_name)
- : agent_name_(agent_name),
- controller_(this),
- ddm_callback_(this),
- sleep_event_fd_(-1),
- control_ctx_(nullptr, adbconnection_client_destroy),
- local_agent_control_sock_(-1),
- remote_agent_control_sock_(-1),
- adb_connection_socket_(-1),
- adb_write_event_fd_(-1),
- shutting_down_(false),
- agent_loaded_(false),
- agent_listening_(false),
- agent_has_socket_(false),
- sent_agent_fds_(false),
- performed_handshake_(false),
- notified_ddm_active_(false),
- next_ddm_id_(1),
- started_debugger_threads_(false) {
+ : agent_name_(agent_name),
+ controller_(this),
+ ddm_callback_(this),
+ appinfo_callback_(this),
+ sleep_event_fd_(-1),
+ control_ctx_(nullptr, adbconnection_client_destroy),
+ local_agent_control_sock_(-1),
+ remote_agent_control_sock_(-1),
+ adb_connection_socket_(-1),
+ adb_write_event_fd_(-1),
+ shutting_down_(false),
+ agent_loaded_(false),
+ agent_listening_(false),
+ agent_has_socket_(false),
+ sent_agent_fds_(false),
+ performed_handshake_(false),
+ notified_ddm_active_(false),
+ next_ddm_id_(1),
+ started_debugger_threads_(false) {
// Add the startup callback.
art::ScopedObjectAccess soa(art::Thread::Current());
art::Runtime::Current()->GetRuntimeCallbacks()->AddDebuggerControlCallback(&controller_);
@@ -200,8 +297,10 @@ static art::ObjPtr<art::mirror::Object> CreateAdbConnectionThread(art::Thread* s
art::Handle<art::mirror::Object> system_thread_group = hs.NewHandle(
system_thread_group_field->GetDeclaringClass()->GetFieldObject<art::mirror::Object>(
system_thread_group_field->GetOffset()));
- return art::WellKnownClasses::java_lang_Thread_init->NewObject<'L', 'L', 'I', 'Z'>(
- hs, self, system_thread_group, thr_name, /*priority=*/ 0, /*daemon=*/ true).Get();
+ return art::WellKnownClasses::java_lang_Thread_init
+ ->NewObject<'L', 'L', 'I', 'Z'>(
+ hs, self, system_thread_group, thr_name, /*priority=*/0, /*daemon=*/true)
+ .Get();
}
struct CallbackData {
@@ -211,9 +310,7 @@ struct CallbackData {
static void* CallbackFunction(void* vdata) {
std::unique_ptr<CallbackData> data(reinterpret_cast<CallbackData*>(vdata));
- art::Thread* self = art::Thread::Attach(kAdbConnectionThreadName,
- true,
- data->thr_);
+ art::Thread* self = art::Thread::Attach(kAdbConnectionThreadName, true, data->thr_);
CHECK(self != nullptr) << "threads_being_born_ should have ensured thread could be attached.";
// The name in Attach() is only for logging. Set the thread name. This is important so
// that the thread is no longer seen as starting up.
@@ -254,6 +351,7 @@ void AdbConnectionState::StartDebuggerThreads() {
{
art::ScopedObjectAccess soa(art::Thread::Current());
art::Runtime::Current()->GetRuntimeCallbacks()->AddDdmCallback(&ddm_callback_);
+ art::Runtime::Current()->GetRuntimeCallbacks()->AddAppInfoCallback(&appinfo_callback_);
}
// Setup the socketpair we use to talk to the agent.
bool has_sockets;
@@ -288,13 +386,11 @@ void AdbConnectionState::StartDebuggerThreads() {
}
// Note: Using pthreads instead of std::thread to not abort when the thread cannot be
// created (exception support required).
- std::unique_ptr<CallbackData> data(new CallbackData { this, thr });
+ std::unique_ptr<CallbackData> data(new CallbackData{this, thr});
started_debugger_threads_ = true;
gPthread.emplace();
- int pthread_create_result = pthread_create(&gPthread.value(),
- nullptr,
- &CallbackFunction,
- data.get());
+ int pthread_create_result =
+ pthread_create(&gPthread.value(), nullptr, &CallbackFunction, data.get());
if (pthread_create_result != 0) {
gPthread.reset();
started_debugger_threads_ = false;
@@ -309,9 +405,7 @@ void AdbConnectionState::StartDebuggerThreads() {
data.release(); // NOLINT pthreads API.
}
-static bool FlagsSet(int16_t data, int16_t flags) {
- return (data & flags) == flags;
-}
+static bool FlagsSet(int16_t data, int16_t flags) { return (data & flags) == flags; }
void AdbConnectionState::CloseFds() {
{
@@ -351,6 +445,41 @@ void AdbConnectionState::PublishDdmData(uint32_t type, const art::ArrayRef<const
SendDdmPacket(NextDdmId(), DdmPacketType::kCmd, type, data);
}
+void AdbConnectionState::SetCurrentProcessName(const std::string& process_name) {
+ DCHECK(IsDebuggableOrProfilable());
+ VLOG(jdwp) << "SetCurrentProcessName '" << process_name << "'";
+ apex_adbconnection_client_set_current_process_name(process_name.c_str());
+ WakeupPollLoop();
+}
+
+void AdbConnectionState::AddApplication(const std::string& package_name) {
+ DCHECK(IsDebuggableOrProfilable());
+ VLOG(jdwp) << "AddApplication'" << package_name << "'";
+ apex_adbconnection_client_add_application(package_name.c_str());
+ WakeupPollLoop();
+}
+
+void AdbConnectionState::RemoveApplication(const std::string& package_name) {
+ DCHECK(IsDebuggableOrProfilable());
+ VLOG(jdwp) << "RemoveApplication'" << package_name << "'";
+ apex_adbconnection_client_remove_application(package_name.c_str());
+ WakeupPollLoop();
+}
+
+void AdbConnectionState::SetWaitingForDebugger(bool waiting) {
+ DCHECK(IsDebuggableOrProfilable());
+ VLOG(jdwp) << "SetWaitingForDebugger'" << waiting << "'";
+ apex_adbconnection_client_set_waiting_for_debugger(waiting);
+ WakeupPollLoop();
+}
+
+void AdbConnectionState::SetUserId(int user_id) {
+ DCHECK(IsDebuggableOrProfilable());
+ VLOG(jdwp) << "SetUserId'" << user_id << "'";
+ apex_adbconnection_client_set_user_id(user_id);
+ WakeupPollLoop();
+}
+
void AdbConnectionState::SendDdmPacket(uint32_t id,
DdmPacketType packet_type,
uint32_t type,
@@ -533,7 +662,7 @@ bool AdbConnectionState::SetupAdbConnection() {
}
void AdbConnectionState::RunPollLoop(art::Thread* self) {
- DCHECK(IsDebuggingPossible() || art::Runtime::Current()->IsProfileableFromShell());
+ DCHECK(IsDebuggableOrProfilable());
CHECK_NE(agent_name_, "");
CHECK_EQ(self->GetState(), art::ThreadState::kNative);
art::Locks::mutator_lock_->AssertNotHeld(self);
@@ -547,6 +676,13 @@ void AdbConnectionState::RunPollLoop(art::Thread* self) {
}
while (!shutting_down_ && control_ctx_) {
bool should_listen_on_connection = !agent_has_socket_ && !sent_agent_fds_;
+ // By default we are always interested in read and hangup events on the control ctx.
+ int16_t interestingControlEventSet = POLLIN | POLLRDHUP;
+ if (apex_adbconnection_client_has_pending_update()) {
+ // If we have an update for ADBd, we also want to know when the control ctx
+ // socket is writable.
+ interestingControlEventSet |= POLLOUT;
+ }
struct pollfd pollfds[4] = {
{sleep_event_fd_, POLLIN, 0},
// -1 as an fd causes it to be ignored by poll
@@ -554,7 +690,7 @@ void AdbConnectionState::RunPollLoop(art::Thread* self) {
// Check for the control_sock_ actually going away. We always monitor for POLLIN, even if
// we already have an adbd socket. This allows to reject incoming debugger connection if
// there is already have one connected.
- {adbconnection_client_pollfd(control_ctx_.get()), POLLIN | POLLRDHUP, 0},
+ {adbconnection_client_pollfd(control_ctx_.get()), interestingControlEventSet, 0},
// if we have not loaded the agent either the adb_connection_socket_ is -1 meaning we
// don't have a real connection yet or the socket through adb needs to be listened to for
// incoming data that the agent or this plugin can handle.
@@ -564,8 +700,9 @@ void AdbConnectionState::RunPollLoop(art::Thread* self) {
PLOG(ERROR) << "Failed to poll!";
return;
}
- // We don't actually care about doing this we just use it to wake us up.
- // const struct pollfd& sleep_event_poll = pollfds[0];
+ VLOG(jdwp) << "adbconnection poll awakening";
+
+ const struct pollfd& sleep_event_poll = pollfds[0];
const struct pollfd& agent_control_sock_poll = pollfds[1];
const struct pollfd& control_sock_poll = pollfds[2];
const struct pollfd& adb_socket_poll = pollfds[3];
@@ -652,12 +789,19 @@ void AdbConnectionState::RunPollLoop(art::Thread* self) {
} else if (agent_listening_ && !sent_agent_fds_) {
VLOG(jdwp) << "Sending agent fds again on data.";
// Agent was already loaded so it can deal with the handshake.
- SendAgentFds(/*require_handshake=*/ true);
+ SendAgentFds(/*require_handshake=*/true);
}
+ } else if (FlagsSet(control_sock_poll.revents, POLLOUT)) {
+ VLOG(jdwp) << "Sending state update to adbd";
+ apex_adbconnection_client_send_update(control_ctx_.get());
} else if (FlagsSet(adb_socket_poll.revents, POLLRDHUP)) {
CHECK(IsDebuggingPossible()); // This path is unexpected for a profileable process.
DCHECK(!agent_has_socket_);
CloseFds();
+ } else if (FlagsSet(sleep_event_poll.revents, POLLIN)) {
+ // Poll was awakened via fdevent, we need to decrease fdevent counter to prevent poll from
+ // triggering again.
+ AcknowledgeWakeup();
} else {
VLOG(jdwp) << "Woke up poll without anything to do!";
}
@@ -916,19 +1060,36 @@ std::string AdbConnectionState::MakeAgentArg() {
return agent_name_ + "=" + parameters.join();
}
-void AdbConnectionState::StopDebuggerThreads() {
- // The regular agent system will take care of unloading the agent (if needed).
- shutting_down_ = true;
- // Wakeup the poll loop.
+void AdbConnectionState::WakeupPollLoop() {
+ if (!control_ctx_) {
+ return;
+ }
+
uint64_t data = 1;
if (sleep_event_fd_ != -1) {
TEMP_FAILURE_RETRY(write(sleep_event_fd_, &data, sizeof(data)));
}
}
+void AdbConnectionState::AcknowledgeWakeup() {
+ uint64_t data;
+ if (sleep_event_fd_ != -1) {
+ TEMP_FAILURE_RETRY(read(sleep_event_fd_, &data, sizeof(data)));
+ }
+}
+
+void AdbConnectionState::StopDebuggerThreads() {
+ // The regular agent system will take care of unloading the agent (if needed).
+ shutting_down_ = true;
+ WakeupPollLoop();
+}
+
// The plugin initialization function.
extern "C" bool ArtPlugin_Initialize() {
DCHECK(art::Runtime::Current()->GetJdwpProvider() == art::JdwpProvider::kAdbConnection);
+
+ RetrieveApexPointers();
+
// TODO Provide some way for apps to set this maybe?
gState.emplace(kDefaultJdwpAgentName);
return ValidateJdwpOptions(art::Runtime::Current()->GetJdwpOptions());
diff --git a/adbconnection/adbconnection.h b/adbconnection/adbconnection.h
index b6309e735d..d1d1e51067 100644
--- a/adbconnection/adbconnection.h
+++ b/adbconnection/adbconnection.h
@@ -24,6 +24,7 @@
#include <limits>
#include <memory>
+#include <string>
#include <vector>
#include "adbconnection/client.h"
@@ -69,6 +70,21 @@ struct AdbConnectionDdmCallback : public art::DdmCallback {
AdbConnectionState* connection_;
};
+struct AdbConnectionAppInfoCallback : public art::AppInfoCallback {
+ explicit AdbConnectionAppInfoCallback(AdbConnectionState* connection) : connection_(connection) {}
+
+ void AddApplication(const std::string& package_name) REQUIRES_SHARED(art::Locks::mutator_lock_);
+ void RemoveApplication(const std::string& package_name)
+ REQUIRES_SHARED(art::Locks::mutator_lock_);
+ void SetCurrentProcessName(const std::string& process_name)
+ REQUIRES_SHARED(art::Locks::mutator_lock_);
+ void SetWaitingForDebugger(bool waiting) REQUIRES_SHARED(art::Locks::mutator_lock_);
+ void SetUserId(int uid) REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+ private:
+ AdbConnectionState* connection_;
+};
+
class AdbConnectionState {
public:
explicit AdbConnectionState(const std::string& name);
@@ -82,6 +98,15 @@ class AdbConnectionState {
// hand-shaking yet.
void PublishDdmData(uint32_t type, const art::ArrayRef<const uint8_t>& data);
+ // Wake up the poll. Call this if the set of interesting event has changed. They will be
+ // recalculated and poll will be called again via fdevent write. This wakeup relies on fdevent
+ // and should be ACKed via AcknowledgeWakeup.
+ void WakeupPollLoop();
+
+ // After a call to WakeupPollLoop, the fdevent internal counter should be decreased via
+ // this method. This should be called after WakeupPollLoop was called and poll triggered.
+ void AcknowledgeWakeup();
+
// Stops debugger threads during shutdown.
void StopDebuggerThreads();
@@ -90,6 +115,22 @@ class AdbConnectionState {
return started_debugger_threads_;
}
+ // Should be called by Framework when it changes its process name.
+ void SetCurrentProcessName(const std::string& process_name);
+
+ // Should be called by Framework when it adds an app to a process.
+ // This can be called several times (see android:process)
+ void AddApplication(const std::string& package_name);
+
+ // Should be called by Framework when it removes an app from a process.
+ void RemoveApplication(const std::string& package_name);
+
+ // Should be called by Framework when its debugging state changes.
+ void SetWaitingForDebugger(bool waiting);
+
+ // Should be called by Framework when the UserID is known.
+ void SetUserId(int uid);
+
private:
uint32_t NextDdmId();
@@ -121,6 +162,7 @@ class AdbConnectionState {
AdbConnectionDebuggerController controller_;
AdbConnectionDdmCallback ddm_callback_;
+ AdbConnectionAppInfoCallback appinfo_callback_;
// Eventfd used to allow the StopDebuggerThreads function to wake up sleeping threads
android::base::unique_fd sleep_event_fd_;
diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc
index 137b04fede..18120fe826 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -55,13 +55,16 @@ namespace art HIDDEN {
static jobjectArray VMDebug_getVmFeatureList(JNIEnv* env, jclass) {
ScopedObjectAccess soa(Thread::ForEnv(env));
- return soa.AddLocalReference<jobjectArray>(CreateStringArray(soa.Self(), {
- "method-trace-profiling",
- "method-trace-profiling-streaming",
- "method-sample-profiling",
- "hprof-heap-dump",
- "hprof-heap-dump-streaming",
- }));
+ return soa.AddLocalReference<jobjectArray>(
+ CreateStringArray(soa.Self(),
+ {
+ "method-trace-profiling",
+ "method-trace-profiling-streaming",
+ "method-sample-profiling",
+ "hprof-heap-dump",
+ "hprof-heap-dump-streaming",
+ "app_info",
+ }));
}
static void VMDebug_startAllocCounting(JNIEnv*, jclass) {
@@ -514,6 +517,49 @@ static void VMDebug_setAllocTrackerStackDepth(JNIEnv* env, jclass, jint stack_de
}
}
+static void VMDebug_setCurrentProcessName(JNIEnv* env, jclass, jstring process_name) {
+ ScopedFastNativeObjectAccess soa(env);
+
+ // Android application ID naming convention states:
+ // "The name can contain uppercase or lowercase letters, numbers, and underscores ('_')"
+ // This is fine to convert to std::string
+ const char* c_process_name = env->GetStringUTFChars(process_name, NULL);
+ Runtime::Current()->GetRuntimeCallbacks()->SetCurrentProcessName(std::string(c_process_name));
+ env->ReleaseStringUTFChars(process_name, c_process_name);
+}
+
+static void VMDebug_addApplication(JNIEnv* env, jclass, jstring package_name) {
+ ScopedFastNativeObjectAccess soa(env);
+
+ // Android application ID naming convention states:
+ // "The name can contain uppercase or lowercase letters, numbers, and underscores ('_')"
+ // This is fine to convert to std::string
+ const char* c_package_name = env->GetStringUTFChars(package_name, NULL);
+ Runtime::Current()->GetRuntimeCallbacks()->AddApplication(std::string(c_package_name));
+ env->ReleaseStringUTFChars(package_name, c_package_name);
+}
+
+static void VMDebug_removeApplication(JNIEnv* env, jclass, jstring package_name) {
+ ScopedFastNativeObjectAccess soa(env);
+
+ // Android application ID naming convention states:
+ // "The name can contain uppercase or lowercase letters, numbers, and underscores ('_')"
+ // This is fine to convert to std::string
+ const char* c_package_name = env->GetStringUTFChars(package_name, NULL);
+ Runtime::Current()->GetRuntimeCallbacks()->RemoveApplication(std::string(c_package_name));
+ env->ReleaseStringUTFChars(package_name, c_package_name);
+}
+
+static void VMDebug_setWaitingForDebugger(JNIEnv* env, jclass, jboolean waiting) {
+ ScopedFastNativeObjectAccess soa(env);
+ Runtime::Current()->GetRuntimeCallbacks()->SetWaitingForDebugger(waiting);
+}
+
+static void VMDebug_setUserId(JNIEnv* env, jclass, jint user_id) {
+ ScopedFastNativeObjectAccess soa(env);
+ Runtime::Current()->GetRuntimeCallbacks()->SetUserId(user_id);
+}
+
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(VMDebug, countInstancesOfClass, "(Ljava/lang/Class;Z)J"),
NATIVE_METHOD(VMDebug, countInstancesOfClasses, "([Ljava/lang/Class;Z)[J"),
@@ -542,6 +588,11 @@ static JNINativeMethod gMethods[] = {
NATIVE_METHOD(VMDebug, nativeAttachAgent, "(Ljava/lang/String;Ljava/lang/ClassLoader;)V"),
NATIVE_METHOD(VMDebug, allowHiddenApiReflectionFrom, "(Ljava/lang/Class;)V"),
NATIVE_METHOD(VMDebug, setAllocTrackerStackDepth, "(I)V"),
+ NATIVE_METHOD(VMDebug, setCurrentProcessName, "(Ljava/lang/String;)V"),
+ NATIVE_METHOD(VMDebug, setWaitingForDebugger, "(Z)V"),
+ NATIVE_METHOD(VMDebug, addApplication, "(Ljava/lang/String;)V"),
+ NATIVE_METHOD(VMDebug, removeApplication, "(Ljava/lang/String;)V"),
+ NATIVE_METHOD(VMDebug, setUserId, "(I)V"),
};
void register_dalvik_system_VMDebug(JNIEnv* env) {
diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc
index aad943020d..0b10d2ee1d 100644
--- a/runtime/runtime_callbacks.cc
+++ b/runtime/runtime_callbacks.cc
@@ -58,12 +58,52 @@ void RuntimeCallbacks::RemoveDdmCallback(DdmCallback* cb) {
Remove(cb, &ddm_callbacks_);
}
+void RuntimeCallbacks::AddAppInfoCallback(AppInfoCallback* cb) {
+ WriterMutexLock mu(Thread::Current(), *callback_lock_);
+ appinfo_callbacks_.push_back(cb);
+}
+
+void RuntimeCallbacks::RemoveAppInfoCallback(AppInfoCallback* cb) {
+ WriterMutexLock mu(Thread::Current(), *callback_lock_);
+ Remove(cb, &appinfo_callbacks_);
+}
+
void RuntimeCallbacks::DdmPublishChunk(uint32_t type, const ArrayRef<const uint8_t>& data) {
for (DdmCallback* cb : COPY(ddm_callbacks_)) {
cb->DdmPublishChunk(type, data);
}
}
+void RuntimeCallbacks::SetCurrentProcessName(const std::string& process_name) {
+ for (AppInfoCallback* cb : COPY(appinfo_callbacks_)) {
+ cb->SetCurrentProcessName(process_name);
+ }
+}
+
+void RuntimeCallbacks::AddApplication(const std::string& package_name) {
+ for (AppInfoCallback* cb : COPY(appinfo_callbacks_)) {
+ cb->AddApplication(package_name);
+ }
+}
+
+void RuntimeCallbacks::RemoveApplication(const std::string& package_name) {
+ for (AppInfoCallback* cb : COPY(appinfo_callbacks_)) {
+ cb->RemoveApplication(package_name);
+ }
+}
+
+void RuntimeCallbacks::SetWaitingForDebugger(bool waiting) {
+ for (AppInfoCallback* cb : COPY(appinfo_callbacks_)) {
+ cb->SetWaitingForDebugger(waiting);
+ }
+}
+
+void RuntimeCallbacks::SetUserId(int user_id) {
+ for (AppInfoCallback* cb : COPY(appinfo_callbacks_)) {
+ cb->SetUserId(user_id);
+ }
+}
+
void RuntimeCallbacks::AddDebuggerControlCallback(DebuggerControlCallback* cb) {
WriterMutexLock mu(Thread::Current(), *callback_lock_);
debugger_control_callbacks_.push_back(cb);
diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h
index 9d7e199bfa..4e355151da 100644
--- a/runtime/runtime_callbacks.h
+++ b/runtime/runtime_callbacks.h
@@ -70,6 +70,19 @@ class DdmCallback {
REQUIRES_SHARED(Locks::mutator_lock_) = 0;
};
+class AppInfoCallback {
+ public:
+ virtual ~AppInfoCallback() {}
+ virtual void SetCurrentProcessName(const std::string& process_name)
+ REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+ virtual void AddApplication(const std::string& package_name)
+ REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+ virtual void RemoveApplication(const std::string& package_name)
+ REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+ virtual void SetWaitingForDebugger(bool waiting) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+ virtual void SetUserId(int uid) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+};
+
class DebuggerControlCallback {
public:
virtual ~DebuggerControlCallback() {}
@@ -240,6 +253,15 @@ class EXPORT RuntimeCallbacks {
void AddDdmCallback(DdmCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
void RemoveDdmCallback(DdmCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
+ void SetCurrentProcessName(const std::string& process_name) REQUIRES_SHARED(Locks::mutator_lock_);
+ void AddApplication(const std::string& package_name) REQUIRES_SHARED(Locks::mutator_lock_);
+ void RemoveApplication(const std::string& package_name) REQUIRES_SHARED(Locks::mutator_lock_);
+ void SetWaitingForDebugger(bool waiting) REQUIRES_SHARED(Locks::mutator_lock_);
+ void SetUserId(int uid) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ void AddAppInfoCallback(AppInfoCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
+ void RemoveAppInfoCallback(AppInfoCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
+
void StartDebugger() REQUIRES_SHARED(Locks::mutator_lock_);
// NO_THREAD_SAFETY_ANALYSIS since this is only called when we are in the middle of shutting down
// and the mutator_lock_ is no longer acquirable.
@@ -279,6 +301,7 @@ class EXPORT RuntimeCallbacks {
GUARDED_BY(callback_lock_);
std::vector<DdmCallback*> ddm_callbacks_
GUARDED_BY(callback_lock_);
+ std::vector<AppInfoCallback*> appinfo_callbacks_ GUARDED_BY(callback_lock_);
std::vector<DebuggerControlCallback*> debugger_control_callbacks_
GUARDED_BY(callback_lock_);
std::vector<ReflectiveValueVisitCallback*> reflective_value_visit_callbacks_