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