1/*
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.settings.applications;
18
19import android.content.Context;
20import android.content.pm.ApplicationInfo;
21import android.content.pm.PackageManager;
22import android.os.Parcel;
23import android.os.Parcelable;
24
25import com.android.settings.R;
26import com.android.settings.Utils;
27
28import java.util.ArrayList;
29
30public class ProcStatsPackageEntry implements Parcelable {
31    private static final String TAG = "ProcStatsEntry";
32    private static boolean DEBUG = ProcessStatsUi.DEBUG;
33
34    private static final float ALWAYS_THRESHOLD = .95f;
35    private static final float SOMETIMES_THRESHOLD = .25f;
36
37    final String mPackage;
38    final ArrayList<ProcStatsEntry> mEntries = new ArrayList<ProcStatsEntry>();
39
40    long mBgDuration;
41    long mAvgBgMem;
42    long mMaxBgMem;
43    double mBgWeight;
44    long mRunDuration;
45    long mAvgRunMem;
46    long mMaxRunMem;
47    double mRunWeight;
48
49    public ApplicationInfo mUiTargetApp;
50    public String mUiLabel;
51    private long mWindowLength;
52
53    public ProcStatsPackageEntry(String pkg, long windowLength) {
54        mPackage = pkg;
55        mWindowLength = windowLength;
56    }
57
58    public ProcStatsPackageEntry(Parcel in) {
59        mPackage = in.readString();
60        in.readTypedList(mEntries, ProcStatsEntry.CREATOR);
61        mBgDuration = in.readLong();
62        mAvgBgMem = in.readLong();
63        mMaxBgMem = in.readLong();
64        mBgWeight = in.readDouble();
65        mRunDuration = in.readLong();
66        mAvgRunMem = in.readLong();
67        mMaxRunMem = in.readLong();
68        mRunWeight = in.readDouble();
69    }
70
71    public CharSequence getRunningFrequency(Context context) {
72        float amountRunning = mRunDuration / (float) mWindowLength;
73        return getFrequency(amountRunning, context);
74    }
75
76    public CharSequence getBackgroundFrequency(Context context) {
77        float amountRunning = mBgDuration / (float) mWindowLength;
78        return getFrequency(amountRunning, context);
79    }
80
81    public void addEntry(ProcStatsEntry entry) {
82        mEntries.add(entry);
83    }
84
85    public void updateMetrics() {
86        mBgDuration = mAvgBgMem = mMaxBgMem = 0;
87        mBgWeight = 0;
88        mRunDuration = mAvgRunMem = mMaxRunMem = 0;
89        mRunWeight = 0;
90        final int N = mEntries.size();
91        for (int i=0; i < N; i++) {
92            ProcStatsEntry entry = mEntries.get(i);
93            mBgDuration = Math.max(entry.mBgDuration, mBgDuration);
94            mAvgBgMem += entry.mAvgBgMem;
95            mBgWeight += entry.mBgWeight;
96            mRunDuration = Math.max(entry.mRunDuration, mRunDuration);
97            mAvgRunMem += entry.mAvgRunMem;
98            mRunWeight += entry.mRunWeight;
99
100            // Each entry is generally a process or something similar.  Since it is extremely
101            // unlikely that any apps are going to avoid running processes at the same time
102            // to avoid memory usage, we will sum the maximum memory usage to create a
103            // hypothetical worst case scenario of memory.
104            mMaxBgMem += entry.mMaxBgMem;
105            mMaxRunMem += entry.mMaxRunMem;
106        }
107        mAvgBgMem /= N;
108        mAvgRunMem /= N;
109    }
110
111    public void retrieveUiData(Context context, PackageManager pm) {
112        mUiTargetApp = null;
113        mUiLabel = mPackage;
114        // Only one app associated with this process.
115        try {
116            if ("os".equals(mPackage)) {
117                mUiTargetApp = pm.getApplicationInfo("android",
118                        PackageManager.GET_DISABLED_COMPONENTS |
119                        PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS |
120                        PackageManager.GET_UNINSTALLED_PACKAGES);
121                mUiLabel = context.getString(R.string.process_stats_os_label);
122            } else {
123                mUiTargetApp = pm.getApplicationInfo(mPackage,
124                        PackageManager.GET_DISABLED_COMPONENTS |
125                        PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS |
126                        PackageManager.GET_UNINSTALLED_PACKAGES);
127                mUiLabel = mUiTargetApp.loadLabel(pm).toString();
128            }
129        } catch (PackageManager.NameNotFoundException e) {
130        }
131    }
132
133    @Override
134    public int describeContents() {
135        return 0;
136    }
137
138    @Override
139    public void writeToParcel(Parcel dest, int flags) {
140        dest.writeString(mPackage);
141        dest.writeTypedList(mEntries);
142        dest.writeLong(mBgDuration);
143        dest.writeLong(mAvgBgMem);
144        dest.writeLong(mMaxBgMem);
145        dest.writeDouble(mBgWeight);
146        dest.writeLong(mRunDuration);
147        dest.writeLong(mAvgRunMem);
148        dest.writeLong(mMaxRunMem);
149        dest.writeDouble(mRunWeight);
150    }
151
152    public static final Parcelable.Creator<ProcStatsPackageEntry> CREATOR
153            = new Parcelable.Creator<ProcStatsPackageEntry>() {
154        public ProcStatsPackageEntry createFromParcel(Parcel in) {
155            return new ProcStatsPackageEntry(in);
156        }
157
158        public ProcStatsPackageEntry[] newArray(int size) {
159            return new ProcStatsPackageEntry[size];
160        }
161    };
162
163    // TODO: Find better place for this.
164    public static CharSequence getFrequency(float amount, Context context) {
165        if (amount > ALWAYS_THRESHOLD) {
166            return context.getString(R.string.always_running,
167                    Utils.formatPercentage((int) (amount * 100)));
168        } else if (amount > SOMETIMES_THRESHOLD) {
169            return context.getString(R.string.sometimes_running,
170                    Utils.formatPercentage((int) (amount * 100)));
171        } else {
172            return context.getString(R.string.rarely_running,
173                    Utils.formatPercentage((int) (amount * 100)));
174        }
175    }
176}
177