1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.am; 18 19 import android.app.ActivityOptions; 20 import android.app.PendingIntent; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.os.Bundle; 24 import android.os.SystemClock; 25 import android.util.ArrayMap; 26 import android.util.MutableLong; 27 import android.util.TimeUtils; 28 import android.util.proto.ProtoOutputStream; 29 import android.util.proto.ProtoUtils; 30 31 import java.io.PrintWriter; 32 33 /** 34 * Tracks the time a user spent in an app. 35 */ 36 public class AppTimeTracker { 37 private final PendingIntent mReceiver; 38 39 private long mTotalTime; 40 private final ArrayMap<String, MutableLong> mPackageTimes = new ArrayMap<>(); 41 42 private long mStartedTime; 43 private String mStartedPackage; 44 private MutableLong mStartedPackageTime; 45 AppTimeTracker(PendingIntent receiver)46 public AppTimeTracker(PendingIntent receiver) { 47 mReceiver = receiver; 48 } 49 start(String packageName)50 public void start(String packageName) { 51 long now = SystemClock.elapsedRealtime(); 52 if (mStartedTime == 0) { 53 mStartedTime = now; 54 } 55 if (!packageName.equals(mStartedPackage)) { 56 if (mStartedPackageTime != null) { 57 long elapsedTime = now - mStartedTime; 58 mStartedPackageTime.value += elapsedTime; 59 mTotalTime += elapsedTime; 60 } 61 mStartedPackage = packageName; 62 mStartedPackageTime = mPackageTimes.get(packageName); 63 if (mStartedPackageTime == null) { 64 mStartedPackageTime = new MutableLong(0); 65 mPackageTimes.put(packageName, mStartedPackageTime); 66 } 67 } 68 } 69 stop()70 public void stop() { 71 if (mStartedTime != 0) { 72 long elapsedTime = SystemClock.elapsedRealtime() - mStartedTime; 73 mTotalTime += elapsedTime; 74 if (mStartedPackageTime != null) { 75 mStartedPackageTime.value += elapsedTime; 76 } 77 mStartedPackage = null; 78 mStartedPackageTime = null; 79 } 80 } 81 deliverResult(Context context)82 public void deliverResult(Context context) { 83 stop(); 84 Bundle extras = new Bundle(); 85 extras.putLong(ActivityOptions.EXTRA_USAGE_TIME_REPORT, mTotalTime); 86 Bundle pkgs = new Bundle(); 87 for (int i=mPackageTimes.size()-1; i>=0; i--) { 88 pkgs.putLong(mPackageTimes.keyAt(i), mPackageTimes.valueAt(i).value); 89 } 90 extras.putBundle(ActivityOptions.EXTRA_USAGE_TIME_REPORT_PACKAGES, pkgs); 91 Intent fillinIntent = new Intent(); 92 fillinIntent.putExtras(extras); 93 try { 94 mReceiver.send(context, 0, fillinIntent); 95 } catch (PendingIntent.CanceledException e) { 96 } 97 } 98 dumpWithHeader(PrintWriter pw, String prefix, boolean details)99 public void dumpWithHeader(PrintWriter pw, String prefix, boolean details) { 100 pw.print(prefix); pw.print("AppTimeTracker #"); 101 pw.print(Integer.toHexString(System.identityHashCode(this))); 102 pw.println(":"); 103 dump(pw, prefix + " ", details); 104 } 105 dump(PrintWriter pw, String prefix, boolean details)106 public void dump(PrintWriter pw, String prefix, boolean details) { 107 pw.print(prefix); pw.print("mReceiver="); pw.println(mReceiver); 108 pw.print(prefix); pw.print("mTotalTime="); 109 TimeUtils.formatDuration(mTotalTime, pw); 110 pw.println(); 111 for (int i = 0; i < mPackageTimes.size(); i++) { 112 pw.print(prefix); pw.print("mPackageTime:"); pw.print(mPackageTimes.keyAt(i)); 113 pw.print("="); 114 TimeUtils.formatDuration(mPackageTimes.valueAt(i).value, pw); 115 pw.println(); 116 } 117 if (details && mStartedTime != 0) { 118 pw.print(prefix); pw.print("mStartedTime="); 119 TimeUtils.formatDuration(SystemClock.elapsedRealtime(), mStartedTime, pw); 120 pw.println(); 121 pw.print(prefix); pw.print("mStartedPackage="); pw.println(mStartedPackage); 122 } 123 } 124 writeToProto(ProtoOutputStream proto, long fieldId, boolean details)125 public void writeToProto(ProtoOutputStream proto, long fieldId, boolean details) { 126 final long token = proto.start(fieldId); 127 proto.write(AppTimeTrackerProto.RECEIVER, mReceiver.toString()); 128 proto.write(AppTimeTrackerProto.TOTAL_DURATION_MS, mTotalTime); 129 for (int i=0; i<mPackageTimes.size(); i++) { 130 final long ptoken = proto.start(AppTimeTrackerProto.PACKAGE_TIMES); 131 proto.write(AppTimeTrackerProto.PackageTime.PACKAGE, mPackageTimes.keyAt(i)); 132 proto.write(AppTimeTrackerProto.PackageTime.DURATION_MS, mPackageTimes.valueAt(i).value); 133 proto.end(ptoken); 134 } 135 if (details && mStartedTime != 0) { 136 ProtoUtils.toDuration(proto, AppTimeTrackerProto.STARTED_TIME, 137 mStartedTime, SystemClock.elapsedRealtime()); 138 proto.write(AppTimeTrackerProto.STARTED_PACKAGE, mStartedPackage); 139 } 140 proto.end(token); 141 } 142 } 143