Skip to content

Commit

Permalink
Merge pull request #61332 from m4gr3d/fix_restart_logic_3x
Browse files Browse the repository at this point in the history
  • Loading branch information
akien-mga authored May 23, 2022
2 parents 88dba8e + b162e7a commit e0ddaf7
Show file tree
Hide file tree
Showing 9 changed files with 163 additions and 88 deletions.
5 changes: 5 additions & 0 deletions COPYRIGHT.txt
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ Comment: The Android Open Source Project
Copyright: 2002, Google Inc.
License: Apache-2.0

Files: ./platform/android/java/lib/src/org/godotengine/godot/utils/ProcessPhoenix.java
Comment: ProcessPhoenix
Copyright: 2015, Jake Wharton
License: Apache-2.0

Files: ./platform/android/power_android.cpp
./platform/osx/power_osx.cpp
./platform/windows/power_windows.cpp
Expand Down
2 changes: 2 additions & 0 deletions misc/scripts/clang_format.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ while IFS= read -rd '' f; do
continue 2
elif [[ "$f" == "platform/android/java/lib/src/org/godotengine/godot/gl/EGLLogWrapper"* ]]; then
continue 2
elif [[ "$f" == "platform/android/java/lib/src/org/godotengine/godot/utils/ProcessPhoenix"* ]]; then
continue 2
fi
python misc/scripts/copyright_headers.py "$f"
continue 2
Expand Down
5 changes: 0 additions & 5 deletions platform/android/export/export_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -791,7 +791,6 @@ void EditorExportPlatformAndroid::_write_tmp_manifest(const Ref<EditorExportPres
}

manifest_text += _get_xr_features_tag(p_preset);
manifest_text += _get_instrumentation_tag(p_preset);
manifest_text += _get_application_tag(p_preset, _has_storage_permission(perms));
manifest_text += "</manifest>\n";
String manifest_path = vformat("res://android/build/src/%s/AndroidManifest.xml", (p_debug ? "debug" : "release"));
Expand Down Expand Up @@ -955,10 +954,6 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
encode_uint32(retain_data_on_uninstall, &p_manifest.write[iofs + 16]);
}

if (tname == "instrumentation" && attrname == "targetPackage") {
string_table.write[attr_value] = get_package_name(package_name);
}

if (tname == "activity" && attrname == "screenOrientation") {
encode_uint32(screen_orientation, &p_manifest.write[iofs + 16]);
}
Expand Down
13 changes: 0 additions & 13 deletions platform/android/export/gradle_export_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,19 +233,6 @@ String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset) {
return manifest_xr_features;
}

String _get_instrumentation_tag(const Ref<EditorExportPreset> &p_preset) {
String package_name = p_preset->get("package/unique_name");
String manifest_instrumentation_text = vformat(
" <instrumentation\n"
" tools:node=\"replace\"\n"
" android:name=\".GodotInstrumentation\"\n"
" android:icon=\"@mipmap/icon\"\n"
" android:label=\"@string/godot_project_name_string\"\n"
" android:targetPackage=\"%s\" />\n",
package_name);
return manifest_instrumentation_text;
}

String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) {
int xr_mode_index = (int)(p_preset->get("xr_features/xr_mode"));
bool uses_xr = xr_mode_index == XR_MODE_OVR || xr_mode_index == XR_MODE_OPENXR;
Expand Down
2 changes: 0 additions & 2 deletions platform/android/export/gradle_export_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,6 @@ String _get_screen_sizes_tag(const Ref<EditorExportPreset> &p_preset);

String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset);

String _get_instrumentation_tag(const Ref<EditorExportPreset> &p_preset);

String _get_activity_tag(const Ref<EditorExportPreset> &p_preset);

String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_storage_permission);
Expand Down
13 changes: 7 additions & 6 deletions platform/android/java/lib/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@

<service android:name=".GodotDownloaderService" />

</application>
<activity
android:name=".utils.ProcessPhoenix"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:process=":phoenix"
android:exported="false"
/>

<instrumentation
android:name=".GodotInstrumentation"
android:icon="@mipmap/icon"
android:label="@string/godot_project_name_string"
android:targetPackage="org.godotengine.godot" />
</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@

package org.godotengine.godot;

import android.content.ComponentName;
import org.godotengine.godot.utils.ProcessPhoenix;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
Expand Down Expand Up @@ -71,34 +72,29 @@ public void onCreate(Bundle savedInstanceState) {

@Override
public void onDestroy() {
Log.v(TAG, "Destroying Godot app...");
super.onDestroy();
onGodotForceQuit(godotFragment);
}

@Override
public final void onGodotForceQuit(Godot instance) {
if (instance == godotFragment) {
System.exit(0);
Log.v(TAG, "Force quitting Godot instance");
ProcessPhoenix.forceQuit(this);
}
}

@Override
public final void onGodotRestartRequested(Godot instance) {
if (instance == godotFragment) {
// HACK:
//
// Currently it's very hard to properly deinitialize Godot on Android to restart the game
// It's very hard to properly de-initialize Godot on Android to restart the game
// from scratch. Therefore, we need to kill the whole app process and relaunch it.
//
// Restarting only the activity, wouldn't be enough unless it did proper cleanup (including
// releasing and reloading native libs or resetting their state somehow and clearing statics).
//
// Using instrumentation is a way of making the whole app process restart, because Android
// will kill any process of the same package which was already running.
//
Bundle args = new Bundle();
args.putParcelable("intent", getIntent());
startInstrumentation(new ComponentName(this, GodotInstrumentation.class), null, args);
Log.v(TAG, "Restarting Godot instance...");
ProcessPhoenix.triggerRebirth(this);
}
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// clang-format off

/* Third-party library.
* Upstream: https://github.com/JakeWharton/ProcessPhoenix
* Commit: 12cb27c2cc9c3fc555e97f2db89e571667de82c4
*/

/*
* Copyright (C) 2014 Jake Wharton
*
* 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 org.godotengine.godot.utils;

import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Process;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;

/**
* Process Phoenix facilitates restarting your application process. This should only be used for
* things like fundamental state changes in your debug builds (e.g., changing from staging to
* production).
* <p>
* Trigger process recreation by calling {@link #triggerRebirth} with a {@link Context} instance.
*/
public final class ProcessPhoenix extends Activity {
private static final String KEY_RESTART_INTENTS = "phoenix_restart_intents";
private static final String KEY_MAIN_PROCESS_PID = "phoenix_main_process_pid";

/**
* Call to restart the application process using the {@linkplain Intent#CATEGORY_DEFAULT default}
* activity as an intent.
* <p>
* Behavior of the current process after invoking this method is undefined.
*/
public static void triggerRebirth(Context context) {
triggerRebirth(context, getRestartIntent(context));
}

/**
* Call to restart the application process using the specified intents.
* <p>
* Behavior of the current process after invoking this method is undefined.
*/
public static void triggerRebirth(Context context, Intent... nextIntents) {
if (nextIntents.length < 1) {
throw new IllegalArgumentException("intents cannot be empty");
}
// create a new task for the first activity.
nextIntents[0].addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);

Intent intent = new Intent(context, ProcessPhoenix.class);
intent.addFlags(FLAG_ACTIVITY_NEW_TASK); // In case we are called with non-Activity context.
intent.putParcelableArrayListExtra(KEY_RESTART_INTENTS, new ArrayList<>(Arrays.asList(nextIntents)));
intent.putExtra(KEY_MAIN_PROCESS_PID, Process.myPid());
context.startActivity(intent);
}

// -- GODOT start --
/**
* Finish the activity and kill its process
*/
public static void forceQuit(Activity activity) {
forceQuit(activity, Process.myPid());
}

/**
* Finish the activity and kill its process
* @param activity
* @param pid
*/
public static void forceQuit(Activity activity, int pid) {
Process.killProcess(pid); // Kill original main process
activity.finish();
Runtime.getRuntime().exit(0); // Kill kill kill!
}

// -- GODOT end --

private static Intent getRestartIntent(Context context) {
String packageName = context.getPackageName();
Intent defaultIntent = context.getPackageManager().getLaunchIntentForPackage(packageName);
if (defaultIntent != null) {
return defaultIntent;
}

throw new IllegalStateException("Unable to determine default activity for "
+ packageName
+ ". Does an activity specify the DEFAULT category in its intent filter?");
}

@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// -- GODOT start --
ArrayList<Intent> intents = getIntent().getParcelableArrayListExtra(KEY_RESTART_INTENTS);
startActivities(intents.toArray(new Intent[intents.size()]));
forceQuit(this, getIntent().getIntExtra(KEY_MAIN_PROCESS_PID, -1));
// -- GODOT end --
}

/**
* Checks if the current process is a temporary Phoenix Process.
* This can be used to avoid initialisation of unused resources or to prevent running code that
* is not multi-process ready.
*
* @return true if the current process is a temporary Phoenix Process
*/
public static boolean isPhoenixProcess(Context context) {
int currentPid = Process.myPid();
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> runningProcesses = manager.getRunningAppProcesses();
if (runningProcesses != null) {
for (ActivityManager.RunningAppProcessInfo processInfo : runningProcesses) {
if (processInfo.pid == currentPid && processInfo.processName.endsWith(":phoenix")) {
return true;
}
}
}
return false;
}
}

0 comments on commit e0ddaf7

Please sign in to comment.