172f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds/*
272f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds * Copyright (C) 2016 The Android Open Source Project
372f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds *
472f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds * Licensed under the Apache License, Version 2.0 (the "License");
572f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds * you may not use this file except in compliance with the License.
672f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds * You may obtain a copy of the License at
772f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds *
872f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds *      http://www.apache.org/licenses/LICENSE-2.0
972f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds *
1072f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds * Unless required by applicable law or agreed to in writing, software
1172f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds * distributed under the License is distributed on an "AS IS" BASIS,
1272f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1372f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds * See the License for the specific language governing permissions and
1472f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds * limitations under the License.
1572f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds */
1672f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynoldspackage com.android.server.notification;
1772f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds
1872f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynoldsimport com.android.internal.annotations.VisibleForTesting;
19520df6e74a385bbdacf5858837485e1a887d02efJulia Reynoldsimport com.android.internal.logging.MetricsLogger;
20520df6e74a385bbdacf5858837485e1a887d02efJulia Reynoldsimport com.android.internal.logging.nano.MetricsProto;
2172f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds
2272f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynoldsimport org.xmlpull.v1.XmlPullParser;
2372f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynoldsimport org.xmlpull.v1.XmlPullParserException;
2472f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynoldsimport org.xmlpull.v1.XmlSerializer;
2572f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds
26520df6e74a385bbdacf5858837485e1a887d02efJulia Reynoldsimport android.annotation.NonNull;
2772f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynoldsimport android.app.AlarmManager;
28a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynoldsimport android.app.Notification;
2972f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynoldsimport android.app.PendingIntent;
3072f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynoldsimport android.content.BroadcastReceiver;
3172f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynoldsimport android.content.Context;
3272f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynoldsimport android.content.Intent;
3372f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynoldsimport android.content.IntentFilter;
3472f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynoldsimport android.net.Uri;
357967230de20aeb6993d8332347752c8e508769e4Julia Reynoldsimport android.os.Binder;
365098977b20699c2ed32a621524abd5a1b2ab7de4Julia Reynoldsimport android.os.SystemClock;
37b6c1f99bd96d2f38980f4473baf3aa908d059db2Julia Reynoldsimport android.os.UserHandle;
3872f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynoldsimport android.service.notification.StatusBarNotification;
3972f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynoldsimport android.util.ArrayMap;
4072f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynoldsimport android.util.Log;
4172f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynoldsimport android.util.Slog;
4272f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds
4372f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynoldsimport java.io.IOException;
4472f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynoldsimport java.io.PrintWriter;
456676dab3d5d875598e12a4491d573e6734d7e64bChris Wrenimport java.util.ArrayList;
466676dab3d5d875598e12a4491d573e6734d7e64bChris Wrenimport java.util.Collection;
476676dab3d5d875598e12a4491d573e6734d7e64bChris Wrenimport java.util.Collections;
4872f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynoldsimport java.util.Date;
49cf63ff1532e793560f62e1c75f3402b48b0f09baJulia Reynoldsimport java.util.List;
5072f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynoldsimport java.util.Map;
5172f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynoldsimport java.util.Objects;
5272f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynoldsimport java.util.Set;
5372f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds
5472f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds/**
5572f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds * NotificationManagerService helper for handling snoozed notifications.
5672f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds */
5772f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynoldspublic class SnoozeHelper {
5872f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    private static final String TAG = "SnoozeHelper";
5972f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
6072f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    private static final String INDENT = "    ";
6172f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds
6272f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    private static final String REPOST_ACTION = SnoozeHelper.class.getSimpleName() + ".EVALUATE";
6372f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    private static final int REQUEST_CODE_REPOST = 1;
6472f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    private static final String REPOST_SCHEME = "repost";
6572f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    private static final String EXTRA_KEY = "key";
6672f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    private static final String EXTRA_USER_ID = "userId";
6772f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds
6872f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    private final Context mContext;
6972f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    private AlarmManager mAm;
7072f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    private final ManagedServices.UserProfiles mUserProfiles;
7172f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds
7272f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    // User id : package name : notification key : record.
7372f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, NotificationRecord>>>
7472f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds            mSnoozedNotifications = new ArrayMap<>();
75b6c1f99bd96d2f38980f4473baf3aa908d059db2Julia Reynolds    // notification key : package.
76b6c1f99bd96d2f38980f4473baf3aa908d059db2Julia Reynolds    private ArrayMap<String, String> mPackages = new ArrayMap<>();
777967230de20aeb6993d8332347752c8e508769e4Julia Reynolds    // key : userId
787967230de20aeb6993d8332347752c8e508769e4Julia Reynolds    private ArrayMap<String, Integer> mUsers = new ArrayMap<>();
7972f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    private Callback mCallback;
8072f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds
8172f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    public SnoozeHelper(Context context, Callback callback,
8272f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds            ManagedServices.UserProfiles userProfiles) {
8372f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        mContext = context;
8472f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        IntentFilter filter = new IntentFilter(REPOST_ACTION);
8572f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        filter.addDataScheme(REPOST_SCHEME);
8672f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        mContext.registerReceiver(mBroadcastReceiver, filter);
8772f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        mAm = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
8872f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        mCallback = callback;
8972f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        mUserProfiles = userProfiles;
9072f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    }
9172f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds
9272f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    protected boolean isSnoozed(int userId, String pkg, String key) {
9372f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        return mSnoozedNotifications.containsKey(userId)
9472f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                && mSnoozedNotifications.get(userId).containsKey(pkg)
9572f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                && mSnoozedNotifications.get(userId).get(pkg).containsKey(key);
9672f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    }
9772f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds
986676dab3d5d875598e12a4491d573e6734d7e64bChris Wren    protected Collection<NotificationRecord> getSnoozed(int userId, String pkg) {
996676dab3d5d875598e12a4491d573e6734d7e64bChris Wren        if (mSnoozedNotifications.containsKey(userId)
1006676dab3d5d875598e12a4491d573e6734d7e64bChris Wren                && mSnoozedNotifications.get(userId).containsKey(pkg)) {
101a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds            return mSnoozedNotifications.get(userId).get(pkg).values();
1026676dab3d5d875598e12a4491d573e6734d7e64bChris Wren        }
1036676dab3d5d875598e12a4491d573e6734d7e64bChris Wren        return Collections.EMPTY_LIST;
1046676dab3d5d875598e12a4491d573e6734d7e64bChris Wren    }
1056676dab3d5d875598e12a4491d573e6734d7e64bChris Wren
106520df6e74a385bbdacf5858837485e1a887d02efJulia Reynolds    protected @NonNull List<NotificationRecord> getSnoozed() {
107cf63ff1532e793560f62e1c75f3402b48b0f09baJulia Reynolds        List<NotificationRecord> snoozedForUser = new ArrayList<>();
108cf63ff1532e793560f62e1c75f3402b48b0f09baJulia Reynolds        int[] userIds = mUserProfiles.getCurrentProfileIds();
109a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds        if (userIds != null) {
110a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds            final int N = userIds.length;
111a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds            for (int i = 0; i < N; i++) {
112a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds                final ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs =
113a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds                        mSnoozedNotifications.get(userIds[i]);
114a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds                if (snoozedPkgs != null) {
115a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds                    final int M = snoozedPkgs.size();
116a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds                    for (int j = 0; j < M; j++) {
117a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds                        final ArrayMap<String, NotificationRecord> records = snoozedPkgs.valueAt(j);
118a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds                        if (records != null) {
119a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds                            snoozedForUser.addAll(records.values());
120a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds                        }
121cf63ff1532e793560f62e1c75f3402b48b0f09baJulia Reynolds                    }
122cf63ff1532e793560f62e1c75f3402b48b0f09baJulia Reynolds                }
123cf63ff1532e793560f62e1c75f3402b48b0f09baJulia Reynolds            }
124cf63ff1532e793560f62e1c75f3402b48b0f09baJulia Reynolds        }
125cf63ff1532e793560f62e1c75f3402b48b0f09baJulia Reynolds        return snoozedForUser;
126cf63ff1532e793560f62e1c75f3402b48b0f09baJulia Reynolds    }
127cf63ff1532e793560f62e1c75f3402b48b0f09baJulia Reynolds
12872f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    /**
129b6c1f99bd96d2f38980f4473baf3aa908d059db2Julia Reynolds     * Snoozes a notification and schedules an alarm to repost at that time.
13072f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds     */
1315098977b20699c2ed32a621524abd5a1b2ab7de4Julia Reynolds    protected void snooze(NotificationRecord record, long duration) {
1327967230de20aeb6993d8332347752c8e508769e4Julia Reynolds        snooze(record);
1335098977b20699c2ed32a621524abd5a1b2ab7de4Julia Reynolds        scheduleRepost(record.sbn.getPackageName(), record.getKey(), record.getUserId(), duration);
134b6c1f99bd96d2f38980f4473baf3aa908d059db2Julia Reynolds    }
135b6c1f99bd96d2f38980f4473baf3aa908d059db2Julia Reynolds
136b6c1f99bd96d2f38980f4473baf3aa908d059db2Julia Reynolds    /**
137b6c1f99bd96d2f38980f4473baf3aa908d059db2Julia Reynolds     * Records a snoozed notification.
138b6c1f99bd96d2f38980f4473baf3aa908d059db2Julia Reynolds     */
1397967230de20aeb6993d8332347752c8e508769e4Julia Reynolds    protected void snooze(NotificationRecord record) {
1407967230de20aeb6993d8332347752c8e508769e4Julia Reynolds        int userId = record.getUser().getIdentifier();
141b6c1f99bd96d2f38980f4473baf3aa908d059db2Julia Reynolds        if (DEBUG) {
142b6c1f99bd96d2f38980f4473baf3aa908d059db2Julia Reynolds            Slog.d(TAG, "Snoozing " + record.getKey());
143b6c1f99bd96d2f38980f4473baf3aa908d059db2Julia Reynolds        }
14472f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        ArrayMap<String, ArrayMap<String, NotificationRecord>> records =
14572f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                mSnoozedNotifications.get(userId);
14672f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        if (records == null) {
14772f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds            records = new ArrayMap<>();
14872f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        }
14972f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        ArrayMap<String, NotificationRecord> pkgRecords = records.get(record.sbn.getPackageName());
15072f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        if (pkgRecords == null) {
15172f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds            pkgRecords = new ArrayMap<>();
15272f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        }
15372f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        pkgRecords.put(record.getKey(), record);
15472f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        records.put(record.sbn.getPackageName(), pkgRecords);
15572f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        mSnoozedNotifications.put(userId, records);
156b6c1f99bd96d2f38980f4473baf3aa908d059db2Julia Reynolds        mPackages.put(record.getKey(), record.sbn.getPackageName());
1577967230de20aeb6993d8332347752c8e508769e4Julia Reynolds        mUsers.put(record.getKey(), userId);
15872f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    }
15972f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds
16072f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    protected boolean cancel(int userId, String pkg, String tag, int id) {
16172f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        if (mSnoozedNotifications.containsKey(userId)) {
16272f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds            ArrayMap<String, NotificationRecord> recordsForPkg =
16372f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                    mSnoozedNotifications.get(userId).get(pkg);
16472f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds            if (recordsForPkg != null) {
16572f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                final Set<Map.Entry<String, NotificationRecord>> records = recordsForPkg.entrySet();
16672f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                String key = null;
16772f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                for (Map.Entry<String, NotificationRecord> record : records) {
16872f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                    final StatusBarNotification sbn = record.getValue().sbn;
16972f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                    if (Objects.equals(sbn.getTag(), tag) && sbn.getId() == id) {
170a8b766fb73641ab67194a54391305bdc9cfc1cddJulia Reynolds                        record.getValue().isCanceled = true;
171a8b766fb73641ab67194a54391305bdc9cfc1cddJulia Reynolds                        return true;
17272f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                    }
17372f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                }
17472f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds            }
17572f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        }
17672f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        return false;
17772f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    }
17872f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds
17972f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    protected boolean cancel(int userId, boolean includeCurrentProfiles) {
18072f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        int[] userIds = {userId};
18172f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        if (includeCurrentProfiles) {
18272f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds            userIds = mUserProfiles.getCurrentProfileIds();
18372f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        }
18472f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        final int N = userIds.length;
18572f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        for (int i = 0; i < N; i++) {
18672f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds            final ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs =
187a8b766fb73641ab67194a54391305bdc9cfc1cddJulia Reynolds                    mSnoozedNotifications.get(userIds[i]);
18872f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds            if (snoozedPkgs != null) {
18972f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                final int M = snoozedPkgs.size();
19072f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                for (int j = 0; j < M; j++) {
19172f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                    final ArrayMap<String, NotificationRecord> records = snoozedPkgs.valueAt(j);
19272f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                    if (records != null) {
19372f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                        int P = records.size();
19472f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                        for (int k = 0; k < P; k++) {
195a8b766fb73641ab67194a54391305bdc9cfc1cddJulia Reynolds                            records.valueAt(k).isCanceled = true;
19672f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                        }
19772f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                    }
19872f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                }
19972f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                return true;
20072f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds            }
20172f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        }
20272f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        return false;
20372f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    }
20472f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds
20572f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    protected boolean cancel(int userId, String pkg) {
20672f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        if (mSnoozedNotifications.containsKey(userId)) {
20772f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds            if (mSnoozedNotifications.get(userId).containsKey(pkg)) {
20872f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                ArrayMap<String, NotificationRecord> records =
209a8b766fb73641ab67194a54391305bdc9cfc1cddJulia Reynolds                        mSnoozedNotifications.get(userId).get(pkg);
21072f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                int N = records.size();
21172f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                for (int i = 0; i < N; i++) {
212a8b766fb73641ab67194a54391305bdc9cfc1cddJulia Reynolds                    records.valueAt(i).isCanceled = true;
21372f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                }
21472f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                return true;
21572f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds            }
21672f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        }
21772f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        return false;
21872f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    }
21972f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds
22072f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    /**
22172f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds     * Updates the notification record so the most up to date information is shown on re-post.
22272f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds     */
22372f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    protected void update(int userId, NotificationRecord record) {
22472f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        ArrayMap<String, ArrayMap<String, NotificationRecord>> records =
22572f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                mSnoozedNotifications.get(userId);
22672f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        if (records == null) {
22772f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds            return;
22872f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        }
22972f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        ArrayMap<String, NotificationRecord> pkgRecords = records.get(record.sbn.getPackageName());
23072f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        if (pkgRecords == null) {
23172f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds            return;
23272f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        }
233a8b766fb73641ab67194a54391305bdc9cfc1cddJulia Reynolds        NotificationRecord existing = pkgRecords.get(record.getKey());
234a8b766fb73641ab67194a54391305bdc9cfc1cddJulia Reynolds        if (existing != null && existing.isCanceled) {
235a8b766fb73641ab67194a54391305bdc9cfc1cddJulia Reynolds            return;
236a8b766fb73641ab67194a54391305bdc9cfc1cddJulia Reynolds        }
23772f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        pkgRecords.put(record.getKey(), record);
23872f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    }
23972f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds
2407967230de20aeb6993d8332347752c8e508769e4Julia Reynolds    protected void repost(String key) {
2417967230de20aeb6993d8332347752c8e508769e4Julia Reynolds        Integer userId = mUsers.get(key);
2427967230de20aeb6993d8332347752c8e508769e4Julia Reynolds        if (userId != null) {
2437967230de20aeb6993d8332347752c8e508769e4Julia Reynolds            repost(key, userId);
2447967230de20aeb6993d8332347752c8e508769e4Julia Reynolds        }
2457967230de20aeb6993d8332347752c8e508769e4Julia Reynolds    }
2467967230de20aeb6993d8332347752c8e508769e4Julia Reynolds
247b6c1f99bd96d2f38980f4473baf3aa908d059db2Julia Reynolds    protected void repost(String key, int userId) {
248b6c1f99bd96d2f38980f4473baf3aa908d059db2Julia Reynolds        final String pkg = mPackages.remove(key);
24972f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        ArrayMap<String, ArrayMap<String, NotificationRecord>> records =
25072f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                mSnoozedNotifications.get(userId);
25172f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        if (records == null) {
25272f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds            return;
25372f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        }
25472f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        ArrayMap<String, NotificationRecord> pkgRecords = records.get(pkg);
25572f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        if (pkgRecords == null) {
25672f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds            return;
25772f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        }
25872f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        final NotificationRecord record = pkgRecords.remove(key);
259a8b766fb73641ab67194a54391305bdc9cfc1cddJulia Reynolds        mPackages.remove(key);
260a8b766fb73641ab67194a54391305bdc9cfc1cddJulia Reynolds        mUsers.remove(key);
261b6c1f99bd96d2f38980f4473baf3aa908d059db2Julia Reynolds
262a8b766fb73641ab67194a54391305bdc9cfc1cddJulia Reynolds        if (record != null && !record.isCanceled) {
263520df6e74a385bbdacf5858837485e1a887d02efJulia Reynolds            MetricsLogger.action(record.getLogMaker()
264520df6e74a385bbdacf5858837485e1a887d02efJulia Reynolds                    .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED)
265520df6e74a385bbdacf5858837485e1a887d02efJulia Reynolds                    .setType(MetricsProto.MetricsEvent.TYPE_OPEN));
26672f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds            mCallback.repost(userId, record);
26772f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        }
26872f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    }
26972f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds
270a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds    protected void repostGroupSummary(String pkg, int userId, String groupKey) {
271a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds        if (mSnoozedNotifications.containsKey(userId)) {
272a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds            ArrayMap<String, ArrayMap<String, NotificationRecord>> keysByPackage
273a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds                    = mSnoozedNotifications.get(userId);
274a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds
275a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds            if (keysByPackage != null && keysByPackage.containsKey(pkg)) {
276a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds                ArrayMap<String, NotificationRecord> recordsByKey = keysByPackage.get(pkg);
277a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds
278a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds                if (recordsByKey != null) {
279a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds                    String groupSummaryKey = null;
280a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds                    int N = recordsByKey.size();
281a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds                    for (int i = 0; i < N; i++) {
282a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds                        final NotificationRecord potentialGroupSummary = recordsByKey.valueAt(i);
283a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds                        if (potentialGroupSummary.sbn.isGroup()
284a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds                                && potentialGroupSummary.getNotification().isGroupSummary()
285a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds                                && groupKey.equals(potentialGroupSummary.getGroupKey())) {
286a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds                            groupSummaryKey = potentialGroupSummary.getKey();
287a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds                            break;
288a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds                        }
289a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds                    }
290a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds
291a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds                    if (groupSummaryKey != null) {
292a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds                        NotificationRecord record = recordsByKey.remove(groupSummaryKey);
293a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds                        mPackages.remove(groupSummaryKey);
294a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds                        mUsers.remove(groupSummaryKey);
295a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds
296a8b766fb73641ab67194a54391305bdc9cfc1cddJulia Reynolds                        if (record != null && !record.isCanceled) {
297a8b766fb73641ab67194a54391305bdc9cfc1cddJulia Reynolds                            MetricsLogger.action(record.getLogMaker()
298a8b766fb73641ab67194a54391305bdc9cfc1cddJulia Reynolds                                    .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED)
299a8b766fb73641ab67194a54391305bdc9cfc1cddJulia Reynolds                                    .setType(MetricsProto.MetricsEvent.TYPE_OPEN));
300a8b766fb73641ab67194a54391305bdc9cfc1cddJulia Reynolds                            mCallback.repost(userId, record);
301a8b766fb73641ab67194a54391305bdc9cfc1cddJulia Reynolds                        }
302a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds                    }
303a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds                }
304a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds            }
305a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds        }
306a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds    }
307a78cdffd802ee7a30747ab369e6edd2358a86f20Julia Reynolds
30872f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    private PendingIntent createPendingIntent(String pkg, String key, int userId) {
30972f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        return PendingIntent.getBroadcast(mContext,
31072f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                REQUEST_CODE_REPOST,
31172f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                new Intent(REPOST_ACTION)
31272f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                        .setData(new Uri.Builder().scheme(REPOST_SCHEME).appendPath(key).build())
31372f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
31472f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                        .putExtra(EXTRA_KEY, key)
31572f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                        .putExtra(EXTRA_USER_ID, userId),
31672f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                PendingIntent.FLAG_UPDATE_CURRENT);
31772f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    }
31872f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds
3195098977b20699c2ed32a621524abd5a1b2ab7de4Julia Reynolds    private void scheduleRepost(String pkg, String key, int userId, long duration) {
3207967230de20aeb6993d8332347752c8e508769e4Julia Reynolds        long identity = Binder.clearCallingIdentity();
3217967230de20aeb6993d8332347752c8e508769e4Julia Reynolds        try {
3227967230de20aeb6993d8332347752c8e508769e4Julia Reynolds            final PendingIntent pi = createPendingIntent(pkg, key, userId);
3237967230de20aeb6993d8332347752c8e508769e4Julia Reynolds            mAm.cancel(pi);
3245098977b20699c2ed32a621524abd5a1b2ab7de4Julia Reynolds            long time = SystemClock.elapsedRealtime() + duration;
3257967230de20aeb6993d8332347752c8e508769e4Julia Reynolds            if (DEBUG) Slog.d(TAG, "Scheduling evaluate for " + new Date(time));
3265098977b20699c2ed32a621524abd5a1b2ab7de4Julia Reynolds            mAm.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, time, pi);
3277967230de20aeb6993d8332347752c8e508769e4Julia Reynolds        } finally {
3287967230de20aeb6993d8332347752c8e508769e4Julia Reynolds            Binder.restoreCallingIdentity(identity);
3297967230de20aeb6993d8332347752c8e508769e4Julia Reynolds        }
33072f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    }
33172f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds
33272f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    public void dump(PrintWriter pw, NotificationManagerService.DumpFilter filter) {
33372f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        pw.println("\n  Snoozed notifications:");
33472f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        for (int userId : mSnoozedNotifications.keySet()) {
33572f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds            pw.print(INDENT);
33672f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds            pw.println("user: " + userId);
33772f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds            ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs =
33872f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                    mSnoozedNotifications.get(userId);
33972f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds            for (String pkg : snoozedPkgs.keySet()) {
34072f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                pw.print(INDENT);
34172f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                pw.print(INDENT);
34272f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                pw.println("package: " + pkg);
34372f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                Set<String> snoozedKeys = snoozedPkgs.get(pkg).keySet();
34472f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                for (String key : snoozedKeys) {
34572f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                    pw.print(INDENT);
34672f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                    pw.print(INDENT);
34772f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                    pw.print(INDENT);
34872f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                    pw.println(key);
34972f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                }
35072f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds            }
35172f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        }
35272f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    }
35372f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds
35472f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    protected void writeXml(XmlSerializer out, boolean forBackup) throws IOException {
35572f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds
35672f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    }
35772f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds
35872f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    public void readXml(XmlPullParser parser, boolean forRestore)
35972f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds            throws XmlPullParserException, IOException {
36072f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds
36172f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    }
36272f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds
36372f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    @VisibleForTesting
36472f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    void setAlarmManager(AlarmManager am) {
36572f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        mAm = am;
36672f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    }
36772f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds
36872f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    protected interface Callback {
36972f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        void repost(int userId, NotificationRecord r);
37072f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    }
37172f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds
37272f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
37372f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        @Override
37472f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        public void onReceive(Context context, Intent intent) {
37572f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds            if (DEBUG) {
37672f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds                Slog.d(TAG, "Reposting notification");
37772f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds            }
37872f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds            if (REPOST_ACTION.equals(intent.getAction())) {
379b6c1f99bd96d2f38980f4473baf3aa908d059db2Julia Reynolds                repost(intent.getStringExtra(EXTRA_KEY), intent.getIntExtra(EXTRA_USER_ID,
380b6c1f99bd96d2f38980f4473baf3aa908d059db2Julia Reynolds                        UserHandle.USER_SYSTEM));
38172f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds            }
38272f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds        }
38372f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds    };
38472f1cbb3b988d6daa61c45a69aa9f8024c029b06Julia Reynolds}
385