Refactor GraphicsStatsService for updateability

Move GraphicsStatsService to android.graphics package.
Move GraphicsStatsService JNI from libservices.core to
libandroid_runtime.
Declare GraphicsStatsService ctor as the only @SystemApi.
Remove MemoryFile usage from GraphicsStatsService, but use
SharedMemory and other SDK APIs instead. This is done to
avoid using unstable API MemoryFile.getFileDescriptor.
Propose new SharedMemory.getFdDup API for next release, which
is hidden for now.
Refactor statsd puller to avoid proto serialization by moving
data directly into AStatsEventList.
"libprotoutil" is added as a static dependancy to libhwui, which
should be fine because its implementation does not link anything.

Bug: 146353313
Test: Ran "adb shell cmd stats pull-source 10068"
Test: Passed unit tests and GraphicsStatsValidationTest CTS
Change-Id: If16c5addbd519cba33e03bd84ac312595032e0e1
This commit is contained in:
Stan Iliev
2020-02-03 16:57:09 -05:00
parent 76d19db2e4
commit c90438175f
12 changed files with 367 additions and 429 deletions

View File

@@ -444,13 +444,6 @@ filegroup {
path: "core/java",
}
filegroup {
name: "graphicsstats_proto",
srcs: [
"libs/hwui/protos/graphicsstats.proto",
],
}
filegroup {
name: "libvibrator_aidl",
srcs: [

View File

@@ -27,6 +27,7 @@ import dalvik.system.VMRuntime;
import java.io.Closeable;
import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.DirectByteBuffer;
import java.nio.NioUtils;
@@ -272,6 +273,20 @@ public final class SharedMemory implements Parcelable, Closeable {
dest.writeFileDescriptor(mFileDescriptor);
}
/**
* Returns a dup'd ParcelFileDescriptor from the SharedMemory FileDescriptor.
* This obeys standard POSIX semantics, where the
* new file descriptor shared state such as file position with the
* original file descriptor.
* TODO: propose this method as a public or system API for next release to achieve parity with
* NDK ASharedMemory_dupFromJava.
*
* @hide
*/
public ParcelFileDescriptor getFdDup() throws IOException {
return ParcelFileDescriptor.dup(mFileDescriptor);
}
public static final @android.annotation.NonNull Parcelable.Creator<SharedMemory> CREATOR =
new Parcelable.Creator<SharedMemory>() {
@Override

View File

@@ -182,6 +182,7 @@ cc_library_shared {
"android_hardware_UsbRequest.cpp",
"android_hardware_location_ActivityRecognitionHardware.cpp",
"android_util_FileObserver.cpp",
"android/graphics/GraphicsStatsService.cpp",
"android/graphics/SurfaceTexture.cpp",
"android/opengl/poly_clip.cpp", // TODO: .arm
"android/opengl/util.cpp",
@@ -273,6 +274,7 @@ cc_library_shared {
"libstats_jni",
"libstatslog",
"server_configurable_flags",
"libstatspull",
],
export_shared_lib_headers: [
// AndroidRuntime.h depends on nativehelper/jni.h

View File

@@ -0,0 +1,195 @@
/*
* Copyright (C) 2017 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 "GraphicsStatsService"
#include <JankTracker.h>
#include <jni.h>
#include <log/log.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedUtfChars.h>
#include <service/GraphicsStatsService.h>
#include <stats_event.h>
#include <stats_pull_atom_callback.h>
#include <statslog.h>
#include "core_jni_helpers.h"
namespace android {
using namespace android::uirenderer;
static jint getAshmemSize(JNIEnv*, jobject) {
return sizeof(ProfileData);
}
static jlong createDump(JNIEnv*, jobject, jint fd, jboolean isProto) {
GraphicsStatsService::Dump* dump =
GraphicsStatsService::createDump(fd,
isProto ? GraphicsStatsService::DumpType::Protobuf
: GraphicsStatsService::DumpType::Text);
return reinterpret_cast<jlong>(dump);
}
static void addToDump(JNIEnv* env, jobject, jlong dumpPtr, jstring jpath, jstring jpackage,
jlong versionCode, jlong startTime, jlong endTime, jbyteArray jdata) {
std::string path;
const ProfileData* data = nullptr;
LOG_ALWAYS_FATAL_IF(jdata == nullptr && jpath == nullptr, "Path and data can't both be null");
ScopedByteArrayRO buffer{env};
if (jdata != nullptr) {
buffer.reset(jdata);
LOG_ALWAYS_FATAL_IF(buffer.size() != sizeof(ProfileData),
"Buffer size %zu doesn't match expected %zu!", buffer.size(),
sizeof(ProfileData));
data = reinterpret_cast<const ProfileData*>(buffer.get());
}
if (jpath != nullptr) {
ScopedUtfChars pathChars(env, jpath);
LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(),
"Failed to get path chars");
path.assign(pathChars.c_str(), pathChars.size());
}
ScopedUtfChars packageChars(env, jpackage);
LOG_ALWAYS_FATAL_IF(packageChars.size() <= 0 || !packageChars.c_str(),
"Failed to get path chars");
GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
LOG_ALWAYS_FATAL_IF(!dump, "null passed for dump pointer");
const std::string package(packageChars.c_str(), packageChars.size());
GraphicsStatsService::addToDump(dump, path, package, versionCode, startTime, endTime, data);
}
static void addFileToDump(JNIEnv* env, jobject, jlong dumpPtr, jstring jpath) {
ScopedUtfChars pathChars(env, jpath);
LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(), "Failed to get path chars");
const std::string path(pathChars.c_str(), pathChars.size());
GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
GraphicsStatsService::addToDump(dump, path);
}
static void finishDump(JNIEnv*, jobject, jlong dumpPtr) {
GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
GraphicsStatsService::finishDump(dump);
}
static void finishDumpInMemory(JNIEnv* env, jobject, jlong dumpPtr, jlong pulledData,
jboolean lastFullDay) {
GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
AStatsEventList* data = reinterpret_cast<AStatsEventList*>(pulledData);
GraphicsStatsService::finishDumpInMemory(dump, data, lastFullDay == JNI_TRUE);
}
static void saveBuffer(JNIEnv* env, jobject clazz, jstring jpath, jstring jpackage,
jlong versionCode, jlong startTime, jlong endTime, jbyteArray jdata) {
ScopedByteArrayRO buffer(env, jdata);
LOG_ALWAYS_FATAL_IF(buffer.size() != sizeof(ProfileData),
"Buffer size %zu doesn't match expected %zu!", buffer.size(),
sizeof(ProfileData));
ScopedUtfChars pathChars(env, jpath);
LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(), "Failed to get path chars");
ScopedUtfChars packageChars(env, jpackage);
LOG_ALWAYS_FATAL_IF(packageChars.size() <= 0 || !packageChars.c_str(),
"Failed to get path chars");
const std::string path(pathChars.c_str(), pathChars.size());
const std::string package(packageChars.c_str(), packageChars.size());
const ProfileData* data = reinterpret_cast<const ProfileData*>(buffer.get());
GraphicsStatsService::saveBuffer(path, package, versionCode, startTime, endTime, data);
}
static jobject gGraphicsStatsServiceObject = nullptr;
static jmethodID gGraphicsStatsService_pullGraphicsStatsMethodID;
static JNIEnv* getJNIEnv() {
JavaVM* vm = AndroidRuntime::getJavaVM();
JNIEnv* env = nullptr;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
if (vm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) {
LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!");
}
}
return env;
}
// graphicsStatsPullCallback is invoked by statsd service to pull GRAPHICS_STATS atom.
static AStatsManager_PullAtomCallbackReturn graphicsStatsPullCallback(int32_t atom_tag,
AStatsEventList* data,
void* cookie) {
JNIEnv* env = getJNIEnv();
if (!env) {
return false;
}
if (gGraphicsStatsServiceObject == nullptr) {
ALOGE("Failed to get graphicsstats service");
return AStatsManager_PULL_SKIP;
}
for (bool lastFullDay : {true, false}) {
env->CallVoidMethod(gGraphicsStatsServiceObject,
gGraphicsStatsService_pullGraphicsStatsMethodID,
(jboolean)(lastFullDay ? JNI_TRUE : JNI_FALSE),
reinterpret_cast<jlong>(data));
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
ALOGE("Failed to invoke graphicsstats service");
return AStatsManager_PULL_SKIP;
}
}
return AStatsManager_PULL_SUCCESS;
}
// Register a puller for GRAPHICS_STATS atom with the statsd service.
static void nativeInit(JNIEnv* env, jobject javaObject) {
gGraphicsStatsServiceObject = env->NewGlobalRef(javaObject);
AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain();
AStatsManager_PullAtomMetadata_setCoolDownNs(metadata, 10 * 1000000); // 10 milliseconds
AStatsManager_PullAtomMetadata_setTimeoutNs(metadata, 2 * NS_PER_SEC); // 2 seconds
AStatsManager_registerPullAtomCallback(android::util::GRAPHICS_STATS,
&graphicsStatsPullCallback, metadata, nullptr);
AStatsManager_PullAtomMetadata_release(metadata);
}
static void nativeDestructor(JNIEnv* env, jobject javaObject) {
AStatsManager_unregisterPullAtomCallback(android::util::GRAPHICS_STATS);
env->DeleteGlobalRef(gGraphicsStatsServiceObject);
gGraphicsStatsServiceObject = nullptr;
}
static const JNINativeMethod sMethods[] =
{{"nGetAshmemSize", "()I", (void*)getAshmemSize},
{"nCreateDump", "(IZ)J", (void*)createDump},
{"nAddToDump", "(JLjava/lang/String;Ljava/lang/String;JJJ[B)V", (void*)addToDump},
{"nAddToDump", "(JLjava/lang/String;)V", (void*)addFileToDump},
{"nFinishDump", "(J)V", (void*)finishDump},
{"nFinishDumpInMemory", "(JJZ)V", (void*)finishDumpInMemory},
{"nSaveBuffer", "(Ljava/lang/String;Ljava/lang/String;JJJ[B)V", (void*)saveBuffer},
{"nativeInit", "()V", (void*)nativeInit},
{"nativeDestructor", "()V", (void*)nativeDestructor}};
int register_android_graphics_GraphicsStatsService(JNIEnv* env) {
jclass graphicsStatsService_class =
FindClassOrDie(env, "android/graphics/GraphicsStatsService");
gGraphicsStatsService_pullGraphicsStatsMethodID =
GetMethodIDOrDie(env, graphicsStatsService_class, "pullGraphicsStats", "(ZJ)V");
return jniRegisterNativeMethods(env, "android/graphics/GraphicsStatsService", sMethods,
NELEM(sMethods));
}
} // namespace android

View File

@@ -14,8 +14,9 @@
* limitations under the License.
*/
package com.android.server;
package android.graphics;
import android.annotation.SystemApi;
import android.app.AlarmManager;
import android.app.AppOpsManager;
import android.content.Context;
@@ -26,13 +27,14 @@ import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.MemoryFile;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.SharedMemory;
import android.os.Trace;
import android.os.UserHandle;
import android.system.ErrnoException;
import android.util.Log;
import android.view.IGraphicsStats;
import android.view.IGraphicsStatsCallback;
@@ -45,6 +47,7 @@ import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
@@ -84,8 +87,8 @@ public class GraphicsStatsService extends IGraphicsStats.Stub {
// This isn't static because we need this to happen after registerNativeMethods, however
// the class is loaded (and thus static ctor happens) before that occurs.
private final int ASHMEM_SIZE = nGetAshmemSize();
private final byte[] ZERO_DATA = new byte[ASHMEM_SIZE];
private final int mAshmemSize = nGetAshmemSize();
private final byte[] mZeroData = new byte[mAshmemSize];
private final Context mContext;
private final AppOpsManager mAppOps;
@@ -97,6 +100,7 @@ public class GraphicsStatsService extends IGraphicsStats.Stub {
private Handler mWriteOutHandler;
private boolean mRotateIsScheduled = false;
@SystemApi
public GraphicsStatsService(Context context) {
mContext = context;
mAppOps = context.getSystemService(AppOpsManager.class);
@@ -108,7 +112,8 @@ public class GraphicsStatsService extends IGraphicsStats.Stub {
throw new IllegalStateException("Graphics stats directory does not exist: "
+ mGraphicsStatsDir.getAbsolutePath());
}
HandlerThread bgthread = new HandlerThread("GraphicsStats-disk", Process.THREAD_PRIORITY_BACKGROUND);
HandlerThread bgthread = new HandlerThread("GraphicsStats-disk",
Process.THREAD_PRIORITY_BACKGROUND);
bgthread.start();
mWriteOutHandler = new Handler(bgthread.getLooper(), new Handler.Callback() {
@@ -159,7 +164,7 @@ public class GraphicsStatsService extends IGraphicsStats.Stub {
active.mCallback.onRotateGraphicsStatsBuffer();
} catch (RemoteException e) {
Log.w(TAG, String.format("Failed to notify '%s' (pid=%d) to rotate buffers",
active.mInfo.packageName, active.mPid), e);
active.mInfo.mPackageName, active.mPid), e);
}
}
// Give a few seconds for everyone to rotate before doing the cleanup
@@ -167,8 +172,8 @@ public class GraphicsStatsService extends IGraphicsStats.Stub {
}
@Override
public ParcelFileDescriptor requestBufferForProcess(String packageName, IGraphicsStatsCallback token)
throws RemoteException {
public ParcelFileDescriptor requestBufferForProcess(String packageName,
IGraphicsStatsCallback token) throws RemoteException {
int uid = Binder.getCallingUid();
int pid = Binder.getCallingPid();
ParcelFileDescriptor pfd = null;
@@ -196,7 +201,7 @@ public class GraphicsStatsService extends IGraphicsStats.Stub {
// current day.
// This method is invoked from native code only.
@SuppressWarnings({"UnusedDeclaration"})
private long pullGraphicsStats(boolean lastFullDay) throws RemoteException {
private void pullGraphicsStats(boolean lastFullDay, long pulledData) throws RemoteException {
int uid = Binder.getCallingUid();
// DUMP and PACKAGE_USAGE_STATS permissions are required to invoke this method.
@@ -213,13 +218,13 @@ public class GraphicsStatsService extends IGraphicsStats.Stub {
long callingIdentity = Binder.clearCallingIdentity();
try {
return pullGraphicsStatsImpl(lastFullDay);
pullGraphicsStatsImpl(lastFullDay, pulledData);
} finally {
Binder.restoreCallingIdentity(callingIdentity);
}
}
private long pullGraphicsStatsImpl(boolean lastFullDay) {
private void pullGraphicsStatsImpl(boolean lastFullDay, long pulledData) {
long targetDay;
if (lastFullDay) {
// Get stats from yesterday. Stats stay constant, because the day is over.
@@ -235,7 +240,7 @@ public class GraphicsStatsService extends IGraphicsStats.Stub {
buffers = new ArrayList<>(mActive.size());
for (int i = 0; i < mActive.size(); i++) {
ActiveBuffer buffer = mActive.get(i);
if (buffer.mInfo.startTime == targetDay) {
if (buffer.mInfo.mStartTime == targetDay) {
try {
buffers.add(new HistoricalBuffer(buffer));
} catch (IOException ex) {
@@ -267,18 +272,7 @@ public class GraphicsStatsService extends IGraphicsStats.Stub {
}
}
} finally {
return nFinishDumpInMemory(dump);
}
}
private ParcelFileDescriptor getPfd(MemoryFile file) {
try {
if (!file.getFileDescriptor().valid()) {
throw new IllegalStateException("Invalid file descriptor");
}
return ParcelFileDescriptor.dup(file.getFileDescriptor());
} catch (IOException ex) {
throw new IllegalStateException("Failed to get PFD from memory file", ex);
nFinishDumpInMemory(dump, pulledData, lastFullDay);
}
}
@@ -286,7 +280,7 @@ public class GraphicsStatsService extends IGraphicsStats.Stub {
int uid, int pid, String packageName, long versionCode) throws RemoteException {
ActiveBuffer buffer = fetchActiveBuffersLocked(token, uid, pid, packageName, versionCode);
scheduleRotateLocked();
return getPfd(buffer.mProcessBuffer);
return buffer.getPfd();
}
private Calendar normalizeDate(long timestamp) {
@@ -301,13 +295,15 @@ public class GraphicsStatsService extends IGraphicsStats.Stub {
private File pathForApp(BufferInfo info) {
String subPath = String.format("%d/%s/%d/total",
normalizeDate(info.startTime).getTimeInMillis(), info.packageName, info.versionCode);
normalizeDate(info.mStartTime).getTimeInMillis(), info.mPackageName,
info.mVersionCode);
return new File(mGraphicsStatsDir, subPath);
}
private void saveBuffer(HistoricalBuffer buffer) {
if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "saving graphicsstats for " + buffer.mInfo.packageName);
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER,
"saving graphicsstats for " + buffer.mInfo.mPackageName);
}
synchronized (mFileAccessLock) {
File path = pathForApp(buffer.mInfo);
@@ -317,8 +313,9 @@ public class GraphicsStatsService extends IGraphicsStats.Stub {
Log.w(TAG, "Unable to create path: '" + parent.getAbsolutePath() + "'");
return;
}
nSaveBuffer(path.getAbsolutePath(), buffer.mInfo.packageName, buffer.mInfo.versionCode,
buffer.mInfo.startTime, buffer.mInfo.endTime, buffer.mData);
nSaveBuffer(path.getAbsolutePath(), buffer.mInfo.mPackageName,
buffer.mInfo.mVersionCode, buffer.mInfo.mStartTime, buffer.mInfo.mEndTime,
buffer.mData);
}
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
@@ -365,7 +362,7 @@ public class GraphicsStatsService extends IGraphicsStats.Stub {
HistoricalBuffer data = new HistoricalBuffer(buffer);
Message.obtain(mWriteOutHandler, SAVE_BUFFER, data).sendToTarget();
} catch (IOException e) {
Log.w(TAG, "Failed to copy graphicsstats from " + buffer.mInfo.packageName, e);
Log.w(TAG, "Failed to copy graphicsstats from " + buffer.mInfo.mPackageName, e);
}
buffer.closeAllBuffers();
}
@@ -386,7 +383,7 @@ public class GraphicsStatsService extends IGraphicsStats.Stub {
if (buffer.mPid == pid
&& buffer.mUid == uid) {
// If the buffer is too old we remove it and return a new one
if (buffer.mInfo.startTime < today) {
if (buffer.mInfo.mStartTime < today) {
buffer.binderDied();
break;
} else {
@@ -410,8 +407,8 @@ public class GraphicsStatsService extends IGraphicsStats.Stub {
HistoricalBuffer buffer = buffers.get(i);
File path = pathForApp(buffer.mInfo);
skipFiles.add(path);
nAddToDump(dump, path.getAbsolutePath(), buffer.mInfo.packageName,
buffer.mInfo.versionCode, buffer.mInfo.startTime, buffer.mInfo.endTime,
nAddToDump(dump, path.getAbsolutePath(), buffer.mInfo.mPackageName,
buffer.mInfo.mVersionCode, buffer.mInfo.mStartTime, buffer.mInfo.mEndTime,
buffer.mData);
}
return skipFiles;
@@ -478,20 +475,20 @@ public class GraphicsStatsService extends IGraphicsStats.Stub {
long versionCode, long startTime, long endTime, byte[] data);
private static native void nAddToDump(long dump, String path);
private static native void nFinishDump(long dump);
private static native long nFinishDumpInMemory(long dump);
private static native void nFinishDumpInMemory(long dump, long pulledData, boolean lastFullDay);
private static native void nSaveBuffer(String path, String packageName, long versionCode,
long startTime, long endTime, byte[] data);
private final class BufferInfo {
final String packageName;
final long versionCode;
long startTime;
long endTime;
final String mPackageName;
final long mVersionCode;
long mStartTime;
long mEndTime;
BufferInfo(String packageName, long versionCode, long startTime) {
this.packageName = packageName;
this.versionCode = versionCode;
this.startTime = startTime;
this.mPackageName = packageName;
this.mVersionCode = versionCode;
this.mStartTime = startTime;
}
}
@@ -501,7 +498,8 @@ public class GraphicsStatsService extends IGraphicsStats.Stub {
final int mPid;
final IGraphicsStatsCallback mCallback;
final IBinder mToken;
MemoryFile mProcessBuffer;
SharedMemory mProcessBuffer;
ByteBuffer mMapping;
ActiveBuffer(IGraphicsStatsCallback token, int uid, int pid, String packageName,
long versionCode)
@@ -512,8 +510,14 @@ public class GraphicsStatsService extends IGraphicsStats.Stub {
mCallback = token;
mToken = mCallback.asBinder();
mToken.linkToDeath(this, 0);
mProcessBuffer = new MemoryFile("GFXStats-" + pid, ASHMEM_SIZE);
mProcessBuffer.writeBytes(ZERO_DATA, 0, 0, ASHMEM_SIZE);
try {
mProcessBuffer = SharedMemory.create("GFXStats-" + pid, mAshmemSize);
mMapping = mProcessBuffer.mapReadWrite();
} catch (ErrnoException ex) {
ex.rethrowAsIOException();
}
mMapping.position(0);
mMapping.put(mZeroData, 0, mAshmemSize);
}
@Override
@@ -523,20 +527,40 @@ public class GraphicsStatsService extends IGraphicsStats.Stub {
}
void closeAllBuffers() {
if (mMapping != null) {
SharedMemory.unmap(mMapping);
mMapping = null;
}
if (mProcessBuffer != null) {
mProcessBuffer.close();
mProcessBuffer = null;
}
}
ParcelFileDescriptor getPfd() {
try {
return mProcessBuffer.getFdDup();
} catch (IOException ex) {
throw new IllegalStateException("Failed to get PFD from memory file", ex);
}
}
void readBytes(byte[] buffer, int count) throws IOException {
if (mMapping == null) {
throw new IOException("SharedMemory has been deactivated");
}
mMapping.position(0);
mMapping.get(buffer, 0, count);
}
}
private final class HistoricalBuffer {
final BufferInfo mInfo;
final byte[] mData = new byte[ASHMEM_SIZE];
final byte[] mData = new byte[mAshmemSize];
HistoricalBuffer(ActiveBuffer active) throws IOException {
mInfo = active.mInfo;
mInfo.endTime = System.currentTimeMillis();
active.mProcessBuffer.readBytes(mData, 0, 0, ASHMEM_SIZE);
mInfo.mEndTime = System.currentTimeMillis();
active.readBytes(mData, mAshmemSize);
}
}
}

View File

@@ -92,9 +92,12 @@ cc_defaults {
"libandroidfw",
"libcrypto",
"libsync",
"libstatspull",
"libstatssocket",
],
static_libs: [
"libEGL_blobCache",
"libprotoutil",
],
},
host: {

View File

@@ -26,9 +26,9 @@
#include <sys/types.h>
#include <unistd.h>
#include <algorithm>
#include <map>
#include <vector>
#include <android/util/ProtoOutputStream.h>
#include <stats_event.h>
#include <statslog.h>
#include "JankTracker.h"
#include "protos/graphicsstats.pb.h"
@@ -61,7 +61,7 @@ public:
}
}
bool valid() { return mFd != -1; }
operator int() { return mFd; } // NOLINT(google-explicit-constructor)
operator int() { return mFd; } // NOLINT(google-explicit-constructor)
private:
int mFd;
@@ -485,79 +485,82 @@ void GraphicsStatsService::finishDump(Dump* dump) {
delete dump;
}
class MemOutputStreamLite : public io::ZeroCopyOutputStream {
public:
explicit MemOutputStreamLite() : mCopyAdapter(), mImpl(&mCopyAdapter) {}
virtual ~MemOutputStreamLite() {}
using namespace google::protobuf;
virtual bool Next(void** data, int* size) override { return mImpl.Next(data, size); }
// Field ids taken from FrameTimingHistogram message in atoms.proto
#define TIME_MILLIS_BUCKETS_FIELD_NUMBER 1
#define FRAME_COUNTS_FIELD_NUMBER 2
virtual void BackUp(int count) override { mImpl.BackUp(count); }
virtual int64 ByteCount() const override { return mImpl.ByteCount(); }
bool Flush() { return mImpl.Flush(); }
void copyData(const DumpMemoryFn& reader, void* param1, void* param2) {
int bufferOffset = 0;
int totalSize = mCopyAdapter.mBuffersSize - mCopyAdapter.mCurrentBufferUnusedSize;
int totalDataLeft = totalSize;
for (auto& it : mCopyAdapter.mBuffers) {
int bufferSize = std::min(totalDataLeft, (int)it.size()); // last buffer is not full
reader(it.data(), bufferOffset, bufferSize, totalSize, param1, param2);
bufferOffset += bufferSize;
totalDataLeft -= bufferSize;
}
static void writeCpuHistogram(AStatsEvent* event,
const uirenderer::protos::GraphicsStatsProto& stat) {
util::ProtoOutputStream proto;
for (int bucketIndex = 0; bucketIndex < stat.histogram_size(); bucketIndex++) {
auto& bucket = stat.histogram(bucketIndex);
proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED |
TIME_MILLIS_BUCKETS_FIELD_NUMBER /* field id */,
(int)bucket.render_millis());
}
private:
struct MemAdapter : public io::CopyingOutputStream {
// Data is stored in an array of buffers.
// JNI SetByteArrayRegion assembles data in one continuous Java byte[] buffer.
std::vector<std::vector<unsigned char>> mBuffers;
int mBuffersSize = 0; // total bytes allocated in mBuffers
int mCurrentBufferUnusedSize = 0; // unused bytes in the last buffer mBuffers.back()
unsigned char* mCurrentBuffer = nullptr; // pointer to next free byte in mBuffers.back()
explicit MemAdapter() {}
virtual ~MemAdapter() {}
virtual bool Write(const void* buffer, int size) override {
while (size > 0) {
if (0 == mCurrentBufferUnusedSize) {
mCurrentBufferUnusedSize =
std::max(size, mBuffersSize ? 2 * mBuffersSize : 10000);
mBuffers.emplace_back();
mBuffers.back().resize(mCurrentBufferUnusedSize);
mCurrentBuffer = mBuffers.back().data();
mBuffersSize += mCurrentBufferUnusedSize;
}
int dataMoved = std::min(mCurrentBufferUnusedSize, size);
memcpy(mCurrentBuffer, buffer, dataMoved);
mCurrentBufferUnusedSize -= dataMoved;
mCurrentBuffer += dataMoved;
buffer = reinterpret_cast<const unsigned char*>(buffer) + dataMoved;
size -= dataMoved;
}
return true;
}
};
MemOutputStreamLite::MemAdapter mCopyAdapter;
io::CopyingOutputStreamAdaptor mImpl;
};
void GraphicsStatsService::finishDumpInMemory(Dump* dump, const DumpMemoryFn& reader, void* param1,
void* param2) {
MemOutputStreamLite stream;
dump->updateProto();
bool success = dump->proto().SerializeToZeroCopyStream(&stream) && stream.Flush();
delete dump;
if (!success) {
return;
for (int bucketIndex = 0; bucketIndex < stat.histogram_size(); bucketIndex++) {
auto& bucket = stat.histogram(bucketIndex);
proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
FRAME_COUNTS_FIELD_NUMBER /* field id */,
(long long)bucket.frame_count());
}
stream.copyData(reader, param1, param2);
std::vector<uint8_t> outVector;
proto.serializeToVector(&outVector);
AStatsEvent_writeByteArray(event, outVector.data(), outVector.size());
}
static void writeGpuHistogram(AStatsEvent* event,
const uirenderer::protos::GraphicsStatsProto& stat) {
util::ProtoOutputStream proto;
for (int bucketIndex = 0; bucketIndex < stat.gpu_histogram_size(); bucketIndex++) {
auto& bucket = stat.gpu_histogram(bucketIndex);
proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED |
TIME_MILLIS_BUCKETS_FIELD_NUMBER /* field id */,
(int)bucket.render_millis());
}
for (int bucketIndex = 0; bucketIndex < stat.gpu_histogram_size(); bucketIndex++) {
auto& bucket = stat.gpu_histogram(bucketIndex);
proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
FRAME_COUNTS_FIELD_NUMBER /* field id */,
(long long)bucket.frame_count());
}
std::vector<uint8_t> outVector;
proto.serializeToVector(&outVector);
AStatsEvent_writeByteArray(event, outVector.data(), outVector.size());
}
void GraphicsStatsService::finishDumpInMemory(Dump* dump, AStatsEventList* data,
bool lastFullDay) {
dump->updateProto();
auto& serviceDump = dump->proto();
for (int stat_index = 0; stat_index < serviceDump.stats_size(); stat_index++) {
auto& stat = serviceDump.stats(stat_index);
AStatsEvent* event = AStatsEventList_addStatsEvent(data);
AStatsEvent_setAtomId(event, android::util::GRAPHICS_STATS);
AStatsEvent_writeString(event, stat.package_name().c_str());
AStatsEvent_writeInt64(event, (int64_t)stat.version_code());
AStatsEvent_writeInt64(event, (int64_t)stat.stats_start());
AStatsEvent_writeInt64(event, (int64_t)stat.stats_end());
AStatsEvent_writeInt32(event, (int32_t)stat.pipeline());
AStatsEvent_writeInt32(event, (int32_t)stat.summary().total_frames());
AStatsEvent_writeInt32(event, (int32_t)stat.summary().missed_vsync_count());
AStatsEvent_writeInt32(event, (int32_t)stat.summary().high_input_latency_count());
AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_ui_thread_count());
AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_bitmap_upload_count());
AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_draw_count());
AStatsEvent_writeInt32(event, (int32_t)stat.summary().missed_deadline_count());
writeCpuHistogram(event, stat);
writeGpuHistogram(event, stat);
// TODO: fill in UI mainline module version, when the feature is available.
AStatsEvent_writeInt64(event, (int64_t)0);
AStatsEvent_writeBool(event, !lastFullDay);
AStatsEvent_build(event);
}
}
} /* namespace uirenderer */
} /* namespace android */

View File

@@ -20,6 +20,7 @@
#include "JankTracker.h"
#include "utils/Macros.h"
#include <stats_pull_atom_callback.h>
namespace android {
namespace uirenderer {
@@ -27,9 +28,6 @@ namespace protos {
class GraphicsStatsProto;
}
typedef void (*DumpMemoryFn)(void* buffer, int bufferOffset, int bufferSize, int totalSize,
void* param1, void* param2);
/*
* The exported entry points used by GraphicsStatsService.java in f/b/services/core
*
@@ -56,8 +54,8 @@ public:
int64_t startTime, int64_t endTime, const ProfileData* data);
ANDROID_API static void addToDump(Dump* dump, const std::string& path);
ANDROID_API static void finishDump(Dump* dump);
ANDROID_API static void finishDumpInMemory(Dump* dump, const DumpMemoryFn& reader, void* param1,
void* param2);
ANDROID_API static void finishDumpInMemory(Dump* dump, AStatsEventList* data,
bool lastFullDay);
// Visible for testing
static bool parseFromFile(const std::string& path, protos::GraphicsStatsProto* output);

View File

@@ -13,7 +13,6 @@ cc_library_static {
],
srcs: [
":graphicsstats_proto",
":lib_alarmManagerService_native",
"BroadcastRadio/JavaRef.cpp",
"BroadcastRadio/NativeCallbackThread.cpp",
@@ -53,7 +52,6 @@ cc_library_static {
"com_android_server_UsbHostManager.cpp",
"com_android_server_VibratorService.cpp",
"com_android_server_PersistentDataBlockService.cpp",
"com_android_server_GraphicsStatsService.cpp",
"com_android_server_am_CachedAppOptimizer.cpp",
"com_android_server_am_LowMemDetector.cpp",
"com_android_server_incremental_IncrementalManagerService.cpp",
@@ -107,8 +105,6 @@ cc_defaults {
"libinputflinger",
"libinputflinger_base",
"libinputservice",
"libprotobuf-cpp-lite",
"libprotoutil",
"libstatshidl",
"libstatspull",
"libstatssocket",

View File

@@ -1,292 +0,0 @@
/*
* Copyright (C) 2017 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 "GraphicsStatsService"
#include <jni.h>
#include <log/log.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedUtfChars.h>
#include <JankTracker.h>
#include <service/GraphicsStatsService.h>
#include <stats_pull_atom_callback.h>
#include <stats_event.h>
#include <statslog.h>
#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
#include <android/util/ProtoOutputStream.h>
#include "android/graphics/Utils.h"
#include "core_jni_helpers.h"
#include "protos/graphicsstats.pb.h"
#include <cstring>
#include <memory>
namespace android {
using namespace android::uirenderer;
static jint getAshmemSize(JNIEnv*, jobject) {
return sizeof(ProfileData);
}
static jlong createDump(JNIEnv*, jobject, jint fd, jboolean isProto) {
GraphicsStatsService::Dump* dump = GraphicsStatsService::createDump(fd, isProto
? GraphicsStatsService::DumpType::Protobuf : GraphicsStatsService::DumpType::Text);
return reinterpret_cast<jlong>(dump);
}
static void addToDump(JNIEnv* env, jobject, jlong dumpPtr, jstring jpath, jstring jpackage,
jlong versionCode, jlong startTime, jlong endTime, jbyteArray jdata) {
std::string path;
const ProfileData* data = nullptr;
LOG_ALWAYS_FATAL_IF(jdata == nullptr && jpath == nullptr, "Path and data can't both be null");
ScopedByteArrayRO buffer{env};
if (jdata != nullptr) {
buffer.reset(jdata);
LOG_ALWAYS_FATAL_IF(buffer.size() != sizeof(ProfileData),
"Buffer size %zu doesn't match expected %zu!", buffer.size(), sizeof(ProfileData));
data = reinterpret_cast<const ProfileData*>(buffer.get());
}
if (jpath != nullptr) {
ScopedUtfChars pathChars(env, jpath);
LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(), "Failed to get path chars");
path.assign(pathChars.c_str(), pathChars.size());
}
ScopedUtfChars packageChars(env, jpackage);
LOG_ALWAYS_FATAL_IF(packageChars.size() <= 0 || !packageChars.c_str(), "Failed to get path chars");
GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
LOG_ALWAYS_FATAL_IF(!dump, "null passed for dump pointer");
const std::string package(packageChars.c_str(), packageChars.size());
GraphicsStatsService::addToDump(dump, path, package, versionCode, startTime, endTime, data);
}
static void addFileToDump(JNIEnv* env, jobject, jlong dumpPtr, jstring jpath) {
ScopedUtfChars pathChars(env, jpath);
LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(), "Failed to get path chars");
const std::string path(pathChars.c_str(), pathChars.size());
GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
GraphicsStatsService::addToDump(dump, path);
}
static void finishDump(JNIEnv*, jobject, jlong dumpPtr) {
GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
GraphicsStatsService::finishDump(dump);
}
static jlong finishDumpInMemory(JNIEnv* env, jobject, jlong dumpPtr) {
GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
std::vector<uint8_t>* result = new std::vector<uint8_t>();
GraphicsStatsService::finishDumpInMemory(dump,
[](void* buffer, int bufferOffset, int bufferSize, int totalSize, void* param1, void* param2) {
std::vector<uint8_t>* outBuffer = reinterpret_cast<std::vector<uint8_t>*>(param2);
if (outBuffer->size() < totalSize) {
outBuffer->resize(totalSize);
}
std::memcpy(outBuffer->data() + bufferOffset, buffer, bufferSize);
}, env, result);
return reinterpret_cast<jlong>(result);
}
static void saveBuffer(JNIEnv* env, jobject clazz, jstring jpath, jstring jpackage,
jlong versionCode, jlong startTime, jlong endTime, jbyteArray jdata) {
ScopedByteArrayRO buffer(env, jdata);
LOG_ALWAYS_FATAL_IF(buffer.size() != sizeof(ProfileData),
"Buffer size %zu doesn't match expected %zu!", buffer.size(), sizeof(ProfileData));
ScopedUtfChars pathChars(env, jpath);
LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(), "Failed to get path chars");
ScopedUtfChars packageChars(env, jpackage);
LOG_ALWAYS_FATAL_IF(packageChars.size() <= 0 || !packageChars.c_str(), "Failed to get path chars");
const std::string path(pathChars.c_str(), pathChars.size());
const std::string package(packageChars.c_str(), packageChars.size());
const ProfileData* data = reinterpret_cast<const ProfileData*>(buffer.get());
GraphicsStatsService::saveBuffer(path, package, versionCode, startTime, endTime, data);
}
static jobject gGraphicsStatsServiceObject = nullptr;
static jmethodID gGraphicsStatsService_pullGraphicsStatsMethodID;
static JNIEnv* getJNIEnv() {
JavaVM* vm = AndroidRuntime::getJavaVM();
JNIEnv* env = nullptr;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
if (vm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) {
LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!");
}
}
return env;
}
using namespace google::protobuf;
// Field ids taken from FrameTimingHistogram message in atoms.proto
#define TIME_MILLIS_BUCKETS_FIELD_NUMBER 1
#define FRAME_COUNTS_FIELD_NUMBER 2
static void writeCpuHistogram(AStatsEvent* event,
const uirenderer::protos::GraphicsStatsProto& stat) {
util::ProtoOutputStream proto;
for (int bucketIndex = 0; bucketIndex < stat.histogram_size(); bucketIndex++) {
auto& bucket = stat.histogram(bucketIndex);
proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED |
TIME_MILLIS_BUCKETS_FIELD_NUMBER /* field id */,
(int)bucket.render_millis());
}
for (int bucketIndex = 0; bucketIndex < stat.histogram_size(); bucketIndex++) {
auto& bucket = stat.histogram(bucketIndex);
proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
FRAME_COUNTS_FIELD_NUMBER /* field id */,
(long long)bucket.frame_count());
}
std::vector<uint8_t> outVector;
proto.serializeToVector(&outVector);
AStatsEvent_writeByteArray(event, outVector.data(), outVector.size());
}
static void writeGpuHistogram(AStatsEvent* event,
const uirenderer::protos::GraphicsStatsProto& stat) {
util::ProtoOutputStream proto;
for (int bucketIndex = 0; bucketIndex < stat.gpu_histogram_size(); bucketIndex++) {
auto& bucket = stat.gpu_histogram(bucketIndex);
proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED |
TIME_MILLIS_BUCKETS_FIELD_NUMBER /* field id */,
(int)bucket.render_millis());
}
for (int bucketIndex = 0; bucketIndex < stat.gpu_histogram_size(); bucketIndex++) {
auto& bucket = stat.gpu_histogram(bucketIndex);
proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
FRAME_COUNTS_FIELD_NUMBER /* field id */,
(long long)bucket.frame_count());
}
std::vector<uint8_t> outVector;
proto.serializeToVector(&outVector);
AStatsEvent_writeByteArray(event, outVector.data(), outVector.size());
}
// graphicsStatsPullCallback is invoked by statsd service to pull GRAPHICS_STATS atom.
static AStatsManager_PullAtomCallbackReturn graphicsStatsPullCallback(int32_t atom_tag,
AStatsEventList* data,
void* cookie) {
JNIEnv* env = getJNIEnv();
if (!env) {
return false;
}
if (gGraphicsStatsServiceObject == nullptr) {
ALOGE("Failed to get graphicsstats service");
return AStatsManager_PULL_SKIP;
}
for (bool lastFullDay : {true, false}) {
jlong jdata = (jlong) env->CallLongMethod(
gGraphicsStatsServiceObject,
gGraphicsStatsService_pullGraphicsStatsMethodID,
(jboolean)(lastFullDay ? JNI_TRUE : JNI_FALSE));
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
ALOGE("Failed to invoke graphicsstats service");
return AStatsManager_PULL_SKIP;
}
if (!jdata) {
// null means data is not available for that day.
continue;
}
android::uirenderer::protos::GraphicsStatsServiceDumpProto serviceDump;
std::vector<uint8_t>* buffer = reinterpret_cast<std::vector<uint8_t>*>(jdata);
std::unique_ptr<std::vector<uint8_t>> bufferRelease(buffer);
int dataSize = buffer->size();
if (!dataSize) {
// Data is not available for that day.
continue;
}
io::ArrayInputStream input{buffer->data(), dataSize};
bool success = serviceDump.ParseFromZeroCopyStream(&input);
if (!success) {
ALOGW("Parse failed on GraphicsStatsPuller error='%s' dataSize='%d'",
serviceDump.InitializationErrorString().c_str(), dataSize);
return AStatsManager_PULL_SKIP;
}
for (int stat_index = 0; stat_index < serviceDump.stats_size(); stat_index++) {
auto& stat = serviceDump.stats(stat_index);
AStatsEvent* event = AStatsEventList_addStatsEvent(data);
AStatsEvent_setAtomId(event, android::util::GRAPHICS_STATS);
AStatsEvent_writeString(event, stat.package_name().c_str());
AStatsEvent_writeInt64(event, (int64_t)stat.version_code());
AStatsEvent_writeInt64(event, (int64_t)stat.stats_start());
AStatsEvent_writeInt64(event, (int64_t)stat.stats_end());
AStatsEvent_writeInt32(event, (int32_t)stat.pipeline());
AStatsEvent_writeInt32(event, (int32_t)stat.summary().total_frames());
AStatsEvent_writeInt32(event, (int32_t)stat.summary().missed_vsync_count());
AStatsEvent_writeInt32(event, (int32_t)stat.summary().high_input_latency_count());
AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_ui_thread_count());
AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_bitmap_upload_count());
AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_draw_count());
AStatsEvent_writeInt32(event, (int32_t)stat.summary().missed_deadline_count());
writeCpuHistogram(event, stat);
writeGpuHistogram(event, stat);
// TODO: fill in UI mainline module version, when the feature is available.
AStatsEvent_writeInt64(event, (int64_t)0);
AStatsEvent_writeBool(event, !lastFullDay);
AStatsEvent_build(event);
}
}
return AStatsManager_PULL_SUCCESS;
}
// Register a puller for GRAPHICS_STATS atom with the statsd service.
static void nativeInit(JNIEnv* env, jobject javaObject) {
gGraphicsStatsServiceObject = env->NewGlobalRef(javaObject);
AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain();
AStatsManager_PullAtomMetadata_setCoolDownNs(metadata, 10 * 1000000); // 10 milliseconds
AStatsManager_PullAtomMetadata_setTimeoutNs(metadata, 2 * NS_PER_SEC); // 2 seconds
AStatsManager_registerPullAtomCallback(android::util::GRAPHICS_STATS,
&graphicsStatsPullCallback, metadata, nullptr);
AStatsManager_PullAtomMetadata_release(metadata);
}
static void nativeDestructor(JNIEnv* env, jobject javaObject) {
AStatsManager_unregisterPullAtomCallback(android::util::GRAPHICS_STATS);
env->DeleteGlobalRef(gGraphicsStatsServiceObject);
gGraphicsStatsServiceObject = nullptr;
}
static const JNINativeMethod sMethods[] = {
{ "nGetAshmemSize", "()I", (void*) getAshmemSize },
{ "nCreateDump", "(IZ)J", (void*) createDump },
{ "nAddToDump", "(JLjava/lang/String;Ljava/lang/String;JJJ[B)V", (void*) addToDump },
{ "nAddToDump", "(JLjava/lang/String;)V", (void*) addFileToDump },
{ "nFinishDump", "(J)V", (void*) finishDump },
{ "nFinishDumpInMemory", "(J)J", (void*) finishDumpInMemory },
{ "nSaveBuffer", "(Ljava/lang/String;Ljava/lang/String;JJJ[B)V", (void*) saveBuffer },
{ "nativeInit", "()V", (void*) nativeInit },
{ "nativeDestructor", "()V", (void*)nativeDestructor }
};
int register_android_server_GraphicsStatsService(JNIEnv* env)
{
jclass graphicsStatsService_class = FindClassOrDie(env,
"com/android/server/GraphicsStatsService");
gGraphicsStatsService_pullGraphicsStatsMethodID = GetMethodIDOrDie(env,
graphicsStatsService_class, "pullGraphicsStats", "(Z)J");
return jniRegisterNativeMethods(env, "com/android/server/GraphicsStatsService",
sMethods, NELEM(sMethods));
}
} // namespace android

View File

@@ -49,7 +49,7 @@ int register_android_server_PersistentDataBlockService(JNIEnv* env);
int register_android_server_Watchdog(JNIEnv* env);
int register_android_server_HardwarePropertiesManagerService(JNIEnv* env);
int register_android_server_SyntheticPasswordManager(JNIEnv* env);
int register_android_server_GraphicsStatsService(JNIEnv* env);
int register_android_graphics_GraphicsStatsService(JNIEnv* env);
int register_android_hardware_display_DisplayViewport(JNIEnv* env);
int register_android_server_net_NetworkStatsFactory(JNIEnv* env);
int register_android_server_net_NetworkStatsService(JNIEnv* env);
@@ -102,7 +102,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
register_android_server_HardwarePropertiesManagerService(env);
register_android_server_storage_AppFuse(env);
register_android_server_SyntheticPasswordManager(env);
register_android_server_GraphicsStatsService(env);
register_android_graphics_GraphicsStatsService(env);
register_android_hardware_display_DisplayViewport(env);
register_android_server_net_NetworkStatsFactory(env);
register_android_server_net_NetworkStatsService(env);

View File

@@ -41,6 +41,7 @@ import android.content.res.Configuration;
import android.content.res.Resources.Theme;
import android.database.sqlite.SQLiteCompatibilityWalFlags;
import android.database.sqlite.SQLiteGlobal;
import android.graphics.GraphicsStatsService;
import android.hardware.display.DisplayManagerInternal;
import android.net.ConnectivityModuleConnector;
import android.net.ITetheringConnector;