Add mock notifications for terminal app

This commit adds two mock notifications for the terminal app:
1. VmLauncherService foreground notification
  As a foreground service, VmLauncherService should display a
  notification to indicate the service is running.
  Terminal app should pass a Notification to the
  VmLauncherService for the foreground notification
2. Port forwarding notification
  When the VM opens a port, terminal app should send a
  notification for user to allow or deny the request

Bug: 372170751
Test: Install on komodo
Change-Id: Iac0ae70354f984246abcb6af1d650a548d59387d
diff --git a/android/TerminalApp/AndroidManifest.xml b/android/TerminalApp/AndroidManifest.xml
index 105e454..28b5436 100644
--- a/android/TerminalApp/AndroidManifest.xml
+++ b/android/TerminalApp/AndroidManifest.xml
@@ -9,6 +9,7 @@
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/>
+    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
 
     <uses-feature android:name="android.software.virtualization_framework" android:required="true" />
 
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
index 20b4c96..d71a17c 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
@@ -15,8 +15,15 @@
  */
 package com.android.virtualization.terminal;
 
+import android.Manifest;
+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.PackageManager;
+import android.graphics.drawable.Icon;
 import android.net.http.SslError;
 import android.os.Build;
 import android.os.Bundle;
@@ -72,6 +79,7 @@
     private PrivateKey mPrivateKey;
     private WebView mWebView;
     private AccessibilityManager mAccessibilityManager;
+    private static final int POST_NOTIFICATIONS_PERMISSION_REQUEST_CODE = 101;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -88,6 +96,14 @@
                     .show();
         }
 
+        checkAndRequestPostNotificationsPermission();
+
+        NotificationManager notificationManager = getSystemService(NotificationManager.class);
+        NotificationChannel notificationChannel =
+                new NotificationChannel(TAG, TAG, NotificationManager.IMPORTANCE_LOW);
+        assert notificationManager != null;
+        notificationManager.createNotificationChannel(notificationChannel);
+
         setContentView(R.layout.activity_headless);
 
         MaterialToolbar toolbar = (MaterialToolbar) findViewById(R.id.toolbar);
@@ -311,6 +327,15 @@
         return;
     }
 
+    private void checkAndRequestPostNotificationsPermission() {
+        if (getApplicationContext().checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS)
+                != PackageManager.PERMISSION_GRANTED) {
+            requestPermissions(
+                    new String[]{Manifest.permission.POST_NOTIFICATIONS},
+                    POST_NOTIFICATIONS_PERMISSION_REQUEST_CODE);
+        }
+    }
+
     @Override
     protected void onDestroy() {
         getSystemService(AccessibilityManager.class).removeTouchExplorationStateChangeListener(this);
@@ -393,7 +418,29 @@
         if (!InstallUtils.isImageInstalled(this)) {
             return;
         }
+        // TODO: implement intent for setting, close and tap to the notification
+        // Currently mock a PendingIntent for notification.
+        Intent intent = new Intent();
+        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent,
+                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+
+        Icon icon = Icon.createWithResource(getResources(), R.drawable.ic_launcher_foreground);
+        Notification notification = new Notification.Builder(this, TAG)
+                .setChannelId(TAG)
+                .setSmallIcon(R.drawable.ic_launcher_foreground)
+                .setContentTitle(getResources().getString(R.string.service_notification_title))
+                .setContentText(getResources().getString(R.string.service_notification_content))
+                .setContentIntent(pendingIntent)
+                .setOngoing(true)
+                .addAction(new Notification.Action.Builder(icon,
+                        getResources().getString(R.string.service_notification_settings),
+                        pendingIntent).build())
+                .addAction(new Notification.Action.Builder(icon,
+                        getResources().getString(R.string.service_notification_quit_action),
+                        pendingIntent).build())
+                .build();
+
         android.os.Trace.beginAsyncSection("executeTerminal", 0);
-        VmLauncherServices.startVmLauncherService(this, this);
+        VmLauncherServices.startVmLauncherService(this, this, notification);
     }
 }
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsPortForwardingActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsPortForwardingActivity.kt
index 6c36cc8..7119225 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsPortForwardingActivity.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsPortForwardingActivity.kt
@@ -15,12 +15,21 @@
  */
 package com.android.virtualization.terminal
 
+import android.Manifest
+import android.app.Notification
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.graphics.drawable.Icon
 import android.os.Bundle
 import androidx.appcompat.app.AppCompatActivity
+import androidx.core.app.ActivityCompat
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
 
 class SettingsPortForwardingActivity : AppCompatActivity() {
+    val TAG: String = "VmTerminalApp"
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContentView(R.layout.settings_port_forwarding)
@@ -37,5 +46,43 @@
         val recyclerView: RecyclerView = findViewById(R.id.settings_port_forwarding_recycler_view)
         recyclerView.layoutManager = LinearLayoutManager(this)
         recyclerView.adapter = settingsPortForwardingAdapter
+
+        // TODO: implement intent for accept, deny and tap to the notification
+        // Currently show a mock notification of a port opening
+        val terminalIntent = Intent()
+        val pendingIntent = PendingIntent.getActivity(
+            this, 0, terminalIntent,
+            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+        )
+        val notification =
+            Notification.Builder(this, TAG)
+                .setChannelId(TAG)
+                .setSmallIcon(R.drawable.ic_launcher_foreground)
+                .setContentTitle(resources.getString(R.string.settings_port_forwarding_notification_title))
+                .setContentText(resources.getString(R.string.settings_port_forwarding_notification_content, settingsPortForwardingItems[0].port))
+                .addAction(
+                    Notification.Action.Builder(
+                        Icon.createWithResource(resources, R.drawable.ic_launcher_foreground),
+                        resources.getString(R.string.settings_port_forwarding_notification_accept),
+                        pendingIntent
+                    ).build()
+                )
+                .addAction(
+                    Notification.Action.Builder(
+                        Icon.createWithResource(resources, R.drawable.ic_launcher_foreground),
+                        resources.getString(R.string.settings_port_forwarding_notification_deny),
+                        pendingIntent
+                    ).build()
+                )
+                .build()
+
+        with(NotificationManager.from(this)) {
+            if (ActivityCompat.checkSelfPermission(
+                    this@SettingsPortForwardingActivity, Manifest.permission.POST_NOTIFICATIONS
+                ) == PackageManager.PERMISSION_GRANTED
+            ) {
+                notify(0, notification)
+            }
+        }
     }
 }
\ No newline at end of file
diff --git a/android/TerminalApp/res/values/strings.xml b/android/TerminalApp/res/values/strings.xml
index 1cbaee8..0cdb939 100644
--- a/android/TerminalApp/res/values/strings.xml
+++ b/android/TerminalApp/res/values/strings.xml
@@ -49,6 +49,14 @@
     <string name="settings_port_forwarding_title">Port Forwarding</string>
     <!-- Settings menu subtitle for 'port forwarding' [CHAR LIMIT=none] -->
     <string name="settings_port_forwarding_sub_title">Configure port forwarding</string>
+    <!-- Notification title for new port forwarding [CHAR LIMIT=none] -->
+    <string name="settings_port_forwarding_notification_title">Terminal is trying to open a new port</string>
+    <!-- Notification content for new port forwarding [CHAR LIMIT=none] -->
+    <string name="settings_port_forwarding_notification_content">Port requested to be open: <xliff:g id="port_number" example="8080">%d</xliff:g></string>
+    <!-- Notification action accept [CHAR LIMIT=none] -->
+    <string name="settings_port_forwarding_notification_accept">Accept</string>
+    <!-- Notification action deny [CHAR LIMIT=none] -->
+    <string name="settings_port_forwarding_notification_deny">Deny</string>
 
     <!-- Settings menu title for recoverying image [CHAR LIMIT=none] -->
     <string name="settings_recovery_title">Recovery</string>
@@ -60,4 +68,13 @@
     <string name="settings_recovery_reset_sub_title">Remove all</string>
     <!-- Toast message for reset is completed [CHAR LIMIT=none] -->
     <string name="settings_recovery_reset_message">VM reset</string>
+
+    <!-- Notification action button for settings [CHAR LIMIT=none] -->
+    <string name="service_notification_settings">Settings</string>
+    <!-- Notification title for foreground service notification [CHAR LIMIT=none] -->
+    <string name="service_notification_title">Terminal is running</string>
+    <!-- Notification content for foreground service notification [CHAR LIMIT=none] -->
+    <string name="service_notification_content">Click to open the terminal.</string>
+    <!-- Notification action button for closing the virtual machine [CHAR LIMIT=none] -->
+    <string name="service_notification_quit_action">Close</string>
 </resources>