diff options
-rw-r--r-- | Android.mk | 4 | ||||
-rw-r--r-- | core/java/android/app/ActivityManager.java | 14 | ||||
-rw-r--r-- | core/java/android/app/AppOpsManager.java | 11 | ||||
-rw-r--r-- | core/java/android/app/ContextImpl.java | 5 | ||||
-rw-r--r-- | core/java/android/app/UsageStats.aidl | 20 | ||||
-rw-r--r-- | core/java/android/app/UsageStats.java | 406 | ||||
-rw-r--r-- | core/java/android/app/UsageStatsManager.java | 51 | ||||
-rw-r--r-- | core/java/android/content/Context.java | 10 | ||||
-rw-r--r-- | core/java/android/os/ParcelableParcel.aidl (renamed from core/java/com/android/internal/os/PkgUsageStats.aidl) | 25 | ||||
-rw-r--r-- | core/java/com/android/internal/app/IUsageStats.aidl | 10 | ||||
-rw-r--r-- | core/java/com/android/internal/os/PkgUsageStats.java | 94 | ||||
-rw-r--r-- | services/core/java/com/android/server/am/ActivityManagerService.java | 2 | ||||
-rw-r--r-- | services/core/java/com/android/server/am/UsageStatsService.java | 384 |
13 files changed, 756 insertions, 280 deletions
@@ -76,8 +76,8 @@ LOCAL_SRC_FILES += \ core/java/android/app/ISearchManagerCallback.aidl \ core/java/android/app/IServiceConnection.aidl \ core/java/android/app/IStopUserCallback.aidl \ - core/java/android/app/task/ITaskCallback.aidl \ - core/java/android/app/task/ITaskService.aidl \ + core/java/android/app/task/ITaskCallback.aidl \ + core/java/android/app/task/ITaskService.aidl \ core/java/android/app/IThumbnailRetriever.aidl \ core/java/android/app/ITransientNotification.aidl \ core/java/android/app/IUiAutomationConnection.aidl \ diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 5d809d8..044727d 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -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]; } /** diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index b616c1e..d813dab 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -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>(); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 5ed5030..801182d 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -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) { diff --git a/core/java/android/app/UsageStats.aidl b/core/java/android/app/UsageStats.aidl new file mode 100644 index 0000000..7dee70a --- /dev/null +++ b/core/java/android/app/UsageStats.aidl @@ -0,0 +1,20 @@ +/** + * 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; diff --git a/core/java/android/app/UsageStats.java b/core/java/android/app/UsageStats.java new file mode 100644 index 0000000..0aeba59 --- /dev/null +++ b/core/java/android/app/UsageStats.java @@ -0,0 +1,406 @@ +/* + * 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]; + } + }; +} diff --git a/core/java/android/app/UsageStatsManager.java b/core/java/android/app/UsageStatsManager.java new file mode 100644 index 0000000..fbf9c3b --- /dev/null +++ b/core/java/android/app/UsageStatsManager.java @@ -0,0 +1,51 @@ +/* + * 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.Context; +import android.os.ParcelableParcel; +import android.os.RemoteException; +import android.os.ServiceManager; +import com.android.internal.app.IUsageStats; + +/** + * Access to usage stats data. + * @hide + */ +public class UsageStatsManager { + final Context mContext; + final IUsageStats mService; + + /** @hide */ + public UsageStatsManager(Context context) { + mContext = context; + mService = IUsageStats.Stub.asInterface(ServiceManager.getService( + Context.USAGE_STATS_SERVICE)); + } + + public UsageStats getCurrentStats() { + try { + ParcelableParcel in = mService.getCurrentStats(mContext.getOpPackageName()); + if (in != null) { + return new UsageStats(in.getParcel(), false); + } + } catch (RemoteException e) { + // About to die. + } + return new UsageStats(); + } +} diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 042ee28..a059e48 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2683,6 +2683,16 @@ public abstract class Context { public static final String NETWORK_SCORE_SERVICE = "network_score"; /** + * Use with {@link #getSystemService} to retrieve a {@link + * android.app.UsageStatsManager} for interacting with the status bar. + * + * @see #getSystemService + * @see android.app.UsageStatsManager + * @hide + */ + public static final String USAGE_STATS_SERVICE = "usagestats"; + + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. * diff --git a/core/java/com/android/internal/os/PkgUsageStats.aidl b/core/java/android/os/ParcelableParcel.aidl index 8305271..61f730c 100644 --- a/core/java/com/android/internal/os/PkgUsageStats.aidl +++ b/core/java/android/os/ParcelableParcel.aidl @@ -1,20 +1,19 @@ -/* //device/java/android/android/content/Intent.aidl +/* +** Copyright 2014, The Android Open Source Project ** -** Copyright 2007, 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 ** -** 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 ** -** 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 +** 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.internal.os; +package android.os; -parcelable PkgUsageStats; +parcelable ParcelableParcel; diff --git a/core/java/com/android/internal/app/IUsageStats.aidl b/core/java/com/android/internal/app/IUsageStats.aidl index 1ea7409..7e7f0e1 100644 --- a/core/java/com/android/internal/app/IUsageStats.aidl +++ b/core/java/com/android/internal/app/IUsageStats.aidl @@ -16,13 +16,17 @@ package com.android.internal.app; +import android.app.UsageStats; import android.content.ComponentName; -import com.android.internal.os.PkgUsageStats; +import android.content.res.Configuration; +import android.os.ParcelableParcel; interface IUsageStats { void noteResumeComponent(in ComponentName componentName); void notePauseComponent(in ComponentName componentName); void noteLaunchTime(in ComponentName componentName, int millis); - PkgUsageStats getPkgUsageStats(in ComponentName componentName); - PkgUsageStats[] getAllPkgUsageStats(); + void noteStartConfig(in Configuration config); + UsageStats.PackageStats getPkgUsageStats(String callingPkg, in ComponentName componentName); + UsageStats.PackageStats[] getAllPkgUsageStats(String callingPkg); + ParcelableParcel getCurrentStats(String callingPkg); } diff --git a/core/java/com/android/internal/os/PkgUsageStats.java b/core/java/com/android/internal/os/PkgUsageStats.java deleted file mode 100644 index 8c2c405..0000000 --- a/core/java/com/android/internal/os/PkgUsageStats.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2009 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.internal.os; - -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.HashMap; -import java.util.Map; - -/** - * implementation of PkgUsageStats associated with an - * application package. - * @hide - */ -public class PkgUsageStats implements Parcelable { - public String packageName; - public int launchCount; - public long usageTime; - public Map<String, Long> componentResumeTimes; - - public static final Parcelable.Creator<PkgUsageStats> CREATOR - = new Parcelable.Creator<PkgUsageStats>() { - public PkgUsageStats createFromParcel(Parcel in) { - return new PkgUsageStats(in); - } - - public PkgUsageStats[] newArray(int size) { - return new PkgUsageStats[size]; - } - }; - - public String toString() { - return "PkgUsageStats{" - + Integer.toHexString(System.identityHashCode(this)) - + " " + packageName + "}"; - } - - public PkgUsageStats(String pkgName, int count, long time, Map<String, Long> lastResumeTimes) { - packageName = pkgName; - launchCount = count; - usageTime = time; - componentResumeTimes = new HashMap<String, Long>(lastResumeTimes); - } - - public PkgUsageStats(Parcel source) { - packageName = source.readString(); - launchCount = source.readInt(); - usageTime = source.readLong(); - final int N = source.readInt(); - componentResumeTimes = new HashMap<String, Long>(N); - for (int i = 0; i < N; i++) { - String component = source.readString(); - long lastResumeTime = source.readLong(); - componentResumeTimes.put(component, lastResumeTime); - } - } - - public PkgUsageStats(PkgUsageStats pStats) { - packageName = pStats.packageName; - launchCount = pStats.launchCount; - usageTime = pStats.usageTime; - componentResumeTimes = new HashMap<String, Long>(pStats.componentResumeTimes); - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel dest, int parcelableFlags) { - dest.writeString(packageName); - dest.writeInt(launchCount); - dest.writeLong(usageTime); - dest.writeInt(componentResumeTimes.size()); - for (Map.Entry<String, Long> ent : componentResumeTimes.entrySet()) { - dest.writeString(ent.getKey()); - dest.writeLong(ent.getValue()); - } - } -} diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index efc5606..09e7e12 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -9570,6 +9570,7 @@ public final class ActivityManagerService extends ActivityManagerNative } mAppOpsService.systemReady(); + mUsageStatsService.systemReady(); mSystemReady = true; } @@ -14391,6 +14392,7 @@ public final class ActivityManagerService extends ActivityManagerNative newConfig.seq = mConfigurationSeq; mConfiguration = newConfig; Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + newConfig); + mUsageStatsService.noteStartConfig(newConfig); final Configuration configCopy = new Configuration(mConfiguration); diff --git a/services/core/java/com/android/server/am/UsageStatsService.java b/services/core/java/com/android/server/am/UsageStatsService.java index 42cf900..4a5a554 100644 --- a/services/core/java/com/android/server/am/UsageStatsService.java +++ b/services/core/java/com/android/server/am/UsageStatsService.java @@ -17,26 +17,31 @@ package com.android.server.am; import android.app.AppGlobals; +import android.app.AppOpsManager; +import android.app.UsageStats; import android.content.ComponentName; import android.content.Context; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; +import android.content.res.Configuration; import android.os.Binder; import android.os.IBinder; import android.os.FileUtils; import android.os.Parcel; +import android.os.ParcelableParcel; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; +import android.text.format.DateFormat; import android.util.ArrayMap; import android.util.AtomicFile; import android.util.Slog; +import android.util.TimeUtils; import android.util.Xml; import com.android.internal.app.IUsageStats; import com.android.internal.content.PackageMonitor; -import com.android.internal.os.PkgUsageStats; import com.android.internal.util.FastXmlSerializer; import org.xmlpull.v1.XmlPullParser; @@ -46,7 +51,6 @@ import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; @@ -55,8 +59,6 @@ import java.util.Calendar; import java.util.Collections; import java.util.HashSet; import java.util.List; -import java.util.Map; -import java.util.Set; import java.util.TimeZone; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -75,7 +77,7 @@ public final class UsageStatsService extends IUsageStats.Stub { private static final String TAG = "UsageStats"; // Current on-disk Parcel version - private static final int VERSION = 1008; + private static final int VERSION = 1010; private static final int CHECKIN_VERSION = 4; @@ -94,13 +96,10 @@ public final class UsageStatsService extends IUsageStats.Stub { static IUsageStats sService; private Context mContext; - // structure used to maintain statistics since the last checkin. - final private ArrayMap<String, PkgUsageStatsExtended> mStats - = new ArrayMap<String, PkgUsageStatsExtended>(); + private AppOpsManager mAppOps; - // Maintains the last time any component was resumed, for all time. - final private ArrayMap<String, ArrayMap<String, Long>> mLastResumeTimes - = new ArrayMap<String, ArrayMap<String, Long>>(); + // structure used to maintain statistics since the last checkin. + private LocalUsageStats mStats = new LocalUsageStats(); // To remove last-resume time stats when a pacakge is removed. private PackageMonitor mPackageMonitor; @@ -115,6 +114,7 @@ public final class UsageStatsService extends IUsageStats.Stub { private String mLastResumedPkg; private String mLastResumedComp; private boolean mIsResumed; + private ConfigUsageStatsExtended mCurrentConfigStats; private File mFile; private AtomicFile mHistoryFile; private String mFileLeaf; @@ -127,6 +127,30 @@ public final class UsageStatsService extends IUsageStats.Stub { private final AtomicLong mLastWriteElapsedTime = new AtomicLong(0); private final AtomicBoolean mUnforcedDiskWriteRunning = new AtomicBoolean(false); + static class LocalUsageStats extends UsageStats { + public LocalUsageStats() { + } + public LocalUsageStats(Parcel in, boolean extended) { + super(in, extended); + } + @Override + public PackageStats onNewPackageStats(String pkgName) { + return new PkgUsageStatsExtended(pkgName); + } + @Override + public PackageStats onNewPackageStats(Parcel in) { + return new PkgUsageStatsExtended(in); + } + @Override + public ConfigurationStats onNewConfigurationStats(Configuration config) { + return new ConfigUsageStatsExtended(config); + } + @Override + public ConfigurationStats onNewConfigurationStats(Parcel source) { + return new ConfigUsageStatsExtended(source); + } + } + static class TimeStats { int mCount; final int[] mTimes = new int[NUM_LAUNCH_TIME_BINS]; @@ -166,27 +190,18 @@ public final class UsageStatsService extends IUsageStats.Stub { } } - static class PkgUsageStatsExtended { + static class PkgUsageStatsExtended extends UsageStats.PackageStats { final ArrayMap<String, TimeStats> mLaunchTimes = new ArrayMap<String, TimeStats>(); final ArrayMap<String, TimeStats> mFullyDrawnTimes = new ArrayMap<String, TimeStats>(); - int mLaunchCount; - long mUsageTime; - long mPausedTime; - long mResumedTime; - PkgUsageStatsExtended() { - mLaunchCount = 0; - mUsageTime = 0; + PkgUsageStatsExtended(String pkgName) { + super(pkgName); } PkgUsageStatsExtended(Parcel in) { - mLaunchCount = in.readInt(); - mUsageTime = in.readLong(); - if (localLOGV) Slog.v(TAG, "Launch count: " + mLaunchCount - + ", Usage time:" + mUsageTime); - + super(in); final int numLaunchTimeStats = in.readInt(); if (localLOGV) Slog.v(TAG, "Reading launch times: " + numLaunchTimeStats); mLaunchTimes.ensureCapacity(numLaunchTimeStats); @@ -208,18 +223,6 @@ public final class UsageStatsService extends IUsageStats.Stub { } } - void updateResume(String comp, boolean launched) { - if (launched) { - mLaunchCount++; - } - mResumedTime = SystemClock.elapsedRealtime(); - } - - void updatePause() { - mPausedTime = SystemClock.elapsedRealtime(); - mUsageTime += (mPausedTime - mResumedTime); - } - void addLaunchCount(String comp) { TimeStats times = mLaunchTimes.get(comp); if (times == null) { @@ -247,9 +250,7 @@ public final class UsageStatsService extends IUsageStats.Stub { times.add(millis); } - void writeToParcel(Parcel out) { - out.writeInt(mLaunchCount); - out.writeLong(mUsageTime); + public void writeExtendedToParcel(Parcel out, int parcelableFlags) { final int numLaunchTimeStats = mLaunchTimes.size(); out.writeInt(numLaunchTimeStats); for (int i=0; i<numLaunchTimeStats; i++) { @@ -264,11 +265,21 @@ public final class UsageStatsService extends IUsageStats.Stub { } } - void clear() { + @Override + public boolean clearUsageTimes() { mLaunchTimes.clear(); mFullyDrawnTimes.clear(); - mLaunchCount = 0; - mUsageTime = 0; + return super.clearUsageTimes(); + } + } + + static class ConfigUsageStatsExtended extends UsageStats.ConfigurationStats { + ConfigUsageStatsExtended(Configuration config) { + super(config); + } + + ConfigUsageStatsExtended(Parcel in) { + super(in); } } @@ -364,18 +375,9 @@ public final class UsageStatsService extends IUsageStats.Stub { + VERSION + "; dropping"); return; } - int N = in.readInt(); - while (N > 0) { - N--; - String pkgName = in.readString(); - if (pkgName == null) { - break; - } - if (localLOGV) Slog.v(TAG, "Reading package #" + N + ": " + pkgName); - PkgUsageStatsExtended pus = new PkgUsageStatsExtended(in); - synchronized (mStatsLock) { - mStats.put(pkgName, pus); - } + LocalUsageStats stats = new LocalUsageStats(in, true); + synchronized (mStatsLock) { + mStats = stats; } } @@ -419,12 +421,9 @@ public final class UsageStatsService extends IUsageStats.Stub { try { long lastResumeTime = Long.parseLong(lastResumeTimeStr); synchronized (mStatsLock) { - ArrayMap<String, Long> lrt = mLastResumeTimes.get(pkg); - if (lrt == null) { - lrt = new ArrayMap<String, Long>(); - mLastResumeTimes.put(pkg, lrt); - } - lrt.put(comp, lastResumeTime); + PkgUsageStatsExtended pus = (PkgUsageStatsExtended) + mStats.getOrCreatePackageStats(pkg); + pus.componentResumeTimes.put(comp, lastResumeTime); } } catch (NumberFormatException e) { } @@ -543,6 +542,15 @@ public final class UsageStatsService extends IUsageStats.Stub { return; } + Parcel out = Parcel.obtain(); + synchronized (mStatsLock) { + out.writeInt(VERSION); + mStats.writeExtendedToParcel(out, 0); + if (dayChanged) { + mStats.clearUsageTimes(); + } + } + synchronized (mFileLock) { // Get the most recent file mFileLeaf = getCurrentDateStr(FILE_PREFIX); @@ -553,6 +561,7 @@ public final class UsageStatsService extends IUsageStats.Stub { if (!backupFile.exists()) { if (!mFile.renameTo(backupFile)) { Slog.w(TAG, "Failed to persist new stats"); + out.recycle(); return; } } else { @@ -562,14 +571,10 @@ public final class UsageStatsService extends IUsageStats.Stub { try { // Write mStats to file - writeStatsFLOCK(mFile); + writeStatsFLOCK(mFile, out); mLastWriteElapsedTime.set(currElapsedTime); if (dayChanged) { mLastWriteDay.set(curDay); - // clear stats - synchronized (mStats) { - mStats.clear(); - } mFile = new File(mDir, mFileLeaf); checkFileLimitFLOCK(); } @@ -590,17 +595,15 @@ public final class UsageStatsService extends IUsageStats.Stub { backupFile.renameTo(mFile); } } + out.recycle(); } if (localLOGV) Slog.d(TAG, "Dumped usage stats."); } - private void writeStatsFLOCK(File file) throws IOException { + private void writeStatsFLOCK(File file, Parcel parcel) throws IOException { FileOutputStream stream = new FileOutputStream(file); try { - Parcel out = Parcel.obtain(); - writeStatsToParcelFLOCK(out); - stream.write(out.marshall()); - out.recycle(); + stream.write(parcel.marshall()); stream.flush(); } finally { FileUtils.sync(stream); @@ -608,29 +611,14 @@ public final class UsageStatsService extends IUsageStats.Stub { } } - private void writeStatsToParcelFLOCK(Parcel out) { - synchronized (mStatsLock) { - out.writeInt(VERSION); - Set<String> keys = mStats.keySet(); - out.writeInt(keys.size()); - for (String key : keys) { - PkgUsageStatsExtended pus = mStats.get(key); - out.writeString(key); - pus.writeToParcel(out); - } - } - } - /** Filter out stats for any packages which aren't present anymore. */ private void filterHistoryStats() { synchronized (mStatsLock) { IPackageManager pm = AppGlobals.getPackageManager(); - for (int i=0; i<mLastResumeTimes.size(); i++) { - String pkg = mLastResumeTimes.keyAt(i); + for (int i=mStats.mPackages.size()-1; i>=0; i--) { try { - if (pm.getPackageUid(pkg, 0) < 0) { - mLastResumeTimes.removeAt(i); - i--; + if (pm.getPackageUid(mStats.mPackages.valueAt(i).getPackageName(), 0) < 0) { + mStats.mPackages.removeAt(i); } } catch (RemoteException e) { } @@ -648,10 +636,12 @@ public final class UsageStatsService extends IUsageStats.Stub { out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); out.startTag(null, "usage-history"); synchronized (mStatsLock) { - for (int i=0; i<mLastResumeTimes.size(); i++) { + int NP = mStats.mPackages.size(); + for (int i=0; i<NP; i++) { + UsageStats.PackageStats ps = mStats.mPackages.valueAt(i); out.startTag(null, "pkg"); - out.attribute(null, "name", mLastResumeTimes.keyAt(i)); - ArrayMap<String, Long> comp = mLastResumeTimes.valueAt(i); + out.attribute(null, "name", ps.getPackageName()); + ArrayMap<String, Long> comp = ps.componentResumeTimes; for (int j=0; j<comp.size(); j++) { out.startTag(null, "comp"); out.attribute(null, "name", comp.keyAt(j)); @@ -678,6 +668,10 @@ public final class UsageStatsService extends IUsageStats.Stub { ServiceManager.addService(SERVICE_NAME, asBinder()); } + public void systemReady() { + mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE); + } + /** * Start watching packages to remove stats when a package is uninstalled. * May only be called when the package manager is ready. @@ -687,7 +681,7 @@ public final class UsageStatsService extends IUsageStats.Stub { @Override public void onPackageRemovedAllUsers(String packageName, int uid) { synchronized (mStatsLock) { - mLastResumeTimes.remove(packageName); + mStats.mPackages.remove(packageName); } } }; @@ -729,9 +723,10 @@ public final class UsageStatsService extends IUsageStats.Stub { // to recover. if (REPORT_UNEXPECTED) Slog.i(TAG, "Unexpected resume of " + pkgName + " while already resumed in " + mLastResumedPkg); - PkgUsageStatsExtended pus = mStats.get(mLastResumedPkg); + PkgUsageStatsExtended pus = (PkgUsageStatsExtended)mStats.getPackageStats( + mLastResumedPkg); if (pus != null) { - pus.updatePause(); + pus.pause(); } } } @@ -744,22 +739,13 @@ public final class UsageStatsService extends IUsageStats.Stub { mLastResumedComp = componentName.getClassName(); if (localLOGV) Slog.i(TAG, "started component:" + pkgName); - PkgUsageStatsExtended pus = mStats.get(pkgName); - if (pus == null) { - pus = new PkgUsageStatsExtended(); - mStats.put(pkgName, pus); - } - pus.updateResume(mLastResumedComp, !samePackage); + PkgUsageStatsExtended pus = (PkgUsageStatsExtended) + mStats.getOrCreatePackageStats(pkgName); + pus.resume(!samePackage); if (!sameComp) { pus.addLaunchCount(mLastResumedComp); } - - ArrayMap<String, Long> componentResumeTimes = mLastResumeTimes.get(pkgName); - if (componentResumeTimes == null) { - componentResumeTimes = new ArrayMap<String, Long>(); - mLastResumeTimes.put(pkgName, componentResumeTimes); - } - componentResumeTimes.put(mLastResumedComp, System.currentTimeMillis()); + pus.componentResumeTimes.put(mLastResumedComp, System.currentTimeMillis()); } } @@ -782,13 +768,13 @@ public final class UsageStatsService extends IUsageStats.Stub { if (localLOGV) Slog.i(TAG, "paused component:"+pkgName); - PkgUsageStatsExtended pus = mStats.get(pkgName); + PkgUsageStatsExtended pus = (PkgUsageStatsExtended)mStats.getPackageStats(pkgName); if (pus == null) { // Weird some error here Slog.i(TAG, "No package stats for pkg:"+pkgName); return; } - pus.updatePause(); + pus.pause(); } // Persist current data to file if needed. @@ -808,7 +794,7 @@ public final class UsageStatsService extends IUsageStats.Stub { writeStatsToFile(false, false); synchronized (mStatsLock) { - PkgUsageStatsExtended pus = mStats.get(pkgName); + PkgUsageStatsExtended pus = (PkgUsageStatsExtended)mStats.getPackageStats(pkgName); if (pus != null) { pus.addLaunchTime(componentName.getClassName(), millis); } @@ -827,13 +813,29 @@ public final class UsageStatsService extends IUsageStats.Stub { writeStatsToFile(false, false); synchronized (mStatsLock) { - PkgUsageStatsExtended pus = mStats.get(pkgName); + PkgUsageStatsExtended pus = (PkgUsageStatsExtended)mStats.getPackageStats(pkgName); if (pus != null) { pus.addFullyDrawnTime(componentName.getClassName(), millis); } } } + public void noteStartConfig(Configuration config) { + enforceCallingPermission(); + synchronized (mStatsLock) { + config = new Configuration(config); + ConfigUsageStatsExtended cus = (ConfigUsageStatsExtended) + mStats.getOrCreateConfigurationStats(config); + if (cus != mCurrentConfigStats) { + if (mCurrentConfigStats != null) { + mCurrentConfigStats.stop(); + } + cus.start(); + mCurrentConfigStats = cus; + } + } + } + public void enforceCallingPermission() { if (Binder.getCallingPid() == Process.myPid()) { return; @@ -843,53 +845,71 @@ public final class UsageStatsService extends IUsageStats.Stub { } @Override - public PkgUsageStats getPkgUsageStats(ComponentName componentName) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.PACKAGE_USAGE_STATS, null); + public UsageStats.PackageStats getPkgUsageStats(String callingPkg, + ComponentName componentName) { + checkCallerPermission(callingPkg, "getPkgUsageStats"); String pkgName; if ((componentName == null) || ((pkgName = componentName.getPackageName()) == null)) { return null; } synchronized (mStatsLock) { - PkgUsageStatsExtended pus = mStats.get(pkgName); - Map<String, Long> lastResumeTimes = mLastResumeTimes.get(pkgName); - if (pus == null && lastResumeTimes == null) { + PkgUsageStatsExtended pus = (PkgUsageStatsExtended)mStats.getPackageStats(pkgName); + if (pus == null) { return null; } - int launchCount = pus != null ? pus.mLaunchCount : 0; - long usageTime = pus != null ? pus.mUsageTime : 0; - return new PkgUsageStats(pkgName, launchCount, usageTime, lastResumeTimes); + return new UsageStats.PackageStats(pus); } } @Override - public PkgUsageStats[] getAllPkgUsageStats() { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.PACKAGE_USAGE_STATS, null); + public UsageStats.PackageStats[] getAllPkgUsageStats(String callingPkg) { + checkCallerPermission(callingPkg, "getAllPkgUsageStats"); synchronized (mStatsLock) { - int size = mLastResumeTimes.size(); - if (size <= 0) { + int NP = mStats.mPackages.size(); + if (NP <= 0) { return null; } - PkgUsageStats retArr[] = new PkgUsageStats[size]; - for (int i=0; i<size; i++) { - String pkg = mLastResumeTimes.keyAt(i); - long usageTime = 0; - int launchCount = 0; - - PkgUsageStatsExtended pus = mStats.get(pkg); - if (pus != null) { - usageTime = pus.mUsageTime; - launchCount = pus.mLaunchCount; - } - retArr[i] = new PkgUsageStats(pkg, launchCount, usageTime, - mLastResumeTimes.valueAt(i)); + UsageStats.PackageStats retArr[] = new UsageStats.PackageStats[NP]; + for (int p=0; p<NP; p++) { + UsageStats.PackageStats ps = mStats.mPackages.valueAt(p); + retArr[p] = new UsageStats.PackageStats(ps); } return retArr; } } + @Override + public ParcelableParcel getCurrentStats(String callingPkg) { + checkCallerPermission(callingPkg, "getCurrentStats"); + synchronized (mStatsLock) { + ParcelableParcel out = new ParcelableParcel(null); + mStats.writeToParcel(out.getParcel(), 0); + return out; + } + } + + private void checkCallerPermission(String callingPkg, String callingOp) { + // Because the permission for this is system-only, its use with + // app ops is a little different: the op is disabled by default, + // and enabling it allows apps to get access even if they don't + // hold the permission. + int mode = mAppOps.noteOpNoThrow(AppOpsManager.OP_GET_USAGE_STATS, Binder.getCallingUid(), + callingPkg); + if (mode == AppOpsManager.MODE_ALLOWED) { + return; + } else if (mode != AppOpsManager.MODE_IGNORED) { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.PACKAGE_USAGE_STATS) + == PackageManager.PERMISSION_GRANTED) { + return; + } + } + + String msg = "Package " + callingPkg + " not allowed to call " + callingOp; + throw new SecurityException(msg); + } + static byte[] readFully(FileInputStream stream) throws IOException { int pos = 0; int avail = stream.available(); @@ -963,31 +983,28 @@ public final class UsageStatsService extends IUsageStats.Stub { return; } - pw.println(sb.toString()); - int N = in.readInt(); + final LocalUsageStats stats = new LocalUsageStats(in, true); + final long time = SystemClock.elapsedRealtime(); - while (N > 0) { - N--; - String pkgName = in.readString(); - if (pkgName == null) { - break; - } + pw.println(sb.toString()); + int NP = stats.mPackages.size(); + for (int p=0; p<NP; p++) { + PkgUsageStatsExtended pus = (PkgUsageStatsExtended)stats.mPackages.valueAt(p); sb.setLength(0); - PkgUsageStatsExtended pus = new PkgUsageStatsExtended(in); - if (packages != null && !packages.contains(pkgName)) { + if (packages != null && !packages.contains(pus.getPackageName())) { // This package has not been requested -- don't print // anything for it. } else if (isCompactOutput) { sb.append("P:"); - sb.append(pkgName); + sb.append(pus.getPackageName()); sb.append(','); - sb.append(pus.mLaunchCount); + sb.append(pus.getLaunchCount()); sb.append(','); - sb.append(pus.mUsageTime); + sb.append(pus.getUsageTime(time)); sb.append('\n'); final int NLT = pus.mLaunchTimes.size(); for (int i=0; i<NLT; i++) { - sb.append("A:"); + sb.append("L:"); String activity = pus.mLaunchTimes.keyAt(i); sb.append(activity); TimeStats times = pus.mLaunchTimes.valueAt(i); @@ -1001,7 +1018,7 @@ public final class UsageStatsService extends IUsageStats.Stub { } final int NFDT = pus.mFullyDrawnTimes.size(); for (int i=0; i<NFDT; i++) { - sb.append("A:"); + sb.append("D:"); String activity = pus.mFullyDrawnTimes.keyAt(i); sb.append(activity); TimeStats times = pus.mFullyDrawnTimes.valueAt(i); @@ -1011,15 +1028,23 @@ public final class UsageStatsService extends IUsageStats.Stub { } sb.append('\n'); } + final int NC = pus.componentResumeTimes.size(); + for (int c=0; c<NC; c++) { + pw.print("R:"); pw.print(pus.componentResumeTimes.keyAt(c)); pw.print(","); + pw.println(pus.componentResumeTimes.valueAt(c)); + } } else { sb.append(" "); - sb.append(pkgName); - sb.append(": "); - sb.append(pus.mLaunchCount); - sb.append(" times, "); - sb.append(pus.mUsageTime); - sb.append(" ms"); + sb.append(pus.getPackageName()); + if (pus.getLaunchCount() != 0 || pus.getUsageTime(time) != 0) { + sb.append(": "); + sb.append(pus.getLaunchCount()); + sb.append(" times, "); + TimeUtils.formatDuration(pus.getUsageTime(time), sb); + } else { + sb.append(":"); + } sb.append('\n'); final int NLT = pus.mLaunchTimes.size(); for (int i=0; i<NLT; i++) { @@ -1084,10 +1109,50 @@ public final class UsageStatsService extends IUsageStats.Stub { } sb.append('\n'); } + final int NC = pus.componentResumeTimes.size(); + for (int c=0; c<NC; c++) { + sb.append(" "); + sb.append(pus.componentResumeTimes.keyAt(c)); + sb.append(" last resumed "); + sb.append(DateFormat.format("yyyy-MM-dd-HH-mm-ss", + pus.componentResumeTimes.valueAt(c)).toString()); + sb.append('\n'); + } } pw.write(sb.toString()); } + if (packages == null) { + int NC = stats.mConfigurations.size(); + for (int c=0; c<NC; c++) { + ConfigUsageStatsExtended cus + = (ConfigUsageStatsExtended)stats.mConfigurations.valueAt(c); + sb.setLength(0); + if (isCompactOutput) { + sb.append("C:"); sb.append(cus.getConfiguration().toString()); + sb.append(","); sb.append(cus.getUsageCount()); sb.append(","); + sb.append(cus.getUsageTime(time)); + } else { + sb.append(" "); + sb.append(cus.getConfiguration().toString()); + sb.append(":\n"); + if (cus.getUsageCount() != 0 || cus.getUsageTime(time) != 0) { + sb.append(" Used "); + sb.append(cus.getUsageCount()); + sb.append(" times, "); + TimeUtils.formatDuration(cus.getUsageTime(time), sb); + sb.append("\n"); + } + if (cus.getLastUsedTime() > 0) { + sb.append(" Last used: "); + sb.append(DateFormat.format("yyyy-MM-dd-HH-mm-ss", + cus.getLastUsedTime()).toString()); + sb.append("\n"); + } + } + pw.write(sb.toString()); + } + } } /** @@ -1174,5 +1239,4 @@ public final class UsageStatsService extends IUsageStats.Stub { collectDumpInfoFLOCK(pw, isCompactOutput, deleteAfterPrint, packages); } } - } |