blob: 7d83e90eda9d272be00a88dc52a1cb9ffcebde21 [file] [log] [blame]
/*
* Copyright (C) 2023 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.display.notifications;
import static android.app.Notification.COLOR_DEFAULT;
import static com.android.internal.notification.SystemNotificationChannels.ALERTS;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
import android.content.res.Resources;
import android.util.Slog;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.feature.DisplayManagerFlags;
/**
* Manages notifications for {@link com.android.server.display.DisplayManagerService}.
*/
public class DisplayNotificationManager {
/** Dependency injection interface for {@link DisplayNotificationManager} */
public interface Injector {
/** Get {@link NotificationManager} service or null if not available. */
@Nullable
NotificationManager getNotificationManager();
}
private static final String TAG = "DisplayNotificationManager";
private static final String NOTIFICATION_GROUP_NAME = TAG;
private static final String DISPLAY_NOTIFICATION_TAG = TAG;
private static final int DISPLAY_NOTIFICATION_ID = 1;
private static final long NOTIFICATION_TIMEOUT_MILLISEC = 30000L;
private final Injector mInjector;
private final Context mContext;
private final boolean mConnectedDisplayErrorHandlingEnabled;
private NotificationManager mNotificationManager;
public DisplayNotificationManager(final DisplayManagerFlags flags, final Context context) {
this(flags, context, () -> context.getSystemService(NotificationManager.class));
}
@VisibleForTesting
DisplayNotificationManager(final DisplayManagerFlags flags, final Context context,
final Injector injector) {
mConnectedDisplayErrorHandlingEnabled = flags.isConnectedDisplayErrorHandlingEnabled();
mContext = context;
mInjector = injector;
}
/**
* Initialize services, which may be not yet published during boot.
* see {@link android.os.ServiceManager.ServiceNotFoundException}.
*/
public void onBootCompleted() {
mNotificationManager = mInjector.getNotificationManager();
if (mNotificationManager == null) {
Slog.e(TAG, "onBootCompleted: NotificationManager is null");
return;
}
}
/**
* Send notification about hotplug connection error.
*/
public void onHotplugConnectionError() {
if (!mConnectedDisplayErrorHandlingEnabled) {
Slog.d(TAG, "onHotplugConnectionError:"
+ " mConnectedDisplayErrorHandlingEnabled is false");
return;
}
sendErrorNotification(createErrorNotification(
R.string.connected_display_unavailable_notification_title,
R.string.connected_display_unavailable_notification_content));
}
/**
* Cancel sent notifications.
*/
public void cancelNotifications() {
if (mNotificationManager == null) {
Slog.e(TAG, "Can't cancelNotifications: NotificationManager is null");
return;
}
mNotificationManager.cancel(DISPLAY_NOTIFICATION_TAG, DISPLAY_NOTIFICATION_ID);
}
/**
* Send generic error notification.
*/
@SuppressLint("AndroidFrameworkRequiresPermission")
private void sendErrorNotification(final Notification notification) {
if (mNotificationManager == null) {
Slog.e(TAG, "Can't sendErrorNotification: NotificationManager is null");
return;
}
mNotificationManager.notify(DISPLAY_NOTIFICATION_TAG, DISPLAY_NOTIFICATION_ID,
notification);
}
/**
* @return a newly built notification about an issue with connected display.
*/
private Notification createErrorNotification(final int titleId, final int messageId) {
final Resources resources = mContext.getResources();
final CharSequence title = resources.getText(titleId);
final CharSequence message = resources.getText(messageId);
int color = COLOR_DEFAULT;
try (var attrs = mContext.obtainStyledAttributes(new int[]{R.attr.colorError})) {
color = attrs.getColor(0, color);
} catch (Resources.NotFoundException e) {
Slog.e(TAG, "colorError attribute is not found: " + e.getMessage());
}
return new Notification.Builder(mContext, ALERTS)
.setGroup(NOTIFICATION_GROUP_NAME)
.setSmallIcon(R.drawable.usb_cable_unknown_issue)
.setWhen(0)
.setTimeoutAfter(NOTIFICATION_TIMEOUT_MILLISEC)
.setOngoing(false)
.setTicker(title)
.setColor(color)
.setContentTitle(title)
.setContentText(message)
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setCategory(Notification.CATEGORY_ERROR)
.build();
}
}