summaryrefslogtreecommitdiffstats
path: root/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
blob: df0d7872bc6118d36b0744d3e5478108b11fb62a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the
 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

package com.android.systemui;

import android.annotation.Nullable;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
import android.util.SparseArray;

import com.android.internal.messages.nano.SystemMessageProto;

import javax.inject.Inject;
import javax.inject.Singleton;

/**
 * Tracks state of foreground services and notifications related to foreground services per user.
 */
@Singleton
public class ForegroundServiceController {

    private final SparseArray<ForegroundServicesUserState> mUserServices = new SparseArray<>();
    private final Object mMutex = new Object();

    @Inject
    public ForegroundServiceController() {
    }

    /**
     * @return true if this user has services missing notifications and therefore needs a
     * disclosure notification.
     */
    public boolean isDisclosureNeededForUser(int userId) {
        synchronized (mMutex) {
            final ForegroundServicesUserState services = mUserServices.get(userId);
            if (services == null) return false;
            return services.isDisclosureNeeded();
        }
    }

    /**
     * @return true if this user/pkg has a missing or custom layout notification and therefore needs
     * a disclosure notification for system alert windows.
     */
    public boolean isSystemAlertWarningNeeded(int userId, String pkg) {
        synchronized (mMutex) {
            final ForegroundServicesUserState services = mUserServices.get(userId);
            if (services == null) return false;
            return services.getStandardLayoutKey(pkg) == null;
        }
    }

    /**
     * Returns the key of the foreground service from this package using the standard template,
     * if one exists.
     */
    @Nullable
    public String getStandardLayoutKey(int userId, String pkg) {
        synchronized (mMutex) {
            final ForegroundServicesUserState services = mUserServices.get(userId);
            if (services == null) return null;
            return services.getStandardLayoutKey(pkg);
        }
    }

    /**
     * Gets active app ops for this user and package
     */
    @Nullable
    public ArraySet<Integer> getAppOps(int userId, String pkg) {
        synchronized (mMutex) {
            final ForegroundServicesUserState services = mUserServices.get(userId);
            if (services == null) {
                return null;
            }
            return services.getFeatures(pkg);
        }
    }

    /**
     * Records active app ops. App Ops are stored in FSC in addition to NotificationData in
     * case they change before we have a notification to tag.
     */
    public void onAppOpChanged(int code, int uid, String packageName, boolean active) {
        int userId = UserHandle.getUserId(uid);
        synchronized (mMutex) {
            ForegroundServicesUserState userServices = mUserServices.get(userId);
            if (userServices == null) {
                userServices = new ForegroundServicesUserState();
                mUserServices.put(userId, userServices);
            }
            if (active) {
                userServices.addOp(packageName, code);
            } else {
                userServices.removeOp(packageName, code);
            }
        }
    }

    /**
     * Looks up the {@link ForegroundServicesUserState} for the given {@code userId}, then performs
     * the given {@link UserStateUpdateCallback} on it.  If no state exists for the user ID, creates
     * a new one if {@code createIfNotFound} is true, then performs the update on the new state.
     * If {@code createIfNotFound} is false, no update is performed.
     *
     * @return false if no user state was found and none was created; true otherwise.
     */
    boolean updateUserState(int userId,
            UserStateUpdateCallback updateCallback,
            boolean createIfNotFound) {
        synchronized (mMutex) {
            ForegroundServicesUserState userState = mUserServices.get(userId);
            if (userState == null) {
                if (createIfNotFound) {
                    userState = new ForegroundServicesUserState();
                    mUserServices.put(userId, userState);
                } else {
                    return false;
                }
            }
            return updateCallback.updateUserState(userState);
        }
    }

    /**
     * @return true if {@code sbn} is the system-provided disclosure notification containing the
     * list of running foreground services.
     */
    public boolean isDisclosureNotification(StatusBarNotification sbn) {
        return sbn.getId() == SystemMessageProto.SystemMessage.NOTE_FOREGROUND_SERVICES
                && sbn.getTag() == null
                && sbn.getPackageName().equals("android");
    }

    /**
     * @return true if sbn is one of the window manager "drawing over other apps" notifications
     */
    public boolean isSystemAlertNotification(StatusBarNotification sbn) {
        return sbn.getPackageName().equals("android")
                && sbn.getTag() != null
                && sbn.getTag().contains("AlertWindowNotification");
    }

    /**
     * Callback provided to {@link #updateUserState(int, UserStateUpdateCallback, boolean)}
     * to perform the update.
     */
    interface UserStateUpdateCallback {
        /**
         * Perform update operations on the provided {@code userState}.
         *
         * @return true if the update succeeded.
         */
        boolean updateUserState(ForegroundServicesUserState userState);

        /** Called if the state was not found and was not created. */
        default void userStateNotFound(int userId) {
        }
    }
}