aboutsummaryrefslogtreecommitdiff
path: root/service-builtin/src
diff options
context:
space:
mode:
authorGaurav Bhola <gauravbhola@google.com>2021-09-22 20:18:36 +0000
committerGaurav Bhola <gauravbhola@google.com>2021-09-24 07:04:13 +0000
commit1c3c8f443ab393d1ae622d67c9df0bd554c96b9f (patch)
treecdd982e93d788bf53860df20c89c44223d213cd0 /service-builtin/src
parent8aaf4dfcf47b0733eb69f33b2bf07f8b451347b7 (diff)
downloadCar-1c3c8f443ab393d1ae622d67c9df0bd554c96b9f.tar.gz
Move NotificationHelper and resources to carservice builtin.
- Use reflection to call methods to notify/cancel. Fix: 196947649 Test: atest CarDevicePolicyServiceTest NotificationHelperTest, Manual test by triggering factory reset from kitchen sink, Manual test by switching to a new user with a device policy set up. Change-Id: I3ef0f789e457f10cd1115495f3659b2bd4a81245
Diffstat (limited to 'service-builtin/src')
-rw-r--r--service-builtin/src/com/android/car/admin/NotificationHelper.java195
1 files changed, 195 insertions, 0 deletions
diff --git a/service-builtin/src/com/android/car/admin/NotificationHelper.java b/service-builtin/src/com/android/car/admin/NotificationHelper.java
new file mode 100644
index 0000000000..828b31a107
--- /dev/null
+++ b/service-builtin/src/com/android/car/admin/NotificationHelper.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2021 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.car.admin;
+
+import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE;
+
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.car.ICarResultReceiver;
+import android.car.builtin.util.Slog;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+import com.android.car.R;
+import com.android.car.admin.ui.ManagedDeviceTextView;
+import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Objects;
+
+// TODO(b/196947649): move this class to CarSettings or at least to some common package (not
+// car-admin-ui-lib)
+/**
+ * Helper for notification-related tasks
+ */
+public final class NotificationHelper {
+ // TODO: Move these constants to a common place. Right now a copy of these is present in
+ // CarSettings' FactoryResetActivity.
+ public static final String EXTRA_FACTORY_RESET_CALLBACK = "factory_reset_callback";
+ public static final int FACTORY_RESET_NOTIFICATION_ID = 42;
+ public static final int NEW_USER_DISCLAIMER_NOTIFICATION_ID = 108;
+
+ @VisibleForTesting
+ public static final String CHANNEL_ID_DEFAULT = "channel_id_default";
+ @VisibleForTesting
+ public static final String CHANNEL_ID_HIGH = "channel_id_high";
+ private static final boolean DEBUG = false;
+ @VisibleForTesting
+ static final String TAG = NotificationHelper.class.getSimpleName();
+
+ /**
+ * Creates a notification (and its notification channel) for the given importance type, setting
+ * its name to be {@code Android System}.
+ *
+ * @param context context for showing the notification
+ * @param importance notification importance. Currently only
+ * {@link NotificationManager.IMPORTANCE_HIGH} is supported.
+ */
+ @NonNull
+ public static Notification.Builder newNotificationBuilder(Context context,
+ @NotificationManager.Importance int importance) {
+ Objects.requireNonNull(context, "context cannot be null");
+
+ String channelId, importanceName;
+ switch (importance) {
+ case NotificationManager.IMPORTANCE_DEFAULT:
+ channelId = CHANNEL_ID_DEFAULT;
+ importanceName = context.getString(R.string.importance_default);
+ break;
+ case NotificationManager.IMPORTANCE_HIGH:
+ channelId = CHANNEL_ID_HIGH;
+ importanceName = context.getString(R.string.importance_high);
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported importance: " + importance);
+ }
+ NotificationManager notificationMgr = context.getSystemService(NotificationManager.class);
+ notificationMgr.createNotificationChannel(
+ new NotificationChannel(channelId, importanceName, importance));
+
+ Bundle extras = new Bundle();
+ extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
+ context.getString(com.android.internal.R.string.android_system_label));
+
+ return new Notification.Builder(context, channelId).addExtras(extras);
+ }
+
+ @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE,
+ details = "private constructor")
+ private NotificationHelper() {
+ throw new UnsupportedOperationException("Contains only static methods");
+ }
+
+ /**
+ * Shows the user disclaimer notification.
+ */
+ public static void showUserDisclaimerNotification(int userId, Context context) {
+ // TODO(b/175057848) persist status so it's shown again if car service crashes?
+ PendingIntent pendingIntent = getPendingUserDisclaimerIntent(context, /* extraFlags= */ 0,
+ userId);
+
+ Notification notification = NotificationHelper
+ .newNotificationBuilder(context, NotificationManager.IMPORTANCE_DEFAULT)
+ // TODO(b/177552737): Use a better icon?
+ .setSmallIcon(R.drawable.car_ic_mode)
+ .setContentTitle(context.getString(R.string.new_user_managed_notification_title))
+ .setContentText(ManagedDeviceTextView.getManagedDeviceText(context))
+ .setCategory(Notification.CATEGORY_CAR_INFORMATION)
+ .setContentIntent(pendingIntent)
+ .setOngoing(true)
+ .build();
+
+ if (DEBUG) {
+ Slog.d(TAG, "Showing new managed notification (id "
+ + NEW_USER_DISCLAIMER_NOTIFICATION_ID + " on user " + context.getUser());
+ }
+ context.getSystemService(NotificationManager.class)
+ .notifyAsUser(TAG, NEW_USER_DISCLAIMER_NOTIFICATION_ID,
+ notification, UserHandle.of(userId));
+ }
+
+ /**
+ * Cancels the user disclaimer notification.
+ */
+ public static void cancelUserDisclaimerNotification(int userId, Context context) {
+ if (DEBUG) {
+ Slog.d(TAG, "Canceling notification " + NEW_USER_DISCLAIMER_NOTIFICATION_ID
+ + " for user " + context.getUser());
+ }
+ context.getSystemService(NotificationManager.class)
+ .cancelAsUser(TAG, NEW_USER_DISCLAIMER_NOTIFICATION_ID, UserHandle.of(userId));
+ getPendingUserDisclaimerIntent(context, PendingIntent.FLAG_UPDATE_CURRENT, userId).cancel();
+ }
+
+ /**
+ * Creates and returns the PendingIntent for User Disclaimer notification.
+ */
+ @VisibleForTesting
+ public static PendingIntent getPendingUserDisclaimerIntent(Context context, int extraFlags,
+ int userId) {
+ return PendingIntent
+ .getActivityAsUser(context, NEW_USER_DISCLAIMER_NOTIFICATION_ID,
+ new Intent().setComponent(ComponentName.unflattenFromString(
+ context.getString(R.string.config_newUserDisclaimerActivity)
+ )),
+ PendingIntent.FLAG_IMMUTABLE | extraFlags, null, UserHandle.of(userId));
+ }
+
+ /**
+ * Sends the notification warning the user about the factory reset.
+ */
+ public static void showFactoryResetNotification(Context context, ICarResultReceiver callback) {
+ // The factory request is received by CarService - which runs on system user - but the
+ // notification will be sent to all users.
+ UserHandle currentUser = UserHandle.of(ActivityManager.getCurrentUser());
+
+ ComponentName factoryResetActivity = ComponentName.unflattenFromString(
+ context.getString(R.string.config_factoryResetActivity));
+ @SuppressWarnings("deprecation")
+ Intent intent = new Intent()
+ .setComponent(factoryResetActivity)
+ .putExtra(EXTRA_FACTORY_RESET_CALLBACK, callback.asBinder());
+ PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context,
+ FACTORY_RESET_NOTIFICATION_ID, intent, PendingIntent.FLAG_IMMUTABLE,
+ /* options= */ null, currentUser);
+
+ Notification notification = NotificationHelper
+ .newNotificationBuilder(context, NotificationManager.IMPORTANCE_HIGH)
+ .setSmallIcon(R.drawable.car_ic_warning)
+ .setColor(context.getColor(R.color.red_warning))
+ .setContentTitle(context.getString(R.string.factory_reset_notification_title))
+ .setContentText(context.getString(R.string.factory_reset_notification_text))
+ .setCategory(Notification.CATEGORY_CAR_WARNING)
+ .setOngoing(true)
+ .addAction(/* icon= */ 0,
+ context.getString(R.string.factory_reset_notification_button),
+ pendingIntent)
+ .build();
+
+ Slog.i(TAG, "Showing factory reset notification on all users");
+ context.getSystemService(NotificationManager.class)
+ .notifyAsUser(TAG, FACTORY_RESET_NOTIFICATION_ID, notification, UserHandle.ALL);
+ }
+}