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.settings.applications;
18
19import android.content.pm.ApplicationInfo;
20import android.content.pm.PackageInfo;
21import android.content.pm.PackageManager;
22import android.os.Parcel;
23import android.os.Parcelable;
24import android.util.ArrayMap;
25import android.util.Log;
26import com.android.internal.app.ProcessStats;
27
28import java.util.ArrayList;
29import java.util.Collections;
30import java.util.Comparator;
31
32public final class ProcStatsEntry implements Parcelable {
33    private static final String TAG = "ProcStatsEntry";
34    private static boolean DEBUG = ProcessStatsUi.DEBUG;
35
36    final String mPackage;
37    final int mUid;
38    final String mName;
39    final ArrayList<String> mPackages = new ArrayList<String>();
40    final long mDuration;
41    final long mAvgPss;
42    final long mMaxPss;
43    final long mAvgUss;
44    final long mMaxUss;
45    final long mWeight;
46
47    String mBestTargetPackage;
48
49    ArrayMap<String, ArrayList<Service>> mServices = new ArrayMap<String, ArrayList<Service>>(1);
50
51    public ApplicationInfo mUiTargetApp;
52    public String mUiLabel;
53    public String mUiBaseLabel;
54    public String mUiPackage;
55
56    public ProcStatsEntry(ProcessStats.ProcessState proc, String packageName,
57            ProcessStats.ProcessDataCollection tmpTotals, boolean useUss, boolean weightWithTime) {
58        ProcessStats.computeProcessData(proc, tmpTotals, 0);
59        mPackage = proc.mPackage;
60        mUid = proc.mUid;
61        mName = proc.mName;
62        mPackages.add(packageName);
63        mDuration = tmpTotals.totalTime;
64        mAvgPss = tmpTotals.avgPss;
65        mMaxPss = tmpTotals.maxPss;
66        mAvgUss = tmpTotals.avgUss;
67        mMaxUss = tmpTotals.maxUss;
68        mWeight = (weightWithTime ? mDuration : 1) * (useUss ? mAvgUss : mAvgPss);
69        if (DEBUG) Log.d(TAG, "New proc entry " + proc.mName + ": dur=" + mDuration
70                + " avgpss=" + mAvgPss + " weight=" + mWeight);
71    }
72
73    public ProcStatsEntry(Parcel in) {
74        mPackage = in.readString();
75        mUid = in.readInt();
76        mName = in.readString();
77        in.readStringList(mPackages);
78        mDuration = in.readLong();
79        mAvgPss = in.readLong();
80        mMaxPss = in.readLong();
81        mAvgUss = in.readLong();
82        mMaxUss = in.readLong();
83        mWeight = in.readLong();
84        mBestTargetPackage = in.readString();
85        final int N = in.readInt();
86        if (N > 0) {
87            mServices.ensureCapacity(N);
88            for (int i=0; i<N; i++) {
89                String key = in.readString();
90                ArrayList<Service> value = new ArrayList<Service>();
91                in.readTypedList(value, Service.CREATOR);
92                mServices.append(key, value);
93            }
94        }
95    }
96
97    public void addPackage(String packageName) {
98        mPackages.add(packageName);
99    }
100
101    public void evaluateTargetPackage(PackageManager pm, ProcessStats stats,
102            ProcessStats.ProcessDataCollection totals, Comparator<ProcStatsEntry> compare,
103            boolean useUss, boolean weightWithTime) {
104        mBestTargetPackage = null;
105        if (mPackages.size() == 1) {
106            if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": single pkg " + mPackages.get(0));
107            mBestTargetPackage = mPackages.get(0);
108        } else {
109            // See if there is one significant package that was running here.
110            ArrayList<ProcStatsEntry> subProcs = new ArrayList<ProcStatsEntry>();
111            for (int ipkg=0; ipkg<mPackages.size(); ipkg++) {
112                ProcessStats.PackageState pkgState = stats.mPackages.get(mPackages.get(ipkg), mUid);
113                if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ", pkg "
114                        + mPackages.get(ipkg) + ":");
115                if (pkgState == null) {
116                    Log.w(TAG, "No package state found for " + mPackages.get(ipkg) + "/"
117                            + mUid + " in process " + mName);
118                    continue;
119                }
120                ProcessStats.ProcessState pkgProc = pkgState.mProcesses.get(mName);
121                if (pkgProc == null) {
122                    Log.w(TAG, "No process " + mName + " found in package state "
123                            + mPackages.get(ipkg) + "/" + mUid);
124                    continue;
125                }
126                subProcs.add(new ProcStatsEntry(pkgProc, pkgState.mPackageName, totals, useUss,
127                        weightWithTime));
128            }
129            if (subProcs.size() > 1) {
130                Collections.sort(subProcs, compare);
131                if (subProcs.get(0).mWeight > (subProcs.get(1).mWeight*3)) {
132                    if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": best pkg "
133                            + subProcs.get(0).mPackage + " weight " + subProcs.get(0).mWeight
134                            + " better than " + subProcs.get(1).mPackage
135                            + " weight " + subProcs.get(1).mWeight);
136                    mBestTargetPackage = subProcs.get(0).mPackage;
137                    return;
138                }
139                // Couldn't find one that is best by weight, let's decide on best another
140                // way: the one that has the longest running service, accounts for at least
141                // half of the maximum weight, and has specified an explicit app icon.
142                long maxWeight = subProcs.get(0).mWeight;
143                long bestRunTime = -1;
144                for (int i=0; i<subProcs.size(); i++) {
145                    if (subProcs.get(i).mWeight < (maxWeight/2)) {
146                        if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg "
147                                + subProcs.get(i).mPackage + " weight " + subProcs.get(i).mWeight
148                                + " too small");
149                        continue;
150                    }
151                    try {
152                        ApplicationInfo ai = pm.getApplicationInfo(subProcs.get(i).mPackage, 0);
153                        if (ai.icon == 0) {
154                            if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg "
155                                    + subProcs.get(i).mPackage + " has no icon");
156                            continue;
157                        }
158                    } catch (PackageManager.NameNotFoundException e) {
159                        if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg "
160                                + subProcs.get(i).mPackage + " failed finding app info");
161                        continue;
162                    }
163                    ArrayList<Service> subProcServices = null;
164                    for (int isp=0, NSP=mServices.size(); isp<NSP; isp++) {
165                        ArrayList<Service> subServices = mServices.valueAt(isp);
166                        if (subServices.get(0).mPackage.equals(subProcs.get(i).mPackage)) {
167                            subProcServices = subServices;
168                            break;
169                        }
170                    }
171                    long thisRunTime = 0;
172                    if (subProcServices != null) {
173                        for (int iss=0, NSS=subProcServices.size(); iss<NSS; iss++) {
174                            Service service = subProcServices.get(iss);
175                            if (service.mDuration > thisRunTime) {
176                                if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg "
177                                        + subProcs.get(i).mPackage + " service " + service.mName
178                                        + " run time is " + service.mDuration);
179                                thisRunTime = service.mDuration;
180                                break;
181                            }
182                        }
183                    }
184                    if (thisRunTime > bestRunTime) {
185                        if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg "
186                                + subProcs.get(i).mPackage + " new best run time " + thisRunTime);
187                        mBestTargetPackage = subProcs.get(i).mPackage;
188                        bestRunTime = thisRunTime;
189                    } else {
190                        if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg "
191                                + subProcs.get(i).mPackage + " run time " + thisRunTime
192                                + " not as good as last " + bestRunTime);
193                    }
194                }
195            } else if (subProcs.size() == 1) {
196                mBestTargetPackage = subProcs.get(0).mPackage;
197            }
198        }
199    }
200
201    public void retrieveUiData(PackageManager pm) {
202        mUiTargetApp = null;
203        mUiLabel = mUiBaseLabel = mName;
204        mUiPackage = mBestTargetPackage;
205        if (mUiPackage != null) {
206            // Only one app associated with this process.
207            try {
208                mUiTargetApp = pm.getApplicationInfo(mUiPackage,
209                        PackageManager.GET_DISABLED_COMPONENTS |
210                        PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS |
211                        PackageManager.GET_UNINSTALLED_PACKAGES);
212                String name = mUiBaseLabel = mUiTargetApp.loadLabel(pm).toString();
213                if (mName.equals(mUiPackage)) {
214                    mUiLabel = name;
215                } else {
216                    if (mName.startsWith(mUiPackage)) {
217                        int off = mUiPackage.length();
218                        if (mName.length() > off) {
219                            off++;
220                        }
221                        mUiLabel = name + " (" + mName.substring(off) + ")";
222                    } else {
223                        mUiLabel = name + " (" + mName + ")";
224                    }
225                }
226            } catch (PackageManager.NameNotFoundException e) {
227            }
228        }
229        if (mUiTargetApp == null) {
230            String[] packages = pm.getPackagesForUid(mUid);
231            if (packages != null) {
232                for (String curPkg : packages) {
233                    try {
234                        final PackageInfo pi = pm.getPackageInfo(curPkg,
235                                PackageManager.GET_DISABLED_COMPONENTS |
236                                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS |
237                                PackageManager.GET_UNINSTALLED_PACKAGES);
238                        if (pi.sharedUserLabel != 0) {
239                            mUiTargetApp = pi.applicationInfo;
240                            final CharSequence nm = pm.getText(curPkg,
241                                    pi.sharedUserLabel, pi.applicationInfo);
242                            if (nm != null) {
243                                mUiBaseLabel = nm.toString();
244                                mUiLabel = mUiBaseLabel + " (" + mName + ")";
245                            } else {
246                                mUiBaseLabel = mUiTargetApp.loadLabel(pm).toString();
247                                mUiLabel = mUiBaseLabel + " (" + mName + ")";
248                            }
249                            break;
250                        }
251                    } catch (PackageManager.NameNotFoundException e) {
252                    }
253                }
254            } else {
255                // no current packages for this uid, typically because of uninstall
256                Log.i(TAG, "No package for uid " + mUid);
257            }
258        }
259    }
260
261    public void addService(ProcessStats.ServiceState svc) {
262        ArrayList<Service> services = mServices.get(svc.mPackage);
263        if (services == null) {
264            services = new ArrayList<Service>();
265            mServices.put(svc.mPackage, services);
266        }
267        services.add(new Service(svc));
268    }
269
270    @Override
271    public int describeContents() {
272        return 0;
273    }
274
275    @Override
276    public void writeToParcel(Parcel dest, int flags) {
277        dest.writeString(mPackage);
278        dest.writeInt(mUid);
279        dest.writeString(mName);
280        dest.writeStringList(mPackages);
281        dest.writeLong(mDuration);
282        dest.writeLong(mAvgPss);
283        dest.writeLong(mMaxPss);
284        dest.writeLong(mAvgUss);
285        dest.writeLong(mMaxUss);
286        dest.writeLong(mWeight);
287        dest.writeString(mBestTargetPackage);
288        final int N = mServices.size();
289        dest.writeInt(N);
290        for (int i=0; i<N; i++) {
291            dest.writeString(mServices.keyAt(i));
292            dest.writeTypedList(mServices.valueAt(i));
293        }
294    }
295
296    public static final Parcelable.Creator<ProcStatsEntry> CREATOR
297            = new Parcelable.Creator<ProcStatsEntry>() {
298        public ProcStatsEntry createFromParcel(Parcel in) {
299            return new ProcStatsEntry(in);
300        }
301
302        public ProcStatsEntry[] newArray(int size) {
303            return new ProcStatsEntry[size];
304        }
305    };
306
307    public static final class Service implements Parcelable {
308        final String mPackage;
309        final String mName;
310        final String mProcess;
311        final long mDuration;
312
313        public Service(ProcessStats.ServiceState service) {
314            mPackage = service.mPackage;
315            mName = service.mName;
316            mProcess = service.mProcessName;
317            mDuration = ProcessStats.dumpSingleServiceTime(null, null, service,
318                    ProcessStats.ServiceState.SERVICE_RUN,
319                    ProcessStats.STATE_NOTHING, 0, 0);
320        }
321
322        public Service(Parcel in) {
323            mPackage = in.readString();
324            mName = in.readString();
325            mProcess = in.readString();
326            mDuration = in.readLong();
327        }
328
329        @Override
330        public int describeContents() {
331            return 0;
332        }
333
334        @Override
335        public void writeToParcel(Parcel dest, int flags) {
336            dest.writeString(mPackage);
337            dest.writeString(mName);
338            dest.writeString(mProcess);
339            dest.writeLong(mDuration);
340        }
341
342        public static final Parcelable.Creator<Service> CREATOR
343                = new Parcelable.Creator<Service>() {
344            public Service createFromParcel(Parcel in) {
345                return new Service(in);
346            }
347
348            public Service[] newArray(int size) {
349                return new Service[size];
350            }
351        };
352    }
353}
354