diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index a7e2d1dcceec7..a82c91e2352b3 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright 2020 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. @@ -432,6 +432,7 @@ public class StatsPullAtomService extends SystemService { mContext = context; } + private native void initializeNativePullers(); /** * Use of this StatsPullAtomCallbackImpl means we avoid one class per tagId, which we would * get if we used lambdas. @@ -713,6 +714,7 @@ public class StatsPullAtomService extends SystemService { super.onBootPhase(phase); if (phase == PHASE_SYSTEM_SERVICES_READY) { BackgroundThread.getHandler().post(() -> { + initializeNativePullers(); // Initialize pullers that need JNI. initializePullersState(); registerPullers(); registerEventListeners(); diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 74fbb428fe024..4ce9591807538 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -34,6 +34,7 @@ cc_library_static { "gnss/GnssMeasurement.cpp", "gnss/GnssMeasurementCallback.cpp", "gnss/Utils.cpp", + "stats/SurfaceFlingerPuller.cpp", "com_android_server_adb_AdbDebuggingManager.cpp", "com_android_server_am_BatteryStatsService.cpp", "com_android_server_biometrics_SurfaceToNativeHandleConverter.cpp", @@ -53,6 +54,7 @@ cc_library_static { "com_android_server_SerialService.cpp", "com_android_server_soundtrigger_middleware_AudioSessionProviderImpl.cpp", "com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker.cpp", + "com_android_server_stats_pull_StatsPullAtomService.cpp", "com_android_server_storage_AppFuseBridge.cpp", "com_android_server_SystemServer.cpp", "com_android_server_tv_TvUinputBridge.cpp", @@ -127,6 +129,7 @@ cc_defaults { "libsensorservice", "libsensorservicehidl", "libgui", + "libtimestats_atoms_proto", "libusbhost", "libtinyalsa", "libEGL", diff --git a/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp b/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp new file mode 100644 index 0000000000000..46fe595d49af7 --- /dev/null +++ b/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp @@ -0,0 +1,61 @@ +/* + * Copyright 2020 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. + */ + +#define LOG_TAG "StatsPullAtomService" + +#include +#include +#include +#include +#include +#include + +#include "stats/SurfaceFlingerPuller.h" + +namespace android { + +static server::stats::SurfaceFlingerPuller gSurfaceFlingerPuller; + +static AStatsManager_PullAtomCallbackReturn onSurfaceFlingerPullCallback(int32_t atom_tag, + AStatsEventList* data, + void* cookie) { + return gSurfaceFlingerPuller.pull(atom_tag, data); +} + +static void initializeNativePullers(JNIEnv* env, jobject javaObject) { + // Surface flinger layer & global info. + gSurfaceFlingerPuller = server::stats::SurfaceFlingerPuller(); + AStatsManager_setPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, + /* metadata= */ nullptr, onSurfaceFlingerPullCallback, + /* cookie= */ nullptr); + AStatsManager_setPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO, + /* metadata= */ nullptr, onSurfaceFlingerPullCallback, + /* cookie= */ nullptr); +} + +static const JNINativeMethod sMethods[] = { + {"initializeNativePullers", "()V", (void*)initializeNativePullers}}; + +int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env) { + int res = jniRegisterNativeMethods(env, "com/android/server/stats/pull/StatsPullAtomService", + sMethods, NELEM(sMethods)); + if (res < 0) { + ALOGE("failed to register native methods"); + } + return res; +} + +} // namespace android \ No newline at end of file diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index f257686cbf3d3..b043e643b2bd0 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -61,6 +61,7 @@ int register_android_server_com_android_server_pm_PackageManagerShellCommandData int register_android_server_AdbDebuggingManager(JNIEnv* env); int register_android_server_FaceService(JNIEnv* env); int register_android_server_GpuService(JNIEnv* env); +int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env); }; using namespace android; @@ -115,5 +116,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_AdbDebuggingManager(env); register_android_server_FaceService(env); register_android_server_GpuService(env); + register_android_server_stats_pull_StatsPullAtomService(env); return JNI_VERSION_1_4; } diff --git a/services/core/jni/stats/OWNERS b/services/core/jni/stats/OWNERS new file mode 100644 index 0000000000000..2611e5b6cee2e --- /dev/null +++ b/services/core/jni/stats/OWNERS @@ -0,0 +1,8 @@ +jeffreyhuang@google.com +jtnguyen@google.com +muhammadq@google.com +sharaieko@google.com +singhtejinder@google.com +tsaichristine@google.com +yaochen@google.com +yro@google.com diff --git a/services/core/jni/stats/SurfaceFlingerPuller.cpp b/services/core/jni/stats/SurfaceFlingerPuller.cpp new file mode 100644 index 0000000000000..0e28da764ea79 --- /dev/null +++ b/services/core/jni/stats/SurfaceFlingerPuller.cpp @@ -0,0 +1,168 @@ +/* + * Copyright 2021 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. + */ + +#define LOG_TAG "SurfaceFlingerPuller" + +#include "SurfaceFlingerPuller.h" + +#include +#include +#include +#include + +#include + +namespace android { +namespace server { +namespace stats { + +using android::util::BytesField; +using std::optional; + +namespace { +optional getBytes(const google::protobuf::MessageLite& proto, std::string& data) { + if (!proto.SerializeToString(&data)) { + ALOGW("Unable to serialize surface flinger bytes field"); + return std::nullopt; + } + return {BytesField(data.data(), data.size())}; +} +} // namespace + +AStatsManager_PullAtomCallbackReturn SurfaceFlingerPuller::pull(int32_t atomTag, + AStatsEventList* data) { + // Don't need mutexes here, since there is no global state. + // SurfaceComposerClient is thread safe, and surfaceflinger is internally thread safe. + + bool success = false; + std::string pullDataProto; + status_t err = SurfaceComposerClient::onPullAtom(atomTag, &pullDataProto, &success); + if (!success || err != NO_ERROR) { + ALOGW("Failed to pull atom %" PRId32 + " from surfaceflinger. Success is %d, binder status is %s", + atomTag, (int)success, binder::Status::exceptionToString(err).c_str()); + return AStatsManager_PULL_SKIP; + } + + switch (atomTag) { + case android::util::SURFACEFLINGER_STATS_GLOBAL_INFO: + return parseGlobalInfoPull(pullDataProto, data); + case android::util::SURFACEFLINGER_STATS_LAYER_INFO: + return parseLayerInfoPull(pullDataProto, data); + default: + ALOGW("Invalid atom id for surfaceflinger pullers: %" PRId32, atomTag); + return AStatsManager_PULL_SKIP; + } +} + +AStatsManager_PullAtomCallbackReturn SurfaceFlingerPuller::parseGlobalInfoPull( + const std::string& protoData, AStatsEventList* data) { + android::surfaceflinger::SurfaceflingerStatsGlobalInfoWrapper atomList; + if (!atomList.ParseFromString(protoData)) { + ALOGW("Error parsing surface flinger global stats to proto"); + return AStatsManager_PULL_SKIP; + } + + for (const auto& atom : atomList.atom()) { + // The strings must outlive the BytesFields, which only have a pointer to the data. + std::string frameDurationStr, renderEngineTimeStr, deadlineMissesStr, predictionErrorsStr; + optional frameDuration = getBytes(atom.frame_duration(), frameDurationStr); + optional renderEngineTime = + getBytes(atom.render_engine_timing(), renderEngineTimeStr); + optional deadlineMisses = + getBytes(atom.sf_deadline_misses(), deadlineMissesStr); + optional predictionErrors = + getBytes(atom.sf_prediction_errors(), predictionErrorsStr); + + // Fail if any serialization to bytes failed. + if (!frameDuration || !renderEngineTime || !deadlineMisses || !predictionErrors) { + return AStatsManager_PULL_SKIP; + } + + android::util::addAStatsEvent(data, android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, + atom.total_frames(), atom.missed_frames(), + atom.client_composition_frames(), atom.display_on_millis(), + atom.animation_millis(), atom.event_connection_count(), + frameDuration.value(), renderEngineTime.value(), + atom.total_timeline_frames(), atom.total_janky_frames(), + atom.total_janky_frames_with_long_cpu(), + atom.total_janky_frames_with_long_gpu(), + atom.total_janky_frames_sf_unattributed(), + atom.total_janky_frames_app_unattributed(), + atom.total_janky_frames_sf_scheduling(), + atom.total_jank_frames_sf_prediction_error(), + atom.total_jank_frames_app_buffer_stuffing(), + atom.display_refresh_rate_bucket(), deadlineMisses.value(), + predictionErrors.value(), atom.render_rate_bucket()); + } + return AStatsManager_PULL_SUCCESS; +} + +AStatsManager_PullAtomCallbackReturn SurfaceFlingerPuller::parseLayerInfoPull( + const std::string& protoData, AStatsEventList* data) { + android::surfaceflinger::SurfaceflingerStatsLayerInfoWrapper atomList; + if (!atomList.ParseFromString(protoData)) { + ALOGW("Error parsing surface flinger layer stats to proto"); + return AStatsManager_PULL_SKIP; + } + + for (const auto& atom : atomList.atom()) { + // The strings must outlive the BytesFields, which only have a pointer to the data. + std::string present2PresentStr, post2presentStr, acquire2PresentStr, latch2PresentStr, + desired2PresentStr, post2AcquireStr, frameRateVoteStr, appDeadlineMissesStr; + optional present2Present = + getBytes(atom.present_to_present(), present2PresentStr); + optional post2present = getBytes(atom.post_to_present(), post2presentStr); + optional acquire2Present = + getBytes(atom.acquire_to_present(), acquire2PresentStr); + optional latch2Present = getBytes(atom.latch_to_present(), latch2PresentStr); + optional desired2Present = + getBytes(atom.desired_to_present(), desired2PresentStr); + optional post2Acquire = getBytes(atom.post_to_acquire(), post2AcquireStr); + optional frameRateVote = getBytes(atom.set_frame_rate_vote(), frameRateVoteStr); + optional appDeadlineMisses = + getBytes(atom.app_deadline_misses(), appDeadlineMissesStr); + + // Fail if any serialization to bytes failed. + if (!present2Present || !post2present || !acquire2Present || !latch2Present || + !desired2Present || !post2Acquire || !frameRateVote || !appDeadlineMisses) { + return AStatsManager_PULL_SKIP; + } + + android::util::addAStatsEvent(data, android::util::SURFACEFLINGER_STATS_LAYER_INFO, + atom.layer_name().c_str(), atom.total_frames(), + atom.dropped_frames(), present2Present.value(), + post2present.value(), acquire2Present.value(), + latch2Present.value(), desired2Present.value(), + post2Acquire.value(), atom.late_acquire_frames(), + atom.bad_desired_present_frames(), atom.uid(), + atom.total_timeline_frames(), atom.total_janky_frames(), + atom.total_janky_frames_with_long_cpu(), + atom.total_janky_frames_with_long_gpu(), + atom.total_janky_frames_sf_unattributed(), + atom.total_janky_frames_app_unattributed(), + atom.total_janky_frames_sf_scheduling(), + atom.total_jank_frames_sf_prediction_error(), + atom.total_jank_frames_app_buffer_stuffing(), + atom.display_refresh_rate_bucket(), atom.render_rate_bucket(), + frameRateVote.value(), appDeadlineMisses.value()); + } + return AStatsManager_PULL_SUCCESS; +} + +} // namespace stats +} // namespace server +} // namespace android diff --git a/services/core/jni/stats/SurfaceFlingerPuller.h b/services/core/jni/stats/SurfaceFlingerPuller.h new file mode 100644 index 0000000000000..301af1d64a222 --- /dev/null +++ b/services/core/jni/stats/SurfaceFlingerPuller.h @@ -0,0 +1,44 @@ +/* + * Copyright 2021 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. + */ + +#pragma once + +#include +#include +#include + +namespace android { +namespace server { +namespace stats { + +/** + * Pulls data from surfaceflinger. + * The indirection is needed because surfaceflinger is a bootstrap process. + */ +class SurfaceFlingerPuller { +public: + AStatsManager_PullAtomCallbackReturn pull(int32_t atomTag, AStatsEventList* data); + +private: + AStatsManager_PullAtomCallbackReturn parseGlobalInfoPull(const std::string& protoData, + AStatsEventList* data); + AStatsManager_PullAtomCallbackReturn parseLayerInfoPull(const std::string& protoData, + AStatsEventList* data); +}; + +} // namespace stats +} // namespace server +} // namespace android \ No newline at end of file