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