Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6e620876 authored by Joe Onorato's avatar Joe Onorato
Browse files

Add the concept of apps to the battery stats parser.

AttributionKey is how we identify an app.  It contains either
a set of package names, read from the uid records from batterystats
(or later UidMap from statsd), or one of the hard coded SpecialApps.

Test: atest frameworks/base/tools/powermodel --host
Change-Id: If2dee6bffd2d3dafccfeff5c92bafc651b356b15
parent a3f265fa
Loading
Loading
Loading
Loading
+113 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.powermodel;

import java.util.Set;
import java.util.HashSet;

import com.google.common.collect.ImmutableSet;

public class AttributionKey {
    private final int mUid;
    private final ImmutableSet<String> mPackages;
    private final SpecialApp mSpecialApp;

    public AttributionKey(SpecialApp specialApp) {
        mUid = -1;
        mPackages = ImmutableSet.of();
        mSpecialApp = specialApp;
    }

    public AttributionKey(int uid, Set<String> packages) {
        mUid = uid;
        mPackages = ImmutableSet.copyOf(packages);
        mSpecialApp = null;
    }

    public ImmutableSet<String> getPackages() {
        return mPackages;
    }

    public boolean hasPackage(String pkg) {
        return mPackages.contains(pkg);
    }

    public SpecialApp getSpecialApp() {
        return mSpecialApp;
    }

    public boolean isSpecialApp() {
        return mSpecialApp != null;
    }

    /**
     * Returns the uid for this attribution, or -1 if there isn't one
     * (e.g. if it is a special app).
     */
    public int getUid() {
        return mUid;
    }
    @Override
    public int hashCode() {
        int hash = 7;
        hash = (31 * hash) + (mUid);
        hash = (31 * hash) + (mPackages == null ? 0 : mPackages.hashCode());
        hash = (31 * hash) + (mSpecialApp == null ? 0 : mSpecialApp.hashCode());
        return hash;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null) {
            return false;
        }
        if (this.getClass() != o.getClass()) {
            return false;
        }
        final AttributionKey that = (AttributionKey)o;
        return (this.mUid == that.mUid)
                && this.mPackages != null && this.mPackages.equals(that.mPackages)
                && this.mSpecialApp != null && this.mSpecialApp.equals(that.mSpecialApp);
    }

    @Override
    public String toString() {
        final StringBuilder str = new StringBuilder("AttributionKey(");
        if (mUid >= 0) {
            str.append(" uid=");
            str.append(mUid);
        }
        if (mPackages.size() > 0) {
            str.append(" packages=[");
            for (String pkg: mPackages) {
                str.append(' ');
                str.append(pkg);
            }
            str.append(" ]");
        }
        if (mSpecialApp != null) {
            str.append(" specialApp=");
            str.append(mSpecialApp.name());
        }
        str.append(" )");
        return str.toString();
    }
}
+66 −0
Original line number Diff line number Diff line
@@ -56,6 +56,15 @@ public class RawBatteryStats {
     */
    private ImmutableMap<String,ImmutableList<Record>> mRecordsByType;

    /**
     * The attribution keys for which we have data (corresponding to UIDs we've seen).
     * <p>
     * Does not include the synthetic apps.
     * <p>
     * Don't use this before {@link #indexRecords()} has been called.
     */
    private ImmutableSet<AttributionKey> mApps;

    /**
     * The warnings that have been issued during parsing.
     */
@@ -686,6 +695,13 @@ public class RawBatteryStats {
        return ImmutableList.copyOf((List<T>)list);
    }

    /**
     * Get the UIDs that are covered by this batterystats dump.
     */
    public Set<AttributionKey> getApps() {
        return mApps;
    }

    /**
     * No public constructor. Use {@link #parse}.
     */
@@ -721,6 +737,9 @@ public class RawBatteryStats {

        // Gather the records by class name for the getSingle() and getMultiple() functions.
        indexRecords();

        // Gather the uids from all the places UIDs come from, for getApps().
        indexApps();
    }

    /**
@@ -831,6 +850,53 @@ public class RawBatteryStats {
        mRecordsByType = ImmutableMap.copyOf(result);
    }

    /**
     * Collect the UIDs from the csv.
     *
     * They come from two places.
     * <ul>
     *   <li>The uid to package name map entries ({@link #Uid}) at the beginning.
     *   <li>The uid fields of the rest of the entries, some of which might not
     *       have package names associated with them.
     * </ul>
     *
     * TODO: Is this where we should also do the logic about the special UIDs?
     */
    private void indexApps() {
        final HashMap<Integer,HashSet<String>> uids = new HashMap<Integer,HashSet<String>>();

        // The Uid rows, from which we get package names
        for (Uid record: getMultiple(Uid.class)) {
            HashSet<String> list = uids.get(record.uidKey);
            if (list == null) {
                list = new HashSet<String>();
                uids.put(record.uidKey, list);
            }
            list.add(record.pkg);
        }

        // The uid fields of everything
        for (Record record: mRecords) {
            // The 0 in the INFO records isn't really root, it's just unfilled data.
            // The root uid (0) will show up practically in every record, but don't force it.
            if (record.category != Category.INFO) {
                if (uids.get(record.uid) == null) {
                    // There is no other data about this UID, but it does exist!
                    uids.put(record.uid, new HashSet<String>());
                }
            }
        }

        // Turn our temporary lists of package names into AttributionKeys.
        final HashSet<AttributionKey> result = new HashSet<AttributionKey>();
        for (HashMap.Entry<Integer,HashSet<String>> entry: uids.entrySet()) {
            result.add(new AttributionKey(entry.getKey(), entry.getValue()));
        }

        // Initialize here so uninitialized access will result in NPE.
        mApps = ImmutableSet.copyOf(result);
    }

    /**
     * Init the factory classes.
     */
+85 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.powermodel;

/**
 * Identifiers for well-known apps that have unique characteristics.
 *
 * @more
 * This includes three categories:
 * <ul>
 *   <li><b>Built-in system components</b> – These have predefined UIDs that are
 *   always the same. For example, the system UID is always 1000.</li>
 *   <li><b>Well known apps with shared UIDs</b> – These do not have predefined
 *   UIDs (i.e. are different on each device), but since they have shared UIDs
 *   with varying sets of package names (GmsCore is the canonical example), we
 *   have special logic to capture these into a single entity with a well defined
 *   key. These have the {@link #uid uid} field set to
 *   {@link Uid#UID_VARIES Uid.UID_VARIES}.</li>
 *   <li><b>Synthetic remainder app</b> – The {@link #REMAINDER REMAINDER} app doesn't
 *   represent a real app. It contains accounting for usage which is not attributed
 *   to any UID. This app has the {@link #uid uid} field set to
 *   {@link Uid#UID_SYNTHETIC Uid.UID_SYNTHETIC}.</li>
 * </ul>
 */
public enum SpecialApp {

    /**
     * Synthetic app that accounts for the remaining amount of resources used
     * that is unaccounted for by apps, or overcounted because of inaccuracies
     * in the model.
     */
    REMAINDER(Uid.UID_SYNTHETIC),

    /**
     * Synthetic app that holds system-wide numbers, for example the total amount
     * of various resources used, device-wide.
     */
    GLOBAL(Uid.UID_SYNTHETIC),

    SYSTEM(1000),

    GOOGLE_SERVICES(Uid.UID_VARIES);

    /**
     * Constants for SpecialApps where the uid is not actually a UID.
     */
    public static class Uid {
        /**
         * Constant to indicate that this special app does not have a fixed UID.
         */
        public static final int UID_VARIES = -1;

        /**
         * Constant to indicate that this special app is not actually an app with a UID.
         * 
         * @see SpecialApp#REMAINDER
         * @see SpecialApp#GLOBAL
         */
        public static final int UID_SYNTHETIC = -2;
    }

    /**
     * The fixed UID value of this special app, or {@link #UID_VARIES} if there
     * isn't one.
     */
    public final int uid;

    private SpecialApp(int uid) {
        this.uid = uid;
    }
}