Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1d599da8 authored by Christoph Studer's avatar Christoph Studer
Browse files

NotificationListenerService API: Avoid unnecessary allocations

Instead of producing Ranking objects, RankingMap just populates
them now, allowing developers to re-use objects and avoid
unnecessary allocations.

Also rename isInterceptedByDnd() to meetsInterruptionFilter(),
since DND is not a concept anymore.

Bug: 15415840
Bug: 16099064
Change-Id: If9861cbdf14593e641a4d4ffd1b967647eb8e2b8
parent 1c52d026
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -26336,16 +26336,17 @@ package android.service.notification {
  }
  public static class NotificationListenerService.Ranking {
    ctor public NotificationListenerService.Ranking();
    method public java.lang.String getKey();
    method public int getRank();
    method public boolean isAmbient();
    method public boolean isInterceptedByDoNotDisturb();
    method public boolean meetsInterruptionFilter();
  }
  public static class NotificationListenerService.RankingMap implements android.os.Parcelable {
    method public int describeContents();
    method public java.lang.String[] getOrderedKeys();
    method public android.service.notification.NotificationListenerService.Ranking getRanking(java.lang.String);
    method public boolean getRanking(java.lang.String, android.service.notification.NotificationListenerService.Ranking);
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator CREATOR;
  }
+49 −48
Original line number Diff line number Diff line
@@ -29,7 +29,6 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.ArrayMap;
import android.util.Log;

import java.util.List;
@@ -410,28 +409,20 @@ public abstract class NotificationListenerService extends Service {
    }

    /**
     * Provides access to ranking information on a currently active
     * notification.
     * Stores ranking related information on a currently active notification.
     *
     * <p>
     * Note that this object is not updated on notification events (such as
     * {@link #onNotificationPosted(StatusBarNotification, RankingMap)},
     * {@link #onNotificationRemoved(StatusBarNotification)}, etc.). Make sure
     * to retrieve a new Ranking from the current {@link RankingMap} whenever
     * a notification event occurs.
     * Ranking objects aren't automatically updated as notification events
     * occur. Instead, ranking information has to be retrieved again via the
     * current {@link RankingMap}.
     */
    public static class Ranking {
        private final String mKey;
        private final int mRank;
        private final boolean mIsAmbient;
        private final boolean mIsInterceptedByDnd;
        private String mKey;
        private int mRank = -1;
        private boolean mIsAmbient;
        private boolean mMeetsInterruptionFilter;

        private Ranking(String key, int rank, boolean isAmbient, boolean isInterceptedByDnd) {
            mKey = key;
            mRank = rank;
            mIsAmbient = isAmbient;
            mIsInterceptedByDnd = isInterceptedByDnd;
        }
        public Ranking() {}

        /**
         * Returns the key of the notification this Ranking applies to.
@@ -459,11 +450,19 @@ public abstract class NotificationListenerService extends Service {
        }

        /**
         * Returns whether the notification was intercepted by
         * &quot;Do not disturb&quot;.
         * Returns whether the notification meets the user's interruption
         * filter.
         */
        public boolean isInterceptedByDoNotDisturb() {
            return mIsInterceptedByDnd;
        public boolean meetsInterruptionFilter() {
            return mMeetsInterruptionFilter;
        }

        private void populate(String key, int rank, boolean isAmbient,
                boolean meetsInterruptionFilter) {
            mKey = key;
            mRank = rank;
            mIsAmbient = isAmbient;
            mMeetsInterruptionFilter = meetsInterruptionFilter;
        }
    }

@@ -477,12 +476,9 @@ public abstract class NotificationListenerService extends Service {
     */
    public static class RankingMap implements Parcelable {
        private final NotificationRankingUpdate mRankingUpdate;
        private final ArrayMap<String, Ranking> mRankingCache;
        private boolean mRankingCacheInitialized;

        private RankingMap(NotificationRankingUpdate rankingUpdate) {
            mRankingUpdate = rankingUpdate;
            mRankingCache = new ArrayMap<>(rankingUpdate.getOrderedKeys().length);
        }

        /**
@@ -496,37 +492,42 @@ public abstract class NotificationListenerService extends Service {
        }

        /**
         * Returns the Ranking for the notification with the given key.
         * Populates outRanking with ranking information for the notification
         * with the given key.
         *
         * @return the Ranking of the notification with the given key;
         *     <code>null</code> when the key is unknown.
         * @return true if a valid key has been passed and outRanking has
         *     been populated; false otherwise
         */
        public Ranking getRanking(String key) {
            synchronized (mRankingCache) {
                if (!mRankingCacheInitialized) {
                    initializeRankingCache();
                    mRankingCacheInitialized = true;
                }
            }
            return mRankingCache.get(key);
        public boolean getRanking(String key, Ranking outRanking) {
            int rank = getRank(key);
            outRanking.populate(key, rank, isAmbient(key), !isIntercepted(key));
            return rank >= 0;
        }

        private void initializeRankingCache() {
        private int getRank(String key) {
            // TODO: Optimize.
            String[] orderedKeys = mRankingUpdate.getOrderedKeys();
            int firstAmbientIndex = mRankingUpdate.getFirstAmbientIndex();
            for (int i = 0; i < orderedKeys.length; i++) {
                String key = orderedKeys[i];
                boolean isAmbient = firstAmbientIndex > -1 && firstAmbientIndex <= i;
                boolean isInterceptedByDnd = false;
                // TODO: Optimize.
                for (String s : mRankingUpdate.getDndInterceptedKeys()) {
                    if (s.equals(key)) {
                        isInterceptedByDnd = true;
                        break;
                if (orderedKeys[i].equals(key)) {
                    return i;
                }
            }
            return -1;
        }

        private boolean isAmbient(String key) {
            int rank = getRank(key);
            return rank >= 0 && rank >= mRankingUpdate.getFirstAmbientIndex();
        }

        private boolean isIntercepted(String key) {
            // TODO: Optimize.
            for (String interceptedKey : mRankingUpdate.getInterceptedKeys()) {
                if (interceptedKey.equals(key)) {
                    return true;
                }
                mRankingCache.put(key, new Ranking(key, i, isAmbient, isInterceptedByDnd));
            }
            return false;
        }

        // ----------- Parcelable
+7 −7
Original line number Diff line number Diff line
@@ -24,20 +24,20 @@ import android.os.Parcelable;
public class NotificationRankingUpdate implements Parcelable {
    // TODO: Support incremental updates.
    private final String[] mKeys;
    private final String[] mDndInterceptedKeys;
    private final String[] mInterceptedKeys;
    private final int mFirstAmbientIndex;

    public NotificationRankingUpdate(String[] keys, String[] dndInterceptedKeys,
    public NotificationRankingUpdate(String[] keys, String[] interceptedKeys,
                                     int firstAmbientIndex) {
        mKeys = keys;
        mFirstAmbientIndex = firstAmbientIndex;
        mDndInterceptedKeys = dndInterceptedKeys;
        mInterceptedKeys = interceptedKeys;
    }

    public NotificationRankingUpdate(Parcel in) {
        mKeys = in.readStringArray();
        mFirstAmbientIndex = in.readInt();
        mDndInterceptedKeys = in.readStringArray();
        mInterceptedKeys = in.readStringArray();
    }

    @Override
@@ -49,7 +49,7 @@ public class NotificationRankingUpdate implements Parcelable {
    public void writeToParcel(Parcel out, int flags) {
        out.writeStringArray(mKeys);
        out.writeInt(mFirstAmbientIndex);
        out.writeStringArray(mDndInterceptedKeys);
        out.writeStringArray(mInterceptedKeys);
    }

    public static final Parcelable.Creator<NotificationRankingUpdate> CREATOR
@@ -71,7 +71,7 @@ public class NotificationRankingUpdate implements Parcelable {
        return mFirstAmbientIndex;
    }

    public String[] getDndInterceptedKeys() {
        return mDndInterceptedKeys;
    public String[] getInterceptedKeys() {
        return mInterceptedKeys;
    }
}
+13 −11
Original line number Diff line number Diff line
@@ -75,16 +75,18 @@ public class NotificationData {
    }

    private final ArrayList<Entry> mEntries = new ArrayList<Entry>();
    private RankingMap mRanking;
    private RankingMap mRankingMap;
    private final Ranking mTmpRanking = new Ranking();
    private final Comparator<Entry> mRankingComparator = new Comparator<Entry>() {
        private final Ranking mRankingA = new Ranking();
        private final Ranking mRankingB = new Ranking();

        @Override
        public int compare(Entry a, Entry b) {
            if (mRanking != null) {
                Ranking aRanking = mRanking.getRanking(a.key);
                Ranking bRanking = mRanking.getRanking(b.key);
                int aRank = aRanking != null ? aRanking.getRank() : -1;
                int bRank = bRanking != null ? bRanking.getRank() : -1;
                return aRank - bRank;
            if (mRankingMap != null) {
                mRankingMap.getRanking(a.key, mRankingA);
                mRankingMap.getRanking(b.key, mRankingB);
                return mRankingA.getRank() - mRankingB.getRank();
            }

            final StatusBarNotification na = a.notification;
@@ -138,7 +140,7 @@ public class NotificationData {

    public boolean isAmbient(String key) {
        // TODO: Remove when switching to NotificationListener.
        if (mRanking == null) {
        if (mRankingMap == null) {
            for (Entry entry : mEntries) {
                if (key.equals(entry.key)) {
                    return entry.notification.getNotification().priority ==
@@ -146,15 +148,15 @@ public class NotificationData {
                }
            }
        } else {
            Ranking ranking = mRanking.getRanking(key);
            return ranking != null && ranking.isAmbient();
            mRankingMap.getRanking(key, mTmpRanking);
            return mTmpRanking.isAmbient();
        }
        return false;
    }

    private void updateRankingAndSort(RankingMap ranking) {
        if (ranking != null) {
            mRanking = ranking;
            mRankingMap = ranking;
        }
        Collections.sort(mEntries, mRankingComparator);
    }
+4 −4
Original line number Diff line number Diff line
@@ -2301,7 +2301,7 @@ public class NotificationManagerService extends SystemService {
        int speedBumpIndex = -1;
        final int N = mNotificationList.size();
        ArrayList<String> keys = new ArrayList<String>(N);
        ArrayList<String> dndKeys = new ArrayList<String>(N);
        ArrayList<String> interceptedKeys = new ArrayList<String>(N);
        for (int i = 0; i < N; i++) {
            NotificationRecord record = mNotificationList.get(i);
            if (!info.enabledAndUserMatches(record.sbn.getUserId())) {
@@ -2309,7 +2309,7 @@ public class NotificationManagerService extends SystemService {
            }
            keys.add(record.sbn.getKey());
            if (record.isIntercepted()) {
                dndKeys.add(record.sbn.getKey());
                interceptedKeys.add(record.sbn.getKey());
            }
            if (speedBumpIndex == -1 &&
                    record.sbn.getNotification().priority == Notification.PRIORITY_MIN) {
@@ -2317,8 +2317,8 @@ public class NotificationManagerService extends SystemService {
            }
        }
        String[] keysAr = keys.toArray(new String[keys.size()]);
        String[] dndKeysAr = dndKeys.toArray(new String[dndKeys.size()]);
        return new NotificationRankingUpdate(keysAr, dndKeysAr, speedBumpIndex);
        String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
        return new NotificationRankingUpdate(keysAr, interceptedKeysAr, speedBumpIndex);
    }

    public class NotificationListeners extends ManagedServices {