AlertWindowNotification.java revision d76881e002cf1e266b5080b05d1aee80fc760770
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 mCancelled;
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        // We can't create/post the notification while the window manager lock is held since it will
66        // end up calling into activity manager. So, we post a message to do it later.
67        mService.mH.post(this::onPostNotification);
68    }
69
70    /** Cancels the notification */
71    void cancel() {
72        // We can't call into NotificationManager with WM lock held since it might call into AM.
73        // So, we post a message to do it later.
74        mService.mH.post(this::onCancelNotification);
75    }
76
77    /** Don't call with the window manager lock held! */
78    private void onCancelNotification() {
79        mNotificationManager.cancel(mNotificationTag, NOTIFICATION_ID);
80        mCancelled = true;
81    }
82
83    /** Don't call with the window manager lock held! */
84    private void onPostNotification() {
85        if (mCancelled) {
86            // Notification was cancelled, so nothing more to do...
87            return;
88        }
89
90        final Context context = mService.mContext;
91        final PackageManager pm = context.getPackageManager();
92        final ApplicationInfo aInfo = getApplicationInfo(pm, mPackageName);
93        final String appName = (aInfo != null)
94                ? pm.getApplicationLabel(aInfo).toString() : mPackageName;
95
96        createNotificationChannelIfNeeded(context, appName);
97
98        final String message = context.getString(R.string.alert_windows_notification_message,
99                appName);
100        final Notification.Builder builder = new Notification.Builder(context, mNotificationTag)
101                .setOngoing(true)
102                .setContentTitle(
103                        context.getString(R.string.alert_windows_notification_title, appName))
104                .setContentText(message)
105                .setSmallIcon(R.drawable.alert_window_layer)
106                .setColor(context.getColor(R.color.system_notification_accent_color))
107                .setStyle(new Notification.BigTextStyle().bigText(message))
108                .setLocalOnly(true)
109                .setContentIntent(getContentIntent(context, mPackageName));
110
111        if (aInfo != null) {
112            final Drawable drawable = pm.getApplicationIcon(aInfo);
113            if (drawable != null) {
114                final Bitmap bitmap = mIconUtilities.createIconBitmap(drawable);
115                builder.setLargeIcon(bitmap);
116            }
117        }
118
119        mNotificationManager.notify(mNotificationTag, NOTIFICATION_ID, builder.build());
120    }
121
122    private PendingIntent getContentIntent(Context context, String packageName) {
123        final Intent intent = new Intent(ACTION_MANAGE_OVERLAY_PERMISSION,
124                Uri.fromParts("package", packageName, null));
125        intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
126        // Calls into activity manager...
127        return PendingIntent.getActivity(context, mRequestCode, intent, FLAG_CANCEL_CURRENT);
128    }
129
130    private void createNotificationChannelIfNeeded(Context context, String appName) {
131        if (mNotificationManager.getNotificationChannel(mNotificationTag) != null) {
132            return;
133        }
134        final String nameChannel =
135                context.getString(R.string.alert_windows_notification_channel_name, appName);
136        final NotificationChannel channel =
137                new NotificationChannel(mNotificationTag, nameChannel, IMPORTANCE_MIN);
138        channel.enableLights(false);
139        channel.enableVibration(false);
140        mNotificationManager.createNotificationChannel(channel);
141    }
142
143
144    private ApplicationInfo getApplicationInfo(PackageManager pm, String packageName) {
145        try {
146            return pm.getApplicationInfo(packageName, 0);
147        } catch (PackageManager.NameNotFoundException e) {
148            return null;
149        }
150    }
151}
152