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
17package com.android.internal.app.procstats;
18
19
20import android.os.Parcel;
21import android.os.Parcelable;
22import android.os.SystemClock;
23import android.os.SystemProperties;
24import android.os.UserHandle;
25import android.text.format.DateFormat;
26import android.util.ArrayMap;
27import android.util.ArraySet;
28import android.util.DebugUtils;
29import android.util.Log;
30import android.util.Slog;
31import android.util.SparseArray;
32import android.util.TimeUtils;
33
34import com.android.internal.app.procstats.ProcessStats;
35import static com.android.internal.app.procstats.ProcessStats.STATE_NOTHING;
36
37import java.io.IOException;
38import java.io.InputStream;
39import java.io.PrintWriter;
40import java.util.ArrayList;
41import java.util.Arrays;
42import java.util.Collections;
43import java.util.Comparator;
44import java.util.Objects;
45
46public final class ServiceState {
47    private static final String TAG = "ProcessStats";
48    private static final boolean DEBUG = false;
49
50    public static final int SERVICE_RUN = 0;
51    public static final int SERVICE_STARTED = 1;
52    public static final int SERVICE_BOUND = 2;
53    public static final int SERVICE_EXEC = 3;
54    public static final int SERVICE_COUNT = 4;
55
56    private final String mPackage;
57    private final String mProcessName;
58    private final String mName;
59    private final DurationsTable mDurations;
60
61    private ProcessState mProc;
62    private Object mOwner;
63
64    private int mRunCount;
65    private int mRunState = STATE_NOTHING;
66    private long mRunStartTime;
67
68    private boolean mStarted;
69    private boolean mRestarting;
70    private int mStartedCount;
71    private int mStartedState = STATE_NOTHING;
72    private long mStartedStartTime;
73
74    private int mBoundCount;
75    private int mBoundState = STATE_NOTHING;
76    private long mBoundStartTime;
77
78    private int mExecCount;
79    private int mExecState = STATE_NOTHING;
80    private long mExecStartTime;
81
82    public ServiceState(ProcessStats processStats, String pkg, String name,
83            String processName, ProcessState proc) {
84        mPackage = pkg;
85        mName = name;
86        mProcessName = processName;
87        mProc = proc;
88        mDurations = new DurationsTable(processStats.mTableData);
89    }
90
91    public String getPackage() {
92        return mPackage;
93    }
94
95    public String getProcessName() {
96        return mProcessName;
97    }
98
99    public String getName() {
100        return mName;
101    }
102
103    public ProcessState getProcess() {
104        return mProc;
105    }
106
107    public void setProcess(ProcessState proc) {
108        mProc = proc;
109    }
110
111    public void setMemFactor(int memFactor, long now) {
112        if (isRestarting()) {
113            setRestarting(true, memFactor, now);
114        } else if (isInUse()) {
115            if (mStartedState != ProcessStats.STATE_NOTHING) {
116                setStarted(true, memFactor, now);
117            }
118            if (mBoundState != ProcessStats.STATE_NOTHING) {
119                setBound(true, memFactor, now);
120            }
121            if (mExecState != ProcessStats.STATE_NOTHING) {
122                setExecuting(true, memFactor, now);
123            }
124        }
125    }
126
127    public void applyNewOwner(Object newOwner) {
128        if (mOwner != newOwner) {
129            if (mOwner == null) {
130                mOwner = newOwner;
131                mProc.incActiveServices(mName);
132            } else {
133                // There was already an old owner, reset this object for its
134                // new owner.
135                mOwner = newOwner;
136                if (mStarted || mBoundState != STATE_NOTHING || mExecState != STATE_NOTHING) {
137                    long now = SystemClock.uptimeMillis();
138                    if (mStarted) {
139                        if (DEBUG) Slog.d(TAG, "Service has new owner " + newOwner
140                                + " from " + mOwner + " while started: pkg="
141                                + mPackage + " service=" + mName + " proc=" + mProc);
142                        setStarted(false, 0, now);
143                    }
144                    if (mBoundState != STATE_NOTHING) {
145                        if (DEBUG) Slog.d(TAG, "Service has new owner " + newOwner
146                                + " from " + mOwner + " while bound: pkg="
147                                + mPackage + " service=" + mName + " proc=" + mProc);
148                        setBound(false, 0, now);
149                    }
150                    if (mExecState != STATE_NOTHING) {
151                        if (DEBUG) Slog.d(TAG, "Service has new owner " + newOwner
152                                + " from " + mOwner + " while executing: pkg="
153                                + mPackage + " service=" + mName + " proc=" + mProc);
154                        setExecuting(false, 0, now);
155                    }
156                }
157            }
158        }
159    }
160
161    public void clearCurrentOwner(Object owner, boolean silently) {
162        if (mOwner == owner) {
163            mProc.decActiveServices(mName);
164            if (mStarted || mBoundState != STATE_NOTHING || mExecState != STATE_NOTHING) {
165                long now = SystemClock.uptimeMillis();
166                if (mStarted) {
167                    if (!silently) {
168                        Slog.wtfStack(TAG, "Service owner " + owner
169                                + " cleared while started: pkg=" + mPackage + " service="
170                                + mName + " proc=" + mProc);
171                    }
172                    setStarted(false, 0, now);
173                }
174                if (mBoundState != STATE_NOTHING) {
175                    if (!silently) {
176                        Slog.wtfStack(TAG, "Service owner " + owner
177                                + " cleared while bound: pkg=" + mPackage + " service="
178                                + mName + " proc=" + mProc);
179                    }
180                    setBound(false, 0, now);
181                }
182                if (mExecState != STATE_NOTHING) {
183                    if (!silently) {
184                        Slog.wtfStack(TAG, "Service owner " + owner
185                                + " cleared while exec: pkg=" + mPackage + " service="
186                                + mName + " proc=" + mProc);
187                    }
188                    setExecuting(false, 0, now);
189                }
190            }
191            mOwner = null;
192        }
193    }
194
195    public boolean isInUse() {
196        return mOwner != null || mRestarting;
197    }
198
199    public boolean isRestarting() {
200        return mRestarting;
201    }
202
203    public void add(ServiceState other) {
204        mDurations.addDurations(other.mDurations);
205        mRunCount += other.mRunCount;
206        mStartedCount += other.mStartedCount;
207        mBoundCount += other.mBoundCount;
208        mExecCount += other.mExecCount;
209    }
210
211    public void resetSafely(long now) {
212        mDurations.resetTable();
213        mRunCount = mRunState != STATE_NOTHING ? 1 : 0;
214        mStartedCount = mStartedState != STATE_NOTHING ? 1 : 0;
215        mBoundCount = mBoundState != STATE_NOTHING ? 1 : 0;
216        mExecCount = mExecState != STATE_NOTHING ? 1 : 0;
217        mRunStartTime = mStartedStartTime = mBoundStartTime = mExecStartTime = now;
218    }
219
220    public void writeToParcel(Parcel out, long now) {
221        mDurations.writeToParcel(out);
222        out.writeInt(mRunCount);
223        out.writeInt(mStartedCount);
224        out.writeInt(mBoundCount);
225        out.writeInt(mExecCount);
226    }
227
228    public boolean readFromParcel(Parcel in) {
229        if (!mDurations.readFromParcel(in)) {
230            return false;
231        }
232        mRunCount = in.readInt();
233        mStartedCount = in.readInt();
234        mBoundCount = in.readInt();
235        mExecCount = in.readInt();
236        return true;
237    }
238
239    public void commitStateTime(long now) {
240        if (mRunState != STATE_NOTHING) {
241            mDurations.addDuration(SERVICE_RUN + (mRunState*SERVICE_COUNT),
242                    now - mRunStartTime);
243            mRunStartTime = now;
244        }
245        if (mStartedState != STATE_NOTHING) {
246            mDurations.addDuration(SERVICE_STARTED + (mStartedState*SERVICE_COUNT),
247                    now - mStartedStartTime);
248            mStartedStartTime = now;
249        }
250        if (mBoundState != STATE_NOTHING) {
251            mDurations.addDuration(SERVICE_BOUND + (mBoundState*SERVICE_COUNT),
252                    now - mBoundStartTime);
253            mBoundStartTime = now;
254        }
255        if (mExecState != STATE_NOTHING) {
256            mDurations.addDuration(SERVICE_EXEC + (mExecState*SERVICE_COUNT),
257                    now - mExecStartTime);
258            mExecStartTime = now;
259        }
260    }
261
262    private void updateRunning(int memFactor, long now) {
263        final int state = (mStartedState != STATE_NOTHING || mBoundState != STATE_NOTHING
264                || mExecState != STATE_NOTHING) ? memFactor : STATE_NOTHING;
265        if (mRunState != state) {
266            if (mRunState != STATE_NOTHING) {
267                mDurations.addDuration(SERVICE_RUN + (mRunState*SERVICE_COUNT),
268                        now - mRunStartTime);
269            } else if (state != STATE_NOTHING) {
270                mRunCount++;
271            }
272            mRunState = state;
273            mRunStartTime = now;
274        }
275    }
276
277    public void setStarted(boolean started, int memFactor, long now) {
278        if (mOwner == null) {
279            Slog.wtf(TAG, "Starting service " + this + " without owner");
280        }
281        mStarted = started;
282        updateStartedState(memFactor, now);
283    }
284
285    public void setRestarting(boolean restarting, int memFactor, long now) {
286        mRestarting = restarting;
287        updateStartedState(memFactor, now);
288    }
289
290    public void updateStartedState(int memFactor, long now) {
291        final boolean wasStarted = mStartedState != STATE_NOTHING;
292        final boolean started = mStarted || mRestarting;
293        final int state = started ? memFactor : STATE_NOTHING;
294        if (mStartedState != state) {
295            if (mStartedState != STATE_NOTHING) {
296                mDurations.addDuration(SERVICE_STARTED + (mStartedState*SERVICE_COUNT),
297                        now - mStartedStartTime);
298            } else if (started) {
299                mStartedCount++;
300            }
301            mStartedState = state;
302            mStartedStartTime = now;
303            mProc = mProc.pullFixedProc(mPackage);
304            if (wasStarted != started) {
305                if (started) {
306                    mProc.incStartedServices(memFactor, now, mName);
307                } else {
308                    mProc.decStartedServices(memFactor, now, mName);
309                }
310            }
311            updateRunning(memFactor, now);
312        }
313    }
314
315    public void setBound(boolean bound, int memFactor, long now) {
316        if (mOwner == null) {
317            Slog.wtf(TAG, "Binding service " + this + " without owner");
318        }
319        final int state = bound ? memFactor : STATE_NOTHING;
320        if (mBoundState != state) {
321            if (mBoundState != STATE_NOTHING) {
322                mDurations.addDuration(SERVICE_BOUND + (mBoundState*SERVICE_COUNT),
323                        now - mBoundStartTime);
324            } else if (bound) {
325                mBoundCount++;
326            }
327            mBoundState = state;
328            mBoundStartTime = now;
329            updateRunning(memFactor, now);
330        }
331    }
332
333    public void setExecuting(boolean executing, int memFactor, long now) {
334        if (mOwner == null) {
335            Slog.wtf(TAG, "Executing service " + this + " without owner");
336        }
337        final int state = executing ? memFactor : STATE_NOTHING;
338        if (mExecState != state) {
339            if (mExecState != STATE_NOTHING) {
340                mDurations.addDuration(SERVICE_EXEC + (mExecState*SERVICE_COUNT),
341                        now - mExecStartTime);
342            } else if (executing) {
343                mExecCount++;
344            }
345            mExecState = state;
346            mExecStartTime = now;
347            updateRunning(memFactor, now);
348        }
349    }
350
351    public long getDuration(int opType, int curState, long startTime, int memFactor,
352            long now) {
353        int state = opType + (memFactor*SERVICE_COUNT);
354        long time = mDurations.getValueForId((byte)state);
355        if (curState == memFactor) {
356            time += now - startTime;
357        }
358        return time;
359    }
360
361    public void dumpStats(PrintWriter pw, String prefix, String prefixInner, String headerPrefix,
362            long now, long totalTime, boolean dumpSummary, boolean dumpAll) {
363        dumpStats(pw, prefix, prefixInner, headerPrefix, "Running",
364                mRunCount, ServiceState.SERVICE_RUN, mRunState,
365                mRunStartTime, now, totalTime, !dumpSummary || dumpAll);
366        dumpStats(pw, prefix, prefixInner, headerPrefix, "Started",
367                mStartedCount, ServiceState.SERVICE_STARTED, mStartedState,
368                mStartedStartTime, now, totalTime, !dumpSummary || dumpAll);
369        dumpStats(pw, prefix, prefixInner, headerPrefix, "Bound",
370                mBoundCount, ServiceState.SERVICE_BOUND, mBoundState,
371                mBoundStartTime, now, totalTime, !dumpSummary || dumpAll);
372        dumpStats(pw, prefix, prefixInner, headerPrefix, "Executing",
373                mExecCount, ServiceState.SERVICE_EXEC, mExecState,
374                mExecStartTime, now, totalTime, !dumpSummary || dumpAll);
375        if (dumpAll) {
376            if (mOwner != null) {
377                pw.print("        mOwner="); pw.println(mOwner);
378            }
379            if (mStarted || mRestarting) {
380                pw.print("        mStarted="); pw.print(mStarted);
381                pw.print(" mRestarting="); pw.println(mRestarting);
382            }
383        }
384    }
385
386    private void dumpStats(PrintWriter pw, String prefix, String prefixInner,
387            String headerPrefix, String header,
388            int count, int serviceType, int state, long startTime, long now, long totalTime,
389            boolean dumpAll) {
390        if (count != 0) {
391            if (dumpAll) {
392                pw.print(prefix); pw.print(header);
393                pw.print(" op count "); pw.print(count); pw.println(":");
394                dumpTime(pw, prefixInner, serviceType, state, startTime, now);
395            } else {
396                long myTime = dumpTime(null, null, serviceType, state, startTime, now);
397                pw.print(prefix); pw.print(headerPrefix); pw.print(header);
398                pw.print(" count "); pw.print(count);
399                pw.print(" / time ");
400                DumpUtils.printPercent(pw, (double)myTime/(double)totalTime);
401                pw.println();
402            }
403        }
404    }
405
406    public long dumpTime(PrintWriter pw, String prefix,
407            int serviceType, int curState, long curStartTime, long now) {
408        long totalTime = 0;
409        int printedScreen = -1;
410        for (int iscreen=0; iscreen<ProcessStats.ADJ_COUNT; iscreen+=ProcessStats.ADJ_SCREEN_MOD) {
411            int printedMem = -1;
412            for (int imem=0; imem<ProcessStats.ADJ_MEM_FACTOR_COUNT; imem++) {
413                int state = imem+iscreen;
414                long time = getDuration(serviceType, curState, curStartTime, state, now);
415                String running = "";
416                if (curState == state && pw != null) {
417                    running = " (running)";
418                }
419                if (time != 0) {
420                    if (pw != null) {
421                        pw.print(prefix);
422                        DumpUtils.printScreenLabel(pw, printedScreen != iscreen
423                                ? iscreen : STATE_NOTHING);
424                        printedScreen = iscreen;
425                        DumpUtils.printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING,
426                                (char)0);
427                        printedMem = imem;
428                        pw.print(": ");
429                        TimeUtils.formatDuration(time, pw); pw.println(running);
430                    }
431                    totalTime += time;
432                }
433            }
434        }
435        if (totalTime != 0 && pw != null) {
436            pw.print(prefix);
437            pw.print("    TOTAL: ");
438            TimeUtils.formatDuration(totalTime, pw);
439            pw.println();
440        }
441        return totalTime;
442    }
443
444    public void dumpTimesCheckin(PrintWriter pw, String pkgName, int uid, int vers,
445            String serviceName, long now) {
446        dumpTimeCheckin(pw, "pkgsvc-run", pkgName, uid, vers, serviceName,
447                ServiceState.SERVICE_RUN, mRunCount, mRunState, mRunStartTime, now);
448        dumpTimeCheckin(pw, "pkgsvc-start", pkgName, uid, vers, serviceName,
449                ServiceState.SERVICE_STARTED, mStartedCount, mStartedState, mStartedStartTime, now);
450        dumpTimeCheckin(pw, "pkgsvc-bound", pkgName, uid, vers, serviceName,
451                ServiceState.SERVICE_BOUND, mBoundCount, mBoundState, mBoundStartTime, now);
452        dumpTimeCheckin(pw, "pkgsvc-exec", pkgName, uid, vers, serviceName,
453                ServiceState.SERVICE_EXEC, mExecCount, mExecState, mExecStartTime, now);
454    }
455
456    private void dumpTimeCheckin(PrintWriter pw, String label, String packageName,
457            int uid, int vers, String serviceName, int serviceType, int opCount,
458            int curState, long curStartTime, long now) {
459        if (opCount <= 0) {
460            return;
461        }
462        pw.print(label);
463        pw.print(",");
464        pw.print(packageName);
465        pw.print(",");
466        pw.print(uid);
467        pw.print(",");
468        pw.print(vers);
469        pw.print(",");
470        pw.print(serviceName);
471        pw.print(",");
472        pw.print(opCount);
473        boolean didCurState = false;
474        final int N = mDurations.getKeyCount();
475        for (int i=0; i<N; i++) {
476            final int key = mDurations.getKeyAt(i);
477            long time = mDurations.getValue(key);
478            int type = SparseMappingTable.getIdFromKey(key);
479            int memFactor = type / ServiceState.SERVICE_COUNT;
480            type %= ServiceState.SERVICE_COUNT;
481            if (type != serviceType) {
482                continue;
483            }
484            if (curState == memFactor) {
485                didCurState = true;
486                time += now - curStartTime;
487            }
488            DumpUtils.printAdjTagAndValue(pw, memFactor, time);
489        }
490        if (!didCurState && curState != STATE_NOTHING) {
491            DumpUtils.printAdjTagAndValue(pw, curState, now - curStartTime);
492        }
493        pw.println();
494    }
495
496
497    public String toString() {
498        return "ServiceState{" + Integer.toHexString(System.identityHashCode(this))
499                + " " + mName + " pkg=" + mPackage + " proc="
500                + Integer.toHexString(System.identityHashCode(this)) + "}";
501    }
502}
503