NotificationManagerService(
      Context context, StatusBarManagerService statusBar, LightsService lights) {
    super();
    mContext = context;
    mAm = ActivityManagerNative.getDefault();
    mSound = new NotificationPlayer(TAG);
    mSound.setUsesWakeLock(context);
    mToastQueue = new ArrayList<ToastRecord>();
    mHandler = new WorkerHandler();

    mStatusBar = statusBar;
    statusBar.setNotificationCallbacks(mNotificationCallbacks);

    mNotificationLight = lights.getLight(LightsService.LIGHT_ID_NOTIFICATIONS);
    mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION);

    Resources resources = mContext.getResources();
    mDefaultNotificationColor =
        resources.getColor(com.android.internal.R.color.config_defaultNotificationColor);
    mDefaultNotificationLedOn =
        resources.getInteger(com.android.internal.R.integer.config_defaultNotificationLedOn);
    mDefaultNotificationLedOff =
        resources.getInteger(com.android.internal.R.integer.config_defaultNotificationLedOff);

    // Don't start allowing notifications until the setup wizard has run once.
    // After that, including subsequent boots, init with notifications turned on.
    // This works on the first boot because the setup wizard will toggle this
    // flag at least once and we'll go back to 0 after that.
    if (0
        == Settings.Secure.getInt(
            mContext.getContentResolver(), Settings.Secure.DEVICE_PROVISIONED, 0)) {
      mDisabledNotifications = StatusBarManager.DISABLE_NOTIFICATION_ALERTS;
    }

    // register for various Intents
    IntentFilter filter = new IntentFilter();
    filter.addAction(Intent.ACTION_SCREEN_ON);
    filter.addAction(Intent.ACTION_SCREEN_OFF);
    filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
    filter.addAction(Intent.ACTION_USER_PRESENT);
    mContext.registerReceiver(mIntentReceiver, filter);
    IntentFilter pkgFilter = new IntentFilter();
    pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
    pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
    pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
    pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
    pkgFilter.addDataScheme("package");
    mContext.registerReceiver(mIntentReceiver, pkgFilter);
    IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
    mContext.registerReceiver(mIntentReceiver, sdFilter);

    SettingsObserver observer = new SettingsObserver(mHandler);
    observer.observe();
  }
  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;
    }
  }
  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;
  }