blob: 7ed3eac10d1117f179fa977f5a813d0f96af5547 [file] [log] [blame]
/*
* 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.server.wm;
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
import static android.content.Context.NOTIFICATION_SERVICE;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.provider.Settings.ACTION_MANAGE_OVERLAY_PERMISSION;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import com.android.internal.R;
import com.android.server.policy.IconUtilities;
/** Displays an ongoing notification for a process displaying an alert window */
class AlertWindowNotification {
private static final String CHANNEL_PREFIX = "com.android.server.wm.AlertWindowNotification - ";
private static final int NOTIFICATION_ID = 0;
private static int sNextRequestCode = 0;
private final int mRequestCode;
private final WindowManagerService mService;
private String mNotificationTag;
private final NotificationManager mNotificationManager;
private final String mPackageName;
private boolean mPosted;
private IconUtilities mIconUtilities;
AlertWindowNotification(WindowManagerService service, String packageName) {
mService = service;
mPackageName = packageName;
mNotificationManager =
(NotificationManager) mService.mContext.getSystemService(NOTIFICATION_SERVICE);
mNotificationTag = CHANNEL_PREFIX + mPackageName;
mRequestCode = sNextRequestCode++;
mIconUtilities = new IconUtilities(mService.mContext);
}
void post() {
// We can't create/post the notification while the window manager lock is held since it will
// end up calling into activity manager. So, we post a message to do it later.
mService.mH.post(this::onPostNotification);
}
/** Cancels the notification */
void cancel() {
// We can't call into NotificationManager with WM lock held since it might call into AM.
// So, we post a message to do it later.
mService.mH.post(this::onCancelNotification);
}
/** Don't call with the window manager lock held! */
private void onCancelNotification() {
if (!mPosted) {
// Notification isn't currently posted...
return;
}
mPosted = false;
mNotificationManager.cancel(mNotificationTag, NOTIFICATION_ID);
}
/** Don't call with the window manager lock held! */
private void onPostNotification() {
if (mPosted) {
// Notification already posted...
return;
}
mPosted = true;
final Context context = mService.mContext;
final PackageManager pm = context.getPackageManager();
final ApplicationInfo aInfo = getApplicationInfo(pm, mPackageName);
final String appName = (aInfo != null)
? pm.getApplicationLabel(aInfo).toString() : mPackageName;
createNotificationChannelIfNeeded(context, appName);
final String message = context.getString(R.string.alert_windows_notification_message,
appName);
final Notification.Builder builder = new Notification.Builder(context, mNotificationTag)
.setOngoing(true)
.setContentTitle(
context.getString(R.string.alert_windows_notification_title, appName))
.setContentText(message)
.setSmallIcon(R.drawable.alert_window_layer)
.setColor(context.getColor(R.color.system_notification_accent_color))
.setStyle(new Notification.BigTextStyle().bigText(message))
.setLocalOnly(true)
.setContentIntent(getContentIntent(context, mPackageName));
if (aInfo != null) {
final Drawable drawable = pm.getApplicationIcon(aInfo);
if (drawable != null) {
final Bitmap bitmap = mIconUtilities.createIconBitmap(drawable);
builder.setLargeIcon(bitmap);
}
}
mNotificationManager.notify(mNotificationTag, NOTIFICATION_ID, builder.build());
}
private PendingIntent getContentIntent(Context context, String packageName) {
final Intent intent = new Intent(ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.fromParts("package", packageName, null));
intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
// Calls into activity manager...
return PendingIntent.getActivity(context, mRequestCode, intent, FLAG_CANCEL_CURRENT);
}
private void createNotificationChannelIfNeeded(Context context, String appName) {
if (mNotificationManager.getNotificationChannel(mNotificationTag) != null) {
return;
}
final String nameChannel =
context.getString(R.string.alert_windows_notification_channel_name, appName);
final NotificationChannel channel =
new NotificationChannel(mNotificationTag, nameChannel, IMPORTANCE_MIN);
channel.enableLights(false);
channel.enableVibration(false);
mNotificationManager.createNotificationChannel(channel);
}
private ApplicationInfo getApplicationInfo(PackageManager pm, String packageName) {
try {
return pm.getApplicationInfo(packageName, 0);
} catch (PackageManager.NameNotFoundException e) {
return null;
}
}
}