blob: 59a9cc9fec47cb71e0b9f9d562fbc8da205cdff3 [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.safetycenter;
import static android.os.Build.VERSION_CODES.TIRAMISU;
import android.annotation.Nullable;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Binder;
import android.os.UserHandle;
import android.safetycenter.SafetySourceData;
import android.safetycenter.SafetySourceIssue;
import android.util.Log;
import androidx.annotation.RequiresApi;
import com.android.permission.util.UserUtils;
import com.android.safetycenter.resources.SafetyCenterResourcesContext;
import java.util.List;
/** Class responsible for creating and updating Safety Center's notification channels. */
@RequiresApi(TIRAMISU)
final class SafetyCenterNotificationChannels {
private static final String TAG = "SafetyCenterNC";
private static final String CHANNEL_GROUP_ID = "safety_center_channels";
private static final String CHANNEL_ID_INFORMATION = "safety_center_information";
private static final String CHANNEL_ID_RECOMMENDATION = "safety_center_recommendation";
private static final String CHANNEL_ID_CRITICAL_WARNING = "safety_center_critical_warning";
private final SafetyCenterResourcesContext mResourcesContext;
SafetyCenterNotificationChannels(SafetyCenterResourcesContext safetyCenterResourceContext) {
mResourcesContext = safetyCenterResourceContext;
}
/** Returns a {@link NotificationManager} which will send notifications to the given user. */
@Nullable
static NotificationManager getNotificationManagerForUser(
Context baseContext, UserHandle userHandle) {
Context contextAsUser = baseContext.createContextAsUser(userHandle, 0);
return contextAsUser.getSystemService(NotificationManager.class);
}
/**
* Returns the ID of the appropriate {@link NotificationChannel} for a notification about the
* given {@code issue} after ensuring that channel has been created.
*/
@Nullable
String createAndGetChannelId(NotificationManager notificationManager, SafetySourceIssue issue) {
try {
createAllChannelsWithoutCallingIdentity(notificationManager);
} catch (RuntimeException e) {
Log.w(TAG, "Unable to create notification channels", e);
return null;
}
return getChannelIdForIssue(issue);
}
/**
* Creates all Safety Center {@link NotificationChannel}s instances and their group, for all
* current users, dropping any calling identity so those channels can be unblockable. Throws a
* {@link RuntimeException} if any channel is malformed and could not be created.
*/
void createAllChannelsForAllUsers(Context context) {
List<UserHandle> users = UserUtils.getUserHandles(context);
for (int i = 0; i < users.size(); i++) {
try {
createAllChannelsWithoutCallingIdentity(
getNotificationManagerForUser(context, users.get(i)));
} catch (RuntimeException e) {
Log.w(TAG, "Unable to create notification channels", e);
}
}
}
@Nullable
private String getChannelIdForIssue(SafetySourceIssue issue) {
switch (issue.getSeverityLevel()) {
case SafetySourceData.SEVERITY_LEVEL_INFORMATION:
return CHANNEL_ID_INFORMATION;
case SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION:
return CHANNEL_ID_RECOMMENDATION;
case SafetySourceData.SEVERITY_LEVEL_CRITICAL_WARNING:
return CHANNEL_ID_CRITICAL_WARNING;
default:
Log.w(TAG, "No applicable notification channel for issue " + issue);
return null;
}
}
/**
* Creates all Safety Center {@link NotificationChannel}s instances and their group using the
* given {@link NotificationManager}, dropping any calling identity so those channels can be
* unblockable. Throws a {@link RuntimeException} if any channel is malformed and could not be
* created.
*/
// TODO(b/265277413): Recreate/update these channels on locale changes by calling this method
@Nullable
private void createAllChannelsWithoutCallingIdentity(NotificationManager notificationManager) {
// Clearing calling identity to be able to make unblockable system notification channels
final long callingId = Binder.clearCallingIdentity();
try {
notificationManager.createNotificationChannelGroup(getChannelGroupDefinition());
notificationManager.createNotificationChannel(getGreenChannelDefinition());
notificationManager.createNotificationChannel(getYellowChannelDefinition());
notificationManager.createNotificationChannel(getRedChannelDefinition());
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
private NotificationChannelGroup getChannelGroupDefinition() {
return new NotificationChannelGroup(
CHANNEL_GROUP_ID, getString("notification_channel_group_name"));
}
private NotificationChannel getGreenChannelDefinition() {
NotificationChannel channel =
new NotificationChannel(
CHANNEL_ID_INFORMATION,
getString("notification_channel_name_information"),
NotificationManager.IMPORTANCE_LOW);
channel.setGroup(CHANNEL_GROUP_ID);
channel.setBlockable(true);
return channel;
}
private NotificationChannel getYellowChannelDefinition() {
NotificationChannel channel =
new NotificationChannel(
CHANNEL_ID_RECOMMENDATION,
getString("notification_channel_name_recommendation"),
NotificationManager.IMPORTANCE_DEFAULT);
channel.setGroup(CHANNEL_GROUP_ID);
channel.setBlockable(false);
return channel;
}
private NotificationChannel getRedChannelDefinition() {
NotificationChannel channel =
new NotificationChannel(
CHANNEL_ID_CRITICAL_WARNING,
getString("notification_channel_name_critical_warning"),
NotificationManager.IMPORTANCE_HIGH);
channel.setGroup(CHANNEL_GROUP_ID);
channel.setBlockable(false);
return channel;
}
private String getString(String name) {
return mResourcesContext.getStringByName(name);
}
}