Index: src/wp-admin/includes/class-plugin-upgrader.php
===================================================================
--- src/wp-admin/includes/class-plugin-upgrader.php	(revision 47262)
+++ src/wp-admin/includes/class-plugin-upgrader.php	(working copy)
@@ -167,7 +167,9 @@
 		$r = $current->response[ $plugin ];
 
 		add_filter( 'upgrader_pre_install', array( $this, 'deactivate_plugin_before_upgrade' ), 10, 2 );
+		add_filter( 'upgrader_pre_install', array( $this, 'active_before' ), 10, 2 );
 		add_filter( 'upgrader_clear_destination', array( $this, 'delete_old_plugin' ), 10, 4 );
+		add_filter( 'upgrader_post_install', array( $this, 'active_after' ), 10, 2 );
 		// There's a Trac ticket to move up the directory for zips which are made a bit differently, useful for non-.org plugins.
 		// 'source_selection' => array( $this, 'source_selection' ),
 		if ( $parsed_args['clear_update_cache'] ) {
@@ -192,7 +194,9 @@
 		// Cleanup our hooks, in case something else does a upgrade on this connection.
 		remove_action( 'upgrader_process_complete', 'wp_clean_plugins_cache', 9 );
 		remove_filter( 'upgrader_pre_install', array( $this, 'deactivate_plugin_before_upgrade' ) );
+		remove_filter( 'upgrader_pre_install', array( $this, 'active_before' ) );
 		remove_filter( 'upgrader_clear_destination', array( $this, 'delete_old_plugin' ) );
+		remove_filter( 'upgrader_post_install', array( $this, 'active_after' ) );
 
 		if ( ! $this->result || is_wp_error( $this->result ) ) {
 			return $this->result;
@@ -411,8 +415,8 @@
 	 * @since 2.8.0
 	 * @since 4.1.0 Added a return value.
 	 *
-	 * @param bool|WP_Error  $return Upgrade offer return.
-	 * @param array          $plugin Plugin package arguments.
+	 * @param bool|WP_Error $return Upgrade offer return.
+	 * @param array         $plugin Plugin package arguments.
 	 * @return bool|WP_Error The passed in $return param or WP_Error.
 	 */
 	public function deactivate_plugin_before_upgrade( $return, $plugin ) {
@@ -440,6 +444,76 @@
 	}
 
 	/**
+	 * Turn on maintenance mode before attempting to background update an active plugin.
+	 *
+	 * Hooked to the {@see 'upgrader_pre_install'} filter by Plugin_Upgrader::upgrade().
+	 *
+	 * @since 5.4.0
+	 *
+	 * @param bool|WP_Error $return Upgrade offer return.
+	 * @param array         $plugin Plugin package arguments.
+	 * @return bool|WP_Error The passed in $return param or WP_Error.
+	 */
+	public function active_before( $return, $plugin ) {
+		if ( is_wp_error( $return ) ) {
+			return $return;
+		}
+
+		// Only enable maintenance mode when in cron (background update).
+		if ( ! wp_doing_cron() ) {
+			return $return;
+		}
+
+		$plugin = isset( $plugin['plugin'] ) ? $plugin['plugin'] : '';
+
+		if ( ! is_plugin_active( $plugin ) ) { // If not active.
+			return $return;
+		}
+
+		// Bulk edit handles maintenance mode separately.
+		if ( ! $this->bulk ) {
+			$this->maintenance_mode( true );
+		}
+
+		return $return;
+	}
+
+	/**
+	 * Turn off maintenance mode after upgrading an active plugin.
+	 *
+	 * Hooked to the {@see 'upgrader_post_install'} filter by Plugin_Upgrader::upgrade().
+	 *
+	 * @since 5.4.0
+	 *
+	 * @param bool|WP_Error $return Upgrade offer return.
+	 * @param array         $plugin Plugin package arguments.
+	 * @return bool|WP_Error The passed in $return param or WP_Error.
+	 */
+	public function active_after( $return, $plugin ) {
+		if ( is_wp_error( $return ) ) {
+			return $return;
+		}
+
+		// Only enable maintenance mode when in cron (background update).
+		if ( ! wp_doing_cron() ) {
+			return $return;
+		}
+
+		$plugin = isset( $plugin['plugin'] ) ? $plugin['plugin'] : '';
+
+		if ( ! is_plugin_active( $plugin ) ) { // If not active.
+			return $return;
+		}
+
+		// Bulk edit handles maintenance mode separately.
+		if ( ! $this->bulk ) {
+			$this->maintenance_mode( false );
+		}
+
+		return $return;
+	}
+
+	/**
 	 * Delete the old plugin during an upgrade.
 	 *
 	 * Hooked to the {@see 'upgrader_clear_destination'} filter by
@@ -449,10 +523,10 @@
 	 *
 	 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
 	 *
-	 * @param bool|WP_Error $removed
-	 * @param string        $local_destination
-	 * @param string        $remote_destination
-	 * @param array         $plugin
+	 * @param bool|WP_Error $removed  Whether the destination was cleared. true on success, WP_Error on failure.
+	 * @param string        $local_destination The local package destination.
+	 * @param string        $remote_destination The remote package destination.
+	 * @param array         $plugin Extra arguments passed to hooked filters.
 	 * @return bool|WP_Error
 	 */
 	public function delete_old_plugin( $removed, $local_destination, $remote_destination, $plugin ) {
@@ -476,7 +550,7 @@
 
 		// If plugin is in its own directory, recursively delete the directory.
 		// Base check on if plugin includes directory separator AND that it's not the root plugin folder.
-		if ( strpos( $plugin, '/' ) && $this_plugin_dir != $plugins_dir ) {
+		if ( strpos( $plugin, '/' ) && $this_plugin_dir !== $plugins_dir ) {
 			$deleted = $wp_filesystem->delete( $this_plugin_dir, true );
 		} else {
 			$deleted = $wp_filesystem->delete( $plugins_dir . $plugin );
