Fixes to idle alarm scheduling, package importance.

- Add new API to ask the activity manager what the current
  importance of a particular package name is (along with a few
  new useful importance levels).

- Fix my last alarm manager change to actually execute the
  alarms we have now decided should run even while we are idle.

Change-Id: I1f14712b4e390770d53b185c96a1b36f6aadd687
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 34e8b78..9f80fd8 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -99,6 +99,7 @@
     static final boolean DEBUG_BATCH = localLOGV || false;
     static final boolean DEBUG_VALIDATE = localLOGV || false;
     static final boolean DEBUG_ALARM_CLOCK = localLOGV || false;
+    static final boolean RECORD_ALARMS_IN_HISTORY = true;
     static final int ALARM_EVENT = 1;
     static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
 
@@ -1345,15 +1346,6 @@
     }
 
     void rescheduleKernelAlarmsLocked() {
-        if (mPendingIdleUntil != null) {
-            // If we have a pending "idle until" alarm, we will just blindly wait until
-            // it is time for that alarm to go off.  We don't want to wake up for any
-            // other reasons.
-            mNextWakeup = mNextNonWakeup = mPendingIdleUntil.whenElapsed;
-            setLocked(ELAPSED_REALTIME_WAKEUP, mNextWakeup);
-            setLocked(ELAPSED_REALTIME, mNextNonWakeup);
-            return;
-        }
         // Schedule the next upcoming wakeup alarm.  If there is a deliverable batch
         // prior to that which contains no wakeups, we schedule that as well.
         long nextNonWakeup = 0;
@@ -1574,13 +1566,6 @@
     boolean triggerAlarmsLocked(ArrayList<Alarm> triggerList, final long nowELAPSED,
             final long nowRTC) {
         boolean hasWakeup = false;
-        if (mPendingIdleUntil != null) {
-            // If we have a pending "idle until" alarm, don't trigger any alarms
-            // until we are past the idle period.
-            if (nowELAPSED < mPendingIdleUntil.whenElapsed) {
-                return false;
-            }
-        }
         // batches are temporally sorted, so we need only pull from the
         // start of the list until we either empty it or hit a batch
         // that is not yet deliverable
@@ -1816,6 +1801,17 @@
                 if (localLOGV) {
                     Slog.v(TAG, "sending alarm " + alarm);
                 }
+                if (RECORD_ALARMS_IN_HISTORY) {
+                    if (alarm.workSource != null && alarm.workSource.size() > 0) {
+                        for (int wi=0; wi<alarm.workSource.size(); wi++) {
+                            ActivityManagerNative.noteAlarmStart(
+                                    alarm.operation, alarm.workSource.get(wi), alarm.tag);
+                        }
+                    } else {
+                        ActivityManagerNative.noteAlarmStart(
+                                alarm.operation, -1, alarm.tag);
+                    }
+                }
                 alarm.operation.send(getContext(), 0,
                         mBackgroundIntent.putExtra(
                                 Intent.EXTRA_ALARM_COUNT, alarm.count),
@@ -1856,11 +1852,11 @@
                         for (int wi=0; wi<alarm.workSource.size(); wi++) {
                             ActivityManagerNative.noteWakeupAlarm(
                                     alarm.operation, alarm.workSource.get(wi),
-                                    alarm.workSource.getName(wi));
+                                    alarm.workSource.getName(wi), alarm.tag);
                         }
                     } else {
                         ActivityManagerNative.noteWakeupAlarm(
-                                alarm.operation, -1, null);
+                                alarm.operation, -1, null, alarm.tag);
                     }
                 }
             } catch (PendingIntent.CanceledException e) {
@@ -2222,6 +2218,17 @@
                         fs.nesting = 0;
                         fs.aggregateTime += nowELAPSED - fs.startTime;
                     }
+                    if (RECORD_ALARMS_IN_HISTORY) {
+                        if (inflight.mWorkSource != null && inflight.mWorkSource.size() > 0) {
+                            for (int wi=0; wi<inflight.mWorkSource.size(); wi++) {
+                                ActivityManagerNative.noteAlarmFinish(
+                                        pi, inflight.mWorkSource.get(wi), inflight.mTag);
+                            }
+                        } else {
+                            ActivityManagerNative.noteAlarmFinish(
+                                    pi, -1, inflight.mTag);
+                        }
+                    }
                 } else {
                     mLog.w("No in-flight alarm for " + pi + " " + intent);
                 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6341807..b606353 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -42,7 +42,6 @@
 import android.app.ITaskStackListener;
 import android.app.ProfilerInfo;
 import android.app.usage.UsageEvents;
-import android.app.usage.UsageStats;
 import android.app.usage.UsageStatsManagerInternal;
 import android.appwidget.AppWidgetManager;
 import android.content.res.Resources;
@@ -3449,6 +3448,35 @@
         }
     }
 
+    @Override
+    public int getPackageProcessState(String packageName) {
+        int procState = ActivityManager.PROCESS_STATE_NONEXISTENT;
+        synchronized (this) {
+            for (int i=mLruProcesses.size()-1; i>=0; i--) {
+                final ProcessRecord proc = mLruProcesses.get(i);
+                if (procState == ActivityManager.PROCESS_STATE_NONEXISTENT
+                        || procState > proc.setProcState) {
+                    boolean found = false;
+                    for (int j=proc.pkgList.size()-1; j>=0 && !found; j--) {
+                        if (proc.pkgList.keyAt(j).equals(packageName)) {
+                            procState = proc.setProcState;
+                            found = true;
+                        }
+                    }
+                    if (proc.pkgDeps != null && !found) {
+                        for (int j=proc.pkgDeps.size()-1; j>=0; j--) {
+                            if (proc.pkgDeps.valueAt(j).equals(packageName)) {
+                                procState = proc.setProcState;
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return procState;
+    }
+
     private void dispatchProcessesChanged() {
         int N;
         synchronized (this) {
@@ -10501,15 +10529,11 @@
                 Context.WINDOW_SERVICE)).addView(v, lp);
     }
 
-    public void noteWakeupAlarm(IIntentSender sender, int sourceUid, String sourcePkg) {
+    public void noteWakeupAlarm(IIntentSender sender, int sourceUid, String sourcePkg, String tag) {
         if (!(sender instanceof PendingIntentRecord)) {
             return;
         }
         final PendingIntentRecord rec = (PendingIntentRecord)sender;
-        final String tag;
-        synchronized (this) {
-            tag = getTagForIntentSenderLocked(rec, "*walarm*:");
-        }
         final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
         synchronized (stats) {
             if (mBatteryStatsService.isOnBattery()) {
@@ -10524,6 +10548,34 @@
         }
     }
 
+    public void noteAlarmStart(IIntentSender sender, int sourceUid, String tag) {
+        if (!(sender instanceof PendingIntentRecord)) {
+            return;
+        }
+        final PendingIntentRecord rec = (PendingIntentRecord)sender;
+        final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+        synchronized (stats) {
+            mBatteryStatsService.enforceCallingPermission();
+            int MY_UID = Binder.getCallingUid();
+            int uid = rec.uid == MY_UID ? Process.SYSTEM_UID : rec.uid;
+            mBatteryStatsService.noteAlarmStart(tag, sourceUid >= 0 ? sourceUid : uid);
+        }
+    }
+
+    public void noteAlarmFinish(IIntentSender sender, int sourceUid, String tag) {
+        if (!(sender instanceof PendingIntentRecord)) {
+            return;
+        }
+        final PendingIntentRecord rec = (PendingIntentRecord)sender;
+        final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+        synchronized (stats) {
+            mBatteryStatsService.enforceCallingPermission();
+            int MY_UID = Binder.getCallingUid();
+            int uid = rec.uid == MY_UID ? Process.SYSTEM_UID : rec.uid;
+            mBatteryStatsService.noteAlarmFinish(tag, sourceUid >= 0 ? sourceUid : uid);
+        }
+    }
+
     public boolean killPids(int[] pids, String pReason, boolean secure) {
         if (Binder.getCallingUid() != Process.SYSTEM_UID) {
             throw new SecurityException("killPids only available to the system");
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index ed108c2..bfc4fc7 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -315,6 +315,20 @@
         }
     }
 
+    public void noteAlarmStart(String name, int uid) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteAlarmStartLocked(name, uid);
+        }
+    }
+
+    public void noteAlarmFinish(String name, int uid) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteAlarmFinishLocked(name, uid);
+        }
+    }
+
     public void noteStartWakelock(int uid, int pid, String name, String historyName, int type,
             boolean unimportantForLogging) {
         enforceCallingPermission();
@@ -884,7 +898,7 @@
         pw.println("    Enable or disable a running option.  Option state is not saved across boots.");
         pw.println("    Options are:");
         pw.println("      full-history: include additional detailed events in battery history:");
-        pw.println("          wake_lock_in and proc events");
+        pw.println("          wake_lock_in, alarms and proc events");
         pw.println("      no-auto-reset: don't automatically reset stats when unplugged");
     }
 
diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java
index 8a9f3e1..23d5c05 100644
--- a/services/core/java/com/android/server/job/controllers/AppIdleController.java
+++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java
@@ -49,16 +49,12 @@
     private static volatile AppIdleController sController;
     final ArrayList<JobStatus> mTrackedTasks = new ArrayList<JobStatus>();
     private final UsageStatsManagerInternal mUsageStatsInternal;
-    private final BatteryManagerInternal mBatteryManagerInternal;
+    private final BatteryManager mBatteryManager;
     private boolean mPluggedIn;
 
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override public void onReceive(Context context, Intent intent) {
-            if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
-                int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
-                // TODO: Allow any charger type
-                onPluggedIn((plugged & BatteryManager.BATTERY_PLUGGED_AC) != 0);
-            }
+            onPluggedIn(mBatteryManager.isCharging());
         }
     };
 
@@ -74,23 +70,19 @@
     private AppIdleController(StateChangedListener stateChangedListener, Context context) {
         super(stateChangedListener, context);
         mUsageStatsInternal = LocalServices.getService(UsageStatsManagerInternal.class);
-        mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class);
-        mPluggedIn = isPowered();
+        mBatteryManager = context.getSystemService(BatteryManager.class);
+        mPluggedIn = mBatteryManager.isCharging();
         mUsageStatsInternal.addAppIdleStateChangeListener(this);
         registerReceivers();
     }
 
     private void registerReceivers() {
         // Monitor battery charging state
-        IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+        IntentFilter filter = new IntentFilter(BatteryManager.ACTION_CHARGING);
+        filter.addAction(BatteryManager.ACTION_DISCHARGING);
         mContext.registerReceiver(mReceiver, filter);
     }
 
-    private boolean isPowered() {
-        // TODO: Allow any charger type
-        return mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_AC);
-    }
-
     @Override
     public void maybeStartTrackingJob(JobStatus jobStatus) {
         synchronized (mTrackedTasks) {