Skip to content

Commit a212e95

Browse files
hoxyqmeta-codesync[bot]
authored andcommitted
Define TracingDelegate for Android Host (#54628)
Summary: Pull Request resolved: #54628 # Changelog: [Internal] An implemementation of HostTargetTracingDelegate on the android side. Notes: - `getTracingState_next()` function will replace `getTracingState()` in the diff on top. - `TracingStateListener `is temporarily added to `devsupport/interfaces`, will be moved to `devsupport/inspector` in one of the diffs on top. Reviewed By: sbuggay Differential Revision: D87123723 fbshipit-source-id: 02083e3a63a2effc0dbbc7284b61c41a4c63584a
1 parent be0dae0 commit a212e95

File tree

4 files changed

+275
-2
lines changed

4 files changed

+275
-2
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
package com.facebook.react.devsupport.interfaces
9+
10+
import com.facebook.proguard.annotations.DoNotStripAny
11+
12+
@DoNotStripAny
13+
internal fun interface TracingStateListener {
14+
public fun onStateChanged(state: TracingState, screenshotsEnabled: Boolean)
15+
}

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostInspectorTarget.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import com.facebook.react.bridge.UiThreadUtil
1313
import com.facebook.react.common.annotations.FrameworkAPI
1414
import com.facebook.react.common.annotations.UnstableReactNativeAPI
1515
import com.facebook.react.devsupport.interfaces.TracingState
16+
import com.facebook.react.devsupport.interfaces.TracingStateListener
1617
import com.facebook.react.devsupport.perfmonitor.PerfMonitorInspectorTarget
1718
import com.facebook.react.devsupport.perfmonitor.PerfMonitorUpdateListener
1819
import com.facebook.soloader.SoLoader
@@ -43,6 +44,12 @@ internal class ReactHostInspectorTarget(reactHostImpl: ReactHostImpl) :
4344

4445
external fun tracingStateAsInt(): Int
4546

47+
external fun getTracingState_next(): TracingState
48+
49+
external fun registerTracingStateListener(listener: TracingStateListener): Long
50+
51+
external fun unregisterTracingStateListener(subscriptionId: Long)
52+
4653
override fun getTracingState(): TracingState {
4754
return TracingState.entries[tracingStateAsInt()]
4855
}

packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactHostInspectorTarget.cpp

Lines changed: 153 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,36 @@ using namespace facebook::jni;
1616
using namespace facebook::react::jsinspector_modern;
1717

1818
namespace facebook::react {
19+
20+
namespace {
21+
jni::local_ref<JTracingState::javaobject> convertCPPTracingStateToJava(
22+
TracingState tracingState) {
23+
auto tracingStateClass = jni::findClassLocal(
24+
"com/facebook/react/devsupport/interfaces/TracingState");
25+
auto valueOfMethod =
26+
tracingStateClass->getStaticMethod<JTracingState(jstring)>("valueOf");
27+
28+
switch (tracingState) {
29+
case TracingState::Disabled:
30+
return valueOfMethod(
31+
tracingStateClass, jni::make_jstring("DISABLED").get());
32+
33+
case TracingState::EnabledInBackgroundMode:
34+
return valueOfMethod(
35+
tracingStateClass,
36+
jni::make_jstring("ENABLEDINBACKGROUNDMODE").get());
37+
38+
case TracingState::EnabledInCDPMode:
39+
return valueOfMethod(
40+
tracingStateClass, jni::make_jstring("ENABLEDINCDPMODE").get());
41+
42+
default:
43+
jni::throwNewJavaException(
44+
"java/lang/IllegalStateException", "Unexpected new TracingState.");
45+
}
46+
}
47+
} // namespace
48+
1949
JReactHostInspectorTarget::JReactHostInspectorTarget(
2050
alias_ref<JReactHostInspectorTarget::javaobject> jobj,
2151
alias_ref<JReactHostImpl> reactHostImpl,
@@ -29,7 +59,8 @@ JReactHostInspectorTarget::JReactHostInspectorTarget(
2959
std::function<void()>&& callback) mutable {
3060
auto jrunnable = JNativeRunnable::newObjectCxxArgs(std::move(callback));
3161
javaExecutor->execute(jrunnable);
32-
}) {
62+
}),
63+
tracingDelegate_(std::make_unique<TracingDelegate>()) {
3364
auto& inspectorFlags = InspectorFlags::getInstance();
3465
if (inspectorFlags.getFuseboxEnabled()) {
3566
inspectorTarget_ = HostTarget::create(*this, inspectorExecutor_);
@@ -216,6 +247,14 @@ void JReactHostInspectorTarget::registerNatives() {
216247
JReactHostInspectorTarget::stopAndDiscardBackgroundTrace),
217248
makeNativeMethod(
218249
"tracingStateAsInt", JReactHostInspectorTarget::tracingState),
250+
makeNativeMethod(
251+
"getTracingState_next", JReactHostInspectorTarget::getTracingState),
252+
makeNativeMethod(
253+
"registerTracingStateListener",
254+
JReactHostInspectorTarget::registerTracingStateListener),
255+
makeNativeMethod(
256+
"unregisterTracingStateListener",
257+
JReactHostInspectorTarget::unregisterTracingStateListener),
219258
});
220259
}
221260

@@ -224,4 +263,117 @@ jint JReactHostInspectorTarget::tracingState() {
224263
return static_cast<jint>(state);
225264
}
226265

266+
jni::local_ref<JTracingState::javaobject>
267+
JReactHostInspectorTarget::getTracingState() {
268+
return convertCPPTracingStateToJava(tracingDelegate_->getTracingState());
269+
}
270+
271+
jlong JReactHostInspectorTarget::registerTracingStateListener(
272+
jni::alias_ref<JTracingStateListener::javaobject> listener) {
273+
auto cppListener = [globalRef = make_global(listener)](
274+
TracingState state, bool screenshotsEnabled) {
275+
static auto method =
276+
globalRef->getClass()
277+
->getMethod<void(
278+
jni::local_ref<JTracingState::javaobject>, jboolean)>(
279+
"onStateChanged");
280+
281+
method(
282+
globalRef,
283+
convertCPPTracingStateToJava(state),
284+
static_cast<jboolean>(screenshotsEnabled));
285+
};
286+
287+
return static_cast<jlong>(
288+
tracingDelegate_->registerTracingStateListener(std::move(cppListener)));
289+
}
290+
291+
void JReactHostInspectorTarget::unregisterTracingStateListener(
292+
jlong subscriptionId) {
293+
tracingDelegate_->unregisterTracingStateListener(subscriptionId);
294+
}
295+
296+
HostTargetTracingDelegate* JReactHostInspectorTarget::getTracingDelegate() {
297+
return tracingDelegate_.get();
298+
}
299+
300+
void TracingDelegate::onTracingStarted(
301+
tracing::Mode tracingMode,
302+
bool screenshotsCategoryEnabled) {
303+
TracingState nextState = TracingState::Disabled;
304+
switch (tracingMode) {
305+
case tracing::Mode::CDP:
306+
nextState = TracingState::EnabledInCDPMode;
307+
break;
308+
case tracing::Mode::Background:
309+
nextState = TracingState::EnabledInBackgroundMode;
310+
break;
311+
default:
312+
throw std::logic_error("Unexpected new Tracing Mode");
313+
}
314+
315+
std::vector<TracingStateListener> listeners;
316+
{
317+
std::lock_guard<std::mutex> lock(mutex_);
318+
319+
tracingState_ = nextState;
320+
listeners = copySubscribedListeners();
321+
}
322+
323+
notifyListeners(listeners, nextState, screenshotsCategoryEnabled);
324+
}
325+
326+
void TracingDelegate::onTracingStopped() {
327+
std::vector<TracingStateListener> listeners;
328+
{
329+
std::lock_guard<std::mutex> lock(mutex_);
330+
331+
tracingState_ = TracingState::Disabled;
332+
listeners = copySubscribedListeners();
333+
}
334+
335+
notifyListeners(listeners, TracingState::Disabled, false);
336+
}
337+
338+
TracingState TracingDelegate::getTracingState() {
339+
std::lock_guard<std::mutex> lock(mutex_);
340+
341+
return tracingState_;
342+
}
343+
344+
size_t TracingDelegate::registerTracingStateListener(
345+
TracingStateListener listener) {
346+
std::lock_guard<std::mutex> lock(mutex_);
347+
348+
auto id = nextSubscriptionId_++;
349+
subscriptions_[id] = std::move(listener);
350+
return id;
351+
}
352+
353+
void TracingDelegate::unregisterTracingStateListener(size_t subscriptionId) {
354+
std::lock_guard<std::mutex> lock(mutex_);
355+
356+
subscriptions_.erase(subscriptionId);
357+
}
358+
359+
std::vector<TracingStateListener> TracingDelegate::copySubscribedListeners() {
360+
std::vector<TracingStateListener> listeners;
361+
listeners.reserve(subscriptions_.size());
362+
363+
for (auto& [_, listener] : subscriptions_) {
364+
listeners.push_back(listener);
365+
}
366+
367+
return listeners;
368+
}
369+
370+
void TracingDelegate::notifyListeners(
371+
const std::vector<TracingStateListener>& listeners,
372+
TracingState state,
373+
bool screenshotsCategoryEnabled) {
374+
for (const auto& listener : listeners) {
375+
listener(state, screenshotsCategoryEnabled);
376+
}
377+
}
378+
227379
} // namespace facebook::react

packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactHostInspectorTarget.h

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,15 @@
88
#pragma once
99

1010
#include <fbjni/fbjni.h>
11+
1112
#include <jsinspector-modern/HostTarget.h>
1213
#include <react/jni/InspectorNetworkRequestListener.h>
1314
#include <react/jni/JExecutor.h>
15+
16+
#include <mutex>
17+
#include <optional>
1418
#include <string>
19+
#include <vector>
1520

1621
namespace facebook::react {
1722

@@ -20,7 +25,11 @@ struct JTaskInterface : public jni::JavaClass<JTaskInterface> {
2025
};
2126

2227
struct JTracingState : public jni::JavaClass<JTracingState> {
23-
static constexpr auto kJavaDescriptor = "Lcom/facebook/react/devsupport/TracingState;";
28+
static constexpr auto kJavaDescriptor = "Lcom/facebook/react/devsupport/interfaces/TracingState;";
29+
};
30+
31+
struct JTracingStateListener : public jni::JavaClass<JTracingStateListener> {
32+
static constexpr auto kJavaDescriptor = "Lcom/facebook/react/devsupport/interfaces/TracingStateListener;";
2433
};
2534

2635
struct JReactHostImpl : public jni::JavaClass<JReactHostImpl> {
@@ -55,6 +64,71 @@ struct JReactHostImpl : public jni::JavaClass<JReactHostImpl> {
5564
}
5665
};
5766

67+
enum class TracingState {
68+
Disabled,
69+
EnabledInBackgroundMode,
70+
EnabledInCDPMode,
71+
};
72+
73+
/**
74+
* A callback that will be invoked when tracing state has changed.
75+
*/
76+
using TracingStateListener = std::function<void(TracingState state, bool screenshotsCategoryEnabled)>;
77+
78+
class TracingDelegate : public jsinspector_modern::HostTargetTracingDelegate {
79+
public:
80+
void onTracingStarted(jsinspector_modern::tracing::Mode tracingMode, bool screenshotsCategoryEnabled) override;
81+
void onTracingStopped() override;
82+
83+
/**
84+
* A synchronous way to get the current tracing state.
85+
* Could be called from any thread.
86+
*/
87+
TracingState getTracingState();
88+
/**
89+
* Register a listener that will be notified when the tracing state changes.
90+
* Could be called from any thread.
91+
*/
92+
size_t registerTracingStateListener(TracingStateListener listener);
93+
/**
94+
* Unregister previously registered listener with the id returned from
95+
* TracingDelegate::registerTracingStateListener().
96+
*/
97+
void unregisterTracingStateListener(size_t subscriptionId);
98+
99+
private:
100+
/**
101+
* Covers read / write operations on tracingState_ and subscriptions_.
102+
*/
103+
std::mutex mutex_;
104+
/**
105+
* Since HostInspectorTarget creates HostTarget, the default value is Disabled.
106+
* However, the TracingDelegate is subscribed at the construction of HostTarget, so it will be notified as early as
107+
* possible.
108+
*/
109+
TracingState tracingState_ = TracingState::Disabled;
110+
/**
111+
* Map of subscription ID to listener.
112+
*/
113+
std::unordered_map<size_t, TracingStateListener> subscriptions_;
114+
/**
115+
* A counter for generating unique subscription IDs.
116+
*/
117+
uint64_t nextSubscriptionId_ = 0;
118+
/**
119+
* Returns a collection of listeners that are subscribed at the time of the call.
120+
* Expected to be only called with mutex_ locked.
121+
*/
122+
std::vector<TracingStateListener> copySubscribedListeners();
123+
/**
124+
* Notifies specified listeners about the state change.
125+
*/
126+
void notifyListeners(
127+
const std::vector<TracingStateListener> &listeners,
128+
TracingState state,
129+
bool screenshotsCategoryEnabled);
130+
};
131+
58132
class JReactHostInspectorTarget : public jni::HybridClass<JReactHostInspectorTarget>,
59133
public jsinspector_modern::HostTargetDelegate {
60134
public:
@@ -99,6 +173,26 @@ class JReactHostInspectorTarget : public jni::HybridClass<JReactHostInspectorTar
99173

100174
jsinspector_modern::HostTarget *getInspectorTarget();
101175

176+
/**
177+
* Get the current tracing state. Could be called from any thread.
178+
*/
179+
jni::local_ref<JTracingState::javaobject> getTracingState();
180+
181+
/**
182+
* Register a listener that will be notified when the tracing state changes.
183+
* Could be called from any thread.
184+
*
185+
* \return A unique subscription ID to use for unregistering the listener.
186+
*/
187+
jlong registerTracingStateListener(jni::alias_ref<JTracingStateListener::javaobject> listener);
188+
189+
/**
190+
* Unregister a previously registered tracing state listener.
191+
*
192+
* \param subscriptionId The subscription ID returned from JReactHostInspectorTarget::registerTracingStateListener.
193+
*/
194+
void unregisterTracingStateListener(jlong subscriptionId);
195+
102196
// HostTargetDelegate methods
103197
jsinspector_modern::HostTargetMetadata getMetadata() override;
104198
void onReload(const PageReloadRequest &request) override;
@@ -109,6 +203,7 @@ class JReactHostInspectorTarget : public jni::HybridClass<JReactHostInspectorTar
109203
jsinspector_modern::ScopedExecutor<jsinspector_modern::NetworkRequestListener> executor) override;
110204
std::optional<jsinspector_modern::tracing::TraceRecordingState>
111205
unstable_getTraceRecordingThatWillBeEmittedOnInitialization() override;
206+
jsinspector_modern::HostTargetTracingDelegate *getTracingDelegate() override;
112207

113208
private:
114209
JReactHostInspectorTarget(
@@ -140,6 +235,10 @@ class JReactHostInspectorTarget : public jni::HybridClass<JReactHostInspectorTar
140235
* CDP session is created.
141236
*/
142237
std::optional<jsinspector_modern::tracing::TraceRecordingState> stashedTraceRecordingState_;
238+
/**
239+
* Encapsulates the logic around tracing for this HostInspectorTarget.
240+
*/
241+
std::unique_ptr<TracingDelegate> tracingDelegate_;
143242

144243
friend HybridBase;
145244
};

0 commit comments

Comments
 (0)