AlertWindowNotification.java revision af38fd7b0f4de5d4f2b0f750e9d2775c06c66b38
1/*
2 * Copyright (C) 2017 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 */
16
17package com.android.server.wm;
18
19import static android.app.NotificationManager.IMPORTANCE_MIN;
20import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
21import static android.content.Context.NOTIFICATION_SERVICE;
22import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
23import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
24import static android.provider.Settings.ACTION_MANAGE_OVERLAY_PERMISSION;
25
26import android.app.Notification;
27import android.app.NotificationChannel;
28import android.app.NotificationManager;
29import android.app.PendingIntent;
30import android.content.Context;
31
32import android.content.Intent;
33import android.content.pm.ApplicationInfo;
34import android.content.pm.PackageManager;
35import android.graphics.Bitmap;
36import android.graphics.drawable.Drawable;
37
38import android.net.Uri;
39import com.android.internal.R;
40import com.android.server.policy.IconUtilities;
41
42/** Displays an ongoing notification for a process displaying an alert window */
43class AlertWindowNotification {
44    private static final String CHANNEL_PREFIX = "com.android.server.wm.AlertWindowNotification - ";
45    private static final int NOTIFICATION_ID = 0;
46
47    private static int sNextRequestCode = 0;
48    private final int mRequestCode;
49    private final WindowManagerService mService;
50    private String mNotificationTag;
51    private final NotificationManager mNotificationManager;
52    private final String mPackageName;
53    private boolean mPosted;
54    private IconUtilities mIconUtilities;
55
56    AlertWindowNotification(WindowManagerService service, String packageName) {
57        mService = service;
58        mPackageName = packageName;
59        mNotificationManager =
60                (NotificationManager) mService.mContext.getSystemService(NOTIFICATION_SERVICE);
61        mNotificationTag = CHANNEL_PREFIX + mPackageName;
62        mRequestCode = sNextRequestCode++;
63        mIconUtilities = new IconUtilities(mService.mContext);
64    }
65
66    void post() {
67        // We can't create/post the notification while the window manager lock is held since it will
68        // end up calling into activity manager. So, we post a message to do it later.
69        mService.mH.post(this::onPostNotification);
70    }
71
72    /** Cancels the notification */
73    void cancel() {
74        // We can't call into NotificationManager with WM lock held since it might call into AM.
75        // So, we post a message to do it later.
76        mService.mH.post(this::onCancelNotification);
77    }
78
79    /** Don't call with the window manager lock held! */
80    private void onCancelNotification() {
81        if (!mPosted) {
82            // Notification isn't currently posted...
83            return;
84        }
85        mPosted = false;
86        mNotificationManager.cancel(mNotificationTag, NOTIFICATION_ID);
87    }
88
89    /** Don't call with the window manager lock held! */
90    private void onPostNotification() {
91        if (mPosted) {
92            // Notification already posted...
93            return;
94        }
95        mPosted = true;
96
97        final Context context = mService.mContext;
98        final PackageManager pm = context.getPackageManager();
99        final ApplicationInfo aInfo = getApplicationInfo(pm, mPackageName);
100        final String appName = (aInfo != null)
101                ? pm.getApplicationLabel(aInfo).toString() : mPackageName;
102
103        createNotificationChannelIfNeeded(context, appName);
104
105        final String message = context.getString(R.string.alert_windows_notification_message,
106                appName);
107        final Notification.Builder builder = new Notification.Builder(context, mNotificationTag)
108                .setOngoing(true)
109                .setContentTitle(
110                        context.getString(R.string.alert_windows_notification_title, appName))
111                .setContentText(message)
112                .setSmallIcon(R.drawable.alert_window_layer)
113                .setColor(context.getColor(R.color.system_notification_accent_color))
114                .setStyle(new Notification.BigTextStyle().bigText(message))
115                .setLocalOnly(true)
116                .setContentIntent(getContentIntent(context, mPackageName));
117
118        if (aInfo != null) {
119            final Drawable drawable = pm.getApplicationIcon(aInfo);
120            if (drawable != null) {
121                final Bitmap bitmap = mIconUtilities.createIconBitmap(drawable);
122                builder.setLargeIcon(bitmap);
123            }
124        }
125
126        mNotificationManager.notify(mNotificationTag, NOTIFICATION_ID, builder.build());
127    }
128
129    private PendingIntent getContentIntent(Context context, String packageName) {
130        final Intent intent = new Intent(ACTION_MANAGE_OVERLAY_PERMISSION,
131                Uri.fromParts("package", packageName, null));
132        intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
133        // Calls into activity manager...
134        return PendingIntent.getActivity(context, mRequestCode, intent, FLAG_CANCEL_CURRENT);
135    }
136
137    private void createNotificationChannelIfNeeded(Context context, String appName) {
138        if (mNotificationManager.getNotificationChannel(mNotificationTag) != null) {
139            return;
140        }
141        final String nameChannel =
142                context.getString(R.string.alert_windows_notification_channel_name, appName);
143        final NotificationChannel channel =
144                new NotificationChannel(mNotificationTag, nameChannel, IMPORTANCE_MIN);
145        channel.enableLights(false);
146        channel.enableVibration(false);
147        mNotificationManager.createNotificationChannel(channel);
148    }
149
150
151    private ApplicationInfo getApplicationInfo(PackageManager pm, String packageName) {
152        try {
153            return pm.getApplicationInfo(packageName, 0);
154        } catch (PackageManager.NameNotFoundException e) {
155            return null;
156        }
157    }
158}
159