154bbef435ed857fc68941672799fc8001c101119Chris Wren/**
254bbef435ed857fc68941672799fc8001c101119Chris Wren * Copyright (c) 2014, The Android Open Source Project
354bbef435ed857fc68941672799fc8001c101119Chris Wren *
454bbef435ed857fc68941672799fc8001c101119Chris Wren * Licensed under the Apache License, Version 2.0 (the "License");
554bbef435ed857fc68941672799fc8001c101119Chris Wren * you may not use this file except in compliance with the License.
654bbef435ed857fc68941672799fc8001c101119Chris Wren * You may obtain a copy of the License at
754bbef435ed857fc68941672799fc8001c101119Chris Wren *
854bbef435ed857fc68941672799fc8001c101119Chris Wren *     http://www.apache.org/licenses/LICENSE-2.0
954bbef435ed857fc68941672799fc8001c101119Chris Wren *
1054bbef435ed857fc68941672799fc8001c101119Chris Wren * Unless required by applicable law or agreed to in writing, software
1154bbef435ed857fc68941672799fc8001c101119Chris Wren * distributed under the License is distributed on an "AS IS" BASIS,
1254bbef435ed857fc68941672799fc8001c101119Chris Wren * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1354bbef435ed857fc68941672799fc8001c101119Chris Wren * See the License for the specific language governing permissions and
1454bbef435ed857fc68941672799fc8001c101119Chris Wren * limitations under the License.
1554bbef435ed857fc68941672799fc8001c101119Chris Wren */
1654bbef435ed857fc68941672799fc8001c101119Chris Wrenpackage com.android.server.notification;
1754bbef435ed857fc68941672799fc8001c101119Chris Wren
1854bbef435ed857fc68941672799fc8001c101119Chris Wrenimport android.app.Notification;
1954bbef435ed857fc68941672799fc8001c101119Chris Wrenimport android.content.Context;
2054bbef435ed857fc68941672799fc8001c101119Chris Wrenimport android.os.Handler;
2154bbef435ed857fc68941672799fc8001c101119Chris Wrenimport android.os.Message;
2254bbef435ed857fc68941672799fc8001c101119Chris Wrenimport android.os.UserHandle;
233ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wrenimport android.service.notification.NotificationListenerService;
2454bbef435ed857fc68941672799fc8001c101119Chris Wrenimport android.text.TextUtils;
2554bbef435ed857fc68941672799fc8001c101119Chris Wrenimport android.util.ArrayMap;
263ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wrenimport android.util.ArraySet;
27cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studerimport android.util.Log;
2854bbef435ed857fc68941672799fc8001c101119Chris Wrenimport android.util.Slog;
2954bbef435ed857fc68941672799fc8001c101119Chris Wrenimport android.util.SparseIntArray;
3054bbef435ed857fc68941672799fc8001c101119Chris Wrenimport org.xmlpull.v1.XmlPullParser;
3154bbef435ed857fc68941672799fc8001c101119Chris Wrenimport org.xmlpull.v1.XmlPullParserException;
3254bbef435ed857fc68941672799fc8001c101119Chris Wrenimport org.xmlpull.v1.XmlSerializer;
3354bbef435ed857fc68941672799fc8001c101119Chris Wren
3454bbef435ed857fc68941672799fc8001c101119Chris Wrenimport java.io.IOException;
3554bbef435ed857fc68941672799fc8001c101119Chris Wrenimport java.io.PrintWriter;
3654bbef435ed857fc68941672799fc8001c101119Chris Wrenimport java.util.ArrayList;
3754bbef435ed857fc68941672799fc8001c101119Chris Wrenimport java.util.Collections;
383ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wrenimport java.util.Set;
3954bbef435ed857fc68941672799fc8001c101119Chris Wrenimport java.util.concurrent.TimeUnit;
4054bbef435ed857fc68941672799fc8001c101119Chris Wren
4154bbef435ed857fc68941672799fc8001c101119Chris Wrenpublic class RankingHelper implements RankingConfig {
4254bbef435ed857fc68941672799fc8001c101119Chris Wren    private static final String TAG = "RankingHelper";
4354bbef435ed857fc68941672799fc8001c101119Chris Wren    private static final boolean DEBUG = false;
4454bbef435ed857fc68941672799fc8001c101119Chris Wren
4554bbef435ed857fc68941672799fc8001c101119Chris Wren    private static final int XML_VERSION = 1;
4654bbef435ed857fc68941672799fc8001c101119Chris Wren
4754bbef435ed857fc68941672799fc8001c101119Chris Wren    private static final String TAG_RANKING = "ranking";
4854bbef435ed857fc68941672799fc8001c101119Chris Wren    private static final String TAG_PACKAGE = "package";
4954bbef435ed857fc68941672799fc8001c101119Chris Wren    private static final String ATT_VERSION = "version";
5054bbef435ed857fc68941672799fc8001c101119Chris Wren
5154bbef435ed857fc68941672799fc8001c101119Chris Wren    private static final String ATT_NAME = "name";
5254bbef435ed857fc68941672799fc8001c101119Chris Wren    private static final String ATT_UID = "uid";
5354bbef435ed857fc68941672799fc8001c101119Chris Wren    private static final String ATT_PRIORITY = "priority";
543ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren    private static final String ATT_VISIBILITY = "visibility";
5554bbef435ed857fc68941672799fc8001c101119Chris Wren
5654bbef435ed857fc68941672799fc8001c101119Chris Wren    private final NotificationSignalExtractor[] mSignalExtractors;
571031c974855ff4117a6d7866e664295786840319Chris Wren    private final NotificationComparator mPreliminaryComparator = new NotificationComparator();
58cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer    private final GlobalSortKeyComparator mFinalComparator = new GlobalSortKeyComparator();
5954bbef435ed857fc68941672799fc8001c101119Chris Wren
6054bbef435ed857fc68941672799fc8001c101119Chris Wren    // Package name to uid, to priority. Would be better as Table<String, Int, Int>
6154bbef435ed857fc68941672799fc8001c101119Chris Wren    private final ArrayMap<String, SparseIntArray> mPackagePriorities;
623ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren    private final ArrayMap<String, SparseIntArray> mPackageVisibilities;
631031c974855ff4117a6d7866e664295786840319Chris Wren    private final ArrayMap<String, NotificationRecord> mProxyByGroupTmp;
6454bbef435ed857fc68941672799fc8001c101119Chris Wren
6554bbef435ed857fc68941672799fc8001c101119Chris Wren    private final Context mContext;
6654bbef435ed857fc68941672799fc8001c101119Chris Wren    private final Handler mRankingHandler;
6754bbef435ed857fc68941672799fc8001c101119Chris Wren
6854bbef435ed857fc68941672799fc8001c101119Chris Wren    public RankingHelper(Context context, Handler rankingHandler, String[] extractorNames) {
6954bbef435ed857fc68941672799fc8001c101119Chris Wren        mContext = context;
7054bbef435ed857fc68941672799fc8001c101119Chris Wren        mRankingHandler = rankingHandler;
7154bbef435ed857fc68941672799fc8001c101119Chris Wren        mPackagePriorities = new ArrayMap<String, SparseIntArray>();
723ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren        mPackageVisibilities = new ArrayMap<String, SparseIntArray>();
7354bbef435ed857fc68941672799fc8001c101119Chris Wren
7454bbef435ed857fc68941672799fc8001c101119Chris Wren        final int N = extractorNames.length;
7554bbef435ed857fc68941672799fc8001c101119Chris Wren        mSignalExtractors = new NotificationSignalExtractor[N];
7654bbef435ed857fc68941672799fc8001c101119Chris Wren        for (int i = 0; i < N; i++) {
7754bbef435ed857fc68941672799fc8001c101119Chris Wren            try {
7854bbef435ed857fc68941672799fc8001c101119Chris Wren                Class<?> extractorClass = mContext.getClassLoader().loadClass(extractorNames[i]);
7954bbef435ed857fc68941672799fc8001c101119Chris Wren                NotificationSignalExtractor extractor =
8054bbef435ed857fc68941672799fc8001c101119Chris Wren                        (NotificationSignalExtractor) extractorClass.newInstance();
8154bbef435ed857fc68941672799fc8001c101119Chris Wren                extractor.initialize(mContext);
8254bbef435ed857fc68941672799fc8001c101119Chris Wren                extractor.setConfig(this);
8354bbef435ed857fc68941672799fc8001c101119Chris Wren                mSignalExtractors[i] = extractor;
8454bbef435ed857fc68941672799fc8001c101119Chris Wren            } catch (ClassNotFoundException e) {
8554bbef435ed857fc68941672799fc8001c101119Chris Wren                Slog.w(TAG, "Couldn't find extractor " + extractorNames[i] + ".", e);
8654bbef435ed857fc68941672799fc8001c101119Chris Wren            } catch (InstantiationException e) {
8754bbef435ed857fc68941672799fc8001c101119Chris Wren                Slog.w(TAG, "Couldn't instantiate extractor " + extractorNames[i] + ".", e);
8854bbef435ed857fc68941672799fc8001c101119Chris Wren            } catch (IllegalAccessException e) {
8954bbef435ed857fc68941672799fc8001c101119Chris Wren                Slog.w(TAG, "Problem accessing extractor " + extractorNames[i] + ".", e);
9054bbef435ed857fc68941672799fc8001c101119Chris Wren            }
9154bbef435ed857fc68941672799fc8001c101119Chris Wren        }
921031c974855ff4117a6d7866e664295786840319Chris Wren        mProxyByGroupTmp = new ArrayMap<String, NotificationRecord>();
9354bbef435ed857fc68941672799fc8001c101119Chris Wren    }
9454bbef435ed857fc68941672799fc8001c101119Chris Wren
952b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock    public <T extends NotificationSignalExtractor> T findExtractor(Class<T> extractorClass) {
962b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock        final int N = mSignalExtractors.length;
972b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock        for (int i = 0; i < N; i++) {
982b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock            final NotificationSignalExtractor extractor = mSignalExtractors[i];
992b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock            if (extractorClass.equals(extractor.getClass())) {
1002b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock                return (T) extractor;
1012b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock            }
1022b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock        }
1032b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock        return null;
1042b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock    }
1052b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock
10654bbef435ed857fc68941672799fc8001c101119Chris Wren    public void extractSignals(NotificationRecord r) {
10754bbef435ed857fc68941672799fc8001c101119Chris Wren        final int N = mSignalExtractors.length;
10854bbef435ed857fc68941672799fc8001c101119Chris Wren        for (int i = 0; i < N; i++) {
10954bbef435ed857fc68941672799fc8001c101119Chris Wren            NotificationSignalExtractor extractor = mSignalExtractors[i];
11054bbef435ed857fc68941672799fc8001c101119Chris Wren            try {
11154bbef435ed857fc68941672799fc8001c101119Chris Wren                RankingReconsideration recon = extractor.process(r);
11254bbef435ed857fc68941672799fc8001c101119Chris Wren                if (recon != null) {
11354bbef435ed857fc68941672799fc8001c101119Chris Wren                    Message m = Message.obtain(mRankingHandler,
11454bbef435ed857fc68941672799fc8001c101119Chris Wren                            NotificationManagerService.MESSAGE_RECONSIDER_RANKING, recon);
11554bbef435ed857fc68941672799fc8001c101119Chris Wren                    long delay = recon.getDelay(TimeUnit.MILLISECONDS);
11654bbef435ed857fc68941672799fc8001c101119Chris Wren                    mRankingHandler.sendMessageDelayed(m, delay);
11754bbef435ed857fc68941672799fc8001c101119Chris Wren                }
11854bbef435ed857fc68941672799fc8001c101119Chris Wren            } catch (Throwable t) {
11954bbef435ed857fc68941672799fc8001c101119Chris Wren                Slog.w(TAG, "NotificationSignalExtractor failed.", t);
12054bbef435ed857fc68941672799fc8001c101119Chris Wren            }
12154bbef435ed857fc68941672799fc8001c101119Chris Wren        }
12254bbef435ed857fc68941672799fc8001c101119Chris Wren    }
12354bbef435ed857fc68941672799fc8001c101119Chris Wren
12454bbef435ed857fc68941672799fc8001c101119Chris Wren    public void readXml(XmlPullParser parser) throws XmlPullParserException, IOException {
12554bbef435ed857fc68941672799fc8001c101119Chris Wren        int type = parser.getEventType();
12654bbef435ed857fc68941672799fc8001c101119Chris Wren        if (type != XmlPullParser.START_TAG) return;
12754bbef435ed857fc68941672799fc8001c101119Chris Wren        String tag = parser.getName();
12854bbef435ed857fc68941672799fc8001c101119Chris Wren        if (!TAG_RANKING.equals(tag)) return;
12954bbef435ed857fc68941672799fc8001c101119Chris Wren        mPackagePriorities.clear();
13054bbef435ed857fc68941672799fc8001c101119Chris Wren        final int version = safeInt(parser, ATT_VERSION, XML_VERSION);
13154bbef435ed857fc68941672799fc8001c101119Chris Wren        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
13254bbef435ed857fc68941672799fc8001c101119Chris Wren            tag = parser.getName();
13354bbef435ed857fc68941672799fc8001c101119Chris Wren            if (type == XmlPullParser.END_TAG && TAG_RANKING.equals(tag)) {
13454bbef435ed857fc68941672799fc8001c101119Chris Wren                return;
13554bbef435ed857fc68941672799fc8001c101119Chris Wren            }
13654bbef435ed857fc68941672799fc8001c101119Chris Wren            if (type == XmlPullParser.START_TAG) {
13754bbef435ed857fc68941672799fc8001c101119Chris Wren                if (TAG_PACKAGE.equals(tag)) {
13854bbef435ed857fc68941672799fc8001c101119Chris Wren                    int uid = safeInt(parser, ATT_UID, UserHandle.USER_ALL);
13954bbef435ed857fc68941672799fc8001c101119Chris Wren                    int priority = safeInt(parser, ATT_PRIORITY, Notification.PRIORITY_DEFAULT);
1403ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                    int vis = safeInt(parser, ATT_VISIBILITY,
1413ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                            NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
14254bbef435ed857fc68941672799fc8001c101119Chris Wren                    String name = parser.getAttributeValue(null, ATT_NAME);
14354bbef435ed857fc68941672799fc8001c101119Chris Wren
1443ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                    if (!TextUtils.isEmpty(name)) {
1453ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                        if (priority != Notification.PRIORITY_DEFAULT) {
1463ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                            SparseIntArray priorityByUid = mPackagePriorities.get(name);
1473ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                            if (priorityByUid == null) {
1483ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                                priorityByUid = new SparseIntArray();
1493ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                                mPackagePriorities.put(name, priorityByUid);
1503ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                            }
1513ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                            priorityByUid.put(uid, priority);
1523ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                        }
1533ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                        if (vis != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
1543ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                            SparseIntArray visibilityByUid = mPackageVisibilities.get(name);
1553ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                            if (visibilityByUid == null) {
1563ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                                visibilityByUid = new SparseIntArray();
1573ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                                mPackageVisibilities.put(name, visibilityByUid);
1583ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                            }
1593ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                            visibilityByUid.put(uid, vis);
16054bbef435ed857fc68941672799fc8001c101119Chris Wren                        }
16154bbef435ed857fc68941672799fc8001c101119Chris Wren                    }
16254bbef435ed857fc68941672799fc8001c101119Chris Wren                }
16354bbef435ed857fc68941672799fc8001c101119Chris Wren            }
16454bbef435ed857fc68941672799fc8001c101119Chris Wren        }
16554bbef435ed857fc68941672799fc8001c101119Chris Wren        throw new IllegalStateException("Failed to reach END_DOCUMENT");
16654bbef435ed857fc68941672799fc8001c101119Chris Wren    }
16754bbef435ed857fc68941672799fc8001c101119Chris Wren
16854bbef435ed857fc68941672799fc8001c101119Chris Wren    public void writeXml(XmlSerializer out) throws IOException {
16954bbef435ed857fc68941672799fc8001c101119Chris Wren        out.startTag(null, TAG_RANKING);
17054bbef435ed857fc68941672799fc8001c101119Chris Wren        out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION));
17154bbef435ed857fc68941672799fc8001c101119Chris Wren
1723ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren        final Set<String> packageNames = new ArraySet<>(mPackagePriorities.size()
1733ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                + mPackageVisibilities.size());
1743ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren        packageNames.addAll(mPackagePriorities.keySet());
1753ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren        packageNames.addAll(mPackageVisibilities.keySet());
1763ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren        final Set<Integer> packageUids = new ArraySet<>();
1773ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren        for (String packageName : packageNames) {
1783ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren            packageUids.clear();
1793ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren            SparseIntArray priorityByUid = mPackagePriorities.get(packageName);
1803ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren            SparseIntArray visibilityByUid = mPackageVisibilities.get(packageName);
1813ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren            if (priorityByUid != null) {
1823ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                final int M = priorityByUid.size();
1833ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                for (int j = 0; j < M; j++) {
1843ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                    packageUids.add(priorityByUid.keyAt(j));
1853ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                }
1863ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren            }
1873ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren            if (visibilityByUid != null) {
1883ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                final int M = visibilityByUid.size();
1893ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                for (int j = 0; j < M; j++) {
1903ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                    packageUids.add(visibilityByUid.keyAt(j));
1913ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                }
1923ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren            }
1933ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren            for (Integer uid : packageUids) {
19454bbef435ed857fc68941672799fc8001c101119Chris Wren                out.startTag(null, TAG_PACKAGE);
1953ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                out.attribute(null, ATT_NAME, packageName);
1963ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                if (priorityByUid != null) {
1973ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                    final int priority = priorityByUid.get(uid);
1983ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                    if (priority != Notification.PRIORITY_DEFAULT) {
1993ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                        out.attribute(null, ATT_PRIORITY, Integer.toString(priority));
2003ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                    }
2013ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                }
2023ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                if (visibilityByUid != null) {
2033ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                    final int visibility = visibilityByUid.get(uid);
2043ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                    if (visibility != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
2053ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                        out.attribute(null, ATT_VISIBILITY, Integer.toString(visibility));
2063ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                    }
2073ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                }
20854bbef435ed857fc68941672799fc8001c101119Chris Wren                out.attribute(null, ATT_UID, Integer.toString(uid));
20954bbef435ed857fc68941672799fc8001c101119Chris Wren                out.endTag(null, TAG_PACKAGE);
21054bbef435ed857fc68941672799fc8001c101119Chris Wren            }
21154bbef435ed857fc68941672799fc8001c101119Chris Wren        }
21254bbef435ed857fc68941672799fc8001c101119Chris Wren        out.endTag(null, TAG_RANKING);
21354bbef435ed857fc68941672799fc8001c101119Chris Wren    }
21454bbef435ed857fc68941672799fc8001c101119Chris Wren
21554bbef435ed857fc68941672799fc8001c101119Chris Wren    private void updateConfig() {
21654bbef435ed857fc68941672799fc8001c101119Chris Wren        final int N = mSignalExtractors.length;
21754bbef435ed857fc68941672799fc8001c101119Chris Wren        for (int i = 0; i < N; i++) {
21854bbef435ed857fc68941672799fc8001c101119Chris Wren            mSignalExtractors[i].setConfig(this);
21954bbef435ed857fc68941672799fc8001c101119Chris Wren        }
22054bbef435ed857fc68941672799fc8001c101119Chris Wren        mRankingHandler.sendEmptyMessage(NotificationManagerService.MESSAGE_RANKING_CONFIG_CHANGE);
22154bbef435ed857fc68941672799fc8001c101119Chris Wren    }
22254bbef435ed857fc68941672799fc8001c101119Chris Wren
22354bbef435ed857fc68941672799fc8001c101119Chris Wren    public void sort(ArrayList<NotificationRecord> notificationList) {
2241031c974855ff4117a6d7866e664295786840319Chris Wren        final int N = notificationList.size();
225cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer        // clear global sort keys
2261031c974855ff4117a6d7866e664295786840319Chris Wren        for (int i = N - 1; i >= 0; i--) {
227cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer            notificationList.get(i).setGlobalSortKey(null);
2281031c974855ff4117a6d7866e664295786840319Chris Wren        }
2291031c974855ff4117a6d7866e664295786840319Chris Wren
23085374052e3c359884b7a54266666243d049dca7eChristoph Studer        // rank each record individually
23185374052e3c359884b7a54266666243d049dca7eChristoph Studer        Collections.sort(notificationList, mPreliminaryComparator);
232cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer
233cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer        synchronized (mProxyByGroupTmp) {
234cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer            // record individual ranking result and nominate proxies for each group
235cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer            for (int i = N - 1; i >= 0; i--) {
236cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                final NotificationRecord record = notificationList.get(i);
237cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                record.setAuthoritativeRank(i);
238cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                final String groupKey = record.getGroupKey();
239cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                boolean isGroupSummary = record.getNotification().isGroupSummary();
240cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                if (isGroupSummary || !mProxyByGroupTmp.containsKey(groupKey)) {
241cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                    mProxyByGroupTmp.put(groupKey, record);
242cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                }
243cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer            }
244cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer            // assign global sort key:
245cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer            //   is_recently_intrusive:group_rank:is_group_summary:group_sort_key:rank
246cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer            for (int i = 0; i < N; i++) {
247cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                final NotificationRecord record = notificationList.get(i);
248cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                NotificationRecord groupProxy = mProxyByGroupTmp.get(record.getGroupKey());
249cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                String groupSortKey = record.getNotification().getSortKey();
250cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer
251cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                // We need to make sure the developer provided group sort key (gsk) is handled
252cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                // correctly:
253cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                //   gsk="" < gsk=non-null-string < gsk=null
254cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                //
255cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                // We enforce this by using different prefixes for these three cases.
256cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                String groupSortKeyPortion;
257cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                if (groupSortKey == null) {
258cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                    groupSortKeyPortion = "nsk";
259cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                } else if (groupSortKey.equals("")) {
260cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                    groupSortKeyPortion = "esk";
261cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                } else {
262cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                    groupSortKeyPortion = "gsk=" + groupSortKey;
263cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                }
264cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer
265cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                boolean isGroupSummary = record.getNotification().isGroupSummary();
266cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                record.setGlobalSortKey(
267cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                        String.format("intrsv=%c:grnk=0x%04x:gsmry=%c:%s:rnk=0x%04x",
268cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                        record.isRecentlyIntrusive() ? '0' : '1',
269cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                        groupProxy.getAuthoritativeRank(),
270cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                        isGroupSummary ? '0' : '1',
271cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                        groupSortKeyPortion,
272cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer                        record.getAuthoritativeRank()));
273cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer            }
274cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer            mProxyByGroupTmp.clear();
2751031c974855ff4117a6d7866e664295786840319Chris Wren        }
276cd4adf8b5ef9ac1f90fdddbb405404e173aedc87Christoph Studer
2771031c974855ff4117a6d7866e664295786840319Chris Wren        // Do a second ranking pass, using group proxies
2781031c974855ff4117a6d7866e664295786840319Chris Wren        Collections.sort(notificationList, mFinalComparator);
27954bbef435ed857fc68941672799fc8001c101119Chris Wren    }
28054bbef435ed857fc68941672799fc8001c101119Chris Wren
28154bbef435ed857fc68941672799fc8001c101119Chris Wren    public int indexOf(ArrayList<NotificationRecord> notificationList, NotificationRecord target) {
2821031c974855ff4117a6d7866e664295786840319Chris Wren        return Collections.binarySearch(notificationList, target, mFinalComparator);
28354bbef435ed857fc68941672799fc8001c101119Chris Wren    }
28454bbef435ed857fc68941672799fc8001c101119Chris Wren
28554bbef435ed857fc68941672799fc8001c101119Chris Wren    private static int safeInt(XmlPullParser parser, String att, int defValue) {
28654bbef435ed857fc68941672799fc8001c101119Chris Wren        final String val = parser.getAttributeValue(null, att);
28754bbef435ed857fc68941672799fc8001c101119Chris Wren        return tryParseInt(val, defValue);
28854bbef435ed857fc68941672799fc8001c101119Chris Wren    }
28954bbef435ed857fc68941672799fc8001c101119Chris Wren
29054bbef435ed857fc68941672799fc8001c101119Chris Wren    private static int tryParseInt(String value, int defValue) {
29154bbef435ed857fc68941672799fc8001c101119Chris Wren        if (TextUtils.isEmpty(value)) return defValue;
29254bbef435ed857fc68941672799fc8001c101119Chris Wren        try {
29354bbef435ed857fc68941672799fc8001c101119Chris Wren            return Integer.valueOf(value);
29454bbef435ed857fc68941672799fc8001c101119Chris Wren        } catch (NumberFormatException e) {
29554bbef435ed857fc68941672799fc8001c101119Chris Wren            return defValue;
29654bbef435ed857fc68941672799fc8001c101119Chris Wren        }
29754bbef435ed857fc68941672799fc8001c101119Chris Wren    }
29854bbef435ed857fc68941672799fc8001c101119Chris Wren
29954bbef435ed857fc68941672799fc8001c101119Chris Wren    @Override
30054bbef435ed857fc68941672799fc8001c101119Chris Wren    public int getPackagePriority(String packageName, int uid) {
30154bbef435ed857fc68941672799fc8001c101119Chris Wren        int priority = Notification.PRIORITY_DEFAULT;
30254bbef435ed857fc68941672799fc8001c101119Chris Wren        SparseIntArray priorityByUid = mPackagePriorities.get(packageName);
30354bbef435ed857fc68941672799fc8001c101119Chris Wren        if (priorityByUid != null) {
30454bbef435ed857fc68941672799fc8001c101119Chris Wren            priority = priorityByUid.get(uid, Notification.PRIORITY_DEFAULT);
30554bbef435ed857fc68941672799fc8001c101119Chris Wren        }
30654bbef435ed857fc68941672799fc8001c101119Chris Wren        return priority;
30754bbef435ed857fc68941672799fc8001c101119Chris Wren    }
30854bbef435ed857fc68941672799fc8001c101119Chris Wren
30954bbef435ed857fc68941672799fc8001c101119Chris Wren    @Override
31054bbef435ed857fc68941672799fc8001c101119Chris Wren    public void setPackagePriority(String packageName, int uid, int priority) {
31154bbef435ed857fc68941672799fc8001c101119Chris Wren        if (priority == getPackagePriority(packageName, uid)) {
31254bbef435ed857fc68941672799fc8001c101119Chris Wren            return;
31354bbef435ed857fc68941672799fc8001c101119Chris Wren        }
31454bbef435ed857fc68941672799fc8001c101119Chris Wren        SparseIntArray priorityByUid = mPackagePriorities.get(packageName);
31554bbef435ed857fc68941672799fc8001c101119Chris Wren        if (priorityByUid == null) {
31654bbef435ed857fc68941672799fc8001c101119Chris Wren            priorityByUid = new SparseIntArray();
31754bbef435ed857fc68941672799fc8001c101119Chris Wren            mPackagePriorities.put(packageName, priorityByUid);
31854bbef435ed857fc68941672799fc8001c101119Chris Wren        }
31954bbef435ed857fc68941672799fc8001c101119Chris Wren        priorityByUid.put(uid, priority);
32054bbef435ed857fc68941672799fc8001c101119Chris Wren        updateConfig();
32154bbef435ed857fc68941672799fc8001c101119Chris Wren    }
32254bbef435ed857fc68941672799fc8001c101119Chris Wren
3233ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren    @Override
3243ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren    public int getPackageVisibilityOverride(String packageName, int uid) {
3253ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren        int visibility = NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE;
3263ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren        SparseIntArray visibilityByUid = mPackageVisibilities.get(packageName);
3273ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren        if (visibilityByUid != null) {
3283ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren            visibility = visibilityByUid.get(uid,
3293ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren                    NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
3303ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren        }
3313ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren        return visibility;
3323ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren    }
3333ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren
3343ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren    @Override
3353ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren    public void setPackageVisibilityOverride(String packageName, int uid, int visibility) {
3363ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren        if (visibility == getPackageVisibilityOverride(packageName, uid)) {
3373ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren            return;
3383ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren        }
3393ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren        SparseIntArray visibilityByUid = mPackageVisibilities.get(packageName);
3403ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren        if (visibilityByUid == null) {
3413ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren            visibilityByUid = new SparseIntArray();
3423ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren            mPackageVisibilities.put(packageName, visibilityByUid);
3433ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren        }
3443ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren        visibilityByUid.put(uid, visibility);
3453ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren        updateConfig();
3463ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren    }
3473ad4e3a45bbe44129b14c4d391431e44f1e04f0cChris Wren
34854bbef435ed857fc68941672799fc8001c101119Chris Wren    public void dump(PrintWriter pw, String prefix, NotificationManagerService.DumpFilter filter) {
34954bbef435ed857fc68941672799fc8001c101119Chris Wren        if (filter == null) {
35054bbef435ed857fc68941672799fc8001c101119Chris Wren            final int N = mSignalExtractors.length;
35154bbef435ed857fc68941672799fc8001c101119Chris Wren            pw.print(prefix);
35254bbef435ed857fc68941672799fc8001c101119Chris Wren            pw.print("mSignalExtractors.length = ");
35354bbef435ed857fc68941672799fc8001c101119Chris Wren            pw.println(N);
35454bbef435ed857fc68941672799fc8001c101119Chris Wren            for (int i = 0; i < N; i++) {
35554bbef435ed857fc68941672799fc8001c101119Chris Wren                pw.print(prefix);
35654bbef435ed857fc68941672799fc8001c101119Chris Wren                pw.print("  ");
35754bbef435ed857fc68941672799fc8001c101119Chris Wren                pw.println(mSignalExtractors[i]);
35854bbef435ed857fc68941672799fc8001c101119Chris Wren            }
35954bbef435ed857fc68941672799fc8001c101119Chris Wren        }
36054bbef435ed857fc68941672799fc8001c101119Chris Wren        final int N = mPackagePriorities.size();
36154bbef435ed857fc68941672799fc8001c101119Chris Wren        if (filter == null) {
36254bbef435ed857fc68941672799fc8001c101119Chris Wren            pw.print(prefix);
36354bbef435ed857fc68941672799fc8001c101119Chris Wren            pw.println("package priorities:");
36454bbef435ed857fc68941672799fc8001c101119Chris Wren        }
36554bbef435ed857fc68941672799fc8001c101119Chris Wren        for (int i = 0; i < N; i++) {
36654bbef435ed857fc68941672799fc8001c101119Chris Wren            String name = mPackagePriorities.keyAt(i);
36754bbef435ed857fc68941672799fc8001c101119Chris Wren            if (filter == null || filter.matches(name)) {
36854bbef435ed857fc68941672799fc8001c101119Chris Wren                SparseIntArray priorityByUid = mPackagePriorities.get(name);
36954bbef435ed857fc68941672799fc8001c101119Chris Wren                final int M = priorityByUid.size();
37054bbef435ed857fc68941672799fc8001c101119Chris Wren                for (int j = 0; j < M; j++) {
37154bbef435ed857fc68941672799fc8001c101119Chris Wren                    int uid = priorityByUid.keyAt(j);
37254bbef435ed857fc68941672799fc8001c101119Chris Wren                    int priority = priorityByUid.get(uid);
37354bbef435ed857fc68941672799fc8001c101119Chris Wren                    pw.print(prefix);
37454bbef435ed857fc68941672799fc8001c101119Chris Wren                    pw.print("  ");
37554bbef435ed857fc68941672799fc8001c101119Chris Wren                    pw.print(name);
37654bbef435ed857fc68941672799fc8001c101119Chris Wren                    pw.print(" (");
37754bbef435ed857fc68941672799fc8001c101119Chris Wren                    pw.print(uid);
37854bbef435ed857fc68941672799fc8001c101119Chris Wren                    pw.print(") has priority: ");
37954bbef435ed857fc68941672799fc8001c101119Chris Wren                    pw.println(priority);
38054bbef435ed857fc68941672799fc8001c101119Chris Wren                }
38154bbef435ed857fc68941672799fc8001c101119Chris Wren            }
38254bbef435ed857fc68941672799fc8001c101119Chris Wren        }
38354bbef435ed857fc68941672799fc8001c101119Chris Wren    }
38454bbef435ed857fc68941672799fc8001c101119Chris Wren}
385