Repository: flutter
Issue: When using FlutterEngineGroup on Android, the state of the page stack cannot be restored when a process is terminated due to a permission change.
Labels: platform-android,a: quality,has reproducible steps,P2,team-android,triaged-android,found in release: 3.27,found in release: 3.28

### Steps to reproduce

One solution to restore the page stack is to transition between pages using `Navigator.restorablePush`.

I confirmed this behavior when developing an application using `MultipleFlutter` with `FlutterEngineGroup`.

The above solution did not seem to work on `Flutter` screens launched by `FlutterEngine` generated using `FlutterEngineGroup`.

In more detail, this issue occurs when `Intent` is generated using `CachedEngineIntentBuilder` and then `Activity` is launched.

When launching without using a cached `FlutterEngine`, the page stack state seems to be restored using `Navigator.restorablePush`.

Below are the steps and sample code:

1. Use the `FlutterEngine` created using `FlutterEngineGroup` to create an `Intent` using `CachedEngineIntentBuilder` and launch FlutterActivity.
2. Within the launched Flutter screen, `Navigator.restorablePush` is used to perform screen transitions.
3. Change the permissions from the app information (change from Allow to Deny)
4. Back to Application

### Expected results

Expects page stack restoration via `Navigator.restorablePush`.

### Actual results

The page stack is not restored and it appears as if the stack is discarded.

### Code sample

<details close><summary>Code sample Flutter</summary>

```dart

void main() {
  runApp(
    const MyApp(
      entryPoint: EntryPoint.main,
    ),
  );
}

@pragma('vm:entry-point')
void secondMain() {
  runApp(
    const MyApp(
      entryPoint: EntryPoint.second,
    ),
  );
}

enum EntryPoint {
  main,
  second,
}

class MyApp extends StatelessWidget {
  const MyApp({super.key, required this.entryPoint});

  final EntryPoint entryPoint;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      restorationScopeId: entryPoint.name,
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: Provider.value(
        value: entryPoint,
        child: MyHomePage(
          entryPoint: entryPoint,
        ),
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key, required this.entryPoint});

  final EntryPoint entryPoint;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('${entryPoint}Main'),
        leading: entryPoint == EntryPoint.second
            ? BackButton(
                onPressed: () => SystemNavigator.pop(animated: true),
              )
            : null,
      ),
      body: Center(
        child: ElevatedButton(
          child: const Text('NextPage'),
          onPressed: () {
            Navigator.restorablePush(
              context,
              _buildRoute,
            );
            // Navigator.of(context).push(MaterialPageRoute<void>(
            //   builder: (BuildContext context) => const NextPage(),
            // ));
          },
        ),
      ),
    );
  }

  static Route _buildRoute(BuildContext context, Object? params) {
    return MaterialPageRoute<void>(
      builder: (BuildContext context) => const NextPage(),
    );
  }
}

class NextPage extends StatelessWidget {
  const NextPage({super.key});

  static const MethodChannel methodChannel = MethodChannel('channel');

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('NextPage'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            methodChannel.invokeMethod('startNextActivity');
          },
          child: const Text('NextActivity'),
        ),
      ),
    );
  }
}

```

</details>

<details close><summary>Code sample Android</summary>

```Kotlin

/**
 * Application Class.
 */
class SampleApplication : Application() {

    enum class EntryPoint(val mainMethod: String) {
        MAIN("main"), SECOND("secondMain");
    }

    lateinit var engines: FlutterEngineGroup

    override fun onCreate() {
        super.onCreate()
        engines = FlutterEngineGroup(this)
    }
}

/**
 * MainActivity.
 *
 * This Activity is the main Activity launched by the intent-filter.
 */
class MainActivity : FlutterActivity() {
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "channel").setMethodCallHandler { call, result ->
            if (call.method == "startNextActivity") {
                startActivity(Intent(this, NextActivity::class.java))
                result.success(true)
            } else {
                result.notImplemented()
            }
        }
    }
}


class NextFlutterActivity : FlutterActivity() {
    companion object {
        private const val ENTRY_POINT: String = "EntryPoint"

        fun intentWithCachedEngine(application: SampleApplication, entryPoint: SampleApplication.EntryPoint): Intent {
            if (!FlutterEngineCache.getInstance().contains(entryPoint.name)) {
                val engine: FlutterEngine = application.engines.createAndRunEngine(
                        application,
                        DartExecutor.DartEntrypoint(
                                FlutterInjector.instance().flutterLoader().findAppBundlePath(),
                                entryPoint.mainMethod
                        )
                )
                FlutterEngineCache.getInstance().put(entryPoint.name, engine)
            }
            return CachedEngineIntentBuilder(NextFlutterActivity::class.java, entryPoint.name).build(application).putExtra(ENTRY_POINT, entryPoint)
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        // The engine cache is destroyed on app startup after the activity is destroyed.
        // If you try to get the cached engine in super.onCreate, the engine won't work and the app will crash.
        // Due to the structure of FlutterEngineCache, it is stored statically, so it is inevitable that static variables will be destroyed when the process is killed.
        // Therefore, as a workaround, check the cache before super.onCreate and generate it to keep it around just in case.
        // Related issue: https://github.com/flutter/flutter/issues/106192
        // Workaround on Stack Overflow: https://stackoverflow.com/a/64010515
        val entryPoint = intent.getSerializableExtra(ENTRY_POINT) as? SampleApplication.EntryPoint ?: SampleApplication.EntryPoint.MAIN
        if (!FlutterEngineCache.getInstance().contains(entryPoint.name)) {
            val engine: FlutterEngine = (application as SampleApplication).engines.createAndRunEngine(
                    application,
                    DartExecutor.DartEntrypoint(
                            FlutterInjector.instance().flutterLoader().findAppBundlePath(),
                            entryPoint.mainMethod
                    )
            )
            FlutterEngineCache.getInstance().put(entryPoint.name, engine)
        }
        super.onCreate(savedInstanceState)
    }
}

```

</details>


<details close><summary>Code sample Android Manifest</summary>

```xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <application
        android:name=".SampleApplication"
        android:icon="@mipmap/ic_launcher"
        android:label="multiple_flutter_android_activity">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:hardwareAccelerated="true"
            android:taskAffinity=""
            android:theme="@style/LaunchTheme"
            android:windowSoftInputMode="adjustResize">
            <meta-data
                android:name="io.flutter.embedding.android.NormalTheme"
                android:resource="@style/NormalTheme" />
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".NextFlutterActivity"
            android:theme="@android:style/Theme.Material.NoActionBar" />

        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />
    </application>
    <queries>
        <intent>
            <action android:name="android.intent.action.PROCESS_TEXT" />
            <data android:mimeType="text/plain" />
        </intent>
    </queries>
</manifest>
```

</details>


### Screenshots or Video

<details close>
<summary>When using FlutterEngineGroup</summary>

https://github.com/user-attachments/assets/7ec6af78-ea94-4fd4-b85f-e0f00028f8b4

</details>

<details close>
<summary>When not using FlutterEngineGroup</summary>

https://github.com/user-attachments/assets/cc07f444-6eba-4045-a4df-17a2836b9099

</details>


### Logs

<details close><summary>Logs</summary>

```console
[Paste your logs here]
```

</details>


### Flutter Doctor output

<details close><summary>Doctor output</summary>

```console
% fvm flutter doctor -v
[✓] Flutter (Channel stable, 3.24.5, on macOS 14.5 23F79 darwin-arm64, locale ja-JP)
    • Flutter version 3.24.5 on channel stable at /Users/uu137317/fvm/versions/3.24.5
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision dec2ee5c1f (8 weeks ago), 2024-11-13 11:13:06 -0800
    • Engine revision a18df97ca5
    • Dart version 3.5.4
    • DevTools version 2.37.3

[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    • Android SDK at /Users/uu137317/Library/Android/sdk
    • Platform android-35, build-tools 34.0.0
    • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b829.9-10027231)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 16.0)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 16A242d
    • CocoaPods version 1.15.2

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2022.3)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b829.9-10027231)

[✓] VS Code (version 1.77.1)
    • VS Code at /Users/uu137317/Downloads/Visual Studio Code.app/Contents
    • Flutter extension can be installed from:
      🔨 https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter

[✓] Connected device (4 available)
    • sdk gphone64 arm64 (mobile)     • emulator-5554         • android-arm64  • Android 14 (API 34) (emulator)
    • macOS (desktop)                 • macos                 • darwin-arm64   • macOS 14.5 23F79 darwin-arm64
    • Mac Designed for iPad (desktop) • mac-designed-for-ipad • darwin         • macOS 14.5 23F79 darwin-arm64
    • Chrome (web)                    • chrome                • web-javascript • Google Chrome 131.0.6778.206

[✓] Network resources
    • All expected network resources are available.

• No issues found!

```

</details>