Skip to content

Device labels missing from run tabs on IntelliJ 2025.3 / Android Studio Panda #8795

@lukemmtt

Description

@lukemmtt

Description

Device labels (e.g., "main.dart (macOS)", "main.dart (iPhone 16 Pro)") no longer appear in Run/Debug tab titles starting with IntelliJ 2025.3 (Android Studio Panda). All tabs show only the run configuration name (e.g., "main.dart"), making it impossible to distinguish which device a build is running on when targeting multiple platforms simultaneously.

Root Cause

The reflection code in LaunchState.java (lines 173-185) that sets the display name is silently failing on every launch.

The code gets the private myDisplayNameView field from RunContentDescriptor, then calls getMethod("setValue", Object.class) on the field's runtime class, then calls invoke(). This worked when the field type was MutableReactiveProperty (a public IntelliJ-internal class), but broke in IntelliJ 2025.3 when the field type was changed to MutableStateFlow from kotlinx-coroutines (JetBrains/intellij-community@aaa88849ba4c).

The runtime implementation class kotlinx.coroutines.flow.StateFlowImpl is package-private. Java's access control prevents Method.invoke() on a public method when the declaring class is not accessible from the caller, throwing IllegalAccessException. The exception is caught silently (logged at INFO without stack trace), so the device name is never applied.

Verification

The Android Studio idea.log shows "Error setting display name" (from the catch block in LaunchState.java:184) on every Flutter launch in Panda.

Reproduced independently by calling the same reflection against the actual kotlinx-coroutines-core-jvm.jar shipped with Android Studio Panda:

MutableStateFlow<String> flow = StateFlowKt.MutableStateFlow(null);
Method m = flow.getClass().getMethod("setValue", Object.class);
m.invoke(flow, "test");
// IllegalAccessException: cannot access a member of class
// kotlinx.coroutines.flow.StateFlowImpl with modifiers "public"

Proposed Fix

Use RunContentDescriptor.setDisplayName() (an existing protected method on RunContentDescriptor) via reflection instead of reaching into the private field's internal type:

Method setDisplayName = RunContentDescriptor.class.getDeclaredMethod("setDisplayName", String.class);
setDisplayName.setAccessible(true);
setDisplayName.invoke(descriptor, nameWithDeviceName);

This is more resilient because it calls a stable API method on a public class rather than depending on the internal type of a private field.

An alternative minimal fix would be to add setValueMethod.setAccessible(true) before the invoke() call in the existing code, but this remains fragile if the field type changes again.

Affected Versions

  • Broken: Android Studio Panda 1 (2025.3.1), IntelliJ 2025.3+
  • Working: Android Studio Narwhal (2025.1.x), IntelliJ 2025.1 and earlier

Related

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions