RankingHelper.java revision cf7ed583080b6c958f5a02817110505bae2a17df
1/**
2 * Copyright (c) 2014, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.server.notification;
17
18import android.app.Notification;
19import android.content.Context;
20import android.os.Handler;
21import android.os.Message;
22import android.os.UserHandle;
23import android.text.TextUtils;
24import android.util.ArrayMap;
25import android.util.Log;
26import android.util.Slog;
27import android.util.SparseIntArray;
28import org.xmlpull.v1.XmlPullParser;
29import org.xmlpull.v1.XmlPullParserException;
30import org.xmlpull.v1.XmlSerializer;
31
32import java.io.IOException;
33import java.io.PrintWriter;
34import java.util.ArrayList;
35import java.util.Collections;
36import java.util.concurrent.TimeUnit;
37
38public class RankingHelper implements RankingConfig {
39    private static final String TAG = "RankingHelper";
40    private static final boolean DEBUG = false;
41
42    private static final int XML_VERSION = 1;
43
44    private static final String TAG_RANKING = "ranking";
45    private static final String TAG_PACKAGE = "package";
46    private static final String ATT_VERSION = "version";
47
48    private static final String ATT_NAME = "name";
49    private static final String ATT_UID = "uid";
50    private static final String ATT_PRIORITY = "priority";
51
52    private static final String VALUE_HIGH = "high";
53
54    private final NotificationSignalExtractor[] mSignalExtractors;
55    private final NotificationComparator mRankingComparator = new NotificationComparator();
56
57    // Package name to uid, to priority. Would be better as Table<String, Int, Int>
58    private final ArrayMap<String, SparseIntArray> mPackagePriorities;
59
60    private final Context mContext;
61    private final Handler mRankingHandler;
62
63    public RankingHelper(Context context, Handler rankingHandler, String[] extractorNames) {
64        mContext = context;
65        mRankingHandler = rankingHandler;
66        mPackagePriorities = new ArrayMap<String, SparseIntArray>();
67
68        final int N = extractorNames.length;
69        mSignalExtractors = new NotificationSignalExtractor[N];
70        for (int i = 0; i < N; i++) {
71            try {
72                Class<?> extractorClass = mContext.getClassLoader().loadClass(extractorNames[i]);
73                NotificationSignalExtractor extractor =
74                        (NotificationSignalExtractor) extractorClass.newInstance();
75                extractor.initialize(mContext);
76                extractor.setConfig(this);
77                mSignalExtractors[i] = extractor;
78            } catch (ClassNotFoundException e) {
79                Slog.w(TAG, "Couldn't find extractor " + extractorNames[i] + ".", e);
80            } catch (InstantiationException e) {
81                Slog.w(TAG, "Couldn't instantiate extractor " + extractorNames[i] + ".", e);
82            } catch (IllegalAccessException e) {
83                Slog.w(TAG, "Problem accessing extractor " + extractorNames[i] + ".", e);
84            }
85        }
86    }
87
88    public void extractSignals(NotificationRecord r) {
89        final int N = mSignalExtractors.length;
90        for (int i = 0; i < N; i++) {
91            NotificationSignalExtractor extractor = mSignalExtractors[i];
92            try {
93                RankingReconsideration recon = extractor.process(r);
94                if (recon != null) {
95                    Message m = Message.obtain(mRankingHandler,
96                            NotificationManagerService.MESSAGE_RECONSIDER_RANKING, recon);
97                    long delay = recon.getDelay(TimeUnit.MILLISECONDS);
98                    mRankingHandler.sendMessageDelayed(m, delay);
99                }
100            } catch (Throwable t) {
101                Slog.w(TAG, "NotificationSignalExtractor failed.", t);
102            }
103        }
104    }
105
106    public void readXml(XmlPullParser parser) throws XmlPullParserException, IOException {
107        int type = parser.getEventType();
108        if (type != XmlPullParser.START_TAG) return;
109        String tag = parser.getName();
110        if (!TAG_RANKING.equals(tag)) return;
111        mPackagePriorities.clear();
112        final int version = safeInt(parser, ATT_VERSION, XML_VERSION);
113        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
114            tag = parser.getName();
115            if (type == XmlPullParser.END_TAG && TAG_RANKING.equals(tag)) {
116                return;
117            }
118            if (type == XmlPullParser.START_TAG) {
119                if (TAG_PACKAGE.equals(tag)) {
120                    int uid = safeInt(parser, ATT_UID, UserHandle.USER_ALL);
121                    int priority = safeInt(parser, ATT_PRIORITY, Notification.PRIORITY_DEFAULT);
122                    String name = parser.getAttributeValue(null, ATT_NAME);
123
124                    if (!TextUtils.isEmpty(name) && priority != Notification.PRIORITY_DEFAULT) {
125                        SparseIntArray priorityByUid = mPackagePriorities.get(name);
126                        if (priorityByUid == null) {
127                            priorityByUid = new SparseIntArray();
128                            mPackagePriorities.put(name, priorityByUid);
129                        }
130                        priorityByUid.put(uid, priority);
131                    }
132                }
133            }
134        }
135        throw new IllegalStateException("Failed to reach END_DOCUMENT");
136    }
137
138    public void writeXml(XmlSerializer out) throws IOException {
139        out.startTag(null, TAG_RANKING);
140        out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION));
141
142        final int N = mPackagePriorities.size();
143        for (int i = 0; i < N; i ++) {
144            String name = mPackagePriorities.keyAt(i);
145            SparseIntArray priorityByUid = mPackagePriorities.get(name);
146            final int M = priorityByUid.size();
147            for (int j = 0; j < M; j++) {
148                int uid = priorityByUid.keyAt(j);
149                int priority = priorityByUid.get(uid);
150                out.startTag(null, TAG_PACKAGE);
151                out.attribute(null, ATT_NAME, name);
152                out.attribute(null, ATT_UID, Integer.toString(uid));
153                out.attribute(null, ATT_PRIORITY, Integer.toString(priority));
154                out.endTag(null, TAG_PACKAGE);
155            }
156        }
157        out.endTag(null, TAG_RANKING);
158    }
159
160    private void updateConfig() {
161        final int N = mSignalExtractors.length;
162        for (int i = 0; i < N; i++) {
163            mSignalExtractors[i].setConfig(this);
164        }
165        mRankingHandler.sendEmptyMessage(NotificationManagerService.MESSAGE_RANKING_CONFIG_CHANGE);
166    }
167
168    public void sort(ArrayList<NotificationRecord> notificationList) {
169        Collections.sort(notificationList, mRankingComparator);
170    }
171
172    public int indexOf(ArrayList<NotificationRecord> notificationList, NotificationRecord target) {
173        return Collections.binarySearch(notificationList, target, mRankingComparator);
174    }
175
176    private static int safeInt(XmlPullParser parser, String att, int defValue) {
177        final String val = parser.getAttributeValue(null, att);
178        return tryParseInt(val, defValue);
179    }
180
181    private static int tryParseInt(String value, int defValue) {
182        if (TextUtils.isEmpty(value)) return defValue;
183        try {
184            return Integer.valueOf(value);
185        } catch (NumberFormatException e) {
186            return defValue;
187        }
188    }
189
190    @Override
191    public int getPackagePriority(String packageName, int uid) {
192        int priority = Notification.PRIORITY_DEFAULT;
193        SparseIntArray priorityByUid = mPackagePriorities.get(packageName);
194        if (priorityByUid != null) {
195            priority = priorityByUid.get(uid, Notification.PRIORITY_DEFAULT);
196        }
197        return priority;
198    }
199
200    @Override
201    public void setPackagePriority(String packageName, int uid, int priority) {
202        if (priority == getPackagePriority(packageName, uid)) {
203            return;
204        }
205        SparseIntArray priorityByUid = mPackagePriorities.get(packageName);
206        if (priorityByUid == null) {
207            priorityByUid = new SparseIntArray();
208            mPackagePriorities.put(packageName, priorityByUid);
209        }
210        priorityByUid.put(uid, priority);
211        updateConfig();
212    }
213
214    public void dump(PrintWriter pw, String prefix, NotificationManagerService.DumpFilter filter) {
215        if (filter == null) {
216            final int N = mSignalExtractors.length;
217            pw.print(prefix);
218            pw.print("mSignalExtractors.length = ");
219            pw.println(N);
220            for (int i = 0; i < N; i++) {
221                pw.print(prefix);
222                pw.print("  ");
223                pw.println(mSignalExtractors[i]);
224            }
225        }
226        final int N = mPackagePriorities.size();
227        if (filter == null) {
228            pw.print(prefix);
229            pw.println("package priorities:");
230        }
231        for (int i = 0; i < N; i++) {
232            String name = mPackagePriorities.keyAt(i);
233            if (filter == null || filter.matches(name)) {
234                SparseIntArray priorityByUid = mPackagePriorities.get(name);
235                final int M = priorityByUid.size();
236                for (int j = 0; j < M; j++) {
237                    int uid = priorityByUid.keyAt(j);
238                    int priority = priorityByUid.get(uid);
239                    pw.print(prefix);
240                    pw.print("  ");
241                    pw.print(name);
242                    pw.print(" (");
243                    pw.print(uid);
244                    pw.print(") has priority: ");
245                    pw.println(priority);
246                }
247            }
248        }
249    }
250}
251