/** Called when a notification has been updated. */
 public void registerUpdatedByApp(NotificationRecord notification, NotificationRecord old) {
   notification.stats = old.stats;
   AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(notification);
   for (AggregatedStats stats : aggregatedStatsArray) {
     stats.numUpdatedByApp++;
     stats.countApiUse(notification);
   }
   releaseAggregatedStatsLocked(aggregatedStatsArray);
 }
 /** Called when the user clicked the notification in the UI. */
 public synchronized void registerClickedByUser(NotificationRecord notification) {
   MetricsLogger.histogram(
       mContext,
       "note_click_longevity",
       (int) (System.currentTimeMillis() - notification.getRankingTimeMs()) / (60 * 1000));
   notification.stats.onClick();
   if (ENABLE_SQLITE_LOG) {
     mSQLiteLog.logClicked(notification);
   }
 }
  private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete) {
    // tell the app
    if (sendDelete) {
      if (r.notification.deleteIntent != null) {
        try {
          r.notification.deleteIntent.send();
        } catch (PendingIntent.CanceledException ex) {
          // do nothing - there's no relevant way to recover, and
          //     no reason to let this propagate
          Slog.w(TAG, "canceled PendingIntent for " + r.pkg, ex);
        }
      }
    }

    // status bar
    if (r.notification.icon != 0) {
      long identity = Binder.clearCallingIdentity();
      try {
        mStatusBar.removeNotification(r.statusBarKey);
      } finally {
        Binder.restoreCallingIdentity(identity);
      }
      r.statusBarKey = null;
    }

    // sound
    if (mSoundNotification == r) {
      mSoundNotification = null;
      long identity = Binder.clearCallingIdentity();
      try {
        mSound.stop();
      } finally {
        Binder.restoreCallingIdentity(identity);
      }
    }

    // vibrate
    if (mVibrateNotification == r) {
      mVibrateNotification = null;
      long identity = Binder.clearCallingIdentity();
      try {
        mVibrator.cancel();
      } finally {
        Binder.restoreCallingIdentity(identity);
      }
    }

    // light
    mLights.remove(r);
    if (mLedNotification == r) {
      mLedNotification = null;
    }
  }
  /** Called when a notification has been posted. */
  public synchronized void registerPostedByApp(NotificationRecord notification) {
    notification.stats = new SingleNotificationStats();
    notification.stats.posttimeElapsedMs = SystemClock.elapsedRealtime();

    AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(notification);
    for (AggregatedStats stats : aggregatedStatsArray) {
      stats.numPostedByApp++;
      stats.countApiUse(notification);
    }
    releaseAggregatedStatsLocked(aggregatedStatsArray);
    if (ENABLE_SQLITE_LOG) {
      mSQLiteLog.logPosted(notification);
    }
  }
 private static void putNotificationDetails(NotificationRecord r, ContentValues outCv) {
   outCv.put(COL_NOTIFICATION_ID, r.sbn.getId());
   if (r.sbn.getTag() != null) {
     outCv.put(COL_TAG, r.sbn.getTag());
   }
   outCv.put(COL_WHEN_MS, r.sbn.getPostTime());
   outCv.put(COL_FLAGS, r.getNotification().flags);
   outCv.put(COL_PRIORITY, r.getNotification().priority);
   if (r.getNotification().category != null) {
     outCv.put(COL_CATEGORY, r.getNotification().category);
   }
   outCv.put(
       COL_ACTION_COUNT,
       r.getNotification().actions != null ? r.getNotification().actions.length : 0);
 }
  public void enqueueNotificationInternal(
      String pkg,
      int callingUid,
      int callingPid,
      String tag,
      int id,
      int priority,
      Notification notification,
      int[] idOut) {
    checkIncomingCall(pkg);

    // Limit the number of notifications that any given package except the android
    // package can enqueue.  Prevents DOS attacks and deals with leaks.
    if (!"android".equals(pkg)) {
      synchronized (mNotificationList) {
        int count = 0;
        final int N = mNotificationList.size();
        for (int i = 0; i < N; i++) {
          final NotificationRecord r = mNotificationList.get(i);
          if (r.pkg.equals(pkg)) {
            count++;
            if (count >= MAX_PACKAGE_NOTIFICATIONS) {
              Slog.e(
                  TAG,
                  "Package has already posted "
                      + count
                      + " notifications.  Not showing more.  package="
                      + pkg);
              return;
            }
          }
        }
      }
    }

    // This conditional is a dirty hack to limit the logging done on
    //     behalf of the download manager without affecting other apps.
    if (!pkg.equals("com.android.providers.downloads")
        || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
      EventLog.writeEvent(EventLogTags.NOTIFICATION_ENQUEUE, pkg, id, notification.toString());
    }

    if (pkg == null || notification == null) {
      throw new IllegalArgumentException(
          "null not allowed: pkg=" + pkg + " id=" + id + " notification=" + notification);
    }
    if (notification.icon != 0) {
      if (notification.contentView == null) {
        throw new IllegalArgumentException(
            "contentView required: pkg=" + pkg + " id=" + id + " notification=" + notification);
      }
    }

    synchronized (mNotificationList) {
      NotificationRecord r =
          new NotificationRecord(pkg, tag, id, callingUid, callingPid, priority, notification);
      NotificationRecord old = null;

      int index = indexOfNotificationLocked(pkg, tag, id);
      if (index < 0) {
        mNotificationList.add(r);
      } else {
        old = mNotificationList.remove(index);
        mNotificationList.add(index, r);
        // Make sure we don't lose the foreground service state.
        if (old != null) {
          notification.flags |= old.notification.flags & Notification.FLAG_FOREGROUND_SERVICE;
        }
      }

      // Ensure if this is a foreground service that the proper additional
      // flags are set.
      if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
        notification.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
      }

      if (notification.icon != 0) {
        StatusBarNotification n =
            new StatusBarNotification(pkg, id, tag, r.uid, r.initialPid, notification);
        n.priority = r.priority;

        if (old != null && old.statusBarKey != null) {
          r.statusBarKey = old.statusBarKey;
          long identity = Binder.clearCallingIdentity();
          try {
            mStatusBar.updateNotification(r.statusBarKey, n);
          } finally {
            Binder.restoreCallingIdentity(identity);
          }
        } else {
          long identity = Binder.clearCallingIdentity();
          try {
            r.statusBarKey = mStatusBar.addNotification(n);
            mAttentionLight.pulse();
          } finally {
            Binder.restoreCallingIdentity(identity);
          }
        }
        sendAccessibilityEvent(notification, pkg);
      } else {
        Slog.e(TAG, "Ignoring notification with icon==0: " + notification);
        if (old != null && old.statusBarKey != null) {
          long identity = Binder.clearCallingIdentity();
          try {
            mStatusBar.removeNotification(old.statusBarKey);
          } finally {
            Binder.restoreCallingIdentity(identity);
          }
        }
      }

      // If we're not supposed to beep, vibrate, etc. then don't.
      if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0)
          && (!(old != null && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0))
          && mSystemReady) {

        final AudioManager audioManager =
            (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
        // sound
        final boolean useDefaultSound = (notification.defaults & Notification.DEFAULT_SOUND) != 0;
        if (useDefaultSound || notification.sound != null) {
          Uri uri;
          if (useDefaultSound) {
            uri = Settings.System.DEFAULT_NOTIFICATION_URI;
          } else {
            uri = notification.sound;
          }
          boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0;
          int audioStreamType;
          if (notification.audioStreamType >= 0) {
            audioStreamType = notification.audioStreamType;
          } else {
            audioStreamType = DEFAULT_STREAM_TYPE;
          }
          mSoundNotification = r;
          // do not play notifications if stream volume is 0
          // (typically because ringer mode is silent).
          if (audioManager.getStreamVolume(audioStreamType) != 0) {
            long identity = Binder.clearCallingIdentity();
            try {
              mSound.play(mContext, uri, looping, audioStreamType);
            } finally {
              Binder.restoreCallingIdentity(identity);
            }
          }
        }

        // vibrate
        final boolean useDefaultVibrate =
            (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
        if ((useDefaultVibrate || notification.vibrate != null)
            && audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_NOTIFICATION)) {
          mVibrateNotification = r;

          mVibrator.vibrate(
              useDefaultVibrate ? DEFAULT_VIBRATE_PATTERN : notification.vibrate,
              ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0 : -1);
        }
      }

      // this option doesn't shut off the lights

      // light
      // the most recent thing gets the light
      mLights.remove(old);
      if (mLedNotification == old) {
        mLedNotification = null;
      }
      // Slog.i(TAG, "notification.lights="
      //        + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) != 0));
      if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) {
        mLights.add(r);
        updateLightsLocked();
      } else {
        if (old != null && ((old.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0)) {
          updateLightsLocked();
        }
      }
    }

    idOut[0] = id;
  }
    public void countApiUse(NotificationRecord record) {
      final Notification n = record.getNotification();
      if (n.actions != null) {
        numWithActions++;
      }

      if ((n.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
        numForegroundService++;
      }

      if ((n.flags & Notification.FLAG_ONGOING_EVENT) != 0) {
        numOngoing++;
      }

      if ((n.flags & Notification.FLAG_AUTO_CANCEL) != 0) {
        numAutoCancel++;
      }

      if ((n.defaults & Notification.DEFAULT_SOUND) != 0
          || (n.defaults & Notification.DEFAULT_VIBRATE) != 0
          || n.sound != null
          || n.vibrate != null) {
        numInterrupt++;
      }

      switch (n.visibility) {
        case Notification.VISIBILITY_PRIVATE:
          numPrivate++;
          break;
        case Notification.VISIBILITY_SECRET:
          numSecret++;
          break;
      }

      switch (n.priority) {
        case Notification.PRIORITY_MAX:
          numPriorityMax++;
          break;
        case Notification.PRIORITY_HIGH:
          numPriorityHigh++;
          break;
        case Notification.PRIORITY_LOW:
          numPriorityLow++;
          break;
        case Notification.PRIORITY_MIN:
          numPriorityMin++;
          break;
      }

      for (String Key : n.extras.keySet()) {
        if (Notification.EXTRA_BIG_TEXT.equals(key)) {
          numWithBigText++;
        } else if (Notification.EXTRA_PICTURE.equals(key)) {
          numWithBigPicture++;
        } else if (Notification.EXTRA_LARGE_ICON.equals(key)) {
          numWithLargeIcon++;
        } else if (Notification.EXTRA_TEXT_LINES.equals(key)) {
          numWithInbox++;
        } else if (Notification.EXTRA_MEDIA_SESSION.equals(key)) {
          numWithMediaSession++;
        } else if (Notification.EXTRA_TITLE.equals(key)) {
          numWithTitle++;
        } else if (Notification.EXTRA_TEXT.equals(key)) {
          numWithText++;
        } else if (Notification.EXTRA_SUB_TEXT.equals(key)) {
          numWithSubText++;
        } else if (Notification.EXTRA_INFO_TEXT.equals(key)) {
          numWithInfoText++;
        }
      }
    }