am ba4ac518: Merge "Improve debugging for issue #7586414: AlarmManager wakelocks held" into jb-mr1.1-dev

* commit 'ba4ac51823c59152e2167365b81ac56857b6b4d0':
  Improve debugging for issue #7586414: AlarmManager wakelocks held
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 67d3930..61b2067 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1701,6 +1701,21 @@
             return true;
         }
 
+        case GET_INTENT_FOR_INTENT_SENDER_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IIntentSender r = IIntentSender.Stub.asInterface(
+                data.readStrongBinder());
+            Intent intent = getIntentForIntentSender(r);
+            reply.writeNoException();
+            if (intent != null) {
+                reply.writeInt(1);
+                intent.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+            } else {
+                reply.writeInt(0);
+            }
+            return true;
+        }
+
         case UPDATE_PERSISTENT_CONFIGURATION_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             Configuration config = Configuration.CREATOR.createFromParcel(data);
@@ -3977,6 +3992,20 @@
         return res;
     }
 
+    public Intent getIntentForIntentSender(IIntentSender sender) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(sender.asBinder());
+        mRemote.transact(GET_INTENT_FOR_INTENT_SENDER_TRANSACTION, data, reply, 0);
+        reply.readException();
+        Intent res = reply.readInt() != 0
+                ? Intent.CREATOR.createFromParcel(reply) : null;
+        data.recycle();
+        reply.recycle();
+        return res;
+    }
+
     public void updatePersistentConfiguration(Configuration values) throws RemoteException
     {
         Parcel data = Parcel.obtain();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 8fc1c86..8af17a4 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -341,6 +341,8 @@
 
     public boolean isIntentSenderAnActivity(IIntentSender sender) throws RemoteException;
 
+    public Intent getIntentForIntentSender(IIntentSender sender) throws RemoteException;
+
     public void updatePersistentConfiguration(Configuration values) throws RemoteException;
 
     public long[] getProcessPss(int[] pids) throws RemoteException;
@@ -621,4 +623,5 @@
     int REQUEST_BUG_REPORT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+157;
     int INPUT_DISPATCHING_TIMED_OUT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+158;
     int CLEAR_PENDING_BACKUP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+159;
+    int GET_INTENT_FOR_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+160;
 }
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index d36d99d..5c75aff 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -790,6 +790,20 @@
     }
 
     /**
+     * @hide
+     * Return the Intent of this PendingIntent.
+     */
+    public Intent getIntent() {
+        try {
+            return ActivityManagerNative.getDefault()
+                .getIntentForIntentSender(mTarget);
+        } catch (RemoteException e) {
+            // Should never happen.
+            return null;
+        }
+    }
+
+    /**
      * Comparison operator on two PendingIntent objects, such that true
      * is returned then they both represent the same operation from the
      * same package.  This allows you to use {@link #getActivity},
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 54f2fe3..9821824 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -18,6 +18,8 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.Formatter;
 import java.util.List;
 import java.util.Map;
@@ -1127,8 +1129,10 @@
             if (totalTimeMillis != 0) {
                 sb.append(linePrefix);
                 formatTimeMs(sb, totalTimeMillis);
-                if (name != null) sb.append(name);
-                sb.append(' ');
+                if (name != null) {
+                    sb.append(name);
+                    sb.append(' ');
+                }
                 sb.append('(');
                 sb.append(count);
                 sb.append(" times)");
@@ -1440,8 +1444,21 @@
         }
     }
 
+    static final class TimerEntry {
+        final String mName;
+        final int mId;
+        final BatteryStats.Timer mTimer;
+        final long mTime;
+        TimerEntry(String name, int id, BatteryStats.Timer timer, long time) {
+            mName = name;
+            mId = id;
+            mTimer = timer;
+            mTime = time;
+        }
+    }
+
     @SuppressWarnings("unused")
-    public final void dumpLocked(PrintWriter pw, String prefix, int which, int reqUid) {
+    public final void dumpLocked(PrintWriter pw, String prefix, final int which, int reqUid) {
         final long rawUptime = SystemClock.uptimeMillis() * 1000;
         final long rawRealtime = SystemClock.elapsedRealtime() * 1000;
         final long batteryUptime = getBatteryUptime(rawUptime);
@@ -1516,19 +1533,43 @@
         long txTotal = 0;
         long fullWakeLockTimeTotalMicros = 0;
         long partialWakeLockTimeTotalMicros = 0;
-        
+
+        final Comparator<TimerEntry> timerComparator = new Comparator<TimerEntry>() {
+            @Override
+            public int compare(TimerEntry lhs, TimerEntry rhs) {
+                long lhsTime = lhs.mTime;
+                long rhsTime = rhs.mTime;
+                if (lhsTime < rhsTime) {
+                    return 1;
+                }
+                if (lhsTime > rhsTime) {
+                    return -1;
+                }
+                return 0;
+            }
+        };
+
         if (reqUid < 0) {
             Map<String, ? extends BatteryStats.Timer> kernelWakelocks = getKernelWakelockStats();
             if (kernelWakelocks.size() > 0) {
+                final ArrayList<TimerEntry> timers = new ArrayList<TimerEntry>();
                 for (Map.Entry<String, ? extends BatteryStats.Timer> ent : kernelWakelocks.entrySet()) {
-                    
+                    BatteryStats.Timer timer = ent.getValue();
+                    long totalTimeMillis = computeWakeLock(timer, batteryRealtime, which);
+                    if (totalTimeMillis > 0) {
+                        timers.add(new TimerEntry(ent.getKey(), 0, timer, totalTimeMillis));
+                    }
+                }
+                Collections.sort(timers, timerComparator);
+                for (int i=0; i<timers.size(); i++) {
+                    TimerEntry timer = timers.get(i);
                     String linePrefix = ": ";
                     sb.setLength(0);
                     sb.append(prefix);
                     sb.append("  Kernel Wake lock ");
-                    sb.append(ent.getKey());
-                    linePrefix = printWakeLock(sb, ent.getValue(), batteryRealtime, null, which, 
-                            linePrefix);
+                    sb.append(timer.mName);
+                    linePrefix = printWakeLock(sb, timer.mTimer, batteryRealtime, null,
+                            which, linePrefix);
                     if (!linePrefix.equals(": ")) {
                         sb.append(" realtime");
                         // Only print out wake locks that were held
@@ -1537,7 +1578,9 @@
                 }
             }
         }
-    
+
+        final ArrayList<TimerEntry> timers = new ArrayList<TimerEntry>();
+
         for (int iu = 0; iu < NU; iu++) {
             Uid u = uidStats.valueAt(iu);
             rxTotal += u.getTcpBytesReceived(which);
@@ -1557,8 +1600,18 @@
 
                     Timer partialWakeTimer = wl.getWakeTime(WAKE_TYPE_PARTIAL);
                     if (partialWakeTimer != null) {
-                        partialWakeLockTimeTotalMicros += partialWakeTimer.getTotalTimeLocked(
+                        long totalTimeMicros = partialWakeTimer.getTotalTimeLocked(
                                 batteryRealtime, which);
+                        if (totalTimeMicros > 0) {
+                            if (reqUid < 0) {
+                                // Only show the ordered list of all wake
+                                // locks if the caller is not asking for data
+                                // about a specific uid.
+                                timers.add(new TimerEntry(ent.getKey(), u.getUid(),
+                                        partialWakeTimer, totalTimeMicros));
+                            }
+                            partialWakeLockTimeTotalMicros += totalTimeMicros;
+                        }
                     }
                 }
             }
@@ -1571,7 +1624,7 @@
         sb.append(prefix);
                 sb.append("  Total full wakelock time: "); formatTimeMs(sb,
                         (fullWakeLockTimeTotalMicros + 500) / 1000);
-                sb.append(", Total partial waklock time: "); formatTimeMs(sb,
+                sb.append(", Total partial wakelock time: "); formatTimeMs(sb,
                         (partialWakeLockTimeTotalMicros + 500) / 1000);
         pw.println(sb.toString());
         
@@ -1676,9 +1729,26 @@
                     pw.println(getDischargeAmountScreenOnSinceCharge());
             pw.print(prefix); pw.print("    Amount discharged while screen off: ");
                     pw.println(getDischargeAmountScreenOffSinceCharge());
-            pw.println(" ");
+            pw.println();
         }
-        
+
+        if (timers.size() > 0) {
+            Collections.sort(timers, timerComparator);
+            pw.print(prefix); pw.println("  All partial wake locks:");
+            for (int i=0; i<timers.size(); i++) {
+                TimerEntry timer = timers.get(i);
+                sb.setLength(0);
+                sb.append("  Wake lock #");
+                sb.append(timer.mId);
+                sb.append(" ");
+                sb.append(timer.mName);
+                printWakeLock(sb, timer.mTimer, batteryRealtime, null, which, ": ");
+                sb.append(" realtime");
+                pw.println(sb.toString());
+            }
+            timers.clear();
+            pw.println();
+        }
 
         for (int iu=0; iu<NU; iu++) {
             final int uid = uidStats.keyAt(iu);
diff --git a/core/java/com/android/internal/util/LocalLog.java b/core/java/com/android/internal/util/LocalLog.java
new file mode 100644
index 0000000..f0e6171
--- /dev/null
+++ b/core/java/com/android/internal/util/LocalLog.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2012 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.util;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+import android.util.Slog;
+
+/**
+ * Helper class for logging serious issues, which also keeps a small
+ * snapshot of the logged events that can be printed later, such as part
+ * of a system service's dumpsys output.
+ * @hide
+ */
+public class LocalLog {
+    private final String mTag;
+    private final int mMaxLines = 20;
+    private final ArrayList<String> mLines = new ArrayList<String>(mMaxLines);
+
+    public LocalLog(String tag) {
+        mTag = tag;
+    }
+
+    public void w(String msg) {
+        synchronized (mLines) {
+            Slog.w(mTag, msg);
+            if (mLines.size() >= mMaxLines) {
+                mLines.remove(0);
+            }
+            mLines.add(msg);
+        }
+    }
+
+    public boolean dump(PrintWriter pw, String header, String prefix) {
+        synchronized (mLines) {
+            if (mLines.size() <= 0) {
+                return false;
+            }
+            if (header != null) {
+                pw.println(header);
+            }
+            for (int i=0; i<mLines.size(); i++) {
+                if (prefix != null) {
+                    pw.print(prefix);
+                }
+                pw.println(mLines.get(i));
+            }
+            return true;
+        }
+    }
+}
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java
index 440f8e1..cbd00f3 100644
--- a/services/java/com/android/server/AlarmManagerService.java
+++ b/services/java/com/android/server/AlarmManagerService.java
@@ -22,6 +22,7 @@
 import android.app.IAlarmManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -38,6 +39,7 @@
 import android.os.WorkSource;
 import android.text.TextUtils;
 import android.text.format.Time;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.TimeUtils;
 
@@ -45,16 +47,18 @@
 import java.io.PrintWriter;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.Map;
 import java.util.TimeZone;
 
+import com.android.internal.util.LocalLog;
+
 class AlarmManagerService extends IAlarmManager.Stub {
     // The threshold for how long an alarm can be late before we print a
     // warning message.  The time duration is in milliseconds.
@@ -79,7 +83,9 @@
             = new Intent().addFlags(Intent.FLAG_FROM_BACKGROUND);
     
     private final Context mContext;
-    
+
+    private final LocalLog mLog = new LocalLog(TAG);
+
     private Object mLock = new Object();
     
     private final ArrayList<Alarm> mRtcWakeupAlarms = new ArrayList<Alarm>();
@@ -91,7 +97,7 @@
     private int mDescriptor;
     private int mBroadcastRefCount = 0;
     private PowerManager.WakeLock mWakeLock;
-    private LinkedList<PendingIntent> mInFlight = new LinkedList<PendingIntent>();
+    private ArrayList<InFlight> mInFlight = new ArrayList<InFlight>();
     private final AlarmThread mWaitThread = new AlarmThread();
     private final AlarmHandler mHandler = new AlarmHandler();
     private ClockReceiver mClockReceiver;
@@ -99,18 +105,59 @@
     private final ResultReceiver mResultReceiver = new ResultReceiver();
     private final PendingIntent mTimeTickSender;
     private final PendingIntent mDateChangeSender;
-    
-    private static final class FilterStats {
-        int count;
+
+    private static final class InFlight extends Intent {
+        final PendingIntent mPendingIntent;
+        final Pair<String, ComponentName> mTarget;
+        final BroadcastStats mBroadcastStats;
+        final FilterStats mFilterStats;
+
+        InFlight(AlarmManagerService service, PendingIntent pendingIntent) {
+            mPendingIntent = pendingIntent;
+            Intent intent = pendingIntent.getIntent();
+            mTarget = intent != null
+                    ? new Pair<String, ComponentName>(intent.getAction(), intent.getComponent())
+                    : null;
+            mBroadcastStats = service.getStatsLocked(pendingIntent);
+            FilterStats fs = mBroadcastStats.filterStats.get(mTarget);
+            if (fs == null) {
+                fs = new FilterStats(mBroadcastStats, mTarget);
+                mBroadcastStats.filterStats.put(mTarget, fs);
+            }
+            mFilterStats = fs;
+        }
     }
-    
-    private static final class BroadcastStats {
+
+    private static final class FilterStats {
+        final BroadcastStats mBroadcastStats;
+        final Pair<String, ComponentName> mTarget;
+
         long aggregateTime;
+        int count;
         int numWakeup;
         long startTime;
         int nesting;
-        HashMap<Intent.FilterComparison, FilterStats> filterStats
-                = new HashMap<Intent.FilterComparison, FilterStats>();
+
+        FilterStats(BroadcastStats broadcastStats, Pair<String, ComponentName> target) {
+            mBroadcastStats = broadcastStats;
+            mTarget = target;
+        }
+    }
+    
+    private static final class BroadcastStats {
+        final String mPackageName;
+
+        long aggregateTime;
+        int count;
+        int numWakeup;
+        long startTime;
+        int nesting;
+        final HashMap<Pair<String, ComponentName>, FilterStats> filterStats
+                = new HashMap<Pair<String, ComponentName>, FilterStats>();
+
+        BroadcastStats(String packageName) {
+            mPackageName = packageName;
+        }
     }
     
     private final HashMap<String, BroadcastStats> mBroadcastStats
@@ -496,24 +543,104 @@
                     dumpAlarmList(pw, mElapsedRealtimeAlarms, "  ", "ELAPSED", now);
                 }
             }
-            
-            pw.println(" ");
+
+            pw.println();
             pw.print("  Broadcast ref count: "); pw.println(mBroadcastRefCount);
-            
-            pw.println(" ");
-            pw.println("  Alarm Stats:");
+            pw.println();
+
+            if (mLog.dump(pw, "  Recent problems", "    ")) {
+                pw.println();
+            }
+
+            final FilterStats[] topFilters = new FilterStats[10];
+            final Comparator<FilterStats> comparator = new Comparator<FilterStats>() {
+                @Override
+                public int compare(FilterStats lhs, FilterStats rhs) {
+                    if (lhs.aggregateTime < rhs.aggregateTime) {
+                        return 1;
+                    } else if (lhs.aggregateTime > rhs.aggregateTime) {
+                        return -1;
+                    }
+                    return 0;
+                }
+            };
+            int len = 0;
             for (Map.Entry<String, BroadcastStats> be : mBroadcastStats.entrySet()) {
                 BroadcastStats bs = be.getValue();
-                pw.print("  "); pw.println(be.getKey());
-                pw.print("    "); pw.print(bs.aggregateTime);
-                        pw.print("ms running, "); pw.print(bs.numWakeup);
-                        pw.println(" wakeups");
-                for (Map.Entry<Intent.FilterComparison, FilterStats> fe
+                for (Map.Entry<Pair<String, ComponentName>, FilterStats> fe
                         : bs.filterStats.entrySet()) {
-                    pw.print("    "); pw.print(fe.getValue().count);
-                            pw.print(" alarms: ");
-                            pw.println(fe.getKey().getIntent().toShortString(
-                                    false, true, false, true));
+                    FilterStats fs = fe.getValue();
+                    int pos = len > 0
+                            ? Arrays.binarySearch(topFilters, 0, len, fs, comparator) : 0;
+                    if (pos < 0) {
+                        pos = -pos - 1;
+                    }
+                    if (pos < topFilters.length) {
+                        int copylen = topFilters.length - pos - 1;
+                        if (copylen > 0) {
+                            System.arraycopy(topFilters, pos, topFilters, pos+1, copylen);
+                        }
+                        topFilters[pos] = fs;
+                        if (len < topFilters.length) {
+                            len++;
+                        }
+                    }
+                }
+            }
+            if (len > 0) {
+                pw.println("  Top Alarms:");
+                for (int i=0; i<len; i++) {
+                    FilterStats fs = topFilters[i];
+                    pw.print("    ");
+                    if (fs.nesting > 0) pw.print("*ACTIVE* ");
+                    TimeUtils.formatDuration(fs.aggregateTime, pw);
+                    pw.print(" running, "); pw.print(fs.numWakeup);
+                    pw.print(" wakeups, "); pw.print(fs.count);
+                    pw.print(" alarms: "); pw.print(fs.mBroadcastStats.mPackageName);
+                    pw.println();
+                    pw.print("      ");
+                    if (fs.mTarget.first != null) {
+                        pw.print(" act="); pw.print(fs.mTarget.first);
+                    }
+                    if (fs.mTarget.second != null) {
+                        pw.print(" cmp="); pw.print(fs.mTarget.second.toShortString());
+                    }
+                    pw.println();
+                }
+            }
+
+            pw.println(" ");
+            pw.println("  Alarm Stats:");
+            final ArrayList<FilterStats> tmpFilters = new ArrayList<FilterStats>();
+            for (Map.Entry<String, BroadcastStats> be : mBroadcastStats.entrySet()) {
+                BroadcastStats bs = be.getValue();
+                pw.print("  ");
+                if (bs.nesting > 0) pw.print("*ACTIVE* ");
+                pw.print(be.getKey());
+                pw.print(" "); TimeUtils.formatDuration(bs.aggregateTime, pw);
+                        pw.print(" running, "); pw.print(bs.numWakeup);
+                        pw.println(" wakeups:");
+                tmpFilters.clear();
+                for (Map.Entry<Pair<String, ComponentName>, FilterStats> fe
+                        : bs.filterStats.entrySet()) {
+                    tmpFilters.add(fe.getValue());
+                }
+                Collections.sort(tmpFilters, comparator);
+                for (int i=0; i<tmpFilters.size(); i++) {
+                    FilterStats fs = tmpFilters.get(i);
+                    pw.print("    ");
+                            if (fs.nesting > 0) pw.print("*ACTIVE* ");
+                            TimeUtils.formatDuration(fs.aggregateTime, pw);
+                            pw.print(" "); pw.print(fs.numWakeup);
+                            pw.print(" wakes " ); pw.print(fs.count);
+                            pw.print(" alarms:");
+                            if (fs.mTarget.first != null) {
+                                pw.print(" act="); pw.print(fs.mTarget.first);
+                            }
+                            if (fs.mTarget.second != null) {
+                                pw.print(" cmp="); pw.print(fs.mTarget.second.toShortString());
+                            }
+                            pw.println();
                 }
             }
         }
@@ -708,18 +835,31 @@
                                 setWakelockWorkSource(alarm.operation);
                                 mWakeLock.acquire();
                             }
-                            mInFlight.add(alarm.operation);
+                            final InFlight inflight = new InFlight(AlarmManagerService.this,
+                                    alarm.operation);
+                            mInFlight.add(inflight);
                             mBroadcastRefCount++;
-                            
-                            BroadcastStats bs = getStatsLocked(alarm.operation);
+
+                            final BroadcastStats bs = inflight.mBroadcastStats;
+                            bs.count++;
                             if (bs.nesting == 0) {
+                                bs.nesting = 1;
                                 bs.startTime = nowELAPSED;
                             } else {
                                 bs.nesting++;
                             }
+                            final FilterStats fs = inflight.mFilterStats;
+                            fs.count++;
+                            if (fs.nesting == 0) {
+                                fs.nesting = 1;
+                                fs.startTime = nowELAPSED;
+                            } else {
+                                fs.nesting++;
+                            }
                             if (alarm.type == AlarmManager.ELAPSED_REALTIME_WAKEUP
                                     || alarm.type == AlarmManager.RTC_WAKEUP) {
                                 bs.numWakeup++;
+                                fs.numWakeup++;
                                 ActivityManagerNative.noteWakeupAlarm(
                                         alarm.operation);
                             }
@@ -908,44 +1048,58 @@
         String pkg = pi.getTargetPackage();
         BroadcastStats bs = mBroadcastStats.get(pkg);
         if (bs == null) {
-            bs = new BroadcastStats();
+            bs = new BroadcastStats(pkg);
             mBroadcastStats.put(pkg, bs);
         }
         return bs;
     }
-    
+
     class ResultReceiver implements PendingIntent.OnFinished {
         public void onSendFinished(PendingIntent pi, Intent intent, int resultCode,
                 String resultData, Bundle resultExtras) {
             synchronized (mLock) {
-                BroadcastStats bs = getStatsLocked(pi);
-                if (bs != null) {
+                InFlight inflight = null;
+                for (int i=0; i<mInFlight.size(); i++) {
+                    if (mInFlight.get(i).mPendingIntent == pi) {
+                        inflight = mInFlight.remove(i);
+                        break;
+                    }
+                }
+                if (inflight != null) {
+                    final long nowELAPSED = SystemClock.elapsedRealtime();
+                    BroadcastStats bs = inflight.mBroadcastStats;
                     bs.nesting--;
                     if (bs.nesting <= 0) {
                         bs.nesting = 0;
-                        bs.aggregateTime += SystemClock.elapsedRealtime()
-                                - bs.startTime;
-                        Intent.FilterComparison fc = new Intent.FilterComparison(intent);
-                        FilterStats fs = bs.filterStats.get(fc);
-                        if (fs == null) {
-                            fs = new FilterStats();
-                            bs.filterStats.put(fc, fs);
-                        }
-                        fs.count++;
+                        bs.aggregateTime += nowELAPSED - bs.startTime;
                     }
+                    FilterStats fs = inflight.mFilterStats;
+                    fs.nesting--;
+                    if (fs.nesting <= 0) {
+                        fs.nesting = 0;
+                        fs.aggregateTime += nowELAPSED - fs.startTime;
+                    }
+                } else {
+                    mLog.w("No in-flight alarm for " + pi + " " + intent);
                 }
-                mInFlight.removeFirst();
                 mBroadcastRefCount--;
                 if (mBroadcastRefCount == 0) {
                     mWakeLock.release();
+                    if (mInFlight.size() > 0) {
+                        mLog.w("Finished all broadcasts with " + mInFlight.size()
+                                + " remaining inflights");
+                        for (int i=0; i<mInFlight.size(); i++) {
+                            mLog.w("  Remaining #" + i + ": " + mInFlight.get(i));
+                        }
+                        mInFlight.clear();
+                    }
                 } else {
                     // the next of our alarms is now in flight.  reattribute the wakelock.
-                    final PendingIntent nowInFlight = mInFlight.peekFirst();
-                    if (nowInFlight != null) {
-                        setWakelockWorkSource(nowInFlight);
+                    if (mInFlight.size() > 0) {
+                        setWakelockWorkSource(mInFlight.get(0).mPendingIntent);
                     } else {
                         // should never happen
-                        Slog.e(TAG, "Alarm wakelock still held but sent queue empty");
+                        mLog.w("Alarm wakelock still held but sent queue empty");
                         mWakeLock.setWorkSource(null);
                     }
                 }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 02640b5..82c9030 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -4764,6 +4764,18 @@
         return false;
     }
 
+    public Intent getIntentForIntentSender(IIntentSender pendingResult) {
+        if (!(pendingResult instanceof PendingIntentRecord)) {
+            return null;
+        }
+        try {
+            PendingIntentRecord res = (PendingIntentRecord)pendingResult;
+            return res.key.requestIntent != null ? new Intent(res.key.requestIntent) : null;
+        } catch (ClassCastException e) {
+        }
+        return null;
+    }
+
     public void setProcessLimit(int max) {
         enforceCallingPermission(android.Manifest.permission.SET_PROCESS_LIMIT,
                 "setProcessLimit()");