1 /*
2  * Copyright (C) 2013 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 
17 package com.android.internal.app.procstats;
18 
19 
20 import android.os.Parcel;
21 import android.os.SystemClock;
22 import android.service.procstats.PackageServiceOperationStatsProto;
23 import android.service.procstats.PackageServiceStatsProto;
24 import android.service.procstats.ProcessStatsEnums;
25 import android.util.Slog;
26 import android.util.TimeUtils;
27 import android.util.proto.ProtoOutputStream;
28 
29 import static com.android.internal.app.procstats.ProcessStats.STATE_NOTHING;
30 
31 import java.io.PrintWriter;
32 
33 public final class ServiceState {
34     private static final String TAG = "ProcessStats";
35     private static final boolean DEBUG = false;
36 
37     public static final int SERVICE_RUN = 0;
38     public static final int SERVICE_STARTED = 1;
39     public static final int SERVICE_BOUND = 2;
40     public static final int SERVICE_EXEC = 3;
41     public static final int SERVICE_FOREGROUND = 4;
42     public static final int SERVICE_COUNT = 5;
43 
44     private final String mPackage;
45     private final String mProcessName;
46     private final String mName;
47     private final DurationsTable mDurations;
48 
49     private ProcessState mProc;
50     private Object mOwner;
51 
52     private int mRunCount;
53     private int mRunState = STATE_NOTHING;
54     private long mRunStartTime;
55 
56     private boolean mStarted;
57     private boolean mRestarting;
58     private int mStartedCount;
59     private int mStartedState = STATE_NOTHING;
60     private long mStartedStartTime;
61 
62     private int mBoundCount;
63     private int mBoundState = STATE_NOTHING;
64     private long mBoundStartTime;
65 
66     private int mExecCount;
67     private int mExecState = STATE_NOTHING;
68     private long mExecStartTime;
69 
70     private int mForegroundCount;
71     private int mForegroundState = STATE_NOTHING;
72     private long mForegroundStartTime;
73 
ServiceState(ProcessStats processStats, String pkg, String name, String processName, ProcessState proc)74     public ServiceState(ProcessStats processStats, String pkg, String name,
75             String processName, ProcessState proc) {
76         mPackage = pkg;
77         mName = name;
78         mProcessName = processName;
79         mProc = proc;
80         mDurations = new DurationsTable(processStats.mTableData);
81     }
82 
getPackage()83     public String getPackage() {
84         return mPackage;
85     }
86 
getProcessName()87     public String getProcessName() {
88         return mProcessName;
89     }
90 
getName()91     public String getName() {
92         return mName;
93     }
94 
getProcess()95     public ProcessState getProcess() {
96         return mProc;
97     }
98 
setProcess(ProcessState proc)99     public void setProcess(ProcessState proc) {
100         mProc = proc;
101     }
102 
setMemFactor(int memFactor, long now)103     public void setMemFactor(int memFactor, long now) {
104         if (isRestarting()) {
105             setRestarting(true, memFactor, now);
106         } else if (isInUse()) {
107             if (mStartedState != ProcessStats.STATE_NOTHING) {
108                 setStarted(true, memFactor, now);
109             }
110             if (mBoundState != ProcessStats.STATE_NOTHING) {
111                 setBound(true, memFactor, now);
112             }
113             if (mExecState != ProcessStats.STATE_NOTHING) {
114                 setExecuting(true, memFactor, now);
115             }
116             if (mForegroundState != ProcessStats.STATE_NOTHING) {
117                 setForeground(true, memFactor, now);
118             }
119         }
120     }
121 
applyNewOwner(Object newOwner)122     public void applyNewOwner(Object newOwner) {
123         if (mOwner != newOwner) {
124             if (mOwner == null) {
125                 mOwner = newOwner;
126                 mProc.incActiveServices(mName);
127             } else {
128                 // There was already an old owner, reset this object for its
129                 // new owner.
130                 mOwner = newOwner;
131                 if (mStarted || mBoundState != STATE_NOTHING || mExecState != STATE_NOTHING
132                         || mForegroundState != STATE_NOTHING) {
133                     long now = SystemClock.uptimeMillis();
134                     if (mStarted) {
135                         if (DEBUG) Slog.d(TAG, "Service has new owner " + newOwner
136                                 + " from " + mOwner + " while started: pkg="
137                                 + mPackage + " service=" + mName + " proc=" + mProc);
138                         setStarted(false, 0, now);
139                     }
140                     if (mBoundState != STATE_NOTHING) {
141                         if (DEBUG) Slog.d(TAG, "Service has new owner " + newOwner
142                                 + " from " + mOwner + " while bound: pkg="
143                                 + mPackage + " service=" + mName + " proc=" + mProc);
144                         setBound(false, 0, now);
145                     }
146                     if (mExecState != STATE_NOTHING) {
147                         if (DEBUG) Slog.d(TAG, "Service has new owner " + newOwner
148                                 + " from " + mOwner + " while executing: pkg="
149                                 + mPackage + " service=" + mName + " proc=" + mProc);
150                         setExecuting(false, 0, now);
151                     }
152                     if (mForegroundState != STATE_NOTHING) {
153                         if (DEBUG) Slog.d(TAG, "Service has new owner " + newOwner
154                                 + " from " + mOwner + " while foreground: pkg="
155                                 + mPackage + " service=" + mName + " proc=" + mProc);
156                         setForeground(false, 0, now);
157                     }
158                 }
159             }
160         }
161     }
162 
clearCurrentOwner(Object owner, boolean silently)163     public void clearCurrentOwner(Object owner, boolean silently) {
164         if (mOwner == owner) {
165             mProc.decActiveServices(mName);
166             if (mStarted || mBoundState != STATE_NOTHING || mExecState != STATE_NOTHING
167                     || mForegroundState != STATE_NOTHING) {
168                 long now = SystemClock.uptimeMillis();
169                 if (mStarted) {
170                     if (!silently) {
171                         Slog.wtfStack(TAG, "Service owner " + owner
172                                 + " cleared while started: pkg=" + mPackage + " service="
173                                 + mName + " proc=" + mProc);
174                     }
175                     setStarted(false, 0, now);
176                 }
177                 if (mBoundState != STATE_NOTHING) {
178                     if (!silently) {
179                         Slog.wtfStack(TAG, "Service owner " + owner
180                                 + " cleared while bound: pkg=" + mPackage + " service="
181                                 + mName + " proc=" + mProc);
182                     }
183                     setBound(false, 0, now);
184                 }
185                 if (mExecState != STATE_NOTHING) {
186                     if (!silently) {
187                         Slog.wtfStack(TAG, "Service owner " + owner
188                                 + " cleared while exec: pkg=" + mPackage + " service="
189                                 + mName + " proc=" + mProc);
190                     }
191                     setExecuting(false, 0, now);
192                 }
193                 if (mForegroundState != STATE_NOTHING) {
194                     if (!silently) {
195                         Slog.wtfStack(TAG, "Service owner " + owner
196                                 + " cleared while foreground: pkg=" + mPackage + " service="
197                                 + mName + " proc=" + mProc);
198                     }
199                     setForeground(false, 0, now);
200                 }
201             }
202             mOwner = null;
203         }
204     }
205 
isInUse()206     public boolean isInUse() {
207         return mOwner != null || mRestarting;
208     }
209 
isRestarting()210     public boolean isRestarting() {
211         return mRestarting;
212     }
213 
add(ServiceState other)214     public void add(ServiceState other) {
215         mDurations.addDurations(other.mDurations);
216         mRunCount += other.mRunCount;
217         mStartedCount += other.mStartedCount;
218         mBoundCount += other.mBoundCount;
219         mExecCount += other.mExecCount;
220         mForegroundCount += other.mForegroundCount;
221     }
222 
resetSafely(long now)223     public void resetSafely(long now) {
224         mDurations.resetTable();
225         mRunCount = mRunState != STATE_NOTHING ? 1 : 0;
226         mStartedCount = mStartedState != STATE_NOTHING ? 1 : 0;
227         mBoundCount = mBoundState != STATE_NOTHING ? 1 : 0;
228         mExecCount = mExecState != STATE_NOTHING ? 1 : 0;
229         mForegroundCount = mForegroundState != STATE_NOTHING ? 1 : 0;
230         mRunStartTime = mStartedStartTime = mBoundStartTime = mExecStartTime =
231                 mForegroundStartTime = now;
232     }
233 
writeToParcel(Parcel out, long now)234     public void writeToParcel(Parcel out, long now) {
235         mDurations.writeToParcel(out);
236         out.writeInt(mRunCount);
237         out.writeInt(mStartedCount);
238         out.writeInt(mBoundCount);
239         out.writeInt(mExecCount);
240         out.writeInt(mForegroundCount);
241     }
242 
readFromParcel(Parcel in)243     public boolean readFromParcel(Parcel in) {
244         if (!mDurations.readFromParcel(in)) {
245             return false;
246         }
247         mRunCount = in.readInt();
248         mStartedCount = in.readInt();
249         mBoundCount = in.readInt();
250         mExecCount = in.readInt();
251         mForegroundCount = in.readInt();
252         return true;
253     }
254 
commitStateTime(long now)255     public void commitStateTime(long now) {
256         if (mRunState != STATE_NOTHING) {
257             mDurations.addDuration(SERVICE_RUN + (mRunState*SERVICE_COUNT),
258                     now - mRunStartTime);
259             mRunStartTime = now;
260         }
261         if (mStartedState != STATE_NOTHING) {
262             mDurations.addDuration(SERVICE_STARTED + (mStartedState*SERVICE_COUNT),
263                     now - mStartedStartTime);
264             mStartedStartTime = now;
265         }
266         if (mBoundState != STATE_NOTHING) {
267             mDurations.addDuration(SERVICE_BOUND + (mBoundState*SERVICE_COUNT),
268                     now - mBoundStartTime);
269             mBoundStartTime = now;
270         }
271         if (mExecState != STATE_NOTHING) {
272             mDurations.addDuration(SERVICE_EXEC + (mExecState*SERVICE_COUNT),
273                     now - mExecStartTime);
274             mExecStartTime = now;
275         }
276         if (mForegroundState != STATE_NOTHING) {
277             mDurations.addDuration(SERVICE_FOREGROUND + (mForegroundState*SERVICE_COUNT),
278                     now - mForegroundStartTime);
279             mForegroundStartTime = now;
280         }
281     }
282 
updateRunning(int memFactor, long now)283     private void updateRunning(int memFactor, long now) {
284         final int state = (mStartedState != STATE_NOTHING || mBoundState != STATE_NOTHING
285                 || mExecState != STATE_NOTHING || mForegroundState != STATE_NOTHING)
286                 ? memFactor : STATE_NOTHING;
287         if (mRunState != state) {
288             if (mRunState != STATE_NOTHING) {
289                 mDurations.addDuration(SERVICE_RUN + (mRunState*SERVICE_COUNT),
290                         now - mRunStartTime);
291             } else if (state != STATE_NOTHING) {
292                 mRunCount++;
293             }
294             mRunState = state;
295             mRunStartTime = now;
296         }
297     }
298 
setStarted(boolean started, int memFactor, long now)299     public void setStarted(boolean started, int memFactor, long now) {
300         if (mOwner == null) {
301             Slog.wtf(TAG, "Starting service " + this + " without owner");
302         }
303         mStarted = started;
304         updateStartedState(memFactor, now);
305     }
306 
setRestarting(boolean restarting, int memFactor, long now)307     public void setRestarting(boolean restarting, int memFactor, long now) {
308         mRestarting = restarting;
309         updateStartedState(memFactor, now);
310     }
311 
updateStartedState(int memFactor, long now)312     public void updateStartedState(int memFactor, long now) {
313         final boolean wasStarted = mStartedState != STATE_NOTHING;
314         final boolean started = mStarted || mRestarting;
315         final int state = started ? memFactor : STATE_NOTHING;
316         if (mStartedState != state) {
317             if (mStartedState != STATE_NOTHING) {
318                 mDurations.addDuration(SERVICE_STARTED + (mStartedState*SERVICE_COUNT),
319                         now - mStartedStartTime);
320             } else if (started) {
321                 mStartedCount++;
322             }
323             mStartedState = state;
324             mStartedStartTime = now;
325             mProc = mProc.pullFixedProc(mPackage);
326             if (wasStarted != started) {
327                 if (started) {
328                     mProc.incStartedServices(memFactor, now, mName);
329                 } else {
330                     mProc.decStartedServices(memFactor, now, mName);
331                 }
332             }
333             updateRunning(memFactor, now);
334         }
335     }
336 
setBound(boolean bound, int memFactor, long now)337     public void setBound(boolean bound, int memFactor, long now) {
338         if (mOwner == null) {
339             Slog.wtf(TAG, "Binding service " + this + " without owner");
340         }
341         final int state = bound ? memFactor : STATE_NOTHING;
342         if (mBoundState != state) {
343             if (mBoundState != STATE_NOTHING) {
344                 mDurations.addDuration(SERVICE_BOUND + (mBoundState*SERVICE_COUNT),
345                         now - mBoundStartTime);
346             } else if (bound) {
347                 mBoundCount++;
348             }
349             mBoundState = state;
350             mBoundStartTime = now;
351             updateRunning(memFactor, now);
352         }
353     }
354 
setExecuting(boolean executing, int memFactor, long now)355     public void setExecuting(boolean executing, int memFactor, long now) {
356         if (mOwner == null) {
357             Slog.wtf(TAG, "Executing service " + this + " without owner");
358         }
359         final int state = executing ? memFactor : STATE_NOTHING;
360         if (mExecState != state) {
361             if (mExecState != STATE_NOTHING) {
362                 mDurations.addDuration(SERVICE_EXEC + (mExecState*SERVICE_COUNT),
363                         now - mExecStartTime);
364             } else if (executing) {
365                 mExecCount++;
366             }
367             mExecState = state;
368             mExecStartTime = now;
369             updateRunning(memFactor, now);
370         }
371     }
372 
setForeground(boolean foreground, int memFactor, long now)373     public void setForeground(boolean foreground, int memFactor, long now) {
374         if (mOwner == null) {
375             Slog.wtf(TAG, "Foregrounding service " + this + " without owner");
376         }
377         final int state = foreground ? memFactor : STATE_NOTHING;
378         if (mForegroundState != state) {
379             if (mForegroundState != STATE_NOTHING) {
380                 mDurations.addDuration(SERVICE_FOREGROUND + (mForegroundState*SERVICE_COUNT),
381                         now - mForegroundStartTime);
382             } else if (foreground) {
383                 mForegroundCount++;
384             }
385             mForegroundState = state;
386             mForegroundStartTime = now;
387             updateRunning(memFactor, now);
388         }
389     }
390 
getDuration(int opType, int curState, long startTime, int memFactor, long now)391     public long getDuration(int opType, int curState, long startTime, int memFactor,
392             long now) {
393         int state = opType + (memFactor*SERVICE_COUNT);
394         long time = mDurations.getValueForId((byte)state);
395         if (curState == memFactor) {
396             time += now - startTime;
397         }
398         return time;
399     }
400 
dumpStats(PrintWriter pw, String prefix, String prefixInner, String headerPrefix, long now, long totalTime, boolean dumpSummary, boolean dumpAll)401     public void dumpStats(PrintWriter pw, String prefix, String prefixInner, String headerPrefix,
402             long now, long totalTime, boolean dumpSummary, boolean dumpAll) {
403         dumpStats(pw, prefix, prefixInner, headerPrefix, "Running",
404                 mRunCount, ServiceState.SERVICE_RUN, mRunState,
405                 mRunStartTime, now, totalTime, !dumpSummary || dumpAll);
406         dumpStats(pw, prefix, prefixInner, headerPrefix, "Started",
407                 mStartedCount, ServiceState.SERVICE_STARTED, mStartedState,
408                 mStartedStartTime, now, totalTime, !dumpSummary || dumpAll);
409         dumpStats(pw, prefix, prefixInner, headerPrefix, "Foreground",
410                 mForegroundCount, ServiceState.SERVICE_FOREGROUND, mForegroundState,
411                 mForegroundStartTime, now, totalTime, !dumpSummary || dumpAll);
412         dumpStats(pw, prefix, prefixInner, headerPrefix, "Bound",
413                 mBoundCount, ServiceState.SERVICE_BOUND, mBoundState,
414                 mBoundStartTime, now, totalTime, !dumpSummary || dumpAll);
415         dumpStats(pw, prefix, prefixInner, headerPrefix, "Executing",
416                 mExecCount, ServiceState.SERVICE_EXEC, mExecState,
417                 mExecStartTime, now, totalTime, !dumpSummary || dumpAll);
418         if (dumpAll) {
419             if (mOwner != null) {
420                 pw.print("        mOwner="); pw.println(mOwner);
421             }
422             if (mStarted || mRestarting) {
423                 pw.print("        mStarted="); pw.print(mStarted);
424                 pw.print(" mRestarting="); pw.println(mRestarting);
425             }
426         }
427     }
428 
dumpStats(PrintWriter pw, String prefix, String prefixInner, String headerPrefix, String header, int count, int serviceType, int state, long startTime, long now, long totalTime, boolean dumpAll)429     private void dumpStats(PrintWriter pw, String prefix, String prefixInner,
430             String headerPrefix, String header,
431             int count, int serviceType, int state, long startTime, long now, long totalTime,
432             boolean dumpAll) {
433         if (count != 0) {
434             if (dumpAll) {
435                 pw.print(prefix); pw.print(header);
436                 pw.print(" op count "); pw.print(count); pw.println(":");
437                 dumpTime(pw, prefixInner, serviceType, state, startTime, now);
438             } else {
439                 long myTime = dumpTimeInternal(null, null, serviceType, state, startTime, now,
440                         true);
441                 pw.print(prefix); pw.print(headerPrefix); pw.print(header);
442                 pw.print(" count "); pw.print(count);
443                 pw.print(" / time ");
444                 boolean isRunning = myTime < 0;
445                 if (isRunning) {
446                     myTime = -myTime;
447                 }
448                 DumpUtils.printPercent(pw, (double)myTime/(double)totalTime);
449                 if (isRunning) {
450                     pw.print(" (running)");
451                 }
452                 pw.println();
453             }
454         }
455     }
456 
457     public long dumpTime(PrintWriter pw, String prefix,
458             int serviceType, int curState, long curStartTime, long now) {
459         return dumpTimeInternal(pw, prefix, serviceType, curState, curStartTime, now, false);
460     }
461 
462     long dumpTimeInternal(PrintWriter pw, String prefix,
463             int serviceType, int curState, long curStartTime, long now, boolean negativeIfRunning) {
464         long totalTime = 0;
465         int printedScreen = -1;
466         boolean isRunning = false;
467         for (int iscreen=0; iscreen<ProcessStats.ADJ_COUNT; iscreen+=ProcessStats.ADJ_SCREEN_MOD) {
468             int printedMem = -1;
469             for (int imem=0; imem<ProcessStats.ADJ_MEM_FACTOR_COUNT; imem++) {
470                 int state = imem+iscreen;
471                 long time = getDuration(serviceType, curState, curStartTime, state, now);
472                 String running = "";
473                 if (curState == state && pw != null) {
474                     running = " (running)";
475                     isRunning = true;
476                 }
477                 if (time != 0) {
478                     if (pw != null) {
479                         pw.print(prefix);
480                         DumpUtils.printScreenLabel(pw, printedScreen != iscreen
481                                 ? iscreen : STATE_NOTHING);
482                         printedScreen = iscreen;
483                         DumpUtils.printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING,
484                                 (char)0);
485                         printedMem = imem;
486                         pw.print(": ");
487                         TimeUtils.formatDuration(time, pw); pw.println(running);
488                     }
489                     totalTime += time;
490                 }
491             }
492         }
493         if (totalTime != 0 && pw != null) {
494             pw.print(prefix);
495             pw.print("    TOTAL: ");
496             TimeUtils.formatDuration(totalTime, pw);
497             pw.println();
498         }
499         return (isRunning && negativeIfRunning) ? -totalTime : totalTime;
500     }
501 
502     public void dumpTimesCheckin(PrintWriter pw, String pkgName, int uid, long vers,
503             String serviceName, long now) {
504         dumpTimeCheckin(pw, "pkgsvc-run", pkgName, uid, vers, serviceName,
505                 ServiceState.SERVICE_RUN, mRunCount, mRunState, mRunStartTime, now);
506         dumpTimeCheckin(pw, "pkgsvc-start", pkgName, uid, vers, serviceName,
507                 ServiceState.SERVICE_STARTED, mStartedCount, mStartedState, mStartedStartTime, now);
508         dumpTimeCheckin(pw, "pkgsvc-fg", pkgName, uid, vers, serviceName,
509                 ServiceState.SERVICE_FOREGROUND, mForegroundCount, mForegroundState,
510                 mForegroundStartTime, now);
511         dumpTimeCheckin(pw, "pkgsvc-bound", pkgName, uid, vers, serviceName,
512                 ServiceState.SERVICE_BOUND, mBoundCount, mBoundState, mBoundStartTime, now);
513         dumpTimeCheckin(pw, "pkgsvc-exec", pkgName, uid, vers, serviceName,
514                 ServiceState.SERVICE_EXEC, mExecCount, mExecState, mExecStartTime, now);
515     }
516 
517     private void dumpTimeCheckin(PrintWriter pw, String label, String packageName,
518             int uid, long vers, String serviceName, int serviceType, int opCount,
519             int curState, long curStartTime, long now) {
520         if (opCount <= 0) {
521             return;
522         }
523         pw.print(label);
524         pw.print(",");
525         pw.print(packageName);
526         pw.print(",");
527         pw.print(uid);
528         pw.print(",");
529         pw.print(vers);
530         pw.print(",");
531         pw.print(serviceName);
532         pw.print(",");
533         pw.print(opCount);
534         boolean didCurState = false;
535         final int N = mDurations.getKeyCount();
536         for (int i=0; i<N; i++) {
537             final int key = mDurations.getKeyAt(i);
538             long time = mDurations.getValue(key);
539             int type = SparseMappingTable.getIdFromKey(key);
540             int memFactor = type / ServiceState.SERVICE_COUNT;
541             type %= ServiceState.SERVICE_COUNT;
542             if (type != serviceType) {
543                 continue;
544             }
545             if (curState == memFactor) {
546                 didCurState = true;
547                 time += now - curStartTime;
548             }
549             DumpUtils.printAdjTagAndValue(pw, memFactor, time);
550         }
551         if (!didCurState && curState != STATE_NOTHING) {
552             DumpUtils.printAdjTagAndValue(pw, curState, now - curStartTime);
553         }
554         pw.println();
555     }
556 
557     public void writeToProto(ProtoOutputStream proto, long fieldId, long now) {
558         final long token = proto.start(fieldId);
559         proto.write(PackageServiceStatsProto.SERVICE_NAME, mName);
560 
561         writeTypeToProto(proto, PackageServiceStatsProto.OPERATION_STATS,
562                 ProcessStatsEnums.SERVICE_OPERATION_STATE_RUNNING,
563                 ServiceState.SERVICE_RUN, mRunCount, mRunState, mRunStartTime, now);
564 
565         writeTypeToProto(proto, PackageServiceStatsProto.OPERATION_STATS,
566                 ProcessStatsEnums.SERVICE_OPERATION_STATE_STARTED,
567                 ServiceState.SERVICE_STARTED, mStartedCount, mStartedState, mStartedStartTime, now);
568 
569         writeTypeToProto(proto, PackageServiceStatsProto.OPERATION_STATS,
570                 ProcessStatsEnums.SERVICE_OPERATION_STATE_FOREGROUND,
571                 ServiceState.SERVICE_FOREGROUND, mForegroundCount, mForegroundState,
572                 mForegroundStartTime, now);
573 
574         writeTypeToProto(proto, PackageServiceStatsProto.OPERATION_STATS,
575                 ProcessStatsEnums.SERVICE_OPERATION_STATE_BOUND,
576                 ServiceState.SERVICE_BOUND, mBoundCount, mBoundState, mBoundStartTime, now);
577 
578         writeTypeToProto(proto, PackageServiceStatsProto.OPERATION_STATS,
579                 ProcessStatsEnums.SERVICE_OPERATION_STATE_EXECUTING,
580                 ServiceState.SERVICE_EXEC, mExecCount, mExecState, mExecStartTime, now);
581 
582         proto.end(token);
583     }
584 
585     /**
586      * write the metrics to proto for each operation type.
587      */
588     public void writeTypeToProto(ProtoOutputStream proto, long fieldId, int opType, int serviceType,
589             int opCount, int curState, long curStartTime, long now) {
590         if (opCount <= 0) {
591             return;
592         }
593         final long token = proto.start(fieldId);
594 
595         proto.write(PackageServiceOperationStatsProto.OPERATION, opType);
596         proto.write(PackageServiceOperationStatsProto.COUNT, opCount);
597 
598         boolean didCurState = false;
599         final int N = mDurations.getKeyCount();
600         for (int i=0; i<N; i++) {
601             final int key = mDurations.getKeyAt(i);
602             long time = mDurations.getValue(key);
603             int type = SparseMappingTable.getIdFromKey(key);
604             int memFactor = type / ServiceState.SERVICE_COUNT;
605             type %= ServiceState.SERVICE_COUNT;
606             if (type != serviceType) {
607                 continue;
608             }
609             if (curState == memFactor) {
610                 didCurState = true;
611                 time += now - curStartTime;
612             }
613             final long stateToken = proto.start(PackageServiceOperationStatsProto.STATE_STATS);
614             DumpUtils.printProcStateAdjTagProto(proto,
615                     PackageServiceOperationStatsProto.StateStats.SCREEN_STATE,
616                     PackageServiceOperationStatsProto.StateStats.MEMORY_STATE,
617                     type);
618             proto.write(PackageServiceOperationStatsProto.StateStats.DURATION_MS, time);
619             proto.end(stateToken);
620         }
621         if (!didCurState && curState != STATE_NOTHING) {
622             final long stateToken = proto.start(PackageServiceOperationStatsProto.STATE_STATS);
623             DumpUtils.printProcStateAdjTagProto(proto,
624                     PackageServiceOperationStatsProto.StateStats.SCREEN_STATE,
625                     PackageServiceOperationStatsProto.StateStats.MEMORY_STATE,
626                     curState);
627             proto.write(PackageServiceOperationStatsProto.StateStats.DURATION_MS,
628                     now - curStartTime);
629             proto.end(stateToken);
630         }
631 
632         proto.end(token);
633     }
634 
635     public String toString() {
636         return "ServiceState{" + Integer.toHexString(System.identityHashCode(this))
637                 + " " + mName + " pkg=" + mPackage + " proc="
638                 + Integer.toHexString(System.identityHashCode(mProc)) + "}";
639     }
640 }
641