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

Commit e22b3b14 authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

Usage stats!

Start reworking the usage stats service to be able
to have an API we can publish.

The basic information it keeps is still the same, though
that will be changing in the future.  The one big addition
here is that we are also now collecting configuration usage
stats.

Also introduce the start of an access model for usage stats,
using app ops.  There is an new app op that gives an application
access to usage stats even if it normally wouldn't have it,
disabled by default.

Change-Id: I6ead28e18a7f08eafd057d6ff37dd9cb216358f4
parent ed32c842
Loading
Loading
Loading
Loading
+7 −7
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@ import android.os.BatteryStats;
import android.os.IBinder;
import com.android.internal.app.IUsageStats;
import com.android.internal.app.ProcessStats;
import com.android.internal.os.PkgUsageStats;
import com.android.internal.os.TransferPipe;
import com.android.internal.util.FastPrintWriter;

@@ -2130,14 +2129,15 @@ public class ActivityManager {
                return new HashMap<String, Integer>();
            }

            PkgUsageStats[] allPkgUsageStats = usageStatsService.getAllPkgUsageStats();
            UsageStats.PackageStats[] allPkgUsageStats = usageStatsService.getAllPkgUsageStats(
                    ActivityThread.currentPackageName());
            if (allPkgUsageStats == null) {
                return new HashMap<String, Integer>();
            }

            Map<String, Integer> launchCounts = new HashMap<String, Integer>();
            for (PkgUsageStats pkgUsageStats : allPkgUsageStats) {
                launchCounts.put(pkgUsageStats.packageName, pkgUsageStats.launchCount);
            for (UsageStats.PackageStats pkgUsageStats : allPkgUsageStats) {
                launchCounts.put(pkgUsageStats.getPackageName(), pkgUsageStats.getLaunchCount());
            }

            return launchCounts;
@@ -2251,17 +2251,17 @@ public class ActivityManager {
     *
     * @hide
     */
    public PkgUsageStats[] getAllPackageUsageStats() {
    public UsageStats.PackageStats[] getAllPackageUsageStats() {
        try {
            IUsageStats usageStatsService = IUsageStats.Stub.asInterface(
                    ServiceManager.getService("usagestats"));
            if (usageStatsService != null) {
                return usageStatsService.getAllPkgUsageStats();
                return usageStatsService.getAllPkgUsageStats(ActivityThread.currentPackageName());
            }
        } catch (RemoteException e) {
            Log.w(TAG, "Could not query usage stats", e);
        }
        return new PkgUsageStats[0];
        return new UsageStats.PackageStats[0];
    }

    /**
+10 −1
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.app;

import android.Manifest;
import android.os.Binder;
import android.os.IBinder;
import android.util.ArrayMap;
@@ -184,8 +185,10 @@ public class AppOpsManager {
    public static final int OP_MONITOR_LOCATION = 41;
    /** @hide Continually monitoring location data with a relatively high power request. */
    public static final int OP_MONITOR_HIGH_POWER_LOCATION = 42;
    /** @hide Retrieve current usage stats via {@link UsageStatsManager}. */
    public static final int OP_GET_USAGE_STATS = 43;
    /** @hide */
    public static final int _NUM_OP = 43;
    public static final int _NUM_OP = 44;

    /** Access to coarse location information. */
    public static final String OPSTR_COARSE_LOCATION =
@@ -252,6 +255,7 @@ public class AppOpsManager {
            OP_WAKE_LOCK,
            OP_COARSE_LOCATION,
            OP_COARSE_LOCATION,
            OP_GET_USAGE_STATS,
    };

    /**
@@ -302,6 +306,7 @@ public class AppOpsManager {
            null,
            OPSTR_MONITOR_LOCATION,
            OPSTR_MONITOR_HIGH_POWER_LOCATION,
            null,
    };

    /**
@@ -352,6 +357,7 @@ public class AppOpsManager {
            "WAKE_LOCK",
            "MONITOR_LOCATION",
            "MONITOR_HIGH_POWER_LOCATION",
            "GET_USAGE_STATS"
    };

    /**
@@ -402,6 +408,7 @@ public class AppOpsManager {
            android.Manifest.permission.WAKE_LOCK,
            null, // no permission for generic location monitoring
            null, // no permission for high power location monitoring
            android.Manifest.permission.PACKAGE_USAGE_STATS,
    };

    /**
@@ -451,6 +458,7 @@ public class AppOpsManager {
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_IGNORED, // OP_GET_USAGE_STATS
    };

    /**
@@ -504,6 +512,7 @@ public class AppOpsManager {
            false,
            false,
            false,
            false,
    };

    private static HashMap<String, Integer> sOpStrToOp = new HashMap<String, Integer>();
+5 −0
Original line number Diff line number Diff line
@@ -678,6 +678,11 @@ class ContextImpl extends Context {
                return new NetworkScoreManager(ctx);
            }
        });

        registerService(USAGE_STATS_SERVICE, new ServiceFetcher() {
                public Object createService(ContextImpl ctx) {
                return new UsageStatsManager(ctx.getOuterContext());
        }});
    }

    static ContextImpl getImpl(Context context) {
+20 −0
Original line number Diff line number Diff line
/**
 * Copyright (c) 2014, 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 android.app;

parcelable UsageStats;
parcelable UsageStats.PackageStats;
+406 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 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 android.app;

import android.content.res.Configuration;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
import android.util.ArrayMap;

import java.util.Map;

/**
 * Snapshot of current usage stats data.
 * @hide
 */
public class UsageStats implements Parcelable {
    /** @hide */
    public final ArrayMap<String, PackageStats> mPackages = new ArrayMap<String, PackageStats>();
    /** @hide */
    public final ArrayMap<Configuration, ConfigurationStats> mConfigurations
            = new ArrayMap<Configuration, ConfigurationStats>();

    public static class PackageStats implements Parcelable {
        private final String mPackageName;
        private int mLaunchCount;
        private long mUsageTime;
        private long mResumedTime;

        /** @hide */
        public final ArrayMap<String, Long> componentResumeTimes;

        public static final Parcelable.Creator<PackageStats> CREATOR
                = new Parcelable.Creator<PackageStats>() {
            public PackageStats createFromParcel(Parcel in) {
                return new PackageStats(in);
            }

            public PackageStats[] newArray(int size) {
                return new PackageStats[size];
            }
        };

        public String toString() {
            return "PackageStats{"
            + Integer.toHexString(System.identityHashCode(this))
            + " " + mPackageName + "}";
        }

        /** @hide */
        public PackageStats(String pkgName) {
            mPackageName = pkgName;
            componentResumeTimes = new ArrayMap<String, Long>();
        }

        /** @hide */
        public PackageStats(String pkgName, int count, long time, Map<String, Long> lastResumeTimes) {
            mPackageName = pkgName;
            mLaunchCount = count;
            mUsageTime = time;
            componentResumeTimes = new ArrayMap<String, Long>();
            componentResumeTimes.putAll(lastResumeTimes);
        }

        /** @hide */
        public PackageStats(Parcel source) {
            mPackageName = source.readString();
            mLaunchCount = source.readInt();
            mUsageTime = source.readLong();
            final int N = source.readInt();
            componentResumeTimes = new ArrayMap<String, Long>(N);
            for (int i = 0; i < N; i++) {
                String component = source.readString();
                long lastResumeTime = source.readLong();
                componentResumeTimes.put(component, lastResumeTime);
            }
        }

        /** @hide */
        public PackageStats(PackageStats pStats) {
            mPackageName = pStats.mPackageName;
            mLaunchCount = pStats.mLaunchCount;
            mUsageTime = pStats.mUsageTime;
            componentResumeTimes = new ArrayMap<String, Long>(pStats.componentResumeTimes);
        }

        /** @hide */
        public void resume(boolean launched) {
            if (launched) {
                mLaunchCount++;
            }
            mResumedTime = SystemClock.elapsedRealtime();
        }

        /** @hide */
        public void pause() {
            if (mResumedTime > 0) {
                mUsageTime += SystemClock.elapsedRealtime() - mResumedTime;
            }
            mResumedTime = 0;
        }

        public final String getPackageName() {
            return mPackageName;
        }

        public final long getUsageTime(long elapsedRealtime) {
            return mUsageTime + (mResumedTime > 0 ? (elapsedRealtime- mResumedTime) : 0);
        }

        public final int getLaunchCount() {
            return mLaunchCount;
        }

        /** @hide */
        public boolean clearUsageTimes() {
            mLaunchCount = 0;
            mUsageTime = 0;
            return mResumedTime <= 0 && componentResumeTimes.isEmpty();
        }

        public final int describeContents() {
            return 0;
        }

        public final void writeToParcel(Parcel dest, int parcelableFlags) {
            writeToParcel(dest, parcelableFlags, 0);
        }

        final void writeToParcel(Parcel dest, int parcelableFlags, long elapsedRealtime) {
            dest.writeString(mPackageName);
            dest.writeInt(mLaunchCount);
            dest.writeLong(elapsedRealtime > 0 ? getUsageTime(elapsedRealtime) : mUsageTime);
            dest.writeInt(componentResumeTimes.size());
            for (Map.Entry<String, Long> ent : componentResumeTimes.entrySet()) {
                dest.writeString(ent.getKey());
                dest.writeLong(ent.getValue());
            }
        }

        /** @hide */
        public void writeExtendedToParcel(Parcel dest, int parcelableFlags) {
        }
    }

    public static class ConfigurationStats implements Parcelable {
        private final Configuration mConfiguration;
        private long mLastUsedTime;
        private int mUsageCount;
        private long mUsageTime;
        private long mStartedTime;

        public static final Parcelable.Creator<ConfigurationStats> CREATOR
                = new Parcelable.Creator<ConfigurationStats>() {
            public ConfigurationStats createFromParcel(Parcel in) {
                return new ConfigurationStats(in);
            }

            public ConfigurationStats[] newArray(int size) {
                return new ConfigurationStats[size];
            }
        };

        public String toString() {
            return "ConfigurationStats{"
            + Integer.toHexString(System.identityHashCode(this))
            + " " + mConfiguration + "}";
        }

        /** @hide */
        public ConfigurationStats(Configuration config) {
            mConfiguration = config;
        }

        /** @hide */
        public ConfigurationStats(Parcel source) {
            mConfiguration = Configuration.CREATOR.createFromParcel(source);
            mLastUsedTime = source.readLong();
            mUsageCount = source.readInt();
            mUsageTime = source.readLong();
        }

        /** @hide */
        public ConfigurationStats(ConfigurationStats pStats) {
            mConfiguration = pStats.mConfiguration;
            mLastUsedTime = pStats.mLastUsedTime;
            mUsageCount = pStats.mUsageCount;
            mUsageTime = pStats.mUsageTime;
        }

        public final Configuration getConfiguration() {
            return mConfiguration;
        }

        public final long getLastUsedTime() {
            return mLastUsedTime;
        }

        public final long getUsageTime(long elapsedRealtime) {
            return mUsageTime + (mStartedTime > 0 ? (elapsedRealtime- mStartedTime) : 0);
        }

        public final int getUsageCount() {
            return mUsageCount;
        }

        /** @hide */
        public void start() {
            mLastUsedTime = System.currentTimeMillis();
            mUsageCount++;
            mStartedTime = SystemClock.elapsedRealtime();
        }

        /** @hide */
        public void stop() {
            if (mStartedTime > 0) {
                mUsageTime += SystemClock.elapsedRealtime() - mStartedTime;
            }
            mStartedTime = 0;
        }

        /** @hide */
        public boolean clearUsageTimes() {
            mUsageCount = 0;
            mUsageTime = 0;
            return mLastUsedTime == 0 && mStartedTime <= 0;
        }

        public final int describeContents() {
            return 0;
        }

        public final void writeToParcel(Parcel dest, int parcelableFlags) {
            writeToParcel(dest, parcelableFlags, 0);
        }

        final void writeToParcel(Parcel dest, int parcelableFlags, long elapsedRealtime) {
            mConfiguration.writeToParcel(dest, parcelableFlags);
            dest.writeLong(mLastUsedTime);
            dest.writeInt(mUsageCount);
            dest.writeLong(elapsedRealtime > 0 ? getUsageTime(elapsedRealtime) : mUsageTime);
        }

        /** @hide */
        public void writeExtendedToParcel(Parcel dest, int parcelableFlags) {
        }
    }

    /** @hide */
    public UsageStats() {
    }

    /** @hide */
    public UsageStats(Parcel source, boolean extended) {
        int N = source.readInt();
        for (int i=0; i<N; i++) {
            PackageStats pkg = extended ? onNewPackageStats(source) : new PackageStats(source);
            mPackages.put(pkg.getPackageName(), pkg);
        }
        N = source.readInt();
        for (int i=0; i<N; i++) {
            ConfigurationStats config = extended ? onNewConfigurationStats(source)
                    : new ConfigurationStats(source);
            mConfigurations.put(config.getConfiguration(), config);
        }
    }

    public int getPackageStatsCount() {
        return mPackages.size();
    }

    public PackageStats getPackageStatsAt(int index) {
        return mPackages.valueAt(index);
    }

    public PackageStats getPackageStats(String pkgName) {
        return mPackages.get(pkgName);
    }

    /** @hide */
    public PackageStats getOrCreatePackageStats(String pkgName) {
        PackageStats ps = mPackages.get(pkgName);
        if (ps == null) {
            ps = onNewPackageStats(pkgName);
            mPackages.put(pkgName, ps);
        }
        return ps;
    }

    public int getConfigurationStatsCount() {
        return mConfigurations.size();
    }

    public ConfigurationStats getConfigurationStatsAt(int index) {
        return mConfigurations.valueAt(index);
    }

    public ConfigurationStats getConfigurationStats(Configuration config) {
        return mConfigurations.get(config);
    }

    /** @hide */
    public ConfigurationStats getOrCreateConfigurationStats(Configuration config) {
        ConfigurationStats cs = mConfigurations.get(config);
        if (cs == null) {
            cs = onNewConfigurationStats(config);
            mConfigurations.put(config, cs);
        }
        return cs;
    }

    /** @hide */
    public void clearUsageTimes() {
        for (int i=mPackages.size()-1; i>=0; i--) {
            if (mPackages.valueAt(i).clearUsageTimes()) {
                mPackages.removeAt(i);
            }
        }
        for (int i=mConfigurations.size()-1; i>=0; i--) {
            if (mConfigurations.valueAt(i).clearUsageTimes()) {
                mConfigurations.removeAt(i);
            }
        }
    }

    /** @hide */
    public PackageStats onNewPackageStats(String pkgName) {
        return new PackageStats(pkgName);
    }

    /** @hide */
    public PackageStats onNewPackageStats(Parcel source) {
        return new PackageStats(source);
    }

    /** @hide */
    public ConfigurationStats onNewConfigurationStats(Configuration config) {
        return new ConfigurationStats(config);
    }

    /** @hide */
    public ConfigurationStats onNewConfigurationStats(Parcel source) {
        return new ConfigurationStats(source);
    }

    public int describeContents() {
        return 0;
    }

    public void writeToParcel(Parcel dest, int parcelableFlags) {
        writeToParcelInner(dest, parcelableFlags, false);
    }

    /** @hide */
    public void writeExtendedToParcel(Parcel dest, int parcelableFlags) {
        writeToParcelInner(dest, parcelableFlags, true);
    }

    private void writeToParcelInner(Parcel dest, int parcelableFlags, boolean extended) {
        final long elapsedRealtime = SystemClock.elapsedRealtime();

        int N = mPackages.size();
        dest.writeInt(N);
        for (int i=0; i<N; i++) {
            PackageStats ps = mPackages.valueAt(i);
            ps.writeToParcel(dest, parcelableFlags, elapsedRealtime);
            if (extended) {
                ps.writeExtendedToParcel(dest, parcelableFlags);
            }
        }
        N = mConfigurations.size();
        dest.writeInt(N);
        for (int i=0; i<N; i++) {
            ConfigurationStats cs = mConfigurations.valueAt(i);
            cs.writeToParcel(dest, parcelableFlags, elapsedRealtime);
            if (extended) {
                cs.writeExtendedToParcel(dest, parcelableFlags);
            }
        }
    }

    public static final Parcelable.Creator<UsageStats> CREATOR
            = new Parcelable.Creator<UsageStats>() {
        public UsageStats createFromParcel(Parcel in) {
            return new UsageStats(in, false);
        }

        public UsageStats[] newArray(int size) {
            return new UsageStats[size];
        }
    };
}
Loading