blob: 5179fa7a6eb5506f37c14ecf94a0f10f45bff5ad [file] [log] [blame]
John Reckedc524c2015-03-18 15:24:33 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
John Reckdf1742e2017-01-19 15:56:21 -080019import android.app.AlarmManager;
Jeff Sharkeycd654482016-01-08 17:42:11 -070020import android.app.AppOpsManager;
John Reckedc524c2015-03-18 15:24:33 -070021import android.content.Context;
John Reckdf1742e2017-01-19 15:56:21 -080022import android.content.pm.PackageInfo;
23import android.content.pm.PackageManager;
John Reckedc524c2015-03-18 15:24:33 -070024import android.os.Binder;
John Reckdf1742e2017-01-19 15:56:21 -080025import android.os.Environment;
26import android.os.Handler;
27import android.os.HandlerThread;
John Reckedc524c2015-03-18 15:24:33 -070028import android.os.IBinder;
29import android.os.MemoryFile;
John Reckdf1742e2017-01-19 15:56:21 -080030import android.os.Message;
John Reckedc524c2015-03-18 15:24:33 -070031import android.os.ParcelFileDescriptor;
John Reckdf1742e2017-01-19 15:56:21 -080032import android.os.Process;
John Reckedc524c2015-03-18 15:24:33 -070033import android.os.RemoteException;
John Reckdf1742e2017-01-19 15:56:21 -080034import android.os.Trace;
Tony Mak82566802017-03-20 18:35:43 +000035import android.os.UserHandle;
John Reckedc524c2015-03-18 15:24:33 -070036import android.util.Log;
37import android.view.IGraphicsStats;
John Reckdf1742e2017-01-19 15:56:21 -080038import android.view.IGraphicsStatsCallback;
John Reckedc524c2015-03-18 15:24:33 -070039
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060040import com.android.internal.util.DumpUtils;
Stan Iliev637ba5e2019-08-16 13:43:08 -040041import com.android.internal.util.FastPrintWriter;
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060042
John Reckdf1742e2017-01-19 15:56:21 -080043import java.io.File;
John Reckedc524c2015-03-18 15:24:33 -070044import java.io.FileDescriptor;
45import java.io.IOException;
46import java.io.PrintWriter;
Stan Iliev637ba5e2019-08-16 13:43:08 -040047import java.io.StringWriter;
John Reckedc524c2015-03-18 15:24:33 -070048import java.util.ArrayList;
John Reckdf1742e2017-01-19 15:56:21 -080049import java.util.Arrays;
50import java.util.Calendar;
51import java.util.HashSet;
52import java.util.TimeZone;
John Reckedc524c2015-03-18 15:24:33 -070053
54/**
55 * This service's job is to collect aggregate rendering profile data. It
56 * does this by allowing rendering processes to request an ashmem buffer
John Reckdf1742e2017-01-19 15:56:21 -080057 * to place their stats into.
John Reckedc524c2015-03-18 15:24:33 -070058 *
John Reckdf1742e2017-01-19 15:56:21 -080059 * Buffers are rotated on a daily (in UTC) basis and only the 3 most-recent days
60 * are kept.
John Reckedc524c2015-03-18 15:24:33 -070061 *
John Reckdf1742e2017-01-19 15:56:21 -080062 * The primary consumer of this is incident reports and automated metric checking. It is not
63 * intended for end-developer consumption, for that we have gfxinfo.
John Reckedc524c2015-03-18 15:24:33 -070064 *
John Reckdf1742e2017-01-19 15:56:21 -080065 * Buffer rotation process:
66 * 1) Alarm fires
67 * 2) onRotateGraphicsStatsBuffer() is sent to all active processes
68 * 3) Upon receiving the callback, the process will stop using the previous ashmem buffer and
69 * request a new one.
70 * 4) When that request is received we now know that the ashmem region is no longer in use so
71 * it gets queued up for saving to disk and a new ashmem region is created and returned
72 * for the process to use.
John Reckedc524c2015-03-18 15:24:33 -070073 *
74 * @hide */
75public class GraphicsStatsService extends IGraphicsStats.Stub {
76 public static final String GRAPHICS_STATS_SERVICE = "graphicsstats";
77
78 private static final String TAG = "GraphicsStatsService";
John Reckdf1742e2017-01-19 15:56:21 -080079
80 private static final int SAVE_BUFFER = 1;
81 private static final int DELETE_OLD = 2;
82
Stan Iliev637ba5e2019-08-16 13:43:08 -040083 private static final int AID_STATSD = 1066; // Statsd uid is set to 1066 forever.
84
John Reckdf1742e2017-01-19 15:56:21 -080085 // This isn't static because we need this to happen after registerNativeMethods, however
86 // the class is loaded (and thus static ctor happens) before that occurs.
87 private final int ASHMEM_SIZE = nGetAshmemSize();
88 private final byte[] ZERO_DATA = new byte[ASHMEM_SIZE];
John Reckedc524c2015-03-18 15:24:33 -070089
90 private final Context mContext;
Jeff Sharkeycd654482016-01-08 17:42:11 -070091 private final AppOpsManager mAppOps;
John Reckdf1742e2017-01-19 15:56:21 -080092 private final AlarmManager mAlarmManager;
John Reckedc524c2015-03-18 15:24:33 -070093 private final Object mLock = new Object();
94 private ArrayList<ActiveBuffer> mActive = new ArrayList<>();
John Reckdf1742e2017-01-19 15:56:21 -080095 private File mGraphicsStatsDir;
96 private final Object mFileAccessLock = new Object();
97 private Handler mWriteOutHandler;
98 private boolean mRotateIsScheduled = false;
John Reckedc524c2015-03-18 15:24:33 -070099
100 public GraphicsStatsService(Context context) {
101 mContext = context;
Jeff Sharkeycd654482016-01-08 17:42:11 -0700102 mAppOps = context.getSystemService(AppOpsManager.class);
John Reckdf1742e2017-01-19 15:56:21 -0800103 mAlarmManager = context.getSystemService(AlarmManager.class);
104 File systemDataDir = new File(Environment.getDataDirectory(), "system");
105 mGraphicsStatsDir = new File(systemDataDir, "graphicsstats");
106 mGraphicsStatsDir.mkdirs();
107 if (!mGraphicsStatsDir.exists()) {
108 throw new IllegalStateException("Graphics stats directory does not exist: "
109 + mGraphicsStatsDir.getAbsolutePath());
110 }
111 HandlerThread bgthread = new HandlerThread("GraphicsStats-disk", Process.THREAD_PRIORITY_BACKGROUND);
112 bgthread.start();
113
114 mWriteOutHandler = new Handler(bgthread.getLooper(), new Handler.Callback() {
115 @Override
116 public boolean handleMessage(Message msg) {
117 switch (msg.what) {
118 case SAVE_BUFFER:
119 saveBuffer((HistoricalBuffer) msg.obj);
120 break;
121 case DELETE_OLD:
122 deleteOldBuffers();
123 break;
124 }
125 return true;
126 }
127 });
Stan Iliev637ba5e2019-08-16 13:43:08 -0400128 nativeInit();
John Reckdf1742e2017-01-19 15:56:21 -0800129 }
130
131 /**
132 * Current rotation policy is to rotate at midnight UTC. We don't specify RTC_WAKEUP because
133 * rotation can be delayed if there's otherwise no activity. However exact is used because
134 * we don't want the system to delay it by TOO much.
135 */
136 private void scheduleRotateLocked() {
137 if (mRotateIsScheduled) {
138 return;
139 }
140 mRotateIsScheduled = true;
141 Calendar calendar = normalizeDate(System.currentTimeMillis());
142 calendar.add(Calendar.DATE, 1);
143 mAlarmManager.setExact(AlarmManager.RTC, calendar.getTimeInMillis(), TAG, this::onAlarm,
144 mWriteOutHandler);
145 }
146
147 private void onAlarm() {
John Reck37dfb422017-02-22 17:01:30 -0800148 // We need to make a copy since some of the callbacks won't be proxy and thus
149 // can result in a re-entrant acquisition of mLock that would result in a modification
150 // of mActive during iteration.
151 ActiveBuffer[] activeCopy;
John Reckdf1742e2017-01-19 15:56:21 -0800152 synchronized (mLock) {
153 mRotateIsScheduled = false;
154 scheduleRotateLocked();
John Reck37dfb422017-02-22 17:01:30 -0800155 activeCopy = mActive.toArray(new ActiveBuffer[0]);
156 }
157 for (ActiveBuffer active : activeCopy) {
158 try {
159 active.mCallback.onRotateGraphicsStatsBuffer();
160 } catch (RemoteException e) {
161 Log.w(TAG, String.format("Failed to notify '%s' (pid=%d) to rotate buffers",
162 active.mInfo.packageName, active.mPid), e);
John Reckdf1742e2017-01-19 15:56:21 -0800163 }
164 }
165 // Give a few seconds for everyone to rotate before doing the cleanup
166 mWriteOutHandler.sendEmptyMessageDelayed(DELETE_OLD, 10000);
John Reckedc524c2015-03-18 15:24:33 -0700167 }
168
169 @Override
John Reckdf1742e2017-01-19 15:56:21 -0800170 public ParcelFileDescriptor requestBufferForProcess(String packageName, IGraphicsStatsCallback token)
John Reckedc524c2015-03-18 15:24:33 -0700171 throws RemoteException {
172 int uid = Binder.getCallingUid();
173 int pid = Binder.getCallingPid();
174 ParcelFileDescriptor pfd = null;
175 long callingIdentity = Binder.clearCallingIdentity();
176 try {
Jeff Sharkeycd654482016-01-08 17:42:11 -0700177 mAppOps.checkPackage(uid, packageName);
Tony Mak82566802017-03-20 18:35:43 +0000178 PackageInfo info = mContext.getPackageManager().getPackageInfoAsUser(
179 packageName,
180 0,
181 UserHandle.getUserId(uid));
John Reckedc524c2015-03-18 15:24:33 -0700182 synchronized (mLock) {
Dianne Hackborn73453e42017-12-11 16:30:36 -0800183 pfd = requestBufferForProcessLocked(token, uid, pid, packageName,
184 info.getLongVersionCode());
John Reckedc524c2015-03-18 15:24:33 -0700185 }
John Reckdf1742e2017-01-19 15:56:21 -0800186 } catch (PackageManager.NameNotFoundException ex) {
187 throw new RemoteException("Unable to find package: '" + packageName + "'");
John Reckedc524c2015-03-18 15:24:33 -0700188 } finally {
189 Binder.restoreCallingIdentity(callingIdentity);
190 }
191 return pfd;
192 }
193
Stan Iliev637ba5e2019-08-16 13:43:08 -0400194 // If lastFullDay is true, pullGraphicsStats returns stats for the last complete day/24h period
195 // that does not include today. If lastFullDay is false, pullGraphicsStats returns stats for the
196 // current day.
197 // This method is invoked from native code only.
198 @SuppressWarnings({"UnusedDeclaration"})
199 private long pullGraphicsStats(boolean lastFullDay) throws RemoteException {
200 int uid = Binder.getCallingUid();
201
202 // DUMP and PACKAGE_USAGE_STATS permissions are required to invoke this method.
203 // TODO: remove exception for statsd daemon after required permissions are granted. statsd
204 // TODO: should have these permissions granted by data/etc/platform.xml, but it does not.
205 if (uid != AID_STATSD) {
206 StringWriter sw = new StringWriter();
207 PrintWriter pw = new FastPrintWriter(sw);
208 if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) {
209 pw.flush();
210 throw new RemoteException(sw.toString());
211 }
212 }
213
214 long callingIdentity = Binder.clearCallingIdentity();
215 try {
216 return pullGraphicsStatsImpl(lastFullDay);
217 } finally {
218 Binder.restoreCallingIdentity(callingIdentity);
219 }
220 }
221
222 private long pullGraphicsStatsImpl(boolean lastFullDay) {
223 long targetDay;
224 if (lastFullDay) {
225 // Get stats from yesterday. Stats stay constant, because the day is over.
226 targetDay = normalizeDate(System.currentTimeMillis() - 86400000).getTimeInMillis();
227 } else {
228 // Get stats from today. Stats may change as more apps are run today.
229 targetDay = normalizeDate(System.currentTimeMillis()).getTimeInMillis();
230 }
231
232 // Find active buffers for targetDay.
233 ArrayList<HistoricalBuffer> buffers;
234 synchronized (mLock) {
235 buffers = new ArrayList<>(mActive.size());
236 for (int i = 0; i < mActive.size(); i++) {
237 ActiveBuffer buffer = mActive.get(i);
238 if (buffer.mInfo.startTime == targetDay) {
239 try {
240 buffers.add(new HistoricalBuffer(buffer));
241 } catch (IOException ex) {
242 // Ignore
243 }
244 }
245 }
246 }
247
248 // Dump active and historic buffers for targetDay in a serialized
249 // GraphicsStatsServiceDumpProto proto.
250 long dump = nCreateDump(-1, true);
251 try {
252 synchronized (mFileAccessLock) {
253 HashSet<File> skipList = dumpActiveLocked(dump, buffers);
254 buffers.clear();
255 String subPath = String.format("%d", targetDay);
256 File dateDir = new File(mGraphicsStatsDir, subPath);
257 if (dateDir.exists()) {
258 for (File pkg : dateDir.listFiles()) {
259 for (File version : pkg.listFiles()) {
260 File data = new File(version, "total");
261 if (skipList.contains(data)) {
262 continue;
263 }
264 nAddToDump(dump, data.getAbsolutePath());
265 }
266 }
267 }
268 }
269 } finally {
270 return nFinishDumpInMemory(dump);
271 }
272 }
273
John Reckedc524c2015-03-18 15:24:33 -0700274 private ParcelFileDescriptor getPfd(MemoryFile file) {
275 try {
John Reckdf1742e2017-01-19 15:56:21 -0800276 if (!file.getFileDescriptor().valid()) {
277 throw new IllegalStateException("Invalid file descriptor");
278 }
Josh Gaodb644122019-07-26 13:47:11 -0700279 return ParcelFileDescriptor.dup(file.getFileDescriptor());
John Reckedc524c2015-03-18 15:24:33 -0700280 } catch (IOException ex) {
281 throw new IllegalStateException("Failed to get PFD from memory file", ex);
282 }
283 }
284
John Reckdf1742e2017-01-19 15:56:21 -0800285 private ParcelFileDescriptor requestBufferForProcessLocked(IGraphicsStatsCallback token,
Dianne Hackborn73453e42017-12-11 16:30:36 -0800286 int uid, int pid, String packageName, long versionCode) throws RemoteException {
John Reckdf1742e2017-01-19 15:56:21 -0800287 ActiveBuffer buffer = fetchActiveBuffersLocked(token, uid, pid, packageName, versionCode);
288 scheduleRotateLocked();
John Reckedc524c2015-03-18 15:24:33 -0700289 return getPfd(buffer.mProcessBuffer);
290 }
291
John Reckdf1742e2017-01-19 15:56:21 -0800292 private Calendar normalizeDate(long timestamp) {
293 Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
294 calendar.setTimeInMillis(timestamp);
295 calendar.set(Calendar.HOUR_OF_DAY, 0);
296 calendar.set(Calendar.MINUTE, 0);
297 calendar.set(Calendar.SECOND, 0);
298 calendar.set(Calendar.MILLISECOND, 0);
299 return calendar;
300 }
301
302 private File pathForApp(BufferInfo info) {
303 String subPath = String.format("%d/%s/%d/total",
304 normalizeDate(info.startTime).getTimeInMillis(), info.packageName, info.versionCode);
305 return new File(mGraphicsStatsDir, subPath);
306 }
307
308 private void saveBuffer(HistoricalBuffer buffer) {
309 if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
310 Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "saving graphicsstats for " + buffer.mInfo.packageName);
311 }
312 synchronized (mFileAccessLock) {
313 File path = pathForApp(buffer.mInfo);
314 File parent = path.getParentFile();
315 parent.mkdirs();
316 if (!parent.exists()) {
317 Log.w(TAG, "Unable to create path: '" + parent.getAbsolutePath() + "'");
318 return;
319 }
320 nSaveBuffer(path.getAbsolutePath(), buffer.mInfo.packageName, buffer.mInfo.versionCode,
321 buffer.mInfo.startTime, buffer.mInfo.endTime, buffer.mData);
322 }
323 Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
324 }
325
326 private void deleteRecursiveLocked(File file) {
327 if (file.isDirectory()) {
328 for (File child : file.listFiles()) {
329 deleteRecursiveLocked(child);
330 }
331 }
332 if (!file.delete()) {
333 Log.w(TAG, "Failed to delete '" + file.getAbsolutePath() + "'!");
334 }
335 }
336
337 private void deleteOldBuffers() {
338 Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "deleting old graphicsstats buffers");
339 synchronized (mFileAccessLock) {
340 File[] files = mGraphicsStatsDir.listFiles();
341 if (files == null || files.length <= 3) {
342 return;
343 }
344 long[] sortedDates = new long[files.length];
345 for (int i = 0; i < files.length; i++) {
346 try {
347 sortedDates[i] = Long.parseLong(files[i].getName());
348 } catch (NumberFormatException ex) {
349 // Skip unrecognized folders
350 }
351 }
352 if (sortedDates.length <= 3) {
353 return;
354 }
355 Arrays.sort(sortedDates);
356 for (int i = 0; i < sortedDates.length - 3; i++) {
357 deleteRecursiveLocked(new File(mGraphicsStatsDir, Long.toString(sortedDates[i])));
358 }
359 }
360 Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
361 }
362
363 private void addToSaveQueue(ActiveBuffer buffer) {
364 try {
365 HistoricalBuffer data = new HistoricalBuffer(buffer);
366 Message.obtain(mWriteOutHandler, SAVE_BUFFER, data).sendToTarget();
367 } catch (IOException e) {
368 Log.w(TAG, "Failed to copy graphicsstats from " + buffer.mInfo.packageName, e);
369 }
370 buffer.closeAllBuffers();
371 }
372
John Reckedc524c2015-03-18 15:24:33 -0700373 private void processDied(ActiveBuffer buffer) {
374 synchronized (mLock) {
375 mActive.remove(buffer);
John Reckedc524c2015-03-18 15:24:33 -0700376 }
John Reckdf1742e2017-01-19 15:56:21 -0800377 addToSaveQueue(buffer);
John Reckedc524c2015-03-18 15:24:33 -0700378 }
379
John Reckdf1742e2017-01-19 15:56:21 -0800380 private ActiveBuffer fetchActiveBuffersLocked(IGraphicsStatsCallback token, int uid, int pid,
Dianne Hackborn73453e42017-12-11 16:30:36 -0800381 String packageName, long versionCode) throws RemoteException {
John Reckedc524c2015-03-18 15:24:33 -0700382 int size = mActive.size();
John Reckdf1742e2017-01-19 15:56:21 -0800383 long today = normalizeDate(System.currentTimeMillis()).getTimeInMillis();
John Reckedc524c2015-03-18 15:24:33 -0700384 for (int i = 0; i < size; i++) {
John Reckdf1742e2017-01-19 15:56:21 -0800385 ActiveBuffer buffer = mActive.get(i);
386 if (buffer.mPid == pid
387 && buffer.mUid == uid) {
388 // If the buffer is too old we remove it and return a new one
389 if (buffer.mInfo.startTime < today) {
390 buffer.binderDied();
391 break;
392 } else {
393 return buffer;
394 }
John Reckedc524c2015-03-18 15:24:33 -0700395 }
396 }
397 // Didn't find one, need to create it
398 try {
John Reckdf1742e2017-01-19 15:56:21 -0800399 ActiveBuffer buffers = new ActiveBuffer(token, uid, pid, packageName, versionCode);
John Reckedc524c2015-03-18 15:24:33 -0700400 mActive.add(buffers);
401 return buffers;
402 } catch (IOException ex) {
403 throw new RemoteException("Failed to allocate space");
404 }
405 }
406
John Reckdf1742e2017-01-19 15:56:21 -0800407 private HashSet<File> dumpActiveLocked(long dump, ArrayList<HistoricalBuffer> buffers) {
408 HashSet<File> skipFiles = new HashSet<>(buffers.size());
409 for (int i = 0; i < buffers.size(); i++) {
410 HistoricalBuffer buffer = buffers.get(i);
411 File path = pathForApp(buffer.mInfo);
412 skipFiles.add(path);
413 nAddToDump(dump, path.getAbsolutePath(), buffer.mInfo.packageName,
414 buffer.mInfo.versionCode, buffer.mInfo.startTime, buffer.mInfo.endTime,
415 buffer.mData);
416 }
417 return skipFiles;
418 }
419
420 private void dumpHistoricalLocked(long dump, HashSet<File> skipFiles) {
421 for (File date : mGraphicsStatsDir.listFiles()) {
422 for (File pkg : date.listFiles()) {
423 for (File version : pkg.listFiles()) {
424 File data = new File(version, "total");
425 if (skipFiles.contains(data)) {
426 continue;
427 }
428 nAddToDump(dump, data.getAbsolutePath());
John Reckedc524c2015-03-18 15:24:33 -0700429 }
John Reckedc524c2015-03-18 15:24:33 -0700430 }
431 }
John Reckedc524c2015-03-18 15:24:33 -0700432 }
433
434 @Override
435 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
Jeff Sharkey6df866a2017-03-31 14:08:23 -0600436 if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, fout)) return;
John Reckdf1742e2017-01-19 15:56:21 -0800437 boolean dumpProto = false;
438 for (String str : args) {
439 if ("--proto".equals(str)) {
440 dumpProto = true;
441 break;
442 }
443 }
444 ArrayList<HistoricalBuffer> buffers;
John Reckedc524c2015-03-18 15:24:33 -0700445 synchronized (mLock) {
John Reckdf1742e2017-01-19 15:56:21 -0800446 buffers = new ArrayList<>(mActive.size());
John Reckedc524c2015-03-18 15:24:33 -0700447 for (int i = 0; i < mActive.size(); i++) {
John Reckedc524c2015-03-18 15:24:33 -0700448 try {
John Reckdf1742e2017-01-19 15:56:21 -0800449 buffers.add(new HistoricalBuffer(mActive.get(i)));
450 } catch (IOException ex) {
451 // Ignore
John Reckedc524c2015-03-18 15:24:33 -0700452 }
John Reckedc524c2015-03-18 15:24:33 -0700453 }
John Reckdf1742e2017-01-19 15:56:21 -0800454 }
455 long dump = nCreateDump(fd.getInt$(), dumpProto);
456 try {
457 synchronized (mFileAccessLock) {
458 HashSet<File> skipList = dumpActiveLocked(dump, buffers);
459 buffers.clear();
460 dumpHistoricalLocked(dump, skipList);
John Reckedc524c2015-03-18 15:24:33 -0700461 }
John Reckdf1742e2017-01-19 15:56:21 -0800462 } finally {
463 nFinishDump(dump);
464 }
465 }
466
Stan Iliev637ba5e2019-08-16 13:43:08 -0400467 @Override
468 protected void finalize() throws Throwable {
469 nativeDestructor();
470 }
471
472 private native void nativeInit();
473 private static native void nativeDestructor();
474
John Reckdf1742e2017-01-19 15:56:21 -0800475 private static native int nGetAshmemSize();
476 private static native long nCreateDump(int outFd, boolean isProto);
477 private static native void nAddToDump(long dump, String path, String packageName,
Dianne Hackborn73453e42017-12-11 16:30:36 -0800478 long versionCode, long startTime, long endTime, byte[] data);
John Reckdf1742e2017-01-19 15:56:21 -0800479 private static native void nAddToDump(long dump, String path);
480 private static native void nFinishDump(long dump);
Stan Iliev637ba5e2019-08-16 13:43:08 -0400481 private static native long nFinishDumpInMemory(long dump);
Dianne Hackborn73453e42017-12-11 16:30:36 -0800482 private static native void nSaveBuffer(String path, String packageName, long versionCode,
John Reckdf1742e2017-01-19 15:56:21 -0800483 long startTime, long endTime, byte[] data);
484
485 private final class BufferInfo {
486 final String packageName;
Dianne Hackborn73453e42017-12-11 16:30:36 -0800487 final long versionCode;
John Reckdf1742e2017-01-19 15:56:21 -0800488 long startTime;
489 long endTime;
490
Dianne Hackborn73453e42017-12-11 16:30:36 -0800491 BufferInfo(String packageName, long versionCode, long startTime) {
John Reckdf1742e2017-01-19 15:56:21 -0800492 this.packageName = packageName;
493 this.versionCode = versionCode;
494 this.startTime = startTime;
John Reckedc524c2015-03-18 15:24:33 -0700495 }
496 }
497
498 private final class ActiveBuffer implements DeathRecipient {
John Reckdf1742e2017-01-19 15:56:21 -0800499 final BufferInfo mInfo;
John Reckedc524c2015-03-18 15:24:33 -0700500 final int mUid;
501 final int mPid;
John Reckdf1742e2017-01-19 15:56:21 -0800502 final IGraphicsStatsCallback mCallback;
John Reckedc524c2015-03-18 15:24:33 -0700503 final IBinder mToken;
504 MemoryFile mProcessBuffer;
John Reckedc524c2015-03-18 15:24:33 -0700505
Dianne Hackborn73453e42017-12-11 16:30:36 -0800506 ActiveBuffer(IGraphicsStatsCallback token, int uid, int pid, String packageName,
507 long versionCode)
John Reckedc524c2015-03-18 15:24:33 -0700508 throws RemoteException, IOException {
John Reckdf1742e2017-01-19 15:56:21 -0800509 mInfo = new BufferInfo(packageName, versionCode, System.currentTimeMillis());
John Reckedc524c2015-03-18 15:24:33 -0700510 mUid = uid;
511 mPid = pid;
John Reckdf1742e2017-01-19 15:56:21 -0800512 mCallback = token;
513 mToken = mCallback.asBinder();
John Reckedc524c2015-03-18 15:24:33 -0700514 mToken.linkToDeath(this, 0);
John Reckdf1742e2017-01-19 15:56:21 -0800515 mProcessBuffer = new MemoryFile("GFXStats-" + pid, ASHMEM_SIZE);
516 mProcessBuffer.writeBytes(ZERO_DATA, 0, 0, ASHMEM_SIZE);
John Reckedc524c2015-03-18 15:24:33 -0700517 }
518
519 @Override
520 public void binderDied() {
521 mToken.unlinkToDeath(this, 0);
522 processDied(this);
523 }
524
525 void closeAllBuffers() {
526 if (mProcessBuffer != null) {
527 mProcessBuffer.close();
528 mProcessBuffer = null;
529 }
530 }
531 }
532
John Reckdf1742e2017-01-19 15:56:21 -0800533 private final class HistoricalBuffer {
534 final BufferInfo mInfo;
535 final byte[] mData = new byte[ASHMEM_SIZE];
536 HistoricalBuffer(ActiveBuffer active) throws IOException {
537 mInfo = active.mInfo;
538 mInfo.endTime = System.currentTimeMillis();
539 active.mProcessBuffer.readBytes(mData, 0, 0, ASHMEM_SIZE);
John Reckedc524c2015-03-18 15:24:33 -0700540 }
541 }
542}